Commit dcc56215 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Implement GL_EXT_multisampled_render_to_texture

This change allows the use of resolve attachments in the Vulkan backend. GL_EXT_multisampled_render_to_texture is implemented using this feature. The infrastructure for specifying resolve attachments is designed with eventual support for GL_EXT_multisampled_render_to_texture2 in mind as well as optimizations to glBlitFramebuffer() and multisampled backbuffers. Proper support for glRenderbufferStorageMultisampledEXT is still missing from this change. All tests use this for the depth/stencil attachment and don't read back the data. Currently, the depth/stencil attachment is created as a normal multisampled image. Bug: angleproject:4836 Change-Id: I110a7f63312ae61a657b6094adf7d97c92bd5843 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2304170 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com>
parent d56ed7cf
......@@ -499,6 +499,9 @@ std::size_t BitSetT<N, BitsT, ParamT>::Iterator::getNextBit()
}
template <size_t N>
using BitSet8 = BitSetT<N, uint8_t>;
template <size_t N>
using BitSet32 = BitSetT<N, uint32_t>;
template <size_t N>
......
......@@ -2415,6 +2415,9 @@ void ContextVk::optimizeRenderPassForPresent(VkFramebuffer framebufferHandle)
// Use finalLayout instead of extra barrier for layout change to present
vk::ImageHelper &image = color0RenderTarget->getImageForWrite();
image.setCurrentImageLayout(vk::ImageLayout::Present);
// TODO(syoussefi): We currently don't store the layout of the resolve attachments, so once
// multisampled backbuffers are optimized to use resolve attachments, this information needs to
// be stored somewhere. http://anglebug.com/4836
mRenderPassCommands->updateRenderPassAttachmentFinalLayout(0, image.getCurrentImageLayout());
}
......
......@@ -152,6 +152,11 @@ class FramebufferVk : public FramebufferImpl
const UtilsVk::BlitResolveParameters &params,
vk::ImageHelper *srcImage);
// If resolve attachments are used, some use cases require that the multisampled image (whose
// data is normally discarded) take its data from the resolve attachment.
angle::Result copyResolveToMultisampedAttachment(ContextVk *contextVk,
RenderTargetVk *colorRenderTarget);
angle::Result getFramebuffer(ContextVk *contextVk, vk::Framebuffer **framebufferOut);
angle::Result clearImpl(const gl::Context *context,
......
......@@ -44,11 +44,15 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
void init(vk::ImageHelper *image,
vk::ImageViewHelper *imageViews,
vk::ImageHelper *resolveImage,
vk::ImageViewHelper *resolveImageViews,
uint32_t levelIndexGL,
uint32_t layerIndex);
uint32_t layerIndex,
bool isImageTransient);
void reset();
// This returns the serial from underlying ImageViewHelper, first assigning one if required
ImageViewSerial getAssignImageViewSerial(ContextVk *contextVk) const;
ImageViewSerial getAssignResolveImageViewSerial(ContextVk *contextVk) const;
// Note: RenderTargets should be called in order, with the depth/stencil onRender last.
angle::Result onColorDraw(ContextVk *contextVk);
......@@ -57,11 +61,16 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
vk::ImageHelper &getImageForRenderPass();
const vk::ImageHelper &getImageForRenderPass() const;
vk::ImageHelper &getResolveImageForRenderPass();
const vk::ImageHelper &getResolveImageForRenderPass() const;
vk::ImageHelper &getImageForCopy() const;
vk::ImageHelper &getImageForWrite() const;
// For cube maps we use single-level single-layer 2D array views.
angle::Result getImageView(ContextVk *contextVk, const vk::ImageView **imageViewOut) const;
angle::Result getResolveImageView(ContextVk *contextVk,
const vk::ImageView **imageViewOut) const;
// For 3D textures, the 2D view created for render target is invalid to read from. The
// following will return a view to the whole image (for all types, including 3D and 2DArray).
......@@ -77,7 +86,10 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
// Special mutator for Surface RenderTargets. Allows the Framebuffer to keep a single
// RenderTargetVk pointer.
void updateSwapchainImage(vk::ImageHelper *image, vk::ImageViewHelper *imageViews);
void updateSwapchainImage(vk::ImageHelper *image,
vk::ImageViewHelper *imageViews,
vk::ImageHelper *resolveImage,
vk::ImageViewHelper *resolveImageViews);
angle::Result flushStagedUpdates(ContextVk *contextVk,
vk::ClearValuesArray *deferredClears,
......@@ -90,15 +102,77 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
// as loadOp of the render target in the next renderpass.
void invalidateEntireContent() { mContentDefined = false; }
// See the description of mIsImageTransient for details of how the following two can
// interact.
bool hasResolveAttachment() const { return mResolveImage != nullptr; }
bool isImageTransient() const { return mIsImageTransient; }
private:
angle::Result getImageViewImpl(ContextVk *contextVk,
const vk::ImageHelper &image,
vk::ImageViewHelper *imageViews,
const vk::ImageView **imageViewOut) const;
ImageViewSerial getAssignViewSerialImpl(ContextVk *contextVk,
vk::ImageViewHelper *imageViews) const;
bool isResolveImageOwnerOfData() const;
// The color or depth/stencil attachment of the framebuffer and its view.
vk::ImageHelper *mImage;
vk::ImageViewHelper *mImageViews;
// If present, this is the corresponding resolve attachment and its view. This is used to
// implement GL_EXT_multisampled_render_to_texture, so while the rendering is done on mImage
// during the renderpass, the resolved image is the one that actually holds the data. This
// means that data uploads and blit are done on this image, copies are done out of this image
// etc. This means that if there is no clear, and hasDefinedContent(), the contents of
// mResolveImage must be copied to mImage since the loadOp of the attachment must be set to
// LOAD.
vk::ImageHelper *mResolveImage;
vk::ImageViewHelper *mResolveImageViews;
// Which subresource of the image is used as render target.
uint32_t mLevelIndexGL;
uint32_t mLayerIndex;
// Whether the render target has been invalidated. If so, DONT_CARE is used instead of LOAD for
// loadOp of this attachment.
bool mContentDefined;
// If resolve attachment exists, |mIsImageTransient| is true if the multisampled results need to
// be discarded.
//
// - GL_EXT_multisampled_render_to_texture: this is true for render targets created for this
// extension's usage. Only color attachments use this optimization at the moment.
// - GL_EXT_multisampled_render_to_texture2: this is true for depth/stencil textures per this
// extension, even though a resolve attachment is not even provided.
// - Multisampled swapchain: TODO(syoussefi) this is true for the multisampled color attachment.
// http://anglebug.com/4836
// - glBlitFramebuffer optimization: TODO(timvp) this is **false** in this case, as the
// multisampled attachment and the resolve attachments belong to independent framebuffers.
// http://anglebug.com/4753
//
// Based on the above, we have:
//
// mResolveImage == nullptr | mResolveImage != nullptr
// |
// Normal rendering | Blit optimization
// !IsTransient No resolve | Resolve
// storeOp = STORE | storeOp = STORE
// Owner of data: mImage | Owner of data: mImage
// |
// ---------------------------------------------+---------------------------------------
// |
// EXT_multisampled_render_to_texture2 | GL_EXT_multisampled_render_to_texture
// | or multisampled Swapchain optimization
// IsTransient No resolve | Resolve
// storeOp = DONT_CARE | storeOp = DONT_CARE
// Owner of data: None (not stored) | Owner of data: mResolveImage
//
// In the above, storeOp of the resolve attachment is always STORE. if !IsTransient, storeOp is
// affected by a framebuffer invalidate call.
bool mIsImageTransient;
};
// A vector of rendertargets
......
......@@ -63,6 +63,11 @@ angle::Result RenderbufferVk::setStorageImpl(const gl::Context *context,
}
}
// TODO(syoussefi): if glRenderbufferStorageMultisampleEXT, need to create the image as
// single-sampled and have a multisampled image for intermediate results. Currently, tests
// seem to only use this for depth/stencil buffers and don't attempt to read from it. This
// needs to be fixed and tests added. http://anglebug.com/4836
if ((mImage == nullptr || !mImage->valid()) && (width != 0 && height != 0))
{
if (mImage == nullptr)
......@@ -87,7 +92,7 @@ angle::Result RenderbufferVk::setStorageImpl(const gl::Context *context,
VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
ANGLE_TRY(mImage->initMemory(contextVk, renderer->getMemoryProperties(), flags));
mRenderTarget.init(mImage, &mImageViews, 0, 0);
mRenderTarget.init(mImage, &mImageViews, nullptr, nullptr, 0, 0, false);
}
return angle::Result::Continue;
......@@ -146,7 +151,8 @@ angle::Result RenderbufferVk::setStorageEGLImageTarget(const gl::Context *contex
imageVk->getImage()->getSamples());
}
mRenderTarget.init(mImage, &mImageViews, imageVk->getImageLevel(), imageVk->getImageLayer());
mRenderTarget.init(mImage, &mImageViews, nullptr, nullptr, imageVk->getImageLevel(),
imageVk->getImageLayer(), false);
return angle::Result::Continue;
}
......
......@@ -126,6 +126,8 @@ angle::Result SurfaceVk::getAttachmentRenderTarget(const gl::Context *context,
GLsizei samples,
FramebufferAttachmentRenderTarget **rtOut)
{
ASSERT(samples == 0);
if (binding == GL_BACK)
{
*rtOut = &mColorRenderTarget;
......@@ -233,9 +235,11 @@ OffscreenSurfaceVk::OffscreenSurfaceVk(const egl::SurfaceState &surfaceState)
mColorAttachment(this),
mDepthStencilAttachment(this)
{
mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageViews, 0, 0);
mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageViews, nullptr, nullptr,
0, 0, false);
mDepthStencilRenderTarget.init(&mDepthStencilAttachment.image,
&mDepthStencilAttachment.imageViews, 0, 0);
&mDepthStencilAttachment.imageViews, nullptr, nullptr, 0, 0,
false);
}
OffscreenSurfaceVk::~OffscreenSurfaceVk() {}
......@@ -261,7 +265,8 @@ angle::Result OffscreenSurfaceVk::initializeImpl(DisplayVk *displayVk)
{
ANGLE_TRY(mColorAttachment.initialize(
displayVk, mWidth, mHeight, renderer->getFormat(config->renderTargetFormat), samples));
mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageViews, 0, 0);
mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageViews, nullptr,
nullptr, 0, 0, false);
}
if (config->depthStencilFormat != GL_NONE)
......@@ -269,7 +274,8 @@ angle::Result OffscreenSurfaceVk::initializeImpl(DisplayVk *displayVk)
ANGLE_TRY(mDepthStencilAttachment.initialize(
displayVk, mWidth, mHeight, renderer->getFormat(config->depthStencilFormat), samples));
mDepthStencilRenderTarget.init(&mDepthStencilAttachment.image,
&mDepthStencilAttachment.imageViews, 0, 0);
&mDepthStencilAttachment.imageViews, nullptr, nullptr, 0, 0,
false);
}
return angle::Result::Continue;
......@@ -471,8 +477,9 @@ WindowSurfaceVk::WindowSurfaceVk(const egl::SurfaceState &surfaceState, EGLNativ
{
// Initialize the color render target with the multisampled targets. If not multisampled, the
// render target will be updated to refer to a swapchain image on every acquire.
mColorRenderTarget.init(&mColorImageMS, &mColorImageMSViews, 0, 0);
mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageViews, 0, 0);
mColorRenderTarget.init(&mColorImageMS, &mColorImageMSViews, nullptr, nullptr, 0, 0, false);
mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageViews, nullptr, nullptr,
0, 0, false);
mDepthStencilImageBinding.bind(&mDepthStencilImage);
mColorImageMSBinding.bind(&mColorImageMS);
}
......@@ -930,7 +937,7 @@ angle::Result WindowSurfaceVk::createSwapChain(vk::Context *context,
// Initialize the color render target with the multisampled targets. If not multisampled,
// the render target will be updated to refer to a swapchain image on every acquire.
mColorRenderTarget.init(&mColorImageMS, &mColorImageMSViews, 0, 0);
mColorRenderTarget.init(&mColorImageMS, &mColorImageMSViews, nullptr, nullptr, 0, 0, false);
}
ANGLE_TRY(resizeSwapchainImages(context, imageCount));
......@@ -953,7 +960,8 @@ angle::Result WindowSurfaceVk::createSwapChain(vk::Context *context,
ANGLE_TRY(mDepthStencilImage.initMemory(context, renderer->getMemoryProperties(),
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageViews, 0, 0);
mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageViews, nullptr,
nullptr, 0, 0, false);
// We will need to pass depth/stencil image views to the RenderTargetVk in the future.
}
......@@ -1359,7 +1367,7 @@ VkResult WindowSurfaceVk::nextSwapchainImage(vk::Context *context)
// multisampling, as the swapchain image is essentially unused until then.
if (!mColorImageMS.valid())
{
mColorRenderTarget.updateSwapchainImage(&image.image, &image.imageViews);
mColorRenderTarget.updateSwapchainImage(&image.image, &image.imageViews, nullptr, nullptr);
}
// Notify the owning framebuffer there may be staged updates.
......
......@@ -1828,6 +1828,54 @@ angle::Result TextureVk::getAttachmentRenderTarget(const gl::Context *context,
levelCount));
}
// If samples > 1 here, we have a singlesampled texture that's being multisampled rendered to.
// In this case, create a multisampled image that is otherwise identical to the single sampled
// image. That multisampled image is used as color or depth/stencil attachment, while the
// original image is used as the resolve attachment.
if (samples > 1 && !mMultisampledImage.valid())
{
ASSERT(mState.getBaseLevelDesc().samples <= 1);
// The image is used as either color or depth/stencil attachment. Additionally, its memory
// is lazily allocated as the contents are discarded at the end of the renderpass and with
// tiling GPUs no actual backing memory is required.
//
// Note that the Vulkan image is created with or without
// VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT based on whether the memory that will be used to
// create the image would have VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT. TRANSIENT is
// provided if there is any memory that supports LAZILY_ALLOCATED. However, based on actual
// image requirements, such a memory may not be suitable for the image. We don't support
// such a case, which will result in the |initMemory| call below failing.
const vk::MemoryProperties &memoryProperties =
contextVk->getRenderer()->getMemoryProperties();
const bool hasLazilyAllocatedMemory = memoryProperties.hasLazilyAllocatedMemory();
const VkImageUsageFlags kMultisampledUsageFlags =
(hasLazilyAllocatedMemory ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : 0) |
(mImage->getAspectFlags() == VK_IMAGE_ASPECT_COLOR_BIT
? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
: VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
constexpr VkImageCreateFlags kMultisampledCreateFlags = 0;
ANGLE_TRY(mMultisampledImage.initExternal(
contextVk, mState.getType(), mImage->getExtents(), mImage->getFormat(), samples,
kMultisampledUsageFlags, kMultisampledCreateFlags, rx::vk::ImageLayout::Undefined,
nullptr, mImage->getBaseLevel(), mImage->getMaxLevel(), mImage->getLevelCount(),
mImage->getLayerCount()));
const VkMemoryPropertyFlags kMultisampledMemoryFlags =
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
(hasLazilyAllocatedMemory ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0);
ANGLE_TRY(mMultisampledImage.initMemory(
contextVk, contextVk->getRenderer()->getMemoryProperties(), kMultisampledMemoryFlags));
// Remove the emulated format clear from the multisampled image if any. There is one
// already staged on the resolve image if needed.
mMultisampledImage.removeStagedUpdates(contextVk, mMultisampledImage.getBaseLevel(),
mMultisampledImage.getMaxLevel());
}
// Don't flush staged updates here. We'll handle that in FramebufferVk so it can defer clears.
GLuint layerIndex = 0, layerCount = 0;
......@@ -1902,8 +1950,35 @@ angle::Result TextureVk::initRenderTargets(ContextVk *contextVk,
for (uint32_t layerIndex = 0; layerIndex < layerCount; ++layerIndex)
{
vk::ImageHelper *drawImage = mImage;
vk::ImageViewHelper *drawImageViews = &mImageViews;
vk::ImageHelper *resolveImage = nullptr;
vk::ImageViewHelper *resolveImageViews = nullptr;
// If multisampled render to texture, use the multisampled image as draw image instead, and
// resolve into the texture's image automatically.
if (mMultisampledImage.valid())
{
drawImage = &mMultisampledImage;
drawImageViews = &mMultisampledImageViews;
resolveImage = mImage;
resolveImageViews = &mImageViews;
// If the texture is depth/stencil, GL_EXT_multisampled_render_to_texture2 explicitly
// indicates that there is no need for the image to be resolved. In that case, don't
// specify the resolve image. Note that the multisampled image's data is discarded
// nevertheless per this spec.
if (mImage->getAspectFlags() != VK_IMAGE_ASPECT_COLOR_BIT)
{
resolveImage = nullptr;
resolveImageViews = nullptr;
}
}
mRenderTargets[levelIndex][layerIndex].init(
mImage, &mImageViews, getNativeImageLevel(levelIndex), getNativeImageLayer(layerIndex));
drawImage, drawImageViews, resolveImage, resolveImageViews,
getNativeImageLevel(levelIndex), getNativeImageLayer(layerIndex),
mMultisampledImage.valid());
}
return angle::Result::Continue;
}
......@@ -2263,8 +2338,13 @@ void TextureVk::releaseImage(ContextVk *contextVk)
mImage = nullptr;
}
}
if (mMultisampledImage.valid())
{
mMultisampledImage.releaseImage(renderer);
}
mImageViews.release(renderer);
mMultisampledImageViews.release(renderer);
for (RenderTargetVector &renderTargetLevels : mRenderTargets)
{
......
......@@ -420,6 +420,11 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
// reallocated independently of |mImage| on state changes.
vk::ImageViewHelper mImageViews;
// If multisampled rendering to texture, an intermediate multisampled image is created for use
// as renderpass color or depth/stencil attachment.
vk::ImageHelper mMultisampledImage;
vk::ImageViewHelper mMultisampledImageViews;
// |mSampler| contains the relevant Vulkan sampler states representing the OpenGL Texture
// sampling states for the Texture.
vk::BindingPointer<vk::Sampler> mSampler;
......
......@@ -1790,13 +1790,14 @@ angle::Result UtilsVk::copyImage(ContextVk *contextVk,
renderPassDesc.setSamples(dest->getSamples());
renderPassDesc.packColorAttachment(0, dstFormat.intendedFormatID);
// Multisampled copy is not yet supported.
ASSERT(src->getSamples() == 1 && dest->getSamples() == 1);
// Copy from multisampled image is not supported.
ASSERT(src->getSamples() == 1);
vk::GraphicsPipelineDesc pipelineDesc;
pipelineDesc.initDefaults();
pipelineDesc.setCullMode(VK_CULL_MODE_NONE);
pipelineDesc.setRenderPassDesc(renderPassDesc);
pipelineDesc.setRasterizationSamples(dest->getSamples());
gl::Rectangle renderArea;
renderArea.x = params.destOffset[0];
......@@ -1854,6 +1855,9 @@ angle::Result UtilsVk::copyImage(ContextVk *contextVk,
commandBuffer->draw(6, 0);
descriptorPoolBinding.reset();
// Close the render pass for this temporary framebuffer.
ANGLE_TRY(contextVk->endRenderPass());
return angle::Result::Continue;
}
......
......@@ -115,7 +115,8 @@ angle::Result IOSurfaceSurfaceVkMac::initializeImpl(DisplayVk *displayVk)
displayVk, mWidth, mHeight,
renderer->getFormat(kIOSurfaceFormats[mFormatIndex].nativeSizedInternalFormat), samples,
IOSurfaceGetBaseAddressOfPlane(mIOSurface, mPlane)));
mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageViews, 0, 0);
mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageViews, nullptr, nullptr,
0, 0, false);
return angle::Result::Continue;
}
......
......@@ -35,8 +35,11 @@ namespace
// In the FramebufferDesc object:
// - Depth/stencil serial is at index 0
// - Color serials are at indices [1:gl::IMPLEMENTATION_MAX_DRAW_BUFFERS]
constexpr size_t kFramebufferDescDepthStencilIndex = 0;
constexpr size_t kFramebufferDescColorIndexOffset = 1;
// - Resolve attachments are at indices [gl::IMPLEMENTATION_MAX_DRAW_BUFFERS+1,
// gl::IMPLEMENTATION_MAX_DRAW_BUFFERS*2]
constexpr size_t kFramebufferDescDepthStencilIndex = 0;
constexpr size_t kFramebufferDescColorIndexOffset = 1;
constexpr size_t kFramebufferDescResolveIndexOffset = gl::IMPLEMENTATION_MAX_DRAW_BUFFERS + 1;
uint8_t PackGLBlendOp(GLenum blendOp)
{
......@@ -169,6 +172,31 @@ void UnpackAttachmentDesc(VkAttachmentDescription *desc,
ConvertImageLayoutToVkImageLayout(static_cast<ImageLayout>(ops.finalLayout));
}
void UnpackResolveAttachmentDesc(VkAttachmentDescription *desc, const vk::Format &format)
{
// We would only need this flag for duplicated attachments. Apply it conservatively. In
// practice it's unlikely any application would use the same image as multiple resolve
// attachments simultaneously, so this flag can likely be removed without any issue if it incurs
// a performance penalty.
desc->flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT;
desc->format = format.vkImageFormat;
// We don't support depth/stencil resolve attachments currently.
const angle::Format &angleFormat = format.actualImageFormat();
ASSERT(angleFormat.depthBits == 0 && angleFormat.stencilBits == 0);
// Resolve attachments always have a sample count of 1. For simplicity, unlikely cases where
// the resolve framebuffer is immediately invalidated or cleared are ignored. Therefore, loadOp
// and storeOp can be fixed to DONT_CARE and STORE respectively.
desc->samples = VK_SAMPLE_COUNT_1_BIT;
desc->loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc->storeOp = VK_ATTACHMENT_STORE_OP_STORE;
desc->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc->stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
desc->initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
desc->finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
}
void UnpackStencilState(const vk::PackedStencilOpState &packedState,
uint8_t stencilReference,
VkStencilOpState *stateOut)
......@@ -218,8 +246,12 @@ angle::Result InitializeRenderPassFromDesc(vk::Context *context,
// Unpack the packed and split representation into the format required by Vulkan.
gl::DrawBuffersVector<VkAttachmentReference> colorAttachmentRefs;
VkAttachmentReference depthStencilAttachmentRef = kUnusedAttachment;
gl::AttachmentArray<VkAttachmentDescription> attachmentDescs;
gl::DrawBuffersVector<VkAttachmentReference> colorResolveAttachmentRefs;
// The list of attachments includes all non-resolve and resolve attachments.
FramebufferAttachmentArray<VkAttachmentDescription> attachmentDescs;
// Pack color attachments
uint32_t colorAttachmentCount = 0;
uint32_t attachmentCount = 0;
for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL)
......@@ -256,6 +288,7 @@ angle::Result InitializeRenderPassFromDesc(vk::Context *context,
++attachmentCount;
}
// Pack depth/stencil attachment, if any
if (desc.hasDepthStencilAttachment())
{
uint32_t depthStencilIndex = static_cast<uint32_t>(desc.depthStencilAttachmentIndex());
......@@ -274,6 +307,32 @@ angle::Result InitializeRenderPassFromDesc(vk::Context *context,
++attachmentCount;
}
// Pack color resolve attachments
const uint32_t nonResolveAttachmentCount = attachmentCount;
for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL)
{
if (!desc.hasColorResolveAttachment(colorIndexGL))
{
colorResolveAttachmentRefs.push_back(kUnusedAttachment);
continue;
}
ASSERT(desc.isColorAttachmentEnabled(colorIndexGL));
uint32_t colorResolveIndexVk = attachmentCount;
const vk::Format &format = context->getRenderer()->getFormat(desc[colorIndexGL]);
VkAttachmentReference colorRef;
colorRef.attachment = colorResolveIndexVk;
colorRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
colorResolveAttachmentRefs.push_back(colorRef);
UnpackResolveAttachmentDesc(&attachmentDescs[colorResolveIndexVk], format);
++attachmentCount;
}
VkSubpassDescription subpassDesc = {};
subpassDesc.flags = 0;
......@@ -282,7 +341,8 @@ angle::Result InitializeRenderPassFromDesc(vk::Context *context,
subpassDesc.pInputAttachments = nullptr;
subpassDesc.colorAttachmentCount = static_cast<uint32_t>(colorAttachmentRefs.size());
subpassDesc.pColorAttachments = colorAttachmentRefs.data();
subpassDesc.pResolveAttachments = nullptr;
subpassDesc.pResolveAttachments =
attachmentCount > nonResolveAttachmentCount ? colorResolveAttachmentRefs.data() : nullptr;
subpassDesc.pDepthStencilAttachment =
(depthStencilAttachmentRef.attachment != VK_ATTACHMENT_UNUSED ? &depthStencilAttachmentRef
: nullptr);
......@@ -453,6 +513,13 @@ void RenderPassDesc::packDepthStencilAttachment(angle::FormatID formatID)
mHasDepthStencilAttachment = true;
}
void RenderPassDesc::packColorResolveAttachment(size_t colorIndexGL)
{
ASSERT(isColorAttachmentEnabled(colorIndexGL));
ASSERT(mLogSamples > 0);
mColorResolveAttachmentMask.set(colorIndexGL);
}
RenderPassDesc &RenderPassDesc::operator=(const RenderPassDesc &other)
{
memcpy(this, &other, sizeof(RenderPassDesc));
......@@ -480,7 +547,7 @@ size_t RenderPassDesc::attachmentCount() const
// Note that there are no gaps in depth/stencil attachments. In fact there is a maximum of 1 of
// it.
return colorAttachmentCount + mHasDepthStencilAttachment;
return colorAttachmentCount + mColorResolveAttachmentMask.count() + mHasDepthStencilAttachment;
}
bool operator==(const RenderPassDesc &lhs, const RenderPassDesc &rhs)
......@@ -1748,6 +1815,11 @@ void FramebufferDesc::updateColor(uint32_t index, ImageViewSerial serial)
update(kFramebufferDescColorIndexOffset + index, serial);
}
void FramebufferDesc::updateColorResolve(uint32_t index, ImageViewSerial serial)
{
update(kFramebufferDescResolveIndexOffset + index, serial);
}
void FramebufferDesc::updateDepthStencil(ImageViewSerial serial)
{
update(kFramebufferDescDepthStencilIndex, serial);
......
......@@ -75,6 +75,8 @@ class alignas(4) RenderPassDesc final
// The caller must pack the depth/stencil attachment last, which is packed right after the color
// attachments (including gaps), i.e. with an index starting from |colorAttachmentRange()|.
void packDepthStencilAttachment(angle::FormatID angleFormatID);
// Indicate that a color attachment should have a corresponding resolve attachment.
void packColorResolveAttachment(size_t colorIndexGL);
size_t hash() const;
......@@ -84,6 +86,10 @@ class alignas(4) RenderPassDesc final
bool isColorAttachmentEnabled(size_t colorIndexGL) const;
bool hasDepthStencilAttachment() const { return mHasDepthStencilAttachment; }
bool hasColorResolveAttachment(size_t colorIndexGL) const
{
return mColorResolveAttachmentMask.test(colorIndexGL);
}
// Get the number of attachments in the Vulkan render pass, i.e. after removing disabled
// color attachments.
......@@ -104,8 +110,15 @@ class alignas(4) RenderPassDesc final
uint8_t mLogSamples : 3;
uint8_t mColorAttachmentRange : 4;
uint8_t mHasDepthStencilAttachment : 1;
// Temporary padding for upcoming support for resolve attachments.
ANGLE_MAYBE_UNUSED uint8_t pad;
// Whether each color attachment has a corresponding resolve attachment. Color resolve
// attachments can be used to optimize resolve through glBlitFramebuffer() as well as support
// GL_EXT_multisampled_render_to_texture and GL_EXT_multisampled_render_to_texture2.
//
// Note that depth/stencil resolve attachments require VK_KHR_depth_stencil_resolve which is
// currently not well supported, so ANGLE always takes a fallback path for them. When a resolve
// path is implemented for depth/stencil attachments, another bit must be made free
// (mAttachmentFormats is one element too large, so there are 8 bits there to take).
angle::BitSet8<gl::IMPLEMENTATION_MAX_DRAW_BUFFERS> mColorResolveAttachmentMask;
// Color attachment formats are stored with their GL attachment indices. The depth/stencil
// attachment formats follow the last enabled color attachment. When creating a render pass,
// the disabled attachments are removed and the resulting attachments are packed.
......@@ -126,6 +139,8 @@ class alignas(4) RenderPassDesc final
// - Subpass attachment 2 -> VK_ATTACHMENT_UNUSED
// - Subpass attachment 3 -> Renderpass attachment 1
//
// The resolve attachments are packed after the non-resolve attachments. They use the same
// formats, so they are not specified in this array.
gl::AttachmentArray<uint8_t> mAttachmentFormats;
};
......@@ -837,8 +852,11 @@ class UniformsAndXfbDesc
std::array<BufferSerial, kMaxBufferCount> mBufferSerials;
};
// This is IMPLEMENTATION_MAX_DRAW_BUFFERS + 1 for DS attachment
constexpr size_t kMaxFramebufferAttachments = gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS;
// There can be a maximum of IMPLEMENTATION_MAX_DRAW_BUFFERS color and resolve attachments, plus one
// depth/stencil attachment.
constexpr size_t kMaxFramebufferAttachments = gl::IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 + 1;
template <typename T>
using FramebufferAttachmentArray = std::array<T, kMaxFramebufferAttachments>;
class FramebufferDesc
{
......@@ -850,6 +868,7 @@ class FramebufferDesc
FramebufferDesc &operator=(const FramebufferDesc &other);
void updateColor(uint32_t index, ImageViewSerial serial);
void updateColorResolve(uint32_t index, ImageViewSerial serial);
void updateDepthStencil(ImageViewSerial serial);
size_t hash() const;
void reset();
......@@ -861,7 +880,7 @@ class FramebufferDesc
private:
void update(uint32_t index, ImageViewSerial serial);
gl::AttachmentArray<ImageViewSerial> mSerials;
FramebufferAttachmentArray<ImageViewSerial> mSerials;
uint32_t mMaxValidSerialIndex;
};
......
......@@ -76,17 +76,18 @@ void RendererVk::ensureCapsInitialized() const
// Enable this for simple buffer readback testing, but some functionality is missing.
// TODO(jmadill): Support full mapBufferRange extension.
mNativeExtensions.mapBufferOES = true;
mNativeExtensions.mapBufferRange = true;
mNativeExtensions.textureStorage = true;
mNativeExtensions.drawBuffers = true;
mNativeExtensions.fragDepth = true;
mNativeExtensions.framebufferBlit = true;
mNativeExtensions.framebufferMultisample = true;
mNativeExtensions.copyTexture = true;
mNativeExtensions.copyTexture3d = true;
mNativeExtensions.copyCompressedTexture = true;
mNativeExtensions.debugMarker = true;
mNativeExtensions.mapBufferOES = true;
mNativeExtensions.mapBufferRange = true;
mNativeExtensions.textureStorage = true;
mNativeExtensions.drawBuffers = true;
mNativeExtensions.fragDepth = true;
mNativeExtensions.framebufferBlit = true;
mNativeExtensions.framebufferMultisample = true;
mNativeExtensions.multisampledRenderToTexture = true;
mNativeExtensions.copyTexture = true;
mNativeExtensions.copyTexture3d = true;
mNativeExtensions.copyCompressedTexture = true;
mNativeExtensions.debugMarker = true;
mNativeExtensions.robustness =
!IsSwiftshader(mPhysicalDeviceProperties.vendorID, mPhysicalDeviceProperties.deviceID) &&
!IsARM(mPhysicalDeviceProperties.vendorID);
......
......@@ -56,6 +56,28 @@ VkImageUsageFlags GetStagingBufferUsageFlags(vk::StagingUsage usage)
}
}
bool FindCompatibleMemory(const VkPhysicalDeviceMemoryProperties &memoryProperties,
const VkMemoryRequirements &memoryRequirements,
VkMemoryPropertyFlags requestedMemoryPropertyFlags,
VkMemoryPropertyFlags *memoryPropertyFlagsOut,
uint32_t *typeIndexOut)
{
for (size_t memoryIndex : angle::BitSet32<32>(memoryRequirements.memoryTypeBits))
{
ASSERT(memoryIndex < memoryProperties.memoryTypeCount);
if ((memoryProperties.memoryTypes[memoryIndex].propertyFlags &
requestedMemoryPropertyFlags) == requestedMemoryPropertyFlags)
{
*memoryPropertyFlagsOut = memoryProperties.memoryTypes[memoryIndex].propertyFlags;
*typeIndexOut = static_cast<uint32_t>(memoryIndex);
return true;
}
}
return false;
}
angle::Result FindAndAllocateCompatibleMemory(vk::Context *context,
const vk::MemoryProperties &memoryProperties,
VkMemoryPropertyFlags requestedMemoryPropertyFlags,
......@@ -334,6 +356,19 @@ void MemoryProperties::destroy()
mMemoryProperties = {};
}
bool MemoryProperties::hasLazilyAllocatedMemory() const
{
for (uint32_t typeIndex = 0; typeIndex < mMemoryProperties.memoryTypeCount; ++typeIndex)
{
const VkMemoryType &memoryType = mMemoryProperties.memoryTypes[typeIndex];
if ((memoryType.propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
{
return true;
}
}
return false;
}
angle::Result MemoryProperties::findCompatibleMemoryIndex(
Context *context,
const VkMemoryRequirements &memoryRequirements,
......@@ -347,39 +382,27 @@ angle::Result MemoryProperties::findCompatibleMemoryIndex(
// Not finding a valid memory pool means an out-of-spec driver, or internal error.
// TODO(jmadill): Determine if it is possible to cache indexes.
// TODO(jmadill): More efficient memory allocation.
for (size_t memoryIndex : angle::BitSet32<32>(memoryRequirements.memoryTypeBits))
if (FindCompatibleMemory(mMemoryProperties, memoryRequirements, requestedMemoryPropertyFlags,
memoryPropertyFlagsOut, typeIndexOut))
{
ASSERT(memoryIndex < mMemoryProperties.memoryTypeCount);
if ((mMemoryProperties.memoryTypes[memoryIndex].propertyFlags &
requestedMemoryPropertyFlags) == requestedMemoryPropertyFlags)
{
*memoryPropertyFlagsOut = mMemoryProperties.memoryTypes[memoryIndex].propertyFlags;
*typeIndexOut = static_cast<uint32_t>(memoryIndex);
return angle::Result::Continue;
}
return angle::Result::Continue;
}
// We did not find a compatible memory type, the Vulkan spec says the following -
// There must be at least one memory type with both the
// VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT and VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
// bits set in its propertyFlags
constexpr VkMemoryPropertyFlags fallbackMemoryPropertyFlags =
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
// If the caller wanted a host visible memory, just return the memory index
// with the fallback memory flags.
// We did not find a compatible memory type. If the caller wanted a host visible memory, just
// return the memory index with fallback, guaranteed, memory flags.
if (requestedMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
{
for (size_t memoryIndex : angle::BitSet32<32>(memoryRequirements.memoryTypeBits))
// The Vulkan spec says the following -
// There must be at least one memory type with both the
// VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT and VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
// bits set in its propertyFlags
constexpr VkMemoryPropertyFlags fallbackMemoryPropertyFlags =
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
if (FindCompatibleMemory(mMemoryProperties, memoryRequirements, fallbackMemoryPropertyFlags,
memoryPropertyFlagsOut, typeIndexOut))
{
if ((mMemoryProperties.memoryTypes[memoryIndex].propertyFlags &
fallbackMemoryPropertyFlags) == fallbackMemoryPropertyFlags)
{
*memoryPropertyFlagsOut = mMemoryProperties.memoryTypes[memoryIndex].propertyFlags;
*typeIndexOut = static_cast<uint32_t>(memoryIndex);
return angle::Result::Continue;
}
return angle::Result::Continue;
}
}
......
......@@ -292,6 +292,7 @@ class MemoryProperties final : angle::NonCopyable
MemoryProperties();
void init(VkPhysicalDevice physicalDevice);
bool hasLazilyAllocatedMemory() const;
angle::Result findCompatibleMemoryIndex(Context *context,
const VkMemoryRequirements &memoryRequirements,
VkMemoryPropertyFlags requestedMemoryPropertyFlags,
......
......@@ -243,3 +243,7 @@
4371 VULKAN ANDROID : dEQP-GLES31.functional.shaders.implicit_conversions.es31.arithmetic.input_before_literal.add.int_to_vec3_vertex = FAIL
4371 VULKAN ANDROID : dEQP-GLES31.functional.shaders.implicit_conversions.es31.arithmetic.input_before_literal.add.int_to_uvec3_vertex = FAIL
4371 SWIFTSHADER : dEQP-GLES31.functional.shaders.implicit_conversions.* = FAIL
// Bug in gl.xml, aliasing glRenderbufferStorageMultisampleEXT and glRenderbufferStorageMultisample
// for GLES. See https://github.com/KhronosGroup/OpenGL-Registry/issues/413
4836 VULKAN : dEQP-GLES31.functional.debug.negative_coverage.get_error.buffer.renderbuffer_storage_multisample = FAIL
......@@ -639,3 +639,7 @@
// Fails on SwANGLE bots
4418 SWIFTSHADER : dEQP-GLES3.functional.negative_api.buffer.framebuffer_texture_layer = FAIL
// Bug in gl.xml, aliasing glRenderbufferStorageMultisampleEXT and glRenderbufferStorageMultisample
// for GLES. See https://github.com/KhronosGroup/OpenGL-Registry/issues/413
4836 VULKAN : dEQP-GLES3.functional.negative_api.buffer.renderbuffer_storage_multisample = FAIL
......@@ -118,3 +118,8 @@
// New failures with latest dEQP roll (2020-04-28)
4593 SWIFTSHADER : KHR-GLES31.core.nearest_edge.offset_left = FAIL
// Bug in gl.xml, aliasing glRenderbufferStorageMultisampleEXT and glRenderbufferStorageMultisample
// for GLES. See https://github.com/KhronosGroup/OpenGL-Registry/issues/413
4836 VULKAN : KHR-GLES31.core.texture_storage_multisample.APIDependencies.renderbuffer_storage_multisample_invalid_samples_argument_for_integer_internalformats = FAIL
4836 VULKAN : KHR-GLES31.core.texture_storage_multisample.APIDependencies.renderbuffer_storage_multisample_invalid_samples_argument_for_noninteger_internalformats = FAIL
......@@ -25,34 +25,15 @@ class CopyTexImageTest : public ANGLETest
void testSetUp() override
{
constexpr char kVS[] =
"precision highp float;\n"
"attribute vec4 position;\n"
"varying vec2 texcoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = position;\n"
" texcoord = (position.xy * 0.5) + 0.5;\n"
"}\n";
constexpr char kFS[] =
"precision highp float;\n"
"uniform sampler2D tex;\n"
"varying vec2 texcoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_FragColor = texture2D(tex, texcoord);\n"
"}\n";
mTextureProgram = CompileProgram(kVS, kFS);
mTextureProgram =
CompileProgram(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
if (mTextureProgram == 0)
{
FAIL() << "shader compilation failed.";
}
mTextureUniformLocation = glGetUniformLocation(mTextureProgram, "tex");
mTextureUniformLocation =
glGetUniformLocation(mTextureProgram, essl1_shaders::Texture2DUniform());
ASSERT_GL_NO_ERROR();
}
......@@ -99,7 +80,7 @@ class CopyTexImageTest : public ANGLETest
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(mTextureUniformLocation, 0);
drawQuad(mTextureProgram, "position", 0.5f);
drawQuad(mTextureProgram, essl1_shaders::PositionAttrib(), 0.5f);
// Expect that the rendered quad has the same color as the source texture
EXPECT_PIXEL_NEAR(xs, ys, data[0], data[1], data[2], data[3], 1.0);
......@@ -891,13 +872,8 @@ TEST_P(CopyTexImageTestES3, 3DSubImageDrawMismatchedTextureTypes)
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST(CopyTexImageTest,
ES2_D3D9(),
ES2_D3D11(),
ANGLE_ALL_TEST_PLATFORMS_ES2,
ES2_D3D11_PRESENT_PATH_FAST(),
ES2_METAL(),
ES2_OPENGL(),
ES2_OPENGLES(),
ES2_VULKAN(),
ES3_VULKAN(),
WithEmulateCopyTexImage2DFromRenderbuffers(ES2_OPENGL()),
WithEmulateCopyTexImage2DFromRenderbuffers(ES2_OPENGLES()));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment