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() ...@@ -499,6 +499,9 @@ std::size_t BitSetT<N, BitsT, ParamT>::Iterator::getNextBit()
} }
template <size_t N> template <size_t N>
using BitSet8 = BitSetT<N, uint8_t>;
template <size_t N>
using BitSet32 = BitSetT<N, uint32_t>; using BitSet32 = BitSetT<N, uint32_t>;
template <size_t N> template <size_t N>
......
...@@ -2415,6 +2415,9 @@ void ContextVk::optimizeRenderPassForPresent(VkFramebuffer framebufferHandle) ...@@ -2415,6 +2415,9 @@ void ContextVk::optimizeRenderPassForPresent(VkFramebuffer framebufferHandle)
// Use finalLayout instead of extra barrier for layout change to present // Use finalLayout instead of extra barrier for layout change to present
vk::ImageHelper &image = color0RenderTarget->getImageForWrite(); vk::ImageHelper &image = color0RenderTarget->getImageForWrite();
image.setCurrentImageLayout(vk::ImageLayout::Present); 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()); mRenderPassCommands->updateRenderPassAttachmentFinalLayout(0, image.getCurrentImageLayout());
} }
......
...@@ -152,6 +152,11 @@ class FramebufferVk : public FramebufferImpl ...@@ -152,6 +152,11 @@ class FramebufferVk : public FramebufferImpl
const UtilsVk::BlitResolveParameters &params, const UtilsVk::BlitResolveParameters &params,
vk::ImageHelper *srcImage); 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 getFramebuffer(ContextVk *contextVk, vk::Framebuffer **framebufferOut);
angle::Result clearImpl(const gl::Context *context, angle::Result clearImpl(const gl::Context *context,
......
...@@ -44,11 +44,15 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget ...@@ -44,11 +44,15 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
void init(vk::ImageHelper *image, void init(vk::ImageHelper *image,
vk::ImageViewHelper *imageViews, vk::ImageViewHelper *imageViews,
vk::ImageHelper *resolveImage,
vk::ImageViewHelper *resolveImageViews,
uint32_t levelIndexGL, uint32_t levelIndexGL,
uint32_t layerIndex); uint32_t layerIndex,
bool isImageTransient);
void reset(); void reset();
// This returns the serial from underlying ImageViewHelper, first assigning one if required // This returns the serial from underlying ImageViewHelper, first assigning one if required
ImageViewSerial getAssignImageViewSerial(ContextVk *contextVk) const; ImageViewSerial getAssignImageViewSerial(ContextVk *contextVk) const;
ImageViewSerial getAssignResolveImageViewSerial(ContextVk *contextVk) const;
// Note: RenderTargets should be called in order, with the depth/stencil onRender last. // Note: RenderTargets should be called in order, with the depth/stencil onRender last.
angle::Result onColorDraw(ContextVk *contextVk); angle::Result onColorDraw(ContextVk *contextVk);
...@@ -57,11 +61,16 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget ...@@ -57,11 +61,16 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
vk::ImageHelper &getImageForRenderPass(); vk::ImageHelper &getImageForRenderPass();
const vk::ImageHelper &getImageForRenderPass() const; const vk::ImageHelper &getImageForRenderPass() const;
vk::ImageHelper &getResolveImageForRenderPass();
const vk::ImageHelper &getResolveImageForRenderPass() const;
vk::ImageHelper &getImageForCopy() const; vk::ImageHelper &getImageForCopy() const;
vk::ImageHelper &getImageForWrite() const; vk::ImageHelper &getImageForWrite() const;
// For cube maps we use single-level single-layer 2D array views. // For cube maps we use single-level single-layer 2D array views.
angle::Result getImageView(ContextVk *contextVk, const vk::ImageView **imageViewOut) const; 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 // 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). // 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 ...@@ -77,7 +86,10 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
// Special mutator for Surface RenderTargets. Allows the Framebuffer to keep a single // Special mutator for Surface RenderTargets. Allows the Framebuffer to keep a single
// RenderTargetVk pointer. // 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, angle::Result flushStagedUpdates(ContextVk *contextVk,
vk::ClearValuesArray *deferredClears, vk::ClearValuesArray *deferredClears,
...@@ -90,15 +102,77 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget ...@@ -90,15 +102,77 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
// as loadOp of the render target in the next renderpass. // as loadOp of the render target in the next renderpass.
void invalidateEntireContent() { mContentDefined = false; } 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: 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::ImageHelper *mImage;
vk::ImageViewHelper *mImageViews; 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 mLevelIndexGL;
uint32_t mLayerIndex; uint32_t mLayerIndex;
// Whether the render target has been invalidated. If so, DONT_CARE is used instead of LOAD for // Whether the render target has been invalidated. If so, DONT_CARE is used instead of LOAD for
// loadOp of this attachment. // loadOp of this attachment.
bool mContentDefined; 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 // A vector of rendertargets
......
...@@ -63,6 +63,11 @@ angle::Result RenderbufferVk::setStorageImpl(const gl::Context *context, ...@@ -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 || !mImage->valid()) && (width != 0 && height != 0))
{ {
if (mImage == nullptr) if (mImage == nullptr)
...@@ -87,7 +92,7 @@ angle::Result RenderbufferVk::setStorageImpl(const gl::Context *context, ...@@ -87,7 +92,7 @@ angle::Result RenderbufferVk::setStorageImpl(const gl::Context *context,
VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
ANGLE_TRY(mImage->initMemory(contextVk, renderer->getMemoryProperties(), flags)); 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; return angle::Result::Continue;
...@@ -146,7 +151,8 @@ angle::Result RenderbufferVk::setStorageEGLImageTarget(const gl::Context *contex ...@@ -146,7 +151,8 @@ angle::Result RenderbufferVk::setStorageEGLImageTarget(const gl::Context *contex
imageVk->getImage()->getSamples()); 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; return angle::Result::Continue;
} }
......
...@@ -126,6 +126,8 @@ angle::Result SurfaceVk::getAttachmentRenderTarget(const gl::Context *context, ...@@ -126,6 +126,8 @@ angle::Result SurfaceVk::getAttachmentRenderTarget(const gl::Context *context,
GLsizei samples, GLsizei samples,
FramebufferAttachmentRenderTarget **rtOut) FramebufferAttachmentRenderTarget **rtOut)
{ {
ASSERT(samples == 0);
if (binding == GL_BACK) if (binding == GL_BACK)
{ {
*rtOut = &mColorRenderTarget; *rtOut = &mColorRenderTarget;
...@@ -233,9 +235,11 @@ OffscreenSurfaceVk::OffscreenSurfaceVk(const egl::SurfaceState &surfaceState) ...@@ -233,9 +235,11 @@ OffscreenSurfaceVk::OffscreenSurfaceVk(const egl::SurfaceState &surfaceState)
mColorAttachment(this), mColorAttachment(this),
mDepthStencilAttachment(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, mDepthStencilRenderTarget.init(&mDepthStencilAttachment.image,
&mDepthStencilAttachment.imageViews, 0, 0); &mDepthStencilAttachment.imageViews, nullptr, nullptr, 0, 0,
false);
} }
OffscreenSurfaceVk::~OffscreenSurfaceVk() {} OffscreenSurfaceVk::~OffscreenSurfaceVk() {}
...@@ -261,7 +265,8 @@ angle::Result OffscreenSurfaceVk::initializeImpl(DisplayVk *displayVk) ...@@ -261,7 +265,8 @@ angle::Result OffscreenSurfaceVk::initializeImpl(DisplayVk *displayVk)
{ {
ANGLE_TRY(mColorAttachment.initialize( ANGLE_TRY(mColorAttachment.initialize(
displayVk, mWidth, mHeight, renderer->getFormat(config->renderTargetFormat), samples)); 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) if (config->depthStencilFormat != GL_NONE)
...@@ -269,7 +274,8 @@ angle::Result OffscreenSurfaceVk::initializeImpl(DisplayVk *displayVk) ...@@ -269,7 +274,8 @@ angle::Result OffscreenSurfaceVk::initializeImpl(DisplayVk *displayVk)
ANGLE_TRY(mDepthStencilAttachment.initialize( ANGLE_TRY(mDepthStencilAttachment.initialize(
displayVk, mWidth, mHeight, renderer->getFormat(config->depthStencilFormat), samples)); displayVk, mWidth, mHeight, renderer->getFormat(config->depthStencilFormat), samples));
mDepthStencilRenderTarget.init(&mDepthStencilAttachment.image, mDepthStencilRenderTarget.init(&mDepthStencilAttachment.image,
&mDepthStencilAttachment.imageViews, 0, 0); &mDepthStencilAttachment.imageViews, nullptr, nullptr, 0, 0,
false);
} }
return angle::Result::Continue; return angle::Result::Continue;
...@@ -471,8 +477,9 @@ WindowSurfaceVk::WindowSurfaceVk(const egl::SurfaceState &surfaceState, EGLNativ ...@@ -471,8 +477,9 @@ WindowSurfaceVk::WindowSurfaceVk(const egl::SurfaceState &surfaceState, EGLNativ
{ {
// Initialize the color render target with the multisampled targets. If not multisampled, the // 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. // 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);
mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageViews, 0, 0); mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageViews, nullptr, nullptr,
0, 0, false);
mDepthStencilImageBinding.bind(&mDepthStencilImage); mDepthStencilImageBinding.bind(&mDepthStencilImage);
mColorImageMSBinding.bind(&mColorImageMS); mColorImageMSBinding.bind(&mColorImageMS);
} }
...@@ -930,7 +937,7 @@ angle::Result WindowSurfaceVk::createSwapChain(vk::Context *context, ...@@ -930,7 +937,7 @@ angle::Result WindowSurfaceVk::createSwapChain(vk::Context *context,
// Initialize the color render target with the multisampled targets. If not multisampled, // 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. // 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)); ANGLE_TRY(resizeSwapchainImages(context, imageCount));
...@@ -953,7 +960,8 @@ angle::Result WindowSurfaceVk::createSwapChain(vk::Context *context, ...@@ -953,7 +960,8 @@ angle::Result WindowSurfaceVk::createSwapChain(vk::Context *context,
ANGLE_TRY(mDepthStencilImage.initMemory(context, renderer->getMemoryProperties(), ANGLE_TRY(mDepthStencilImage.initMemory(context, renderer->getMemoryProperties(),
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); 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. // 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) ...@@ -1359,7 +1367,7 @@ VkResult WindowSurfaceVk::nextSwapchainImage(vk::Context *context)
// multisampling, as the swapchain image is essentially unused until then. // multisampling, as the swapchain image is essentially unused until then.
if (!mColorImageMS.valid()) 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. // Notify the owning framebuffer there may be staged updates.
......
...@@ -1828,6 +1828,54 @@ angle::Result TextureVk::getAttachmentRenderTarget(const gl::Context *context, ...@@ -1828,6 +1828,54 @@ angle::Result TextureVk::getAttachmentRenderTarget(const gl::Context *context,
levelCount)); 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. // Don't flush staged updates here. We'll handle that in FramebufferVk so it can defer clears.
GLuint layerIndex = 0, layerCount = 0; GLuint layerIndex = 0, layerCount = 0;
...@@ -1902,8 +1950,35 @@ angle::Result TextureVk::initRenderTargets(ContextVk *contextVk, ...@@ -1902,8 +1950,35 @@ angle::Result TextureVk::initRenderTargets(ContextVk *contextVk,
for (uint32_t layerIndex = 0; layerIndex < layerCount; ++layerIndex) 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( mRenderTargets[levelIndex][layerIndex].init(
mImage, &mImageViews, getNativeImageLevel(levelIndex), getNativeImageLayer(layerIndex)); drawImage, drawImageViews, resolveImage, resolveImageViews,
getNativeImageLevel(levelIndex), getNativeImageLayer(layerIndex),
mMultisampledImage.valid());
} }
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -2263,8 +2338,13 @@ void TextureVk::releaseImage(ContextVk *contextVk) ...@@ -2263,8 +2338,13 @@ void TextureVk::releaseImage(ContextVk *contextVk)
mImage = nullptr; mImage = nullptr;
} }
} }
if (mMultisampledImage.valid())
{
mMultisampledImage.releaseImage(renderer);
}
mImageViews.release(renderer); mImageViews.release(renderer);
mMultisampledImageViews.release(renderer);
for (RenderTargetVector &renderTargetLevels : mRenderTargets) for (RenderTargetVector &renderTargetLevels : mRenderTargets)
{ {
......
...@@ -420,6 +420,11 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface ...@@ -420,6 +420,11 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
// reallocated independently of |mImage| on state changes. // reallocated independently of |mImage| on state changes.
vk::ImageViewHelper mImageViews; 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 // |mSampler| contains the relevant Vulkan sampler states representing the OpenGL Texture
// sampling states for the Texture. // sampling states for the Texture.
vk::BindingPointer<vk::Sampler> mSampler; vk::BindingPointer<vk::Sampler> mSampler;
......
...@@ -1790,13 +1790,14 @@ angle::Result UtilsVk::copyImage(ContextVk *contextVk, ...@@ -1790,13 +1790,14 @@ angle::Result UtilsVk::copyImage(ContextVk *contextVk,
renderPassDesc.setSamples(dest->getSamples()); renderPassDesc.setSamples(dest->getSamples());
renderPassDesc.packColorAttachment(0, dstFormat.intendedFormatID); renderPassDesc.packColorAttachment(0, dstFormat.intendedFormatID);
// Multisampled copy is not yet supported. // Copy from multisampled image is not supported.
ASSERT(src->getSamples() == 1 && dest->getSamples() == 1); ASSERT(src->getSamples() == 1);
vk::GraphicsPipelineDesc pipelineDesc; vk::GraphicsPipelineDesc pipelineDesc;
pipelineDesc.initDefaults(); pipelineDesc.initDefaults();
pipelineDesc.setCullMode(VK_CULL_MODE_NONE); pipelineDesc.setCullMode(VK_CULL_MODE_NONE);
pipelineDesc.setRenderPassDesc(renderPassDesc); pipelineDesc.setRenderPassDesc(renderPassDesc);
pipelineDesc.setRasterizationSamples(dest->getSamples());
gl::Rectangle renderArea; gl::Rectangle renderArea;
renderArea.x = params.destOffset[0]; renderArea.x = params.destOffset[0];
...@@ -1854,6 +1855,9 @@ angle::Result UtilsVk::copyImage(ContextVk *contextVk, ...@@ -1854,6 +1855,9 @@ angle::Result UtilsVk::copyImage(ContextVk *contextVk,
commandBuffer->draw(6, 0); commandBuffer->draw(6, 0);
descriptorPoolBinding.reset(); descriptorPoolBinding.reset();
// Close the render pass for this temporary framebuffer.
ANGLE_TRY(contextVk->endRenderPass());
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -115,7 +115,8 @@ angle::Result IOSurfaceSurfaceVkMac::initializeImpl(DisplayVk *displayVk) ...@@ -115,7 +115,8 @@ angle::Result IOSurfaceSurfaceVkMac::initializeImpl(DisplayVk *displayVk)
displayVk, mWidth, mHeight, displayVk, mWidth, mHeight,
renderer->getFormat(kIOSurfaceFormats[mFormatIndex].nativeSizedInternalFormat), samples, renderer->getFormat(kIOSurfaceFormats[mFormatIndex].nativeSizedInternalFormat), samples,
IOSurfaceGetBaseAddressOfPlane(mIOSurface, mPlane))); 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; return angle::Result::Continue;
} }
......
...@@ -35,8 +35,11 @@ namespace ...@@ -35,8 +35,11 @@ namespace
// In the FramebufferDesc object: // In the FramebufferDesc object:
// - Depth/stencil serial is at index 0 // - Depth/stencil serial is at index 0
// - Color serials are at indices [1:gl::IMPLEMENTATION_MAX_DRAW_BUFFERS] // - Color serials are at indices [1:gl::IMPLEMENTATION_MAX_DRAW_BUFFERS]
constexpr size_t kFramebufferDescDepthStencilIndex = 0; // - Resolve attachments are at indices [gl::IMPLEMENTATION_MAX_DRAW_BUFFERS+1,
constexpr size_t kFramebufferDescColorIndexOffset = 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) uint8_t PackGLBlendOp(GLenum blendOp)
{ {
...@@ -169,6 +172,31 @@ void UnpackAttachmentDesc(VkAttachmentDescription *desc, ...@@ -169,6 +172,31 @@ void UnpackAttachmentDesc(VkAttachmentDescription *desc,
ConvertImageLayoutToVkImageLayout(static_cast<ImageLayout>(ops.finalLayout)); 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, void UnpackStencilState(const vk::PackedStencilOpState &packedState,
uint8_t stencilReference, uint8_t stencilReference,
VkStencilOpState *stateOut) VkStencilOpState *stateOut)
...@@ -218,8 +246,12 @@ angle::Result InitializeRenderPassFromDesc(vk::Context *context, ...@@ -218,8 +246,12 @@ angle::Result InitializeRenderPassFromDesc(vk::Context *context,
// Unpack the packed and split representation into the format required by Vulkan. // Unpack the packed and split representation into the format required by Vulkan.
gl::DrawBuffersVector<VkAttachmentReference> colorAttachmentRefs; gl::DrawBuffersVector<VkAttachmentReference> colorAttachmentRefs;
VkAttachmentReference depthStencilAttachmentRef = kUnusedAttachment; 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 colorAttachmentCount = 0;
uint32_t attachmentCount = 0; uint32_t attachmentCount = 0;
for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL) for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL)
...@@ -256,6 +288,7 @@ angle::Result InitializeRenderPassFromDesc(vk::Context *context, ...@@ -256,6 +288,7 @@ angle::Result InitializeRenderPassFromDesc(vk::Context *context,
++attachmentCount; ++attachmentCount;
} }
// Pack depth/stencil attachment, if any
if (desc.hasDepthStencilAttachment()) if (desc.hasDepthStencilAttachment())
{ {
uint32_t depthStencilIndex = static_cast<uint32_t>(desc.depthStencilAttachmentIndex()); uint32_t depthStencilIndex = static_cast<uint32_t>(desc.depthStencilAttachmentIndex());
...@@ -274,6 +307,32 @@ angle::Result InitializeRenderPassFromDesc(vk::Context *context, ...@@ -274,6 +307,32 @@ angle::Result InitializeRenderPassFromDesc(vk::Context *context,
++attachmentCount; ++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 = {}; VkSubpassDescription subpassDesc = {};
subpassDesc.flags = 0; subpassDesc.flags = 0;
...@@ -282,7 +341,8 @@ angle::Result InitializeRenderPassFromDesc(vk::Context *context, ...@@ -282,7 +341,8 @@ angle::Result InitializeRenderPassFromDesc(vk::Context *context,
subpassDesc.pInputAttachments = nullptr; subpassDesc.pInputAttachments = nullptr;
subpassDesc.colorAttachmentCount = static_cast<uint32_t>(colorAttachmentRefs.size()); subpassDesc.colorAttachmentCount = static_cast<uint32_t>(colorAttachmentRefs.size());
subpassDesc.pColorAttachments = colorAttachmentRefs.data(); subpassDesc.pColorAttachments = colorAttachmentRefs.data();
subpassDesc.pResolveAttachments = nullptr; subpassDesc.pResolveAttachments =
attachmentCount > nonResolveAttachmentCount ? colorResolveAttachmentRefs.data() : nullptr;
subpassDesc.pDepthStencilAttachment = subpassDesc.pDepthStencilAttachment =
(depthStencilAttachmentRef.attachment != VK_ATTACHMENT_UNUSED ? &depthStencilAttachmentRef (depthStencilAttachmentRef.attachment != VK_ATTACHMENT_UNUSED ? &depthStencilAttachmentRef
: nullptr); : nullptr);
...@@ -453,6 +513,13 @@ void RenderPassDesc::packDepthStencilAttachment(angle::FormatID formatID) ...@@ -453,6 +513,13 @@ void RenderPassDesc::packDepthStencilAttachment(angle::FormatID formatID)
mHasDepthStencilAttachment = true; mHasDepthStencilAttachment = true;
} }
void RenderPassDesc::packColorResolveAttachment(size_t colorIndexGL)
{
ASSERT(isColorAttachmentEnabled(colorIndexGL));
ASSERT(mLogSamples > 0);
mColorResolveAttachmentMask.set(colorIndexGL);
}
RenderPassDesc &RenderPassDesc::operator=(const RenderPassDesc &other) RenderPassDesc &RenderPassDesc::operator=(const RenderPassDesc &other)
{ {
memcpy(this, &other, sizeof(RenderPassDesc)); memcpy(this, &other, sizeof(RenderPassDesc));
...@@ -480,7 +547,7 @@ size_t RenderPassDesc::attachmentCount() const ...@@ -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 // Note that there are no gaps in depth/stencil attachments. In fact there is a maximum of 1 of
// it. // it.
return colorAttachmentCount + mHasDepthStencilAttachment; return colorAttachmentCount + mColorResolveAttachmentMask.count() + mHasDepthStencilAttachment;
} }
bool operator==(const RenderPassDesc &lhs, const RenderPassDesc &rhs) bool operator==(const RenderPassDesc &lhs, const RenderPassDesc &rhs)
...@@ -1748,6 +1815,11 @@ void FramebufferDesc::updateColor(uint32_t index, ImageViewSerial serial) ...@@ -1748,6 +1815,11 @@ void FramebufferDesc::updateColor(uint32_t index, ImageViewSerial serial)
update(kFramebufferDescColorIndexOffset + index, serial); update(kFramebufferDescColorIndexOffset + index, serial);
} }
void FramebufferDesc::updateColorResolve(uint32_t index, ImageViewSerial serial)
{
update(kFramebufferDescResolveIndexOffset + index, serial);
}
void FramebufferDesc::updateDepthStencil(ImageViewSerial serial) void FramebufferDesc::updateDepthStencil(ImageViewSerial serial)
{ {
update(kFramebufferDescDepthStencilIndex, serial); update(kFramebufferDescDepthStencilIndex, serial);
......
...@@ -75,6 +75,8 @@ class alignas(4) RenderPassDesc final ...@@ -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 // 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()|. // attachments (including gaps), i.e. with an index starting from |colorAttachmentRange()|.
void packDepthStencilAttachment(angle::FormatID angleFormatID); 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; size_t hash() const;
...@@ -84,6 +86,10 @@ class alignas(4) RenderPassDesc final ...@@ -84,6 +86,10 @@ class alignas(4) RenderPassDesc final
bool isColorAttachmentEnabled(size_t colorIndexGL) const; bool isColorAttachmentEnabled(size_t colorIndexGL) const;
bool hasDepthStencilAttachment() const { return mHasDepthStencilAttachment; } 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 // Get the number of attachments in the Vulkan render pass, i.e. after removing disabled
// color attachments. // color attachments.
...@@ -104,8 +110,15 @@ class alignas(4) RenderPassDesc final ...@@ -104,8 +110,15 @@ class alignas(4) RenderPassDesc final
uint8_t mLogSamples : 3; uint8_t mLogSamples : 3;
uint8_t mColorAttachmentRange : 4; uint8_t mColorAttachmentRange : 4;
uint8_t mHasDepthStencilAttachment : 1; uint8_t mHasDepthStencilAttachment : 1;
// Temporary padding for upcoming support for resolve attachments. // Whether each color attachment has a corresponding resolve attachment. Color resolve
ANGLE_MAYBE_UNUSED uint8_t pad; // 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 // 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, // attachment formats follow the last enabled color attachment. When creating a render pass,
// the disabled attachments are removed and the resulting attachments are packed. // the disabled attachments are removed and the resulting attachments are packed.
...@@ -126,6 +139,8 @@ class alignas(4) RenderPassDesc final ...@@ -126,6 +139,8 @@ class alignas(4) RenderPassDesc final
// - Subpass attachment 2 -> VK_ATTACHMENT_UNUSED // - Subpass attachment 2 -> VK_ATTACHMENT_UNUSED
// - Subpass attachment 3 -> Renderpass attachment 1 // - 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; gl::AttachmentArray<uint8_t> mAttachmentFormats;
}; };
...@@ -837,8 +852,11 @@ class UniformsAndXfbDesc ...@@ -837,8 +852,11 @@ class UniformsAndXfbDesc
std::array<BufferSerial, kMaxBufferCount> mBufferSerials; std::array<BufferSerial, kMaxBufferCount> mBufferSerials;
}; };
// This is IMPLEMENTATION_MAX_DRAW_BUFFERS + 1 for DS attachment // There can be a maximum of IMPLEMENTATION_MAX_DRAW_BUFFERS color and resolve attachments, plus one
constexpr size_t kMaxFramebufferAttachments = gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS; // 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 class FramebufferDesc
{ {
...@@ -850,6 +868,7 @@ class FramebufferDesc ...@@ -850,6 +868,7 @@ class FramebufferDesc
FramebufferDesc &operator=(const FramebufferDesc &other); FramebufferDesc &operator=(const FramebufferDesc &other);
void updateColor(uint32_t index, ImageViewSerial serial); void updateColor(uint32_t index, ImageViewSerial serial);
void updateColorResolve(uint32_t index, ImageViewSerial serial);
void updateDepthStencil(ImageViewSerial serial); void updateDepthStencil(ImageViewSerial serial);
size_t hash() const; size_t hash() const;
void reset(); void reset();
...@@ -861,7 +880,7 @@ class FramebufferDesc ...@@ -861,7 +880,7 @@ class FramebufferDesc
private: private:
void update(uint32_t index, ImageViewSerial serial); void update(uint32_t index, ImageViewSerial serial);
gl::AttachmentArray<ImageViewSerial> mSerials; FramebufferAttachmentArray<ImageViewSerial> mSerials;
uint32_t mMaxValidSerialIndex; uint32_t mMaxValidSerialIndex;
}; };
......
...@@ -76,17 +76,18 @@ void RendererVk::ensureCapsInitialized() const ...@@ -76,17 +76,18 @@ void RendererVk::ensureCapsInitialized() const
// Enable this for simple buffer readback testing, but some functionality is missing. // Enable this for simple buffer readback testing, but some functionality is missing.
// TODO(jmadill): Support full mapBufferRange extension. // TODO(jmadill): Support full mapBufferRange extension.
mNativeExtensions.mapBufferOES = true; mNativeExtensions.mapBufferOES = true;
mNativeExtensions.mapBufferRange = true; mNativeExtensions.mapBufferRange = true;
mNativeExtensions.textureStorage = true; mNativeExtensions.textureStorage = true;
mNativeExtensions.drawBuffers = true; mNativeExtensions.drawBuffers = true;
mNativeExtensions.fragDepth = true; mNativeExtensions.fragDepth = true;
mNativeExtensions.framebufferBlit = true; mNativeExtensions.framebufferBlit = true;
mNativeExtensions.framebufferMultisample = true; mNativeExtensions.framebufferMultisample = true;
mNativeExtensions.copyTexture = true; mNativeExtensions.multisampledRenderToTexture = true;
mNativeExtensions.copyTexture3d = true; mNativeExtensions.copyTexture = true;
mNativeExtensions.copyCompressedTexture = true; mNativeExtensions.copyTexture3d = true;
mNativeExtensions.debugMarker = true; mNativeExtensions.copyCompressedTexture = true;
mNativeExtensions.debugMarker = true;
mNativeExtensions.robustness = mNativeExtensions.robustness =
!IsSwiftshader(mPhysicalDeviceProperties.vendorID, mPhysicalDeviceProperties.deviceID) && !IsSwiftshader(mPhysicalDeviceProperties.vendorID, mPhysicalDeviceProperties.deviceID) &&
!IsARM(mPhysicalDeviceProperties.vendorID); !IsARM(mPhysicalDeviceProperties.vendorID);
......
...@@ -56,6 +56,28 @@ VkImageUsageFlags GetStagingBufferUsageFlags(vk::StagingUsage usage) ...@@ -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, angle::Result FindAndAllocateCompatibleMemory(vk::Context *context,
const vk::MemoryProperties &memoryProperties, const vk::MemoryProperties &memoryProperties,
VkMemoryPropertyFlags requestedMemoryPropertyFlags, VkMemoryPropertyFlags requestedMemoryPropertyFlags,
...@@ -334,6 +356,19 @@ void MemoryProperties::destroy() ...@@ -334,6 +356,19 @@ void MemoryProperties::destroy()
mMemoryProperties = {}; 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( angle::Result MemoryProperties::findCompatibleMemoryIndex(
Context *context, Context *context,
const VkMemoryRequirements &memoryRequirements, const VkMemoryRequirements &memoryRequirements,
...@@ -347,39 +382,27 @@ angle::Result MemoryProperties::findCompatibleMemoryIndex( ...@@ -347,39 +382,27 @@ angle::Result MemoryProperties::findCompatibleMemoryIndex(
// Not finding a valid memory pool means an out-of-spec driver, or internal error. // 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): Determine if it is possible to cache indexes.
// TODO(jmadill): More efficient memory allocation. // 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); return angle::Result::Continue;
if ((mMemoryProperties.memoryTypes[memoryIndex].propertyFlags &
requestedMemoryPropertyFlags) == requestedMemoryPropertyFlags)
{
*memoryPropertyFlagsOut = mMemoryProperties.memoryTypes[memoryIndex].propertyFlags;
*typeIndexOut = static_cast<uint32_t>(memoryIndex);
return angle::Result::Continue;
}
} }
// We did not find a compatible memory type, the Vulkan spec says the following - // We did not find a compatible memory type. If the caller wanted a host visible memory, just
// There must be at least one memory type with both the // return the memory index with fallback, guaranteed, memory flags.
// 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.
if (requestedMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) 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 & return angle::Result::Continue;
fallbackMemoryPropertyFlags) == fallbackMemoryPropertyFlags)
{
*memoryPropertyFlagsOut = mMemoryProperties.memoryTypes[memoryIndex].propertyFlags;
*typeIndexOut = static_cast<uint32_t>(memoryIndex);
return angle::Result::Continue;
}
} }
} }
......
...@@ -292,6 +292,7 @@ class MemoryProperties final : angle::NonCopyable ...@@ -292,6 +292,7 @@ class MemoryProperties final : angle::NonCopyable
MemoryProperties(); MemoryProperties();
void init(VkPhysicalDevice physicalDevice); void init(VkPhysicalDevice physicalDevice);
bool hasLazilyAllocatedMemory() const;
angle::Result findCompatibleMemoryIndex(Context *context, angle::Result findCompatibleMemoryIndex(Context *context,
const VkMemoryRequirements &memoryRequirements, const VkMemoryRequirements &memoryRequirements,
VkMemoryPropertyFlags requestedMemoryPropertyFlags, VkMemoryPropertyFlags requestedMemoryPropertyFlags,
......
...@@ -243,3 +243,7 @@ ...@@ -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_vec3_vertex = FAIL
4371 VULKAN ANDROID : dEQP-GLES31.functional.shaders.implicit_conversions.es31.arithmetic.input_before_literal.add.int_to_uvec3_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 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 @@ ...@@ -639,3 +639,7 @@
// Fails on SwANGLE bots // Fails on SwANGLE bots
4418 SWIFTSHADER : dEQP-GLES3.functional.negative_api.buffer.framebuffer_texture_layer = FAIL 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 @@ ...@@ -118,3 +118,8 @@
// New failures with latest dEQP roll (2020-04-28) // New failures with latest dEQP roll (2020-04-28)
4593 SWIFTSHADER : KHR-GLES31.core.nearest_edge.offset_left = FAIL 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 ...@@ -25,34 +25,15 @@ class CopyTexImageTest : public ANGLETest
void testSetUp() override void testSetUp() override
{ {
constexpr char kVS[] = mTextureProgram =
"precision highp float;\n" CompileProgram(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
"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);
if (mTextureProgram == 0) if (mTextureProgram == 0)
{ {
FAIL() << "shader compilation failed."; FAIL() << "shader compilation failed.";
} }
mTextureUniformLocation = glGetUniformLocation(mTextureProgram, "tex"); mTextureUniformLocation =
glGetUniformLocation(mTextureProgram, essl1_shaders::Texture2DUniform());
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
} }
...@@ -99,7 +80,7 @@ class CopyTexImageTest : public ANGLETest ...@@ -99,7 +80,7 @@ class CopyTexImageTest : public ANGLETest
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(mTextureUniformLocation, 0); 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 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); EXPECT_PIXEL_NEAR(xs, ys, data[0], data[1], data[2], data[3], 1.0);
...@@ -891,13 +872,8 @@ TEST_P(CopyTexImageTestES3, 3DSubImageDrawMismatchedTextureTypes) ...@@ -891,13 +872,8 @@ TEST_P(CopyTexImageTestES3, 3DSubImageDrawMismatchedTextureTypes)
// Use this to select which configurations (e.g. which renderer, which GLES major version) these // Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against. // tests should be run against.
ANGLE_INSTANTIATE_TEST(CopyTexImageTest, ANGLE_INSTANTIATE_TEST(CopyTexImageTest,
ES2_D3D9(), ANGLE_ALL_TEST_PLATFORMS_ES2,
ES2_D3D11(),
ES2_D3D11_PRESENT_PATH_FAST(), ES2_D3D11_PRESENT_PATH_FAST(),
ES2_METAL(),
ES2_OPENGL(),
ES2_OPENGLES(),
ES2_VULKAN(),
ES3_VULKAN(), ES3_VULKAN(),
WithEmulateCopyTexImage2DFromRenderbuffers(ES2_OPENGL()), WithEmulateCopyTexImage2DFromRenderbuffers(ES2_OPENGL()),
WithEmulateCopyTexImage2DFromRenderbuffers(ES2_OPENGLES())); 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