Vulkan: Optimize MSAA using subpass resolve attachments

If a user is performing a blit to resolve a multisample color buffer
into a single attachment, ANGLE can use subpass resolve attachments to
resolve directly into the destination buffer as part of the render pass.
This allows the data to remain in tiler memory and reduce the extra
bandwidth required to write the multisampled data back to perform the
copy.

This work also requires restoring/reopening a render pass if it has been
finished already, assuming the finished render pass was started and for
the framebuffer that is the source for the blit command. Other objects
that were created when the render pass was started need to be updated as
well, such as the source FramebufferVk's resolve attachment, the
CommandBufferHelper's vk::Framebuffer and vk::RenderPassDesc, etc.

While this is better than performing vkCmdResolveImage(), there is still
another major part of optimizing MSAA using resolve attachments not
implemented here: discarding the multisampled image rather than writing
it to GMEM, which requires the user to invalidate the read FBO after the
blit.

This CL was verified with AGI to make sure there are no explicit blits
to resolve the multiple sampled image.

Bug: b/159903491
Test: FramebufferTest_ES31.*Blit*
Test: VulkanPerformanceCounterTest_ES31.MultisampleResolveWithBlit
Change-Id: I320a26088d8f614a295e7feec275d71310391806
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2298663
Commit-Queue: Tim Van Patten <timvp@google.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Charlie Lao <cclao@google.com>
This commit is contained in:
Tim Van Patten 2020-07-14 19:10:12 -06:00 коммит произвёл Commit Bot
Родитель 5dff6075d2
Коммит dff47d5fda
12 изменённых файлов: 548 добавлений и 41 удалений

Просмотреть файл

@ -4071,6 +4071,7 @@ angle::Result ContextVk::flushImpl(const vk::Semaphore *signalSemaphore)
mPerfCounters.renderPasses = 0;
mPerfCounters.writeDescriptorSets = 0;
mPerfCounters.flushedOutsideRenderPassCommandBuffers = 0;
mPerfCounters.resolveImageCommands = 0;
mWaitSemaphores.clear();
mWaitSemaphoreStageMasks.clear();
@ -4484,6 +4485,27 @@ angle::Result ContextVk::startRenderPass(gl::Rectangle renderArea,
return angle::Result::Continue;
}
void ContextVk::restoreFinishedRenderPass(vk::Framebuffer *framebuffer)
{
if (mRenderPassCommandBuffer != nullptr)
{
// The render pass isn't finished yet, so nothing to restore.
return;
}
if (mRenderPassCommands->started() &&
mRenderPassCommands->getFramebufferHandle() == framebuffer->getHandle())
{
// There is already a render pass open for this framebuffer, so just restore the
// pointer rather than starting a whole new render pass. One possible path here
// is if the draw framebuffer binding has changed from FBO A -> B -> A, without
// any commands that started a new render pass for FBO B (such as a clear being
// issued that was deferred).
mRenderPassCommandBuffer = &mRenderPassCommands->getCommandBuffer();
ASSERT(hasStartedRenderPass());
}
}
angle::Result ContextVk::flushCommandsAndEndRenderPass()
{
// Ensure we flush the RenderPass *after* the prior commands.

Просмотреть файл

@ -574,6 +574,9 @@ class ContextVk : public ContextImpl, public vk::Context
return *mRenderPassCommands;
}
// TODO(https://anglebug.com/4968): Support multiple open render passes.
void restoreFinishedRenderPass(vk::Framebuffer *framebuffer);
egl::ContextPriority getContextPriority() const override { return mContextPriority; }
angle::Result startRenderPass(gl::Rectangle renderArea, vk::CommandBuffer **commandBufferOut);
angle::Result flushCommandsAndEndRenderPass();
@ -628,6 +631,7 @@ class ContextVk : public ContextImpl, public vk::Context
vk::DynamicBuffer *getStagingBufferStorage() { return &mStagingBufferStorage; }
const vk::PerfCounters &getPerfCounters() const { return mPerfCounters; }
vk::PerfCounters &getPerfCounters() { return mPerfCounters; }
private:
// Dirty bits.

Просмотреть файл

@ -246,6 +246,20 @@ void AdjustBlitResolveParametersForPreRotation(SurfaceRotation framebufferAngle,
break;
}
}
bool HasResolveAttachment(const gl::AttachmentArray<RenderTargetVk *> &colorRenderTargets,
const gl::DrawBufferMask &getEnabledDrawBuffers)
{
for (size_t colorIndexGL : getEnabledDrawBuffers)
{
RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndexGL];
if (colorRenderTarget->hasResolveAttachment())
{
return true;
}
}
return false;
}
} // anonymous namespace
// static
@ -461,7 +475,7 @@ angle::Result FramebufferVk::clearImpl(const gl::Context *context,
if (clearAnyWithRenderPassLoadOp)
{
vk::Framebuffer *currentFramebuffer = nullptr;
ANGLE_TRY(getFramebuffer(contextVk, &currentFramebuffer));
ANGLE_TRY(getFramebuffer(contextVk, &currentFramebuffer, nullptr));
gl::DrawBufferMask clearColorDrawBuffersMask;
if (clearColorWithRenderPassLoadOp)
@ -1082,8 +1096,40 @@ angle::Result FramebufferVk::blit(const gl::Context *context,
else if (isColorResolve && !flipX && !flipY && areChannelsBlitCompatible &&
(rotation == SurfaceRotation::Identity))
{
ANGLE_TRY(
resolveColorWithCommand(contextVk, params, &readRenderTarget->getImageForCopy()));
// Resolving with a subpass resolve attachment has a few restrictions:
// 1.) glBlitFramebuffer() needs to copy the read color attachment to all enabled
// attachments in the draw framebuffer, but Vulkan requires a 1:1 relationship for
// multisample attachments to resolve attachments in the render pass subpass.
// Due to this, we currently only support using resolve attachments when there is a
// single draw attachment enabled.
// 2.) Using a subpass resolve attachment relies on using the render pass that performs
// the draw to still be open, so it can be updated to use the resolve attachment to draw
// into. If there's no render pass with commands, then the multisampled render pass is
// already done and whose data is already flushed from the tile (in a tile-based
// renderer), so there's no chance for the resolve attachment to take advantage of the
// data already being present in the tile.
vk::Framebuffer *srcVkFramebuffer = nullptr;
ANGLE_TRY(srcFramebufferVk->getFramebuffer(contextVk, &srcVkFramebuffer, nullptr));
// TODO(https://anglebug.com/4968): Support multiple open render passes so we can remove
// this hack to 'restore' the finished render pass.
contextVk->restoreFinishedRenderPass(srcVkFramebuffer);
if ((mState.getEnabledDrawBuffers().count() == 1) &&
contextVk->hasStartedRenderPassWithFramebuffer(srcVkFramebuffer))
{
// glBlitFramebuffer() needs to copy the read color attachment to all enabled
// attachments in the draw framebuffer, but Vulkan requires a 1:1 relationship for
// multisample attachments to resolve attachments in the render pass subpass.
// Due to this, we currently only support using resolve attachments when there is a
// single draw attachment enabled.
ANGLE_TRY(resolveColorWithSubpass(contextVk, params));
}
else
{
ANGLE_TRY(resolveColorWithCommand(contextVk, params,
&readRenderTarget->getImageForCopy()));
}
}
// Otherwise use a shader to do blit or resolve.
else
@ -1186,6 +1232,62 @@ angle::Result FramebufferVk::blit(const gl::Context *context,
return angle::Result::Continue;
} // namespace rx
void FramebufferVk::updateColorResolveAttachment(
uint32_t colorIndexGL,
vk::ImageViewSubresourceSerial resolveImageViewSerial)
{
mCurrentFramebufferDesc.updateColorResolve(colorIndexGL, resolveImageViewSerial);
mFramebuffer = nullptr;
mRenderPassDesc.packColorResolveAttachment(colorIndexGL);
}
angle::Result FramebufferVk::resolveColorWithSubpass(ContextVk *contextVk,
const UtilsVk::BlitResolveParameters &params)
{
// Vulkan requires a 1:1 relationship for multisample attachments to resolve attachments in the
// render pass subpass. Due to this, we currently only support using resolve attachments when
// there is a single draw attachment enabled.
ASSERT(mState.getEnabledDrawBuffers().count() == 1);
uint32_t colorIndexGL = static_cast<uint32_t>(*mState.getEnabledDrawBuffers().begin());
const gl::State &glState = contextVk->getState();
const gl::Framebuffer *srcFramebuffer = glState.getReadFramebuffer();
FramebufferVk *srcFramebufferVk = vk::GetImpl(srcFramebuffer);
// Use the draw FBO's color attachments as resolve attachments in the read FBO.
// - Assign the draw FBO's color attachment Serial to the read FBO's resolve attachment
// - Deactivate the source Framebuffer, since the description changed
// - Update the renderpass description to indicate there's a resolve attachment
vk::ImageViewSubresourceSerial resolveImageViewSerial =
mCurrentFramebufferDesc.getColorImageViewSerial(colorIndexGL);
ASSERT(resolveImageViewSerial.imageViewSerial.valid());
srcFramebufferVk->updateColorResolveAttachment(colorIndexGL, resolveImageViewSerial);
// Since tha source FBO was updated with a resolve attachment it didn't have when the render
// pass was started, we need to:
// 1. Get the new framebuffer
// - The draw framebuffer's ImageView will be used as the resolve attachment, so pass it along
// in case vkCreateFramebuffer() needs to be called to create a new vkFramebuffer with the new
// resolve attachment.
RenderTargetVk *drawRenderTarget = mRenderTargetCache.getColors()[colorIndexGL];
const vk::ImageView *resolveImageView = nullptr;
ANGLE_TRY(drawRenderTarget->getImageView(contextVk, &resolveImageView));
vk::Framebuffer *newSrcFramebuffer = nullptr;
ANGLE_TRY(srcFramebufferVk->getFramebuffer(contextVk, &newSrcFramebuffer, resolveImageView));
// 2. Update the CommandBufferHelper with the new framebuffer and render pass
vk::CommandBufferHelper &commandBufferHelper = contextVk->getStartedRenderPassCommands();
commandBufferHelper.updateRenderPassForResolve(newSrcFramebuffer,
srcFramebufferVk->getRenderPassDesc());
// End the render pass now since we don't (yet) support subpass dependencies.
RenderTargetVk *readRenderTarget = getColorReadRenderTarget();
contextVk->onImageRenderPassWrite(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::ColorAttachment,
&readRenderTarget->getImageForRenderPass());
ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass());
return angle::Result::Continue;
}
angle::Result FramebufferVk::resolveColorWithCommand(ContextVk *contextVk,
const UtilsVk::BlitResolveParameters &params,
vk::ImageHelper *srcImage)
@ -1209,6 +1311,7 @@ angle::Result FramebufferVk::resolveColorWithCommand(ContextVk *contextVk,
resolveRegion.extent.height = params.srcExtents[1];
resolveRegion.extent.depth = 1;
vk::PerfCounters &perfCounters = contextVk->getPerfCounters();
for (size_t colorIndexGL : mState.getEnabledDrawBuffers())
{
RenderTargetVk *drawRenderTarget = mRenderTargetCache.getColors()[colorIndexGL];
@ -1223,6 +1326,8 @@ angle::Result FramebufferVk::resolveColorWithCommand(ContextVk *contextVk,
resolveRegion.dstSubresource.baseArrayLayer = drawRenderTarget->getLayerIndex();
srcImage->resolve(&dstImage, resolveRegion, &commandBuffer);
perfCounters.resolveImageCommands++;
}
return angle::Result::Continue;
@ -1350,7 +1455,7 @@ angle::Result FramebufferVk::invalidateImpl(ContextVk *contextVk,
//- Bind FBO 1, invalidate D/S
// to invalidate the D/S of FBO 2 since it would be the currently active renderpass.
vk::Framebuffer *currentFramebuffer = nullptr;
ANGLE_TRY(getFramebuffer(contextVk, &currentFramebuffer));
ANGLE_TRY(getFramebuffer(contextVk, &currentFramebuffer, nullptr));
if (contextVk->hasStartedRenderPassWithFramebuffer(currentFramebuffer))
{
@ -1644,7 +1749,16 @@ angle::Result FramebufferVk::syncState(const gl::Context *context,
mActiveColorComponentMasksForClear[0].any(), mActiveColorComponentMasksForClear[1].any(),
mActiveColorComponentMasksForClear[2].any(), mActiveColorComponentMasksForClear[3].any());
ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass());
if (command != gl::Command::Blit)
{
// Don't end the render pass when handling a blit to resolve, since we may be able to
// optimize that path which requires modifying the current render pass.
// We're deferring the resolve check to FramebufferVk::blit(), since if the read buffer is
// multisampled-render-to-texture, then srcFramebuffer->getSamples(context) gives > 1, but
// there's no resolve happening as the read buffer's singlesampled image will be used as
// blit src. FramebufferVk::blit() will handle those details for us.
ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass());
}
// Notify the ContextVk to update the pipeline desc.
updateRenderPassDesc();
@ -1700,7 +1814,9 @@ void FramebufferVk::updateRenderPassDesc()
}
}
angle::Result FramebufferVk::getFramebuffer(ContextVk *contextVk, vk::Framebuffer **framebufferOut)
angle::Result FramebufferVk::getFramebuffer(ContextVk *contextVk,
vk::Framebuffer **framebufferOut,
const vk::ImageView *resolveImageViewIn)
{
// First return a presently valid Framebuffer
if (mFramebuffer != nullptr)
@ -1723,6 +1839,7 @@ angle::Result FramebufferVk::getFramebuffer(ContextVk *contextVk, vk::Framebuffe
iter->second.release(contextVk);
}
}
vk::RenderPass *compatibleRenderPass = nullptr;
ANGLE_TRY(contextVk->getCompatibleRenderPass(mRenderPassDesc, &compatibleRenderPass));
@ -1767,19 +1884,31 @@ angle::Result FramebufferVk::getFramebuffer(ContextVk *contextVk, vk::Framebuffe
}
// Color resolve attachments.
for (size_t colorIndexGL : mState.getEnabledDrawBuffers())
if (resolveImageViewIn)
{
RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndexGL];
ASSERT(colorRenderTarget);
ASSERT(!HasResolveAttachment(colorRenderTargets, mState.getEnabledDrawBuffers()));
if (colorRenderTarget->hasResolveAttachment())
// Need to use the passed in ImageView for the resolve attachment, since it came from
// another Framebuffer.
attachments.push_back(resolveImageViewIn->getHandle());
}
else
{
// This Framebuffer owns all of the ImageViews, including its own resolve ImageViews.
for (size_t colorIndexGL : mState.getEnabledDrawBuffers())
{
const vk::ImageView *resolveImageView = nullptr;
ANGLE_TRY(colorRenderTarget->getResolveImageView(contextVk, &resolveImageView));
RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndexGL];
ASSERT(colorRenderTarget);
attachments.push_back(resolveImageView->getHandle());
if (colorRenderTarget->hasResolveAttachment())
{
const vk::ImageView *resolveImageView = nullptr;
ANGLE_TRY(colorRenderTarget->getResolveImageView(contextVk, &resolveImageView));
ASSERT(!attachmentsSize.empty());
attachments.push_back(resolveImageView->getHandle());
ASSERT(!attachmentsSize.empty());
}
}
}
@ -2073,7 +2202,7 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
vk::CommandBuffer **commandBufferOut)
{
vk::Framebuffer *framebuffer = nullptr;
ANGLE_TRY(getFramebuffer(contextVk, &framebuffer));
ANGLE_TRY(getFramebuffer(contextVk, &framebuffer, nullptr));
ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass());

Просмотреть файл

@ -122,7 +122,7 @@ class FramebufferVk : public FramebufferImpl
GLint getSamples() const;
const vk::RenderPassDesc &getRenderPassDesc() const { return mRenderPassDesc; }
const vk::FramebufferDesc &getFramebufferDesc() const { return mCurrentFramebufferDesc; }
// We only support depth/stencil packed format and depthstencil attachment always follow all
// color attachments
size_t getDepthStencilAttachmentIndexVk() const
@ -130,6 +130,10 @@ class FramebufferVk : public FramebufferImpl
return getState().getEnabledDrawBuffers().count();
}
angle::Result getFramebuffer(ContextVk *contextVk,
vk::Framebuffer **framebufferOut,
const vk::ImageView *resolveImageViewIn);
private:
FramebufferVk(RendererVk *renderer,
const gl::FramebufferState &state,
@ -148,6 +152,10 @@ class FramebufferVk : public FramebufferImpl
bool flipX,
bool flipY);
// Resolve color with subpass attachment
angle::Result resolveColorWithSubpass(ContextVk *contextVk,
const UtilsVk::BlitResolveParameters &params);
// Resolve color with vkCmdResolveImage
angle::Result resolveColorWithCommand(ContextVk *contextVk,
const UtilsVk::BlitResolveParameters &params,
@ -158,8 +166,6 @@ class FramebufferVk : public FramebufferImpl
angle::Result copyResolveToMultisampedAttachment(ContextVk *contextVk,
RenderTargetVk *colorRenderTarget);
angle::Result getFramebuffer(ContextVk *contextVk, vk::Framebuffer **framebufferOut);
angle::Result clearImpl(const gl::Context *context,
gl::DrawBufferMask clearColorBuffers,
bool clearDepth,
@ -219,6 +225,9 @@ class FramebufferVk : public FramebufferImpl
VkClearValue getCorrectedColorClearValue(size_t colorIndexGL,
const VkClearColorValue &clearColor) const;
void updateColorResolveAttachment(uint32_t colorIndexGL,
vk::ImageViewSubresourceSerial resolveImageViewSerial);
WindowSurfaceVk *mBackbuffer;
vk::RenderPassDesc mRenderPassDesc;

Просмотреть файл

@ -148,18 +148,15 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
// 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
// Normal rendering | Invalid
// !IsTransient No resolve |
// storeOp = STORE |
// Owner of data: mImage |
// |
// ---------------------------------------------+---------------------------------------
// |

Просмотреть файл

@ -32,15 +32,6 @@ namespace vk
namespace
{
// In the FramebufferDesc object:
// - Depth/stencil serial is at index 0
// - Color serials are at indices [1:gl::IMPLEMENTATION_MAX_DRAW_BUFFERS]
// - Resolve attachments are at indices [gl::IMPLEMENTATION_MAX_DRAW_BUFFERS+1,
// gl::IMPLEMENTATION_MAX_DRAW_BUFFERS*2]
constexpr size_t kFramebufferDescDepthStencilIndex = 0;
constexpr size_t kFramebufferDescColorIndexOffset = 1;
constexpr size_t kFramebufferDescResolveIndexOffset = gl::IMPLEMENTATION_MAX_DRAW_BUFFERS + 1;
uint8_t PackGLBlendOp(GLenum blendOp)
{
switch (blendOp)

Просмотреть файл

@ -911,6 +911,15 @@ constexpr size_t kMaxFramebufferAttachments = gl::IMPLEMENTATION_MAX_DRAW_BUFFER
template <typename T>
using FramebufferAttachmentArray = std::array<T, kMaxFramebufferAttachments>;
// In the FramebufferDesc object:
// - Depth/stencil serial is at index 0
// - Color serials are at indices [1:gl::IMPLEMENTATION_MAX_DRAW_BUFFERS]
// - Resolve attachments are at indices [gl::IMPLEMENTATION_MAX_DRAW_BUFFERS+1,
// gl::IMPLEMENTATION_MAX_DRAW_BUFFERS*2]
constexpr size_t kFramebufferDescDepthStencilIndex = 0;
constexpr size_t kFramebufferDescColorIndexOffset = 1;
constexpr size_t kFramebufferDescResolveIndexOffset = gl::IMPLEMENTATION_MAX_DRAW_BUFFERS + 1;
class FramebufferDesc
{
public:
@ -931,6 +940,12 @@ class FramebufferDesc
uint32_t attachmentCount() const;
ImageViewSubresourceSerial getColorImageViewSerial(uint32_t index)
{
ASSERT(kFramebufferDescColorIndexOffset + index < mSerials.size());
return mSerials[kFramebufferDescColorIndexOffset + index];
}
private:
void update(uint32_t index, ImageViewSubresourceSerial serial);

Просмотреть файл

@ -899,6 +899,14 @@ angle::Result CommandBufferHelper::flushToPrimary(ContextVk *contextVk,
return angle::Result::Continue;
}
void CommandBufferHelper::updateRenderPassForResolve(vk::Framebuffer *newFramebuffer,
const vk::RenderPassDesc &renderPassDesc)
{
ASSERT(newFramebuffer);
mFramebuffer.setHandle(newFramebuffer->getHandle());
mRenderPassDesc = renderPassDesc;
}
// Helper functions used below
char GetLoadOpShorthand(uint32_t loadOp)
{

Просмотреть файл

@ -984,6 +984,9 @@ class CommandBufferHelper : angle::NonCopyable
void onDepthAccess(ResourceAccess access) { UpdateAccess(&mDepthStartAccess, access); }
void onStencilAccess(ResourceAccess access) { UpdateAccess(&mStencilStartAccess, access); }
void updateRenderPassForResolve(vk::Framebuffer *newFramebuffer,
const vk::RenderPassDesc &renderPassDesc);
private:
void addCommandDiagnostics(ContextVk *contextVk);
// Allocator used by this class. Using a pool allocator per CBH to avoid threading issues

Просмотреть файл

@ -753,6 +753,7 @@ struct PerfCounters
uint32_t renderPasses;
uint32_t writeDescriptorSets;
uint32_t flushedOutsideRenderPassCommandBuffers;
uint32_t resolveImageCommands;
};
} // namespace vk

Просмотреть файл

@ -1129,6 +1129,188 @@ TEST_P(FramebufferTest_ES31, IncompleteMultisampleFixedSampleLocationsTex)
ASSERT_GL_NO_ERROR();
}
// Test resolving a multisampled texture with blit
TEST_P(FramebufferTest_ES31, MultisampleResolveWithBlit)
{
constexpr int kSize = 16;
glViewport(0, 0, kSize, kSize);
GLFramebuffer msaaFBO;
glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO.get());
GLTexture texture;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture.get());
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
ASSERT_GL_NO_ERROR();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
texture.get(), 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
essl31_shaders::fs::RedGreenGradient());
drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
ASSERT_GL_NO_ERROR();
// Create another FBO to resolve the multisample buffer into.
GLTexture resolveTexture;
GLFramebuffer resolveFBO;
glBindTexture(GL_TEXTURE_2D, resolveTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture, 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO);
glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO);
EXPECT_PIXEL_NEAR(0, 0, 0, 0, 0, 255, 1.0); // Black
EXPECT_PIXEL_NEAR(kSize - 1, 1, 239, 0, 0, 255, 1.0); // Red
EXPECT_PIXEL_NEAR(0, kSize - 1, 0, 239, 0, 255, 1.0); // Green
EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 239, 239, 0, 255, 1.0); // Yellow
}
// Test resolving a multisampled texture with blit after drawing to mulitiple FBOs.
TEST_P(FramebufferTest_ES31, MultisampleResolveWithBlitMultipleFBOs)
{
// FBO 1 -> multisample draw (red)
// FBO 2 -> multisample draw (green)
// Bind FBO 1 as read
// Bind FBO 3 as draw
// Resolve
constexpr int kSize = 16;
glViewport(0, 0, kSize, kSize);
GLFramebuffer msaaFBORed;
glBindFramebuffer(GL_FRAMEBUFFER, msaaFBORed.get());
GLTexture textureRed;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textureRed.get());
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
ASSERT_GL_NO_ERROR();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
textureRed.get(), 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ANGLE_GL_PROGRAM(redProgram, essl31_shaders::vs::Simple(), essl31_shaders::fs::Red());
drawQuad(redProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
ASSERT_GL_NO_ERROR();
GLFramebuffer msaaFBOGreen;
glBindFramebuffer(GL_FRAMEBUFFER, msaaFBOGreen.get());
GLTexture textureGreen;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textureGreen.get());
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
ASSERT_GL_NO_ERROR();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
textureGreen.get(), 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ANGLE_GL_PROGRAM(greenProgram, essl31_shaders::vs::Simple(), essl31_shaders::fs::Green());
drawQuad(greenProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
ASSERT_GL_NO_ERROR();
// Create another FBO to resolve the multisample buffer into.
GLTexture resolveTexture;
GLFramebuffer resolveFBO;
glBindTexture(GL_TEXTURE_2D, resolveTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture, 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBORed);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO);
glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
}
// Test resolving a multisampled texture with blit after drawing to mulitiple FBOs.
TEST_P(FramebufferTest_ES31, MultisampleResolveWithBlitMultipleResolves)
{
// Draw multisampled in FBO 1
// Bind FBO 1 as read
// Bind FBO 2 as draw
// Resolve
// Bind FBO 3 as draw
// Resolve
constexpr int kSize = 16;
glViewport(0, 0, kSize, kSize);
GLFramebuffer msaaFBORed;
glBindFramebuffer(GL_FRAMEBUFFER, msaaFBORed.get());
GLTexture textureRed;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textureRed.get());
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
ASSERT_GL_NO_ERROR();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
textureRed.get(), 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ANGLE_GL_PROGRAM(redProgram, essl31_shaders::vs::Simple(), essl31_shaders::fs::Red());
drawQuad(redProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
ASSERT_GL_NO_ERROR();
// Create another FBO to resolve the multisample buffer into.
GLTexture resolveTexture1;
GLFramebuffer resolveFBO1;
glBindTexture(GL_TEXTURE_2D, resolveTexture1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO1);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture1, 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBORed);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO1);
glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO1);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
// Create another FBO to resolve the multisample buffer into.
GLTexture resolveTexture2;
GLFramebuffer resolveFBO2;
glBindTexture(GL_TEXTURE_2D, resolveTexture2);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO2);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture2, 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBORed);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO2);
glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO2);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
}
// Test resolving a multisampled texture into a mipmaped texture with blit
TEST_P(FramebufferTest_ES31, MultisampleResolveIntoMipMapWithBlit)
{
@ -1185,6 +1367,100 @@ TEST_P(FramebufferTest_ES31, MultisampleResolveIntoMipMapWithBlit)
EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 251, 251, 0, 255, 1.0); // Yellow
}
// Test resolving a multisampled texture with blit after drawing to mulitiple FBOs.
TEST_P(FramebufferTest_ES31, MultipleTextureMultisampleResolveWithBlitMultipleResolves)
{
// Attach two MSAA textures to FBO1
// Set read buffer 0
// Resolve into FBO2
// Set read buffer 1
// Resolve into FBO3
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_draw_buffers"));
constexpr int kSize = 16;
glViewport(0, 0, kSize, kSize);
GLFramebuffer msaaFBO;
glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO.get());
GLTexture msaaTextureRed;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msaaTextureRed.get());
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
ASSERT_GL_NO_ERROR();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
msaaTextureRed.get(), 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
GLTexture msaaTextureGreen;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msaaTextureGreen.get());
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
ASSERT_GL_NO_ERROR();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D_MULTISAMPLE,
msaaTextureGreen.get(), 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Setup program to render red into attachment 0 and green into attachment 1.
const char *fs = R"(#extension GL_EXT_draw_buffers : enable
precision highp float;
void main()
{
gl_FragData[0] = vec4(1.0, 0.0, 0.0, 1.0); // attachment 0: red
gl_FragData[1] = vec4(0.0, 1.0, 0.0, 1.0); // attachment 1: green
})";
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), fs);
glUseProgram(program);
constexpr GLenum kDrawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
glDrawBuffers(2, kDrawBuffers);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Create another FBO to resolve the multisample buffer into.
GLTexture resolveTexture1;
GLFramebuffer resolveFBO1;
glBindTexture(GL_TEXTURE_2D, resolveTexture1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO1);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture1, 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO1);
glReadBuffer(GL_COLOR_ATTACHMENT0); // Red
glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO1);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
// Create another FBO to resolve the multisample buffer into.
GLTexture resolveTexture2;
GLFramebuffer resolveFBO2;
glBindTexture(GL_TEXTURE_2D, resolveTexture2);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO2);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture2, 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO2);
glReadBuffer(GL_COLOR_ATTACHMENT1); // Green
glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO2);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// If there are no attachments, rendering will be limited to a rectangle having a lower left of
// (0, 0) and an upper right of(width, height), where width and height are the framebuffer
// object's default width and height.
@ -1227,11 +1503,8 @@ void main()
f_color = vec4(1.0, 0.5, 0.25, 1.0);
})";
GLuint program1 = CompileProgram(kVS1, kFS1);
ASSERT_NE(program1, 0u);
GLuint program2 = CompileProgram(kVS2, kFS2);
ASSERT_NE(program2, 0u);
ANGLE_GL_PROGRAM(program1, kVS1, kFS1);
ANGLE_GL_PROGRAM(program2, kVS2, kFS2);
glUseProgram(program1);

Просмотреть файл

@ -31,11 +31,14 @@ class VulkanPerformanceCounterTest : public ANGLETest
const rx::vk::PerfCounters &hackANGLE() const
{
// Hack the angle!
const gl::Context *context = static_cast<gl::Context *>(getEGLWindow()->getContext());
return rx::GetImplAs<rx::ContextVk>(context)->getPerfCounters();
const gl::Context *context = static_cast<const gl::Context *>(getEGLWindow()->getContext());
return rx::GetImplAs<const rx::ContextVk>(context)->getPerfCounters();
}
};
class VulkanPerformanceCounterTest_ES31 : public VulkanPerformanceCounterTest
{};
// Tests that texture updates to unused textures don't break the RP.
TEST_P(VulkanPerformanceCounterTest, NewTextureDoesNotBreakRenderPass)
{
@ -263,6 +266,58 @@ TEST_P(VulkanPerformanceCounterTest, IndependentBufferCopiesShareSingleBarrier)
EXPECT_EQ(expectedFlushCount, actualFlushCount);
}
// Test resolving a multisampled texture with blit doesn't break the render pass so a subpass can be
// used
TEST_P(VulkanPerformanceCounterTest_ES31, MultisampleResolveWithBlit)
{
constexpr int kSize = 16;
glViewport(0, 0, kSize, kSize);
GLFramebuffer msaaFBO;
glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO.get());
GLTexture texture;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture.get());
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
ASSERT_GL_NO_ERROR();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
texture.get(), 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
essl31_shaders::fs::RedGreenGradient());
drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
ASSERT_GL_NO_ERROR();
// Create another FBO to resolve the multisample buffer into.
GLTexture resolveTexture;
GLFramebuffer resolveFBO;
glBindTexture(GL_TEXTURE_2D, resolveTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture, 0);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO);
glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
const rx::vk::PerfCounters &counters = hackANGLE();
EXPECT_EQ(counters.resolveImageCommands, 0u);
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO);
EXPECT_PIXEL_NEAR(0, 0, 0, 0, 0, 255, 1.0); // Black
EXPECT_PIXEL_NEAR(kSize - 1, 1, 239, 0, 0, 255, 1.0); // Red
EXPECT_PIXEL_NEAR(0, kSize - 1, 0, 239, 0, 255, 1.0); // Green
EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 239, 239, 0, 255, 1.0); // Yellow
}
ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest, ES3_VULKAN());
ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest_ES31, ES31_VULKAN());
} // anonymous namespace