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());
} }
......
...@@ -1201,6 +1201,46 @@ angle::Result FramebufferVk::resolveColorWithCommand(ContextVk *contextVk, ...@@ -1201,6 +1201,46 @@ angle::Result FramebufferVk::resolveColorWithCommand(ContextVk *contextVk,
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result FramebufferVk::copyResolveToMultisampedAttachment(ContextVk *contextVk,
RenderTargetVk *colorRenderTarget)
{
ASSERT(colorRenderTarget->hasResolveAttachment());
ASSERT(colorRenderTarget->isImageTransient());
vk::ImageHelper *src = &colorRenderTarget->getResolveImageForRenderPass();
vk::ImageHelper *dest = &colorRenderTarget->getImageForRenderPass();
const vk::ImageView *srcView;
const vk::ImageView *destView;
ANGLE_TRY(colorRenderTarget->getAndRetainCopyImageView(contextVk, &srcView));
ANGLE_TRY(colorRenderTarget->getImageView(contextVk, &destView));
// Note: neither vkCmdCopyImage nor vkCmdBlitImage allow the destination to be multisampled.
// There's no choice but to use a draw-based path to perform this copy.
gl::Extents extents = colorRenderTarget->getExtents();
uint32_t levelVK = colorRenderTarget->getLevelIndex() - src->getBaseLevel();
uint32_t layer = colorRenderTarget->getLayerIndex();
UtilsVk::CopyImageParameters params;
params.srcOffset[0] = 0;
params.srcOffset[1] = 0;
params.srcExtents[0] = extents.width;
params.srcExtents[1] = extents.height;
params.destOffset[0] = 0;
params.destOffset[1] = 0;
params.srcMip = levelVK;
params.srcLayer = layer;
params.srcHeight = extents.height;
params.srcPremultiplyAlpha = false;
params.srcUnmultiplyAlpha = false;
params.srcFlipY = false;
params.destFlipY = false;
params.srcRotation = SurfaceRotation::Identity;
return contextVk->getUtils().copyImage(contextVk, dest, destView, src, srcView, params);
}
bool FramebufferVk::checkStatus(const gl::Context *context) const bool FramebufferVk::checkStatus(const gl::Context *context) const
{ {
// if we have both a depth and stencil buffer, they must refer to the same object // if we have both a depth and stencil buffer, they must refer to the same object
...@@ -1392,7 +1432,10 @@ angle::Result FramebufferVk::updateColorAttachment(const gl::Context *context, ...@@ -1392,7 +1432,10 @@ angle::Result FramebufferVk::updateColorAttachment(const gl::Context *context,
updateActiveColorMasks(colorIndexGL, false, false, false, false); updateActiveColorMasks(colorIndexGL, false, false, false, false);
} }
if (renderTarget && mState.getEnabledDrawBuffers()[colorIndexGL]) const bool enabledColor = renderTarget && mState.getEnabledDrawBuffers()[colorIndexGL];
const bool enabledResolve = enabledColor && renderTarget->hasResolveAttachment();
if (enabledColor)
{ {
mCurrentFramebufferDesc.updateColor(colorIndexGL, mCurrentFramebufferDesc.updateColor(colorIndexGL,
renderTarget->getAssignImageViewSerial(contextVk)); renderTarget->getAssignImageViewSerial(contextVk));
...@@ -1402,6 +1445,16 @@ angle::Result FramebufferVk::updateColorAttachment(const gl::Context *context, ...@@ -1402,6 +1445,16 @@ angle::Result FramebufferVk::updateColorAttachment(const gl::Context *context,
mCurrentFramebufferDesc.updateColor(colorIndexGL, kInvalidImageViewSerial); mCurrentFramebufferDesc.updateColor(colorIndexGL, kInvalidImageViewSerial);
} }
if (enabledResolve)
{
mCurrentFramebufferDesc.updateColorResolve(
colorIndexGL, renderTarget->getAssignResolveImageViewSerial(contextVk));
}
else
{
mCurrentFramebufferDesc.updateColorResolve(colorIndexGL, kInvalidImageViewSerial);
}
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -1492,6 +1545,12 @@ angle::Result FramebufferVk::syncState(const gl::Context *context, ...@@ -1492,6 +1545,12 @@ angle::Result FramebufferVk::syncState(const gl::Context *context,
mCurrentFramebufferDesc.updateColor( mCurrentFramebufferDesc.updateColor(
static_cast<uint32_t>(colorIndexGL), static_cast<uint32_t>(colorIndexGL),
renderTarget->getAssignImageViewSerial(contextVk)); renderTarget->getAssignImageViewSerial(contextVk));
if (renderTarget->hasResolveAttachment())
{
mCurrentFramebufferDesc.updateColorResolve(
static_cast<uint32_t>(colorIndexGL),
renderTarget->getAssignResolveImageViewSerial(contextVk));
}
} }
updateDepthStencilAttachmentSerial(contextVk); updateDepthStencilAttachmentSerial(contextVk);
break; break;
...@@ -1580,6 +1639,7 @@ void FramebufferVk::updateRenderPassDesc() ...@@ -1580,6 +1639,7 @@ void FramebufferVk::updateRenderPassDesc()
mRenderPassDesc = {}; mRenderPassDesc = {};
mRenderPassDesc.setSamples(getSamples()); mRenderPassDesc.setSamples(getSamples());
// Color attachments.
const auto &colorRenderTargets = mRenderTargetCache.getColors(); const auto &colorRenderTargets = mRenderTargetCache.getColors();
const gl::DrawBufferMask enabledDrawBuffers = mState.getEnabledDrawBuffers(); const gl::DrawBufferMask enabledDrawBuffers = mState.getEnabledDrawBuffers();
for (size_t colorIndexGL = 0; colorIndexGL < enabledDrawBuffers.size(); ++colorIndexGL) for (size_t colorIndexGL = 0; colorIndexGL < enabledDrawBuffers.size(); ++colorIndexGL)
...@@ -1591,6 +1651,12 @@ void FramebufferVk::updateRenderPassDesc() ...@@ -1591,6 +1651,12 @@ void FramebufferVk::updateRenderPassDesc()
mRenderPassDesc.packColorAttachment( mRenderPassDesc.packColorAttachment(
colorIndexGL, colorIndexGL,
colorRenderTarget->getImageForRenderPass().getFormat().intendedFormatID); colorRenderTarget->getImageForRenderPass().getFormat().intendedFormatID);
// Add the resolve attachment, if any.
if (colorRenderTarget->hasResolveAttachment())
{
mRenderPassDesc.packColorResolveAttachment(colorIndexGL);
}
} }
else else
{ {
...@@ -1598,6 +1664,7 @@ void FramebufferVk::updateRenderPassDesc() ...@@ -1598,6 +1664,7 @@ void FramebufferVk::updateRenderPassDesc()
} }
} }
// Depth/stencil attachment.
RenderTargetVk *depthStencilRenderTarget = getDepthStencilRenderTarget(); RenderTargetVk *depthStencilRenderTarget = getDepthStencilRenderTarget();
if (depthStencilRenderTarget) if (depthStencilRenderTarget)
{ {
...@@ -1642,6 +1709,7 @@ angle::Result FramebufferVk::getFramebuffer(ContextVk *contextVk, vk::Framebuffe ...@@ -1642,6 +1709,7 @@ angle::Result FramebufferVk::getFramebuffer(ContextVk *contextVk, vk::Framebuffe
std::vector<VkImageView> attachments; std::vector<VkImageView> attachments;
gl::Extents attachmentsSize; gl::Extents attachmentsSize;
// Color attachments.
const auto &colorRenderTargets = mRenderTargetCache.getColors(); const auto &colorRenderTargets = mRenderTargetCache.getColors();
for (size_t colorIndexGL : mState.getEnabledDrawBuffers()) for (size_t colorIndexGL : mState.getEnabledDrawBuffers())
{ {
...@@ -1657,6 +1725,7 @@ angle::Result FramebufferVk::getFramebuffer(ContextVk *contextVk, vk::Framebuffe ...@@ -1657,6 +1725,7 @@ angle::Result FramebufferVk::getFramebuffer(ContextVk *contextVk, vk::Framebuffe
attachmentsSize = colorRenderTarget->getExtents(); attachmentsSize = colorRenderTarget->getExtents();
} }
// Depth/stencil attachment.
RenderTargetVk *depthStencilRenderTarget = getDepthStencilRenderTarget(); RenderTargetVk *depthStencilRenderTarget = getDepthStencilRenderTarget();
if (depthStencilRenderTarget) if (depthStencilRenderTarget)
{ {
...@@ -1670,6 +1739,23 @@ angle::Result FramebufferVk::getFramebuffer(ContextVk *contextVk, vk::Framebuffe ...@@ -1670,6 +1739,23 @@ angle::Result FramebufferVk::getFramebuffer(ContextVk *contextVk, vk::Framebuffe
attachmentsSize = depthStencilRenderTarget->getExtents(); attachmentsSize = depthStencilRenderTarget->getExtents();
} }
// Color resolve attachments.
for (size_t colorIndexGL : mState.getEnabledDrawBuffers())
{
RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndexGL];
ASSERT(colorRenderTarget);
if (colorRenderTarget->hasResolveAttachment())
{
const vk::ImageView *resolveImageView = nullptr;
ANGLE_TRY(colorRenderTarget->getResolveImageView(contextVk, &resolveImageView));
attachments.push_back(resolveImageView->getHandle());
ASSERT(!attachmentsSize.empty());
}
}
if (attachmentsSize.empty()) if (attachmentsSize.empty())
{ {
// No attachments, so use the default values. // No attachments, so use the default values.
...@@ -1932,12 +2018,13 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk, ...@@ -1932,12 +2018,13 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
vk::Framebuffer *framebuffer = nullptr; vk::Framebuffer *framebuffer = nullptr;
ANGLE_TRY(getFramebuffer(contextVk, &framebuffer)); ANGLE_TRY(getFramebuffer(contextVk, &framebuffer));
vk::AttachmentOpsArray renderPassAttachmentOps;
vk::ClearValuesArray packedClearValues;
ANGLE_TRY(contextVk->endRenderPass()); ANGLE_TRY(contextVk->endRenderPass());
// Initialize RenderPass info. // Initialize RenderPass info.
vk::AttachmentOpsArray renderPassAttachmentOps;
vk::ClearValuesArray packedClearValues;
// Color attachments.
const auto &colorRenderTargets = mRenderTargetCache.getColors(); const auto &colorRenderTargets = mRenderTargetCache.getColors();
uint32_t currentAttachmentCount = 0; uint32_t currentAttachmentCount = 0;
for (size_t colorIndexGL : mState.getEnabledDrawBuffers()) for (size_t colorIndexGL : mState.getEnabledDrawBuffers())
...@@ -1948,10 +2035,14 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk, ...@@ -1948,10 +2035,14 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
renderPassAttachmentOps.setLayouts(currentAttachmentCount, vk::ImageLayout::ColorAttachment, renderPassAttachmentOps.setLayouts(currentAttachmentCount, vk::ImageLayout::ColorAttachment,
vk::ImageLayout::ColorAttachment); vk::ImageLayout::ColorAttachment);
const VkAttachmentStoreOp storeOp = colorRenderTarget->isImageTransient()
? VK_ATTACHMENT_STORE_OP_DONT_CARE
: VK_ATTACHMENT_STORE_OP_STORE;
if (mDeferredClears.test(colorIndexGL)) if (mDeferredClears.test(colorIndexGL))
{ {
renderPassAttachmentOps.setOps(currentAttachmentCount, VK_ATTACHMENT_LOAD_OP_CLEAR, renderPassAttachmentOps.setOps(currentAttachmentCount, VK_ATTACHMENT_LOAD_OP_CLEAR,
VK_ATTACHMENT_STORE_OP_STORE); storeOp);
packedClearValues.store(currentAttachmentCount, VK_IMAGE_ASPECT_COLOR_BIT, packedClearValues.store(currentAttachmentCount, VK_IMAGE_ASPECT_COLOR_BIT,
mDeferredClears[colorIndexGL]); mDeferredClears[colorIndexGL]);
mDeferredClears.reset(colorIndexGL); mDeferredClears.reset(colorIndexGL);
...@@ -1962,7 +2053,7 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk, ...@@ -1962,7 +2053,7 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
colorRenderTarget->hasDefinedContent() colorRenderTarget->hasDefinedContent()
? VK_ATTACHMENT_LOAD_OP_LOAD ? VK_ATTACHMENT_LOAD_OP_LOAD
: VK_ATTACHMENT_LOAD_OP_DONT_CARE, : VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_STORE); storeOp);
packedClearValues.store(currentAttachmentCount, VK_IMAGE_ASPECT_COLOR_BIT, packedClearValues.store(currentAttachmentCount, VK_IMAGE_ASPECT_COLOR_BIT,
kUninitializedClearValue); kUninitializedClearValue);
} }
...@@ -1970,11 +2061,37 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk, ...@@ -1970,11 +2061,37 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE); VK_ATTACHMENT_STORE_OP_DONT_CARE);
ANGLE_TRY(colorRenderTarget->onColorDraw(contextVk)); // If there's a resolve attachment, and loadOp needs to be LOAD, the multisampled attachment
// needs to take its value from the resolve attachment. In this case, there's no choice
// but to blit from the resolve image into the multisampled one. It's expected that
// application code results in a clear of the framebuffer at the start of the renderpass, so
// the multisampled image is truely transient.
//
// Note that this only needs to be done if the multisampled image and the resolve attachment
// come from the same source. When optimizing glBlitFramebuffer for example, this is not
// the case. isImageTransient() indicates whether this should happen.
//
// TODO: In an ideal world, this could be done in a subpass so that the multisampled data
// always ever stays on a tiled renderer's tile and no memory backing is allocated for it.
// http://anglebug.com/4881
if (colorRenderTarget->hasResolveAttachment() && colorRenderTarget->isImageTransient() &&
renderPassAttachmentOps[currentAttachmentCount].loadOp == VK_ATTACHMENT_LOAD_OP_LOAD)
{
ANGLE_TRY(copyResolveToMultisampedAttachment(contextVk, colorRenderTarget));
}
currentAttachmentCount++; currentAttachmentCount++;
} }
// Transition the images to the correct layout (through onColorDraw) after the
// resolve-to-multisampled copies are done.
for (size_t colorIndexGL : mState.getEnabledDrawBuffers())
{
RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndexGL];
ANGLE_TRY(colorRenderTarget->onColorDraw(contextVk));
}
// Depth/stencil attachment.
RenderTargetVk *depthStencilRenderTarget = getDepthStencilRenderTarget(); RenderTargetVk *depthStencilRenderTarget = getDepthStencilRenderTarget();
if (depthStencilRenderTarget) if (depthStencilRenderTarget)
{ {
...@@ -1989,6 +2106,12 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk, ...@@ -1989,6 +2106,12 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
} }
if (depthStencilRenderTarget->isImageTransient())
{
depthStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
}
renderPassAttachmentOps.setLayouts(currentAttachmentCount, renderPassAttachmentOps.setLayouts(currentAttachmentCount,
vk::ImageLayout::DepthStencilAttachment, vk::ImageLayout::DepthStencilAttachment,
vk::ImageLayout::DepthStencilAttachment); vk::ImageLayout::DepthStencilAttachment);
......
...@@ -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,
......
...@@ -28,6 +28,8 @@ RenderTargetVk::~RenderTargetVk() {} ...@@ -28,6 +28,8 @@ RenderTargetVk::~RenderTargetVk() {}
RenderTargetVk::RenderTargetVk(RenderTargetVk &&other) RenderTargetVk::RenderTargetVk(RenderTargetVk &&other)
: mImage(other.mImage), : mImage(other.mImage),
mImageViews(other.mImageViews), mImageViews(other.mImageViews),
mResolveImage(other.mResolveImage),
mResolveImageViews(other.mResolveImageViews),
mLevelIndexGL(other.mLevelIndexGL), mLevelIndexGL(other.mLevelIndexGL),
mLayerIndex(other.mLayerIndex), mLayerIndex(other.mLayerIndex),
mContentDefined(other.mContentDefined) mContentDefined(other.mContentDefined)
...@@ -37,48 +39,74 @@ RenderTargetVk::RenderTargetVk(RenderTargetVk &&other) ...@@ -37,48 +39,74 @@ RenderTargetVk::RenderTargetVk(RenderTargetVk &&other)
void RenderTargetVk::init(vk::ImageHelper *image, void RenderTargetVk::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)
{ {
mImage = image; mImage = image;
mImageViews = imageViews; mImageViews = imageViews;
mLevelIndexGL = levelIndexGL; mResolveImage = resolveImage;
mLayerIndex = layerIndex; mResolveImageViews = resolveImageViews;
mLevelIndexGL = levelIndexGL;
mLayerIndex = layerIndex;
// Conservatively assume the content is defined. // Conservatively assume the content is defined.
mContentDefined = true; mContentDefined = true;
mIsImageTransient = isImageTransient;
} }
void RenderTargetVk::reset() void RenderTargetVk::reset()
{ {
mImage = nullptr; mImage = nullptr;
mImageViews = nullptr; mImageViews = nullptr;
mLevelIndexGL = 0; mResolveImage = nullptr;
mLayerIndex = 0; mResolveImageViews = nullptr;
mContentDefined = false; mLevelIndexGL = 0;
mLayerIndex = 0;
mContentDefined = false;
} }
ImageViewSerial RenderTargetVk::getAssignImageViewSerial(ContextVk *contextVk) const ImageViewSerial RenderTargetVk::getAssignViewSerialImpl(ContextVk *contextVk,
vk::ImageViewHelper *imageViews) const
{ {
ASSERT(mImageViews); ASSERT(imageViews);
ASSERT(mLayerIndex < std::numeric_limits<uint16_t>::max()); ASSERT(mLayerIndex < std::numeric_limits<uint16_t>::max());
ASSERT(mLevelIndexGL < std::numeric_limits<uint16_t>::max()); ASSERT(mLevelIndexGL < std::numeric_limits<uint16_t>::max());
ImageViewSerial imageViewSerial = ImageViewSerial imageViewSerial =
mImageViews->getAssignSerial(contextVk, mLevelIndexGL, mLayerIndex); imageViews->getAssignSerial(contextVk, mLevelIndexGL, mLayerIndex);
ASSERT(imageViewSerial.getValue() < std::numeric_limits<uint32_t>::max()); ASSERT(imageViewSerial.getValue() < std::numeric_limits<uint32_t>::max());
return imageViewSerial; return imageViewSerial;
} }
ImageViewSerial RenderTargetVk::getAssignImageViewSerial(ContextVk *contextVk) const
{
return getAssignViewSerialImpl(contextVk, mImageViews);
}
ImageViewSerial RenderTargetVk::getAssignResolveImageViewSerial(ContextVk *contextVk) const
{
return getAssignViewSerialImpl(contextVk, mResolveImageViews);
}
angle::Result RenderTargetVk::onColorDraw(ContextVk *contextVk) angle::Result RenderTargetVk::onColorDraw(ContextVk *contextVk)
{ {
ASSERT(!mImage->getFormat().actualImageFormat().hasDepthOrStencilBits()); ASSERT(!mImage->getFormat().actualImageFormat().hasDepthOrStencilBits());
contextVk->onRenderPassImageWrite(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::ColorAttachment, contextVk->onRenderPassImageWrite(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::ColorAttachment,
mImage); mImage);
mContentDefined = true; if (mResolveImage)
{
contextVk->onRenderPassImageWrite(VK_IMAGE_ASPECT_COLOR_BIT,
vk::ImageLayout::ColorAttachment, mResolveImage);
}
retainImageViews(contextVk); retainImageViews(contextVk);
mContentDefined = true;
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -90,9 +118,15 @@ angle::Result RenderTargetVk::onDepthStencilDraw(ContextVk *contextVk) ...@@ -90,9 +118,15 @@ angle::Result RenderTargetVk::onDepthStencilDraw(ContextVk *contextVk)
VkImageAspectFlags aspectFlags = vk::GetDepthStencilAspectFlags(format); VkImageAspectFlags aspectFlags = vk::GetDepthStencilAspectFlags(format);
contextVk->onRenderPassImageWrite(aspectFlags, vk::ImageLayout::DepthStencilAttachment, mImage); contextVk->onRenderPassImageWrite(aspectFlags, vk::ImageLayout::DepthStencilAttachment, mImage);
mContentDefined = true; if (mResolveImage)
{
contextVk->onRenderPassImageWrite(aspectFlags, vk::ImageLayout::DepthStencilAttachment,
mResolveImage);
}
retainImageViews(contextVk); retainImageViews(contextVk);
mContentDefined = true;
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -108,13 +142,49 @@ const vk::ImageHelper &RenderTargetVk::getImageForRenderPass() const ...@@ -108,13 +142,49 @@ const vk::ImageHelper &RenderTargetVk::getImageForRenderPass() const
return *mImage; return *mImage;
} }
vk::ImageHelper &RenderTargetVk::getResolveImageForRenderPass()
{
ASSERT(mResolveImage && mResolveImage->valid());
return *mResolveImage;
}
const vk::ImageHelper &RenderTargetVk::getResolveImageForRenderPass() const
{
ASSERT(mResolveImage && mResolveImage->valid());
return *mResolveImage;
}
angle::Result RenderTargetVk::getImageViewImpl(ContextVk *contextVk,
const vk::ImageHelper &image,
vk::ImageViewHelper *imageViews,
const vk::ImageView **imageViewOut) const
{
ASSERT(image.valid() && imageViews);
int32_t levelVK = mLevelIndexGL - mImage->getBaseLevel();
return imageViews->getLevelLayerDrawImageView(contextVk, image, levelVK, mLayerIndex,
imageViewOut);
}
angle::Result RenderTargetVk::getImageView(ContextVk *contextVk, angle::Result RenderTargetVk::getImageView(ContextVk *contextVk,
const vk::ImageView **imageViewOut) const const vk::ImageView **imageViewOut) const
{ {
ASSERT(mImage && mImage->valid() && mImageViews); ASSERT(mImage);
int32_t levelVK = mLevelIndexGL - mImage->getBaseLevel(); return getImageViewImpl(contextVk, *mImage, mImageViews, imageViewOut);
return mImageViews->getLevelLayerDrawImageView(contextVk, *mImage, levelVK, mLayerIndex, }
imageViewOut);
angle::Result RenderTargetVk::getResolveImageView(ContextVk *contextVk,
const vk::ImageView **imageViewOut) const
{
ASSERT(mResolveImage);
return getImageViewImpl(contextVk, *mResolveImage, mResolveImageViews, imageViewOut);
}
bool RenderTargetVk::isResolveImageOwnerOfData() const
{
// If there's a resolve attachment and the image itself is transient, it's the resolve
// attachment that owns the data, so all non-render-pass accesses to the render target data
// should go through the resolve attachment.
return hasResolveAttachment() && isImageTransient();
} }
angle::Result RenderTargetVk::getAndRetainCopyImageView(ContextVk *contextVk, angle::Result RenderTargetVk::getAndRetainCopyImageView(ContextVk *contextVk,
...@@ -122,7 +192,9 @@ angle::Result RenderTargetVk::getAndRetainCopyImageView(ContextVk *contextVk, ...@@ -122,7 +192,9 @@ angle::Result RenderTargetVk::getAndRetainCopyImageView(ContextVk *contextVk,
{ {
retainImageViews(contextVk); retainImageViews(contextVk);
const vk::ImageViewHelper *imageViews = mImageViews; const vk::ImageViewHelper *imageViews =
isResolveImageOwnerOfData() ? mResolveImageViews : mImageViews;
// If the source of render target is a texture or renderbuffer, this will always be valid. This // If the source of render target is a texture or renderbuffer, this will always be valid. This
// is also where 3D or 2DArray images could be the source of the render target. // is also where 3D or 2DArray images could be the source of the render target.
if (imageViews->hasCopyImageView()) if (imageViews->hasCopyImageView())
...@@ -132,8 +204,10 @@ angle::Result RenderTargetVk::getAndRetainCopyImageView(ContextVk *contextVk, ...@@ -132,8 +204,10 @@ angle::Result RenderTargetVk::getAndRetainCopyImageView(ContextVk *contextVk,
} }
// Otherwise, this must come from the surface, in which case the image is 2D, so the image view // Otherwise, this must come from the surface, in which case the image is 2D, so the image view
// used to draw is just as good for fetching. // used to draw is just as good for fetching. If resolve attachment is present, fetching is
return getImageView(contextVk, imageViewOut); // done from that.
return isResolveImageOwnerOfData() ? getResolveImageView(contextVk, imageViewOut)
: getImageView(contextVk, imageViewOut);
} }
const vk::Format &RenderTargetVk::getImageFormat() const const vk::Format &RenderTargetVk::getImageFormat() const
...@@ -149,23 +223,28 @@ gl::Extents RenderTargetVk::getExtents() const ...@@ -149,23 +223,28 @@ gl::Extents RenderTargetVk::getExtents() const
return mImage->getLevelExtents2D(levelVK); return mImage->getLevelExtents2D(levelVK);
} }
void RenderTargetVk::updateSwapchainImage(vk::ImageHelper *image, vk::ImageViewHelper *imageViews) void RenderTargetVk::updateSwapchainImage(vk::ImageHelper *image,
vk::ImageViewHelper *imageViews,
vk::ImageHelper *resolveImage,
vk::ImageViewHelper *resolveImageViews)
{ {
ASSERT(image && image->valid() && imageViews); ASSERT(image && image->valid() && imageViews);
mImage = image; mImage = image;
mImageViews = imageViews; mImageViews = imageViews;
mResolveImage = resolveImage;
mResolveImageViews = resolveImageViews;
} }
vk::ImageHelper &RenderTargetVk::getImageForCopy() const vk::ImageHelper &RenderTargetVk::getImageForCopy() const
{ {
ASSERT(mImage && mImage->valid()); ASSERT(mImage && mImage->valid() && (mResolveImage == nullptr || mResolveImage->valid()));
return *mImage; return isResolveImageOwnerOfData() ? *mResolveImage : *mImage;
} }
vk::ImageHelper &RenderTargetVk::getImageForWrite() const vk::ImageHelper &RenderTargetVk::getImageForWrite() const
{ {
ASSERT(mImage && mImage->valid()); ASSERT(mImage && mImage->valid() && (mResolveImage == nullptr || mResolveImage->valid()));
return *mImage; return isResolveImageOwnerOfData() ? *mResolveImage : *mImage;
} }
angle::Result RenderTargetVk::flushStagedUpdates(ContextVk *contextVk, angle::Result RenderTargetVk::flushStagedUpdates(ContextVk *contextVk,
...@@ -176,6 +255,8 @@ angle::Result RenderTargetVk::flushStagedUpdates(ContextVk *contextVk, ...@@ -176,6 +255,8 @@ angle::Result RenderTargetVk::flushStagedUpdates(ContextVk *contextVk,
// contents. Therefore, set mContentDefined so that the next render pass will have loadOp=LOAD. // contents. Therefore, set mContentDefined so that the next render pass will have loadOp=LOAD.
mContentDefined = true; mContentDefined = true;
ASSERT(mImage->valid() && (!isResolveImageOwnerOfData() || mResolveImage->valid()));
// Note that the layer index for 3D textures is always zero according to Vulkan. // Note that the layer index for 3D textures is always zero according to Vulkan.
uint32_t layerIndex = mLayerIndex; uint32_t layerIndex = mLayerIndex;
if (mImage->getType() == VK_IMAGE_TYPE_3D) if (mImage->getType() == VK_IMAGE_TYPE_3D)
...@@ -183,19 +264,37 @@ angle::Result RenderTargetVk::flushStagedUpdates(ContextVk *contextVk, ...@@ -183,19 +264,37 @@ angle::Result RenderTargetVk::flushStagedUpdates(ContextVk *contextVk,
layerIndex = 0; layerIndex = 0;
} }
ASSERT(mImage->valid()); vk::ImageHelper *image = isResolveImageOwnerOfData() ? mResolveImage : mImage;
if (!mImage->isUpdateStaged(mLevelIndexGL, layerIndex))
// All updates should be staged on the image that owns the data as the source of truth. With
// multisampled-render-to-texture framebuffers, that is the resolve image. In that case, even
// though deferred clears set the loadOp of the transient multisampled image, the clears
// themselves are staged on the resolve image. The |flushSingleSubresourceStagedUpdates| call
// below will either flush all staged updates to the resolve image, or if the only staged update
// is a clear, it will accumulate it in the |deferredClears| array. Later, when the render pass
// is started, the deferred clears are applied to the transient multisampled image.
ASSERT(!isResolveImageOwnerOfData() || !mImage->isUpdateStaged(mLevelIndexGL, layerIndex));
ASSERT(isResolveImageOwnerOfData() || mResolveImage == nullptr ||
!mResolveImage->isUpdateStaged(mLevelIndexGL, layerIndex));
if (!image->isUpdateStaged(mLevelIndexGL, layerIndex))
{
return angle::Result::Continue; return angle::Result::Continue;
}
vk::CommandBuffer *commandBuffer; vk::CommandBuffer *commandBuffer;
ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&commandBuffer)); ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&commandBuffer));
return mImage->flushSingleSubresourceStagedUpdates( return image->flushSingleSubresourceStagedUpdates(
contextVk, mLevelIndexGL, layerIndex, commandBuffer, deferredClears, deferredClearIndex); contextVk, mLevelIndexGL, layerIndex, commandBuffer, deferredClears, deferredClearIndex);
} }
void RenderTargetVk::retainImageViews(ContextVk *contextVk) const void RenderTargetVk::retainImageViews(ContextVk *contextVk) const
{ {
mImageViews->retain(&contextVk->getResourceUseList()); mImageViews->retain(&contextVk->getResourceUseList());
if (mResolveImageViews)
{
mResolveImageViews->retain(&contextVk->getResourceUseList());
}
} }
gl::ImageIndex RenderTargetVk::getImageIndex() const gl::ImageIndex RenderTargetVk::getImageIndex() const
......
...@@ -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()));
......
...@@ -13,46 +13,6 @@ using namespace angle; ...@@ -13,46 +13,6 @@ using namespace angle;
namespace namespace
{ {
constexpr char kBasicVertexShader[] =
R"(attribute vec3 position;
void main()
{
gl_Position = vec4(position, 1);
})";
constexpr char kGreenFragmentShader[] =
R"(void main()
{
gl_FragColor = vec4(0, 1, 0, 1);
})";
constexpr char kRedFragmentShader[] =
R"(void main()
{
gl_FragColor = vec4(1, 0, 0, 1);
})";
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";
class MultisampledRenderToTextureTest : public ANGLETest class MultisampledRenderToTextureTest : public ANGLETest
{ {
protected: protected:
...@@ -72,16 +32,18 @@ class MultisampledRenderToTextureTest : public ANGLETest ...@@ -72,16 +32,18 @@ class MultisampledRenderToTextureTest : public ANGLETest
void setupCopyTexProgram() void setupCopyTexProgram()
{ {
mCopyTextureProgram.makeRaster(kVS, kFS); mCopyTextureProgram.makeRaster(essl1_shaders::vs::Texture2D(),
essl1_shaders::fs::Texture2D());
ASSERT_GL_TRUE(mCopyTextureProgram.valid()); ASSERT_GL_TRUE(mCopyTextureProgram.valid());
mCopyTextureUniformLocation = glGetUniformLocation(mCopyTextureProgram, "tex"); mCopyTextureUniformLocation =
glGetUniformLocation(mCopyTextureProgram, essl1_shaders::Texture2DUniform());
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
} }
void verifyResults(GLuint texture, void verifyResults(GLuint texture,
GLubyte data[4], const GLColor expected,
GLint fboSize, GLint fboSize,
GLint xs, GLint xs,
GLint ys, GLint ys,
...@@ -97,14 +59,14 @@ class MultisampledRenderToTextureTest : public ANGLETest ...@@ -97,14 +59,14 @@ class MultisampledRenderToTextureTest : public ANGLETest
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(mCopyTextureUniformLocation, 0); glUniform1i(mCopyTextureUniformLocation, 0);
drawQuad(mCopyTextureProgram, "position", 0.5f); drawQuad(mCopyTextureProgram, 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_COLOR_NEAR(xs, ys, expected, 1.0);
EXPECT_PIXEL_NEAR(xs, ye - 1, data[0], data[1], data[2], data[3], 1.0); EXPECT_PIXEL_COLOR_NEAR(xs, ye - 1, expected, 1.0);
EXPECT_PIXEL_NEAR(xe - 1, ys, data[0], data[1], data[2], data[3], 1.0); EXPECT_PIXEL_COLOR_NEAR(xe - 1, ys, expected, 1.0);
EXPECT_PIXEL_NEAR(xe - 1, ye - 1, data[0], data[1], data[2], data[3], 1.0); EXPECT_PIXEL_COLOR_NEAR(xe - 1, ye - 1, expected, 1.0);
EXPECT_PIXEL_NEAR((xs + xe) / 2, (ys + ye) / 2, data[0], data[1], data[2], data[3], 1.0); EXPECT_PIXEL_COLOR_NEAR((xs + xe) / 2, (ys + ye) / 2, expected, 1.0);
} }
void clearAndDrawQuad(GLuint program, GLsizei viewportWidth, GLsizei viewportHeight) void clearAndDrawQuad(GLuint program, GLsizei viewportWidth, GLsizei viewportHeight)
...@@ -114,7 +76,8 @@ class MultisampledRenderToTextureTest : public ANGLETest ...@@ -114,7 +76,8 @@ class MultisampledRenderToTextureTest : public ANGLETest
glViewport(0, 0, viewportWidth, viewportHeight); glViewport(0, 0, viewportWidth, viewportHeight);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
drawQuad(program, "position", 0.0f); drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
ASSERT_GL_NO_ERROR();
} }
GLProgram mCopyTextureProgram; GLProgram mCopyTextureProgram;
...@@ -290,13 +253,24 @@ TEST_P(MultisampledRenderToTextureTest, FramebufferCompleteness) ...@@ -290,13 +253,24 @@ TEST_P(MultisampledRenderToTextureTest, FramebufferCompleteness)
texture, 0, 4); texture, 0, 4);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
GLsizei maxSamples = 0;
glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
GLRenderbuffer renderbuffer; GLRenderbuffer renderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 8, GL_DEPTH_COMPONENT16, 64, 64); glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, maxSamples, GL_DEPTH_COMPONENT16, 64, 64);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
glCheckFramebufferStatus(GL_FRAMEBUFFER)); if (maxSamples > 4)
{
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
else
{
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
}
} }
// Draw test with color attachment only. // Draw test with color attachment only.
...@@ -304,10 +278,10 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorAttachmentMultisampleDrawTest) ...@@ -304,10 +278,10 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorAttachmentMultisampleDrawTest)
{ {
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
// Set up texture and bind to FBO // Set up texture and bind to FBO
GLsizei size = 6; constexpr GLsizei kSize = 6;
GLTexture texture; GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
GLFramebuffer FBO; GLFramebuffer FBO;
...@@ -317,14 +291,14 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorAttachmentMultisampleDrawTest) ...@@ -317,14 +291,14 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorAttachmentMultisampleDrawTest)
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Set viewport and clear to black // Set viewport and clear to black
glViewport(0, 0, size, size); glViewport(0, 0, kSize, kSize);
glClearColor(0.0, 0.0, 0.0, 1.0); glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
// Set up Green square program // Set up Green square program
ANGLE_GL_PROGRAM(program, kBasicVertexShader, kGreenFragmentShader); ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
glUseProgram(program); glUseProgram(program);
GLint positionLocation = glGetAttribLocation(program, "position"); GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
ASSERT_NE(-1, positionLocation); ASSERT_NE(-1, positionLocation);
setupQuadVertexBuffer(0.5f, 0.5f); setupQuadVertexBuffer(0.5f, 0.5f);
...@@ -336,12 +310,12 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorAttachmentMultisampleDrawTest) ...@@ -336,12 +310,12 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorAttachmentMultisampleDrawTest)
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
EXPECT_PIXEL_COLOR_EQ(size / 2, size / 2, GLColor::green); EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::green);
// Set up Red square program // Set up Red square program
ANGLE_GL_PROGRAM(program2, kBasicVertexShader, kRedFragmentShader); ANGLE_GL_PROGRAM(program2, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
glUseProgram(program2); glUseProgram(program2);
GLint positionLocation2 = glGetAttribLocation(program2, "position"); GLint positionLocation2 = glGetAttribLocation(program2, essl1_shaders::PositionAttrib());
ASSERT_NE(-1, positionLocation2); ASSERT_NE(-1, positionLocation2);
setupQuadVertexBuffer(0.5f, 0.75f); setupQuadVertexBuffer(0.5f, 0.75f);
...@@ -352,7 +326,7 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorAttachmentMultisampleDrawTest) ...@@ -352,7 +326,7 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorAttachmentMultisampleDrawTest)
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
EXPECT_PIXEL_COLOR_EQ(size / 2, size / 2, GLColor::red); EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::red);
glDisableVertexAttribArray(0); glDisableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
...@@ -362,11 +336,11 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorAttachmentMultisampleDrawTest) ...@@ -362,11 +336,11 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorAttachmentMultisampleDrawTest)
TEST_P(MultisampledRenderToTextureTest, 2DColorDepthMultisampleDrawTest) TEST_P(MultisampledRenderToTextureTest, 2DColorDepthMultisampleDrawTest)
{ {
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
GLsizei size = 6; constexpr GLsizei kSize = 6;
// create complete framebuffer with depth buffer // create complete framebuffer with depth buffer
GLTexture texture; GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
GLFramebuffer FBO; GLFramebuffer FBO;
...@@ -376,23 +350,23 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorDepthMultisampleDrawTest) ...@@ -376,23 +350,23 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorDepthMultisampleDrawTest)
GLRenderbuffer renderbuffer; GLRenderbuffer renderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, size, size); glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, kSize, kSize);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Set viewport and clear framebuffer // Set viewport and clear framebuffer
glViewport(0, 0, size, size); glViewport(0, 0, kSize, kSize);
glClearColor(0.0, 0.0, 0.0, 1.0); glClearColor(0.0, 0.0, 0.0, 1.0);
glClearDepthf(0.5f); glClearDepthf(0.5f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Draw first green square // Draw first green square
ANGLE_GL_PROGRAM(program, kBasicVertexShader, kGreenFragmentShader); ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_GREATER); glDepthFunc(GL_GREATER);
glUseProgram(program); glUseProgram(program);
GLint positionLocation = glGetAttribLocation(program, "position"); GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
ASSERT_NE(-1, positionLocation); ASSERT_NE(-1, positionLocation);
setupQuadVertexBuffer(0.8f, 0.5f); setupQuadVertexBuffer(0.8f, 0.5f);
...@@ -404,12 +378,12 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorDepthMultisampleDrawTest) ...@@ -404,12 +378,12 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorDepthMultisampleDrawTest)
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
EXPECT_PIXEL_COLOR_EQ(size / 2, size / 2, GLColor::green); EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::green);
// Draw red square behind green square // Draw red square behind green square
ANGLE_GL_PROGRAM(program2, kBasicVertexShader, kRedFragmentShader); ANGLE_GL_PROGRAM(program2, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
glUseProgram(program2); glUseProgram(program2);
GLint positionLocation2 = glGetAttribLocation(program2, "position"); GLint positionLocation2 = glGetAttribLocation(program2, essl1_shaders::PositionAttrib());
ASSERT_NE(-1, positionLocation2); ASSERT_NE(-1, positionLocation2);
setupQuadVertexBuffer(0.7f, 1.0f); setupQuadVertexBuffer(0.7f, 1.0f);
...@@ -420,23 +394,21 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorDepthMultisampleDrawTest) ...@@ -420,23 +394,21 @@ TEST_P(MultisampledRenderToTextureTest, 2DColorDepthMultisampleDrawTest)
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(size / 2, size / 2, GLColor::green); EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::green);
glDisableVertexAttribArray(0); glDisableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
} }
// Read pixels with pack buffer. ES3+. // Read pixels with pack buffer. ES3+.
TEST_P(MultisampledRenderToTextureES3Test, MultisampleReadPixelsTest) TEST_P(MultisampledRenderToTextureES3Test, ReadPixelsTest)
{ {
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
// PBO only available ES3 and above constexpr GLsizei kSize = 6;
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
GLsizei size = 6;
GLTexture texture; GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, size, size); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kSize, kSize);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
GLFramebuffer FBO; GLFramebuffer FBO;
...@@ -446,7 +418,7 @@ TEST_P(MultisampledRenderToTextureES3Test, MultisampleReadPixelsTest) ...@@ -446,7 +418,7 @@ TEST_P(MultisampledRenderToTextureES3Test, MultisampleReadPixelsTest)
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Set viewport and clear to red // Set viewport and clear to red
glViewport(0, 0, size, size); glViewport(0, 0, kSize, kSize);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f); glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
...@@ -454,8 +426,8 @@ TEST_P(MultisampledRenderToTextureES3Test, MultisampleReadPixelsTest) ...@@ -454,8 +426,8 @@ TEST_P(MultisampledRenderToTextureES3Test, MultisampleReadPixelsTest)
// Bind Pack Pixel Buffer and read to it // Bind Pack Pixel Buffer and read to it
GLBuffer PBO; GLBuffer PBO;
glBindBuffer(GL_PIXEL_PACK_BUFFER, PBO); glBindBuffer(GL_PIXEL_PACK_BUFFER, PBO);
glBufferData(GL_PIXEL_PACK_BUFFER, 4 * size * size, nullptr, GL_STATIC_DRAW); glBufferData(GL_PIXEL_PACK_BUFFER, 4 * kSize * kSize, nullptr, GL_STATIC_DRAW);
glReadPixels(0, 0, size, size, GL_RGBA, GL_UNSIGNED_BYTE, 0); glReadPixels(0, 0, kSize, kSize, GL_RGBA, GL_UNSIGNED_BYTE, 0);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
// Retrieving pixel color // Retrieving pixel color
...@@ -470,15 +442,15 @@ TEST_P(MultisampledRenderToTextureES3Test, MultisampleReadPixelsTest) ...@@ -470,15 +442,15 @@ TEST_P(MultisampledRenderToTextureES3Test, MultisampleReadPixelsTest)
} }
// CopyTexImage from a multisampled texture functionality test. // CopyTexImage from a multisampled texture functionality test.
TEST_P(MultisampledRenderToTextureTest, MultisampleCopyTexImageTest) TEST_P(MultisampledRenderToTextureTest, CopyTexImageTest)
{ {
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
GLsizei size = 16; constexpr GLsizei kSize = 16;
setupCopyTexProgram(); setupCopyTexProgram();
GLTexture texture; GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// Disable mipmapping // Disable mipmapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
...@@ -501,25 +473,27 @@ TEST_P(MultisampledRenderToTextureTest, MultisampleCopyTexImageTest) ...@@ -501,25 +473,27 @@ TEST_P(MultisampledRenderToTextureTest, MultisampleCopyTexImageTest)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, size, size, 0); glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, kSize, kSize, 0);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
GLubyte expected[4] = {64, 255, 191, 255}; verifyResults(copyToTex, {64, 255, 191, 255}, kSize, 0, 0, kSize, kSize);
verifyResults(copyToTex, expected, size, 0, 0, size, size);
} }
// CopyTexSubImage from a multisampled texture functionality test. // CopyTexSubImage from a multisampled texture functionality test.
TEST_P(MultisampledRenderToTextureTest, MultisampleCopyTexSubImageTest) TEST_P(MultisampledRenderToTextureTest, CopyTexSubImageTest)
{ {
// Fails on Pixel 2. http://anglebug.com/4906
ANGLE_SKIP_TEST_IF(IsAndroid());
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
GLsizei size = 16; constexpr GLsizei kSize = 16;
setupCopyTexProgram(); setupCopyTexProgram();
GLTexture texture; GLTexture texture;
// Create texture in copyFBO0 with color (.25, 1, .75, .5) // Create texture in copyFBO0 with color (.25, 1, .75, .5)
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// Disable mipmapping // Disable mipmapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
...@@ -538,7 +512,7 @@ TEST_P(MultisampledRenderToTextureTest, MultisampleCopyTexSubImageTest) ...@@ -538,7 +512,7 @@ TEST_P(MultisampledRenderToTextureTest, MultisampleCopyTexSubImageTest)
// Create texture in copyFBO[1] with color (1, .75, .5, .25) // Create texture in copyFBO[1] with color (1, .75, .5, .25)
GLTexture texture1; GLTexture texture1;
glBindTexture(GL_TEXTURE_2D, texture1); glBindTexture(GL_TEXTURE_2D, texture1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// Disable mipmapping // Disable mipmapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
...@@ -564,50 +538,48 @@ TEST_P(MultisampledRenderToTextureTest, MultisampleCopyTexSubImageTest) ...@@ -564,50 +538,48 @@ TEST_P(MultisampledRenderToTextureTest, MultisampleCopyTexSubImageTest)
// copyFBO0 -> copyToTex // copyFBO0 -> copyToTex
// copyToTex should hold what was originally in copyFBO0 : (.25, 1, .75, .5) // copyToTex should hold what was originally in copyFBO0 : (.25, 1, .75, .5)
glBindFramebuffer(GL_FRAMEBUFFER, copyFBO0); glBindFramebuffer(GL_FRAMEBUFFER, copyFBO0);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, size, size, 0); glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, kSize, kSize, 0);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
GLubyte expected0[4] = {64, 255, 191, 255}; const GLColor expected0(64, 255, 191, 255);
verifyResults(copyToTex, expected0, size, 0, 0, size, size); verifyResults(copyToTex, expected0, kSize, 0, 0, kSize, kSize);
// copyFBO[1] - copySubImage -> copyToTex // copyFBO[1] - copySubImage -> copyToTex
// copyToTex should have subportion what was in copyFBO[1] : (1, .75, .5, .25) // copyToTex should have subportion what was in copyFBO[1] : (1, .75, .5, .25)
// The rest should still be untouched: (.25, 1, .75, .5) // The rest should still be untouched: (.25, 1, .75, .5)
GLint half = size / 2; GLint half = kSize / 2;
glBindFramebuffer(GL_FRAMEBUFFER, copyFBO1); glBindFramebuffer(GL_FRAMEBUFFER, copyFBO1);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, half, half, half, half, half, half); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, half, half, half, half, half, half);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
GLubyte expected1[4] = {255, 191, 127, 255}; const GLColor expected1(255, 191, 127, 255);
verifyResults(copyToTex, expected1, size, half, half, size, size); verifyResults(copyToTex, expected1, kSize, half, half, kSize, kSize);
// Verify rest is untouched // Verify rest is untouched
verifyResults(copyToTex, expected0, size, 0, 0, half, half); verifyResults(copyToTex, expected0, kSize, 0, 0, half, half);
verifyResults(copyToTex, expected0, size, 0, half, half, size); verifyResults(copyToTex, expected0, kSize, 0, half, half, kSize);
verifyResults(copyToTex, expected0, size, half, 0, size, half); verifyResults(copyToTex, expected0, kSize, half, 0, kSize, half);
} }
// BlitFramebuffer functionality test. ES3+. // BlitFramebuffer functionality test. ES3+.
TEST_P(MultisampledRenderToTextureES3Test, MultisampleBlitFramebufferTest) TEST_P(MultisampledRenderToTextureES3Test, BlitFramebufferTest)
{ {
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
// blitFramebuffer only available ES3 and above
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
GLsizei size = 16; constexpr GLsizei kSize = 16;
// Create multisampled framebuffer to use as source. // Create multisampled framebuffer to use as source.
GLRenderbuffer depthMS; GLRenderbuffer depthMS;
glBindRenderbuffer(GL_RENDERBUFFER, depthMS.get()); glBindRenderbuffer(GL_RENDERBUFFER, depthMS);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT24, size, size); glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT24, kSize, kSize);
GLTexture colorMS; GLTexture colorMS;
glBindTexture(GL_TEXTURE_2D, colorMS); glBindTexture(GL_TEXTURE_2D, colorMS);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLFramebuffer fboMS; GLFramebuffer fboMS;
glBindFramebuffer(GL_FRAMEBUFFER, fboMS); glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthMS.get()); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthMS);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
colorMS, 0, 4); colorMS, 0, 4);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
...@@ -623,7 +595,7 @@ TEST_P(MultisampledRenderToTextureES3Test, MultisampleBlitFramebufferTest) ...@@ -623,7 +595,7 @@ TEST_P(MultisampledRenderToTextureES3Test, MultisampleBlitFramebufferTest)
ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_EQUAL); glDepthFunc(GL_EQUAL);
drawQuad(drawRed.get(), essl1_shaders::PositionAttrib(), 0.0f); drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.0f);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
// Create single sampled framebuffer to use as dest. // Create single sampled framebuffer to use as dest.
...@@ -631,61 +603,42 @@ TEST_P(MultisampledRenderToTextureES3Test, MultisampleBlitFramebufferTest) ...@@ -631,61 +603,42 @@ TEST_P(MultisampledRenderToTextureES3Test, MultisampleBlitFramebufferTest)
glBindFramebuffer(GL_FRAMEBUFFER, fboSS); glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
GLTexture colorSS; GLTexture colorSS;
glBindTexture(GL_TEXTURE_2D, colorSS); glBindTexture(GL_TEXTURE_2D, colorSS);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorSS, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorSS, 0);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
// Bind MS to READ as SS is already bound to DRAW. // Bind MS to READ as SS is already bound to DRAW.
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboMS.get()); glBindFramebuffer(GL_READ_FRAMEBUFFER, fboMS);
glBlitFramebuffer(0, 0, size, size, 0, 0, size, size, GL_COLOR_BUFFER_BIT, GL_NEAREST); glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
// Bind SS to READ so we can readPixels from it // Bind SS to READ so we can readPixels from it
glBindFramebuffer(GL_FRAMEBUFFER, fboSS.get()); glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(size - 1, 0, GLColor::red); EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(0, size - 1, GLColor::red); EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(size - 1, size - 1, GLColor::red); EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(size / 2, size / 2, GLColor::red); EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::red);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
} }
// GenerateMipmap functionality test // GenerateMipmap functionality test
TEST_P(MultisampledRenderToTextureTest, MultisampleGenerateMipmapTest) TEST_P(MultisampledRenderToTextureTest, GenerateMipmapTest)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
GLsizei size = 64;
// Vertex Shader source
constexpr char kVS[] = R"(attribute vec4 position;
varying vec2 vTexCoord;
void main()
{
gl_Position = position;
vTexCoord = (position.xy * 0.5) + 0.5;
})";
// Fragment Shader source
constexpr char kFS[] = R"(precision mediump float;
uniform sampler2D uTexture;
varying vec2 vTexCoord;
void main()
{ {
gl_FragColor = texture2D(uTexture, vTexCoord); // Fails on Pixel 2. http://anglebug.com/4906
})"; ANGLE_SKIP_TEST_IF(IsAndroid());
GLProgram m2DProgram; ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
m2DProgram.makeRaster(kVS, kFS); constexpr GLsizei kSize = 64;
ASSERT_GL_TRUE(m2DProgram.valid());
ASSERT_GL_NO_ERROR(); setupCopyTexProgram();
glUseProgram(mCopyTextureProgram);
// Initialize texture with blue // Initialize texture with blue
GLTexture texture; GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size, size, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, kSize, kSize, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
...@@ -696,7 +649,7 @@ void main() ...@@ -696,7 +649,7 @@ void main()
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
glClearColor(0.0f, 0.0f, 1.0f, 1.0f); glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, size, size); glViewport(0, 0, kSize, kSize);
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
...@@ -707,32 +660,83 @@ void main() ...@@ -707,32 +660,83 @@ void main()
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
// Now draw the texture to various different sized areas. // Now draw the texture to various different sized areas.
clearAndDrawQuad(m2DProgram, size, size); clearAndDrawQuad(mCopyTextureProgram, kSize, kSize);
EXPECT_PIXEL_COLOR_EQ(size / 2, size / 2, GLColor::blue); EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::blue);
// Use mip level 1 // Use mip level 1
clearAndDrawQuad(m2DProgram, size / 2, size / 2); clearAndDrawQuad(mCopyTextureProgram, kSize / 2, kSize / 2);
EXPECT_PIXEL_COLOR_EQ(size / 4, size / 4, GLColor::blue); EXPECT_PIXEL_COLOR_EQ(kSize / 4, kSize / 4, GLColor::blue);
// Use mip level 2 // Use mip level 2
clearAndDrawQuad(m2DProgram, size / 4, size / 4); clearAndDrawQuad(mCopyTextureProgram, kSize / 4, kSize / 4);
EXPECT_PIXEL_COLOR_EQ(size / 8, size / 8, GLColor::blue); EXPECT_PIXEL_COLOR_EQ(kSize / 8, kSize / 8, GLColor::blue);
ASSERT_GL_NO_ERROR();
}
// Draw, copy, then blend. The copy will make sure an implicit resolve happens. Regardless, the
// following draw should retain the data written by the first draw command.
TEST_P(MultisampledRenderToTextureTest, DrawCopyThenBlend)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
constexpr GLsizei kSize = 64;
setupCopyTexProgram();
// Create multisampled framebuffer to draw into
GLTexture colorMS;
glBindTexture(GL_TEXTURE_2D, colorMS);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLFramebuffer fboMS;
glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
colorMS, 0, 4);
ASSERT_GL_NO_ERROR();
// Draw red into the multisampled color buffer.
ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
glUseProgram(drawColor);
GLint colorUniformLocation =
glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Create a texture and copy into it.
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Draw again into the framebuffer, this time blending. This tests that the framebuffer's data,
// residing in the single-sampled texture, is available to the multisampled intermediate image
// for blending.
// Blend half-transparent green into the multisampled color buffer.
glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 0.5f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Verify that the texture is now yellow
const GLColor kExpected(127, 127, 0, 191);
EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
// For completeness, verify that the texture used as copy target is red.
const GLColor expectedCopyResult(255, 0, 0, 255);
verifyResults(texture, expectedCopyResult, kSize, 0, 0, kSize, kSize);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
} }
ANGLE_INSTANTIATE_TEST(MultisampledRenderToTextureTest,
ES2_D3D9(), ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(MultisampledRenderToTextureTest);
ES2_D3D11(), ANGLE_INSTANTIATE_TEST_ES3(MultisampledRenderToTextureES3Test);
ES3_D3D11(),
ES2_OPENGL(),
ES3_OPENGL(),
ES2_OPENGLES(),
ES3_OPENGLES(),
ES2_VULKAN(),
ES3_VULKAN());
ANGLE_INSTANTIATE_TEST(MultisampledRenderToTextureES3Test,
ES3_D3D11(),
ES3_OPENGL(),
ES3_OPENGLES(),
ES3_VULKAN());
} // namespace } // namespace
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