зеркало из https://github.com/AvaloniaUI/angle.git
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:
Родитель
5dff6075d2
Коммит
dff47d5fda
|
@ -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, ¤tFramebuffer));
|
||||
ANGLE_TRY(getFramebuffer(contextVk, ¤tFramebuffer, 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 ¶ms)
|
||||
{
|
||||
// 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 ¶ms,
|
||||
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, ¤tFramebuffer));
|
||||
ANGLE_TRY(getFramebuffer(contextVk, ¤tFramebuffer, 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 ¶ms);
|
||||
|
||||
// Resolve color with vkCmdResolveImage
|
||||
angle::Result resolveColorWithCommand(ContextVk *contextVk,
|
||||
const UtilsVk::BlitResolveParameters ¶ms,
|
||||
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче