Commit f39e0f01 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Use subpass to unresolve render-to-texture attachments

GL_EXT_multisampled_render_to_texture allows singlesampled textures to be used with multisampled framebuffers in such a way that the final resolve operation is automatically done. In Vulkan terminology, the render-to-texture GL attachment is used as a Vulkan subpass resolve attachment with an implicit (ideally-)lazy-memory multisampled image as the color attachment. This extension expects that if the texture is drawn to after the automatic resolve, the implicit multisampled image would take its fragment colors from the singlesampled image. In other words, the opposite of a resolve operation should be automatically performed at the start of the render pass. This change refers to this operation as "unresolve". The goal of this extension is to allow tiling GPUs to always keep multisampled data on tile memory and only ever load/store singlesampled data. The latter is achieved by using a subpass resolve attachment and setting storeOp of the multisampled color attachment to DONT_CARE. This change achieves the former by using an initial subpass that uses the resolve attachment as input attachment, draws into the multisampled color attachment and sets loadOp of said attachment to DONT_CARE. Bug: angleproject:4881 Change-Id: I99f410530365963567c77a7d62fc9db1500e5e3e Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2397206 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 5ec560fb
...@@ -110,7 +110,7 @@ void GlslangWarmup() ...@@ -110,7 +110,7 @@ void GlslangWarmup()
EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules); EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);
// EShMessages messages = EShMsgDefault; // EShMessages messages = EShMsgDefault;
TBuiltInResource builtInResources(glslang::DefaultTBuiltInResource); const TBuiltInResource builtInResources(glslang::DefaultTBuiltInResource);
glslang::TShader dummyShader(EShLangVertex); glslang::TShader dummyShader(EShLangVertex);
const char *kShaderString = R"(#version 450 core const char *kShaderString = R"(#version 450 core
...@@ -902,6 +902,52 @@ constexpr gl::ShaderMap<EShLanguage> kShLanguageMap = { ...@@ -902,6 +902,52 @@ constexpr gl::ShaderMap<EShLanguage> kShLanguageMap = {
{gl::ShaderType::Compute, EShLangCompute}, {gl::ShaderType::Compute, EShLangCompute},
}; };
angle::Result CompileShader(const GlslangErrorCallback &callback,
const TBuiltInResource &builtInResources,
gl::ShaderType shaderType,
const std::string &shaderSource,
glslang::TShader *shader,
glslang::TProgram *program)
{
// Enable SPIR-V and Vulkan rules when parsing GLSL
constexpr EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);
ANGLE_TRACE_EVENT0("gpu.angle", "Glslang CompileShader TShader::parse");
const char *shaderString = shaderSource.c_str();
int shaderLength = static_cast<int>(shaderSource.size());
shader->setStringsWithLengths(&shaderString, &shaderLength, 1);
shader->setEntryPoint("main");
bool result = shader->parse(&builtInResources, 450, ECoreProfile, false, false, messages);
if (!result)
{
ERR() << "Internal error parsing Vulkan shader corresponding to " << shaderType << ":\n"
<< shader->getInfoLog() << "\n"
<< shader->getInfoDebugLog() << "\n";
ANGLE_GLSLANG_CHECK(callback, false, GlslangError::InvalidShader);
}
program->addShader(shader);
return angle::Result::Continue;
}
angle::Result LinkProgram(const GlslangErrorCallback &callback, glslang::TProgram *program)
{
// Enable SPIR-V and Vulkan rules
constexpr EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);
bool linkResult = program->link(messages);
if (!linkResult)
{
ERR() << "Internal error linking Vulkan shaders:\n" << program->getInfoLog() << "\n";
ANGLE_GLSLANG_CHECK(callback, false, GlslangError::InvalidShader);
}
return angle::Result::Continue;
}
#if defined(ANGLE_ENABLE_ASSERTS) #if defined(ANGLE_ENABLE_ASSERTS)
void ValidateSpirvMessage(spv_message_level_t level, void ValidateSpirvMessage(spv_message_level_t level,
const char *source, const char *source,
...@@ -2160,9 +2206,6 @@ angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback, ...@@ -2160,9 +2206,6 @@ angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback,
const gl::ShaderMap<std::string> &shaderSources, const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<SpirvBlob> *spirvBlobsOut) gl::ShaderMap<SpirvBlob> *spirvBlobsOut)
{ {
// Enable SPIR-V and Vulkan rules when parsing GLSL
EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);
TBuiltInResource builtInResources(glslang::DefaultTBuiltInResource); TBuiltInResource builtInResources(glslang::DefaultTBuiltInResource);
GetBuiltInResourcesFromCaps(glCaps, &builtInResources); GetBuiltInResourcesFromCaps(glCaps, &builtInResources);
...@@ -2186,33 +2229,11 @@ angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback, ...@@ -2186,33 +2229,11 @@ angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback,
continue; continue;
} }
ANGLE_TRACE_EVENT0("gpu.angle", "GlslangGetShaderSpirvCode TShader::parse"); ANGLE_TRY(CompileShader(callback, builtInResources, shaderType, shaderSources[shaderType],
shaders[shaderType], &program));
const char *shaderString = shaderSources[shaderType].c_str();
int shaderLength = static_cast<int>(shaderSources[shaderType].size());
glslang::TShader *shader = shaders[shaderType];
shader->setStringsWithLengths(&shaderString, &shaderLength, 1);
shader->setEntryPoint("main");
bool result = shader->parse(&builtInResources, 450, ECoreProfile, false, false, messages);
if (!result)
{
ERR() << "Internal error parsing Vulkan shader corresponding to " << shaderType << ":\n"
<< shader->getInfoLog() << "\n"
<< shader->getInfoDebugLog() << "\n";
ANGLE_GLSLANG_CHECK(callback, false, GlslangError::InvalidShader);
}
program.addShader(shader);
} }
bool linkResult = program.link(messages); ANGLE_TRY(LinkProgram(callback, &program));
if (!linkResult)
{
ERR() << "Internal error linking Vulkan shaders:\n" << program.getInfoLog() << "\n";
ANGLE_GLSLANG_CHECK(callback, false, GlslangError::InvalidShader);
}
for (const gl::ShaderType shaderType : linkedShaderStages) for (const gl::ShaderType shaderType : linkedShaderStages)
{ {
...@@ -2228,4 +2249,23 @@ angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback, ...@@ -2228,4 +2249,23 @@ angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback,
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result GlslangCompileShaderOneOff(const GlslangErrorCallback &callback,
gl::ShaderType shaderType,
const std::string &shaderSource,
SpirvBlob *spirvBlobOut)
{
const TBuiltInResource builtInResources(glslang::DefaultTBuiltInResource);
glslang::TShader shader(kShLanguageMap[shaderType]);
glslang::TProgram program;
ANGLE_TRY(
CompileShader(callback, builtInResources, shaderType, shaderSource, &shader, &program));
ANGLE_TRY(LinkProgram(callback, &program));
glslang::TIntermediate *intermediate = program.getIntermediate(kShLanguageMap[shaderType]);
glslang::GlslangToSpv(*intermediate, *spirvBlobOut);
return angle::Result::Continue;
}
} // namespace rx } // namespace rx
...@@ -136,6 +136,11 @@ angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback, ...@@ -136,6 +136,11 @@ angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback,
const gl::ShaderMap<std::string> &shaderSources, const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<SpirvBlob> *spirvBlobsOut); gl::ShaderMap<SpirvBlob> *spirvBlobsOut);
angle::Result GlslangCompileShaderOneOff(const GlslangErrorCallback &callback,
gl::ShaderType shaderType,
const std::string &shaderSource,
SpirvBlob *spirvBlobOut);
} // namespace rx } // namespace rx
#endif // LIBANGLE_RENDERER_GLSLANG_WRAPPER_UTILS_H_ #endif // LIBANGLE_RENDERER_GLSLANG_WRAPPER_UTILS_H_
...@@ -3046,8 +3046,7 @@ angle::Result ContextVk::syncState(const gl::Context *context, ...@@ -3046,8 +3046,7 @@ angle::Result ContextVk::syncState(const gl::Context *context,
&mGraphicsPipelineTransition, depthStencilState, drawFramebuffer); &mGraphicsPipelineTransition, depthStencilState, drawFramebuffer);
mGraphicsPipelineDesc->updateStencilBackWriteMask( mGraphicsPipelineDesc->updateStencilBackWriteMask(
&mGraphicsPipelineTransition, depthStencilState, drawFramebuffer); &mGraphicsPipelineTransition, depthStencilState, drawFramebuffer);
mGraphicsPipelineDesc->updateRenderPassDesc(&mGraphicsPipelineTransition, onDrawFramebufferRenderPassDescChange(mDrawFramebuffer);
mDrawFramebuffer->getRenderPassDesc());
break; break;
} }
case gl::State::DIRTY_BIT_RENDERBUFFER_BINDING: case gl::State::DIRTY_BIT_RENDERBUFFER_BINDING:
...@@ -3413,17 +3412,22 @@ void ContextVk::invalidateDriverUniforms() ...@@ -3413,17 +3412,22 @@ void ContextVk::invalidateDriverUniforms()
void ContextVk::onDrawFramebufferChange(FramebufferVk *framebufferVk) void ContextVk::onDrawFramebufferChange(FramebufferVk *framebufferVk)
{ {
const vk::RenderPassDesc &renderPassDesc = framebufferVk->getRenderPassDesc(); // Ensure that the pipeline description is updated.
// Ensure that the RenderPass description is updated.
invalidateCurrentGraphicsPipeline();
if (mGraphicsPipelineDesc->getRasterizationSamples() != if (mGraphicsPipelineDesc->getRasterizationSamples() !=
static_cast<uint32_t>(framebufferVk->getSamples())) static_cast<uint32_t>(framebufferVk->getSamples()))
{ {
mGraphicsPipelineDesc->updateRasterizationSamples(&mGraphicsPipelineTransition, mGraphicsPipelineDesc->updateRasterizationSamples(&mGraphicsPipelineTransition,
framebufferVk->getSamples()); framebufferVk->getSamples());
} }
mGraphicsPipelineDesc->updateRenderPassDesc(&mGraphicsPipelineTransition, renderPassDesc);
onDrawFramebufferRenderPassDescChange(framebufferVk);
}
void ContextVk::onDrawFramebufferRenderPassDescChange(FramebufferVk *framebufferVk)
{
invalidateCurrentGraphicsPipeline();
mGraphicsPipelineDesc->updateRenderPassDesc(&mGraphicsPipelineTransition,
framebufferVk->getRenderPassDesc());
} }
void ContextVk::invalidateCurrentTransformFeedbackBuffers() void ContextVk::invalidateCurrentTransformFeedbackBuffers()
...@@ -4577,6 +4581,9 @@ angle::Result ContextVk::beginNewRenderPass( ...@@ -4577,6 +4581,9 @@ angle::Result ContextVk::beginNewRenderPass(
mRenderPassCommands->beginRenderPass(framebuffer, renderArea, renderPassDesc, mRenderPassCommands->beginRenderPass(framebuffer, renderArea, renderPassDesc,
renderPassAttachmentOps, depthStencilAttachmentIndex, renderPassAttachmentOps, depthStencilAttachmentIndex,
clearValues, commandBufferOut); clearValues, commandBufferOut);
// Restart at subpass 0.
mGraphicsPipelineDesc->resetSubpass(&mGraphicsPipelineTransition);
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -4610,6 +4617,16 @@ angle::Result ContextVk::startRenderPass(gl::Rectangle renderArea, ...@@ -4610,6 +4617,16 @@ angle::Result ContextVk::startRenderPass(gl::Rectangle renderArea,
return angle::Result::Continue; return angle::Result::Continue;
} }
void ContextVk::startNextSubpass()
{
ASSERT(hasStartedRenderPass());
mRenderPassCommands->getCommandBuffer().nextSubpass(VK_SUBPASS_CONTENTS_INLINE);
// The graphics pipelines are bound to a subpass, so update the subpass as well.
mGraphicsPipelineDesc->nextSubpass(&mGraphicsPipelineTransition);
}
void ContextVk::restoreFinishedRenderPass(vk::Framebuffer *framebuffer) void ContextVk::restoreFinishedRenderPass(vk::Framebuffer *framebuffer)
{ {
if (mRenderPassCommandBuffer != nullptr) if (mRenderPassCommandBuffer != nullptr)
......
...@@ -358,6 +358,7 @@ class ContextVk : public ContextImpl, public vk::Context ...@@ -358,6 +358,7 @@ class ContextVk : public ContextImpl, public vk::Context
void invalidateDefaultAttribute(size_t attribIndex); void invalidateDefaultAttribute(size_t attribIndex);
void invalidateDefaultAttributes(const gl::AttributesMask &dirtyMask); void invalidateDefaultAttributes(const gl::AttributesMask &dirtyMask);
void onDrawFramebufferChange(FramebufferVk *framebufferVk); void onDrawFramebufferChange(FramebufferVk *framebufferVk);
void onDrawFramebufferRenderPassDescChange(FramebufferVk *framebufferVk);
void onHostVisibleBufferWrite() { mIsAnyHostVisibleBufferWritten = true; } void onHostVisibleBufferWrite() { mIsAnyHostVisibleBufferWritten = true; }
void invalidateCurrentTransformFeedbackBuffers(); void invalidateCurrentTransformFeedbackBuffers();
...@@ -586,6 +587,7 @@ class ContextVk : public ContextImpl, public vk::Context ...@@ -586,6 +587,7 @@ class ContextVk : public ContextImpl, public vk::Context
egl::ContextPriority getContextPriority() const override { return mContextPriority; } egl::ContextPriority getContextPriority() const override { return mContextPriority; }
angle::Result startRenderPass(gl::Rectangle renderArea, vk::CommandBuffer **commandBufferOut); angle::Result startRenderPass(gl::Rectangle renderArea, vk::CommandBuffer **commandBufferOut);
void startNextSubpass();
angle::Result flushCommandsAndEndRenderPass(); angle::Result flushCommandsAndEndRenderPass();
angle::Result syncExternalMemory(); angle::Result syncExternalMemory();
......
...@@ -1348,46 +1348,6 @@ angle::Result FramebufferVk::resolveColorWithCommand(ContextVk *contextVk, ...@@ -1348,46 +1348,6 @@ 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();
vk::LevelIndex levelVk = src->toVkLevel(colorRenderTarget->getLevelIndex());
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.get();
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
...@@ -1779,9 +1739,9 @@ angle::Result FramebufferVk::syncState(const gl::Context *context, ...@@ -1779,9 +1739,9 @@ angle::Result FramebufferVk::syncState(const gl::Context *context,
ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass()); ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass());
} }
// Notify the ContextVk to update the pipeline desc.
updateRenderPassDesc(); updateRenderPassDesc();
// Notify the ContextVk to update the pipeline desc.
FramebufferVk *currentDrawFramebuffer = vk::GetImpl(context->getState().getDrawFramebuffer()); FramebufferVk *currentDrawFramebuffer = vk::GetImpl(context->getState().getDrawFramebuffer());
if (currentDrawFramebuffer == this) if (currentDrawFramebuffer == this)
{ {
...@@ -2225,14 +2185,12 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk, ...@@ -2225,14 +2185,12 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
const gl::Rectangle &renderArea, const gl::Rectangle &renderArea,
vk::CommandBuffer **commandBufferOut) vk::CommandBuffer **commandBufferOut)
{ {
vk::Framebuffer *framebuffer = nullptr;
ANGLE_TRY(getFramebuffer(contextVk, &framebuffer, nullptr));
ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass()); ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass());
// Initialize RenderPass info. // Initialize RenderPass info.
vk::AttachmentOpsArray renderPassAttachmentOps; vk::AttachmentOpsArray renderPassAttachmentOps;
vk::PackedClearValuesArray packedClearValues; vk::PackedClearValuesArray packedClearValues;
gl::DrawBufferMask previousUnresolveMask = mRenderPassDesc.getColorUnresolveAttachmentMask();
// Color attachments. // Color attachments.
const auto &colorRenderTargets = mRenderTargetCache.getColors(); const auto &colorRenderTargets = mRenderTargetCache.getColors();
...@@ -2274,22 +2232,29 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk, ...@@ -2274,22 +2232,29 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
VK_ATTACHMENT_STORE_OP_DONT_CARE); VK_ATTACHMENT_STORE_OP_DONT_CARE);
// If there's a resolve attachment, and loadOp needs to be LOAD, the multisampled attachment // 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 // needs to take its value from the resolve attachment. In this case, an initial subpass is
// but to blit from the resolve image into the multisampled one. It's expected that // added for this very purpose which uses the resolve attachment as input attachment. As a
// application code results in a clear of the framebuffer at the start of the renderpass, so // result, loadOp of the multisampled attachment can remain DONT_CARE.
// the multisampled image is truely transient.
// //
// Note that this only needs to be done if the multisampled image and the resolve attachment // 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 // come from the same source. isImageTransient() indicates whether this should happen.
// the case. isImageTransient() indicates whether this should happen. if (colorRenderTarget->hasResolveAttachment() && colorRenderTarget->isImageTransient())
//
// 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[colorIndexVk].loadOp == VK_ATTACHMENT_LOAD_OP_LOAD)
{ {
ANGLE_TRY(copyResolveToMultisampedAttachment(contextVk, colorRenderTarget)); if (renderPassAttachmentOps[colorIndexVk].loadOp == VK_ATTACHMENT_LOAD_OP_LOAD)
{
renderPassAttachmentOps[colorIndexVk].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
// Update the render pass desc to specify that this attachment should be unresolved.
mRenderPassDesc.packColorUnresolveAttachment(colorIndexGL);
}
else
{
mRenderPassDesc.removeColorUnresolveAttachment(colorIndexGL);
}
}
else
{
ASSERT(!mRenderPassDesc.getColorUnresolveAttachmentMask().test(colorIndexGL));
} }
++colorIndexVk; ++colorIndexVk;
...@@ -2377,12 +2342,29 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk, ...@@ -2377,12 +2342,29 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
stencilStoreOp); stencilStoreOp);
} }
// If render pass description is changed, the previous render pass desc is no longer compatible.
// Tell the context so that the graphics pipelines can be recreated.
//
// Note that render passes are compatible only if the differences are in loadOp/storeOp values,
// or the existence of resolve attachments in single subpass render passes. The modification
// here can add/remove a subpass, or modify its input attachments.
gl::DrawBufferMask unresolveMask = mRenderPassDesc.getColorUnresolveAttachmentMask();
if (previousUnresolveMask != unresolveMask)
{
contextVk->onDrawFramebufferRenderPassDescChange(this);
// Make sure framebuffer is recreated.
mFramebuffer = nullptr;
mCurrentFramebufferDesc.updateColorUnresolveMask(unresolveMask);
}
vk::Framebuffer *framebuffer = nullptr;
ANGLE_TRY(getFramebuffer(contextVk, &framebuffer, nullptr));
ANGLE_TRY(contextVk->beginNewRenderPass(*framebuffer, renderArea, mRenderPassDesc, ANGLE_TRY(contextVk->beginNewRenderPass(*framebuffer, renderArea, mRenderPassDesc,
renderPassAttachmentOps, depthStencilAttachmentIndex, renderPassAttachmentOps, depthStencilAttachmentIndex,
packedClearValues, commandBufferOut)); packedClearValues, commandBufferOut));
// Transition the images to the correct layout (through onColorDraw) after the // Transition the images to the correct layout (through onColorDraw).
// resolve-to-multisampled copies are done.
for (size_t colorIndexGL : mState.getColorAttachmentsMask()) for (size_t colorIndexGL : mState.getColorAttachmentsMask())
{ {
RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndexGL]; RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndexGL];
...@@ -2398,6 +2380,18 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk, ...@@ -2398,6 +2380,18 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
depthStencilRenderTarget->onDepthStencilDraw(contextVk, isReadOnlyDepthMode()); depthStencilRenderTarget->onDepthStencilDraw(contextVk, isReadOnlyDepthMode());
} }
if (unresolveMask.any())
{
// Unresolve attachments if any.
UtilsVk::UnresolveParameters params;
params.unresolveMask = unresolveMask;
ANGLE_TRY(contextVk->getUtils().unresolve(contextVk, this, params));
// The unresolve subpass has only one draw call.
contextVk->startNextSubpass();
}
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -163,11 +163,6 @@ class FramebufferVk : public FramebufferImpl ...@@ -163,11 +163,6 @@ 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 clearImpl(const gl::Context *context, angle::Result clearImpl(const gl::Context *context,
gl::DrawBufferMask clearColorBuffers, gl::DrawBufferMask clearColorBuffers,
bool clearDepth, bool clearDepth,
......
...@@ -97,4 +97,15 @@ angle::Result GlslangWrapperVk::TransformSpirV( ...@@ -97,4 +97,15 @@ angle::Result GlslangWrapperVk::TransformSpirV(
removeEarlyFragmentTestsOptimization, removeDebugInfo, variableInfoMap, initialSpirvBlob, removeEarlyFragmentTestsOptimization, removeDebugInfo, variableInfoMap, initialSpirvBlob,
shaderCodeOut); shaderCodeOut);
} }
// static
angle::Result GlslangWrapperVk::CompileShaderOneOff(vk::Context *context,
gl::ShaderType shaderType,
const std::string &shaderSource,
SpirvBlob *spirvBlobOut)
{
return GlslangCompileShaderOneOff(
[context](GlslangError error) { return ErrorHandler(context, error); }, shaderType,
shaderSource, spirvBlobOut);
}
} // namespace rx } // namespace rx
...@@ -49,6 +49,11 @@ class GlslangWrapperVk ...@@ -49,6 +49,11 @@ class GlslangWrapperVk
const ShaderInterfaceVariableInfoMap &variableInfoMap, const ShaderInterfaceVariableInfoMap &variableInfoMap,
const SpirvBlob &initialSpirvBlob, const SpirvBlob &initialSpirvBlob,
SpirvBlob *shaderCodeOut); SpirvBlob *shaderCodeOut);
static angle::Result CompileShaderOneOff(vk::Context *context,
gl::ShaderType shaderType,
const std::string &shaderSource,
SpirvBlob *spirvBlobOut);
}; };
} // namespace rx } // namespace rx
......
...@@ -81,17 +81,19 @@ angle::Result RenderbufferVk::setStorageImpl(const gl::Context *context, ...@@ -81,17 +81,19 @@ angle::Result RenderbufferVk::setStorageImpl(const gl::Context *context,
const bool isDepthStencilFormat = textureFormat.hasDepthOrStencilBits(); const bool isDepthStencilFormat = textureFormat.hasDepthOrStencilBits();
ASSERT(textureFormat.redBits > 0 || isDepthStencilFormat); ASSERT(textureFormat.redBits > 0 || isDepthStencilFormat);
const VkImageUsageFlags usage =
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT |
(isDepthStencilFormat ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT
: VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
// TODO(syoussefi): Currently not supported for depth/stencil images. Tests (and Chromium) only // TODO(syoussefi): Currently not supported for depth/stencil images. Tests (and Chromium) only
// use this for depth/stencil buffers and don't attempt to read from it. This needs to be fixed // 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 // and tests added. http://anglebug.com/4836
const bool isRenderToTexture = const bool isRenderToTexture =
mode == gl::MultisamplingMode::MultisampledRenderToTexture && !isDepthStencilFormat; mode == gl::MultisamplingMode::MultisampledRenderToTexture && !isDepthStencilFormat;
const VkImageUsageFlags usage =
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT |
(isDepthStencilFormat ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT
: VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) |
(isRenderToTexture ? VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT : 0);
const uint32_t imageSamples = isRenderToTexture ? 1 : samples; const uint32_t imageSamples = isRenderToTexture ? 1 : samples;
VkExtent3D extents = {static_cast<uint32_t>(width), static_cast<uint32_t>(height), 1u}; VkExtent3D extents = {static_cast<uint32_t>(width), static_cast<uint32_t>(height), 1u};
......
...@@ -1870,8 +1870,12 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev ...@@ -1870,8 +1870,12 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev
bool isAdreno540 = mPhysicalDeviceProperties.deviceID == angle::kDeviceID_Adreno540; bool isAdreno540 = mPhysicalDeviceProperties.deviceID == angle::kDeviceID_Adreno540;
ANGLE_FEATURE_CONDITION(&mFeatures, forceMaxUniformBufferSize16KB, isQualcomm && isAdreno540); ANGLE_FEATURE_CONDITION(&mFeatures, forceMaxUniformBufferSize16KB, isQualcomm && isAdreno540);
// Feature disabled due to driver bugs:
//
// - Swiftshader on mac: http://anglebug.com/4937
// - Intel on windows: http://anglebug.com/5032
ANGLE_FEATURE_CONDITION(&mFeatures, enableMultisampledRenderToTexture, ANGLE_FEATURE_CONDITION(&mFeatures, enableMultisampledRenderToTexture,
!(IsApple() && isSwiftShader)); !(IsApple() && isSwiftShader) && !(isIntel && IsWindows()));
ANGLE_FEATURE_CONDITION(&mFeatures, preferredLargeHeapBlockSize4MB, !isQualcomm); ANGLE_FEATURE_CONDITION(&mFeatures, preferredLargeHeapBlockSize4MB, !isQualcomm);
......
...@@ -1266,7 +1266,8 @@ angle::Result TextureVk::ensureImageAllocated(ContextVk *contextVk, const vk::Fo ...@@ -1266,7 +1266,8 @@ angle::Result TextureVk::ensureImageAllocated(ContextVk *contextVk, const vk::Fo
else if (renderer->hasImageFormatFeatureBits(format.vkImageFormat, else if (renderer->hasImageFormatFeatureBits(format.vkImageFormat,
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
{ {
mImageUsageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; mImageUsageFlags |=
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
} }
return angle::Result::Continue; return angle::Result::Continue;
......
...@@ -164,6 +164,11 @@ class UtilsVk : angle::NonCopyable ...@@ -164,6 +164,11 @@ class UtilsVk : angle::NonCopyable
uint32_t destLevelCount; uint32_t destLevelCount;
}; };
struct UnresolveParameters
{
gl::DrawBufferMask unresolveMask;
};
// Based on the maximum number of levels in GenerateMipmap.comp. // Based on the maximum number of levels in GenerateMipmap.comp.
static constexpr uint32_t kGenerateMipmapMaxLevels = 6; static constexpr uint32_t kGenerateMipmapMaxLevels = 6;
static uint32_t GetGenerateMipmapMaxLevels(ContextVk *contextVk); static uint32_t GetGenerateMipmapMaxLevels(ContextVk *contextVk);
...@@ -238,6 +243,10 @@ class UtilsVk : angle::NonCopyable ...@@ -238,6 +243,10 @@ class UtilsVk : angle::NonCopyable
const vk::Sampler &sampler, const vk::Sampler &sampler,
const GenerateMipmapParameters &params); const GenerateMipmapParameters &params);
angle::Result unresolve(ContextVk *contextVk,
const FramebufferVk *framebuffer,
const UnresolveParameters &params);
// Overlay utilities. // Overlay utilities.
angle::Result cullOverlayWidgets(ContextVk *contextVk, angle::Result cullOverlayWidgets(ContextVk *contextVk,
vk::BufferHelper *enabledWidgetsBuffer, vk::BufferHelper *enabledWidgetsBuffer,
...@@ -397,21 +406,30 @@ class UtilsVk : angle::NonCopyable ...@@ -397,21 +406,30 @@ class UtilsVk : angle::NonCopyable
ImageClear = 0, ImageClear = 0,
ImageCopy = 1, ImageCopy = 1,
BlitResolve = 2, BlitResolve = 2,
// Note: unresolve is special as it has a different layout per attachment
Unresolve1Attachment = 3,
Unresolve2Attachments = 4,
Unresolve3Attachments = 5,
Unresolve4Attachments = 6,
Unresolve5Attachments = 7,
Unresolve6Attachments = 8,
Unresolve7Attachments = 9,
Unresolve8Attachments = 10,
// Functions implemented in compute // Functions implemented in compute
ComputeStartIndex = 3, // Special value to separate draw and dispatch functions. ComputeStartIndex = 11, // Special value to separate draw and dispatch functions.
ConvertIndexBuffer = 3, ConvertIndexBuffer = 11,
ConvertVertexBuffer = 4, ConvertVertexBuffer = 12,
BlitResolveStencilNoExport = 5, BlitResolveStencilNoExport = 13,
OverlayCull = 6, OverlayCull = 14,
OverlayDraw = 7, OverlayDraw = 15,
ConvertIndexIndirectBuffer = 8, ConvertIndexIndirectBuffer = 16,
ConvertIndexIndirectLineLoopBuffer = 9, ConvertIndexIndirectLineLoopBuffer = 17,
ConvertIndirectLineLoopBuffer = 10, ConvertIndirectLineLoopBuffer = 18,
GenerateMipmap = 11, GenerateMipmap = 19,
InvalidEnum = 12, InvalidEnum = 20,
EnumCount = 12, EnumCount = 20,
}; };
// Common function that creates the pipeline for the specified function, binds it and prepares // Common function that creates the pipeline for the specified function, binds it and prepares
...@@ -455,6 +473,9 @@ class UtilsVk : angle::NonCopyable ...@@ -455,6 +473,9 @@ class UtilsVk : angle::NonCopyable
angle::Result ensureOverlayCullResourcesInitialized(ContextVk *contextVk); angle::Result ensureOverlayCullResourcesInitialized(ContextVk *contextVk);
angle::Result ensureOverlayDrawResourcesInitialized(ContextVk *contextVk); angle::Result ensureOverlayDrawResourcesInitialized(ContextVk *contextVk);
angle::Result ensureGenerateMipmapResourcesInitialized(ContextVk *contextVk); angle::Result ensureGenerateMipmapResourcesInitialized(ContextVk *contextVk);
angle::Result ensureUnresolveResourcesInitialized(ContextVk *contextVk,
Function function,
uint32_t attachmentIndex);
angle::Result ensureSamplersInitialized(ContextVk *context); angle::Result ensureSamplersInitialized(ContextVk *context);
...@@ -502,6 +523,11 @@ class UtilsVk : angle::NonCopyable ...@@ -502,6 +523,11 @@ class UtilsVk : angle::NonCopyable
vk::ShaderProgramHelper vk::ShaderProgramHelper
mGenerateMipmapPrograms[vk::InternalShader::GenerateMipmap_comp::kArrayLen]; mGenerateMipmapPrograms[vk::InternalShader::GenerateMipmap_comp::kArrayLen];
// Unresolve shaders are special as they are generated on the fly due to the large number of
// combinations.
std::unordered_map<uint32_t, vk::RefCounted<vk::ShaderAndSerial>> mUnresolveFragShaders;
std::unordered_map<uint32_t, vk::ShaderProgramHelper> mUnresolvePrograms;
vk::Sampler mPointSampler; vk::Sampler mPointSampler;
vk::Sampler mLinearSampler; vk::Sampler mLinearSampler;
}; };
......
...@@ -136,6 +136,9 @@ class alignas(4) RenderPassDesc final ...@@ -136,6 +136,9 @@ class alignas(4) RenderPassDesc final
// Remove the resolve attachment. Used when optimizing blit through resolve attachment to // Remove the resolve attachment. Used when optimizing blit through resolve attachment to
// temporarily pack a resolve attachment and then remove it. // temporarily pack a resolve attachment and then remove it.
void removeColorResolveAttachment(size_t colorIndexGL); void removeColorResolveAttachment(size_t colorIndexGL);
// Indicate that a color attachment should take its data from the resolve attachment initially.
void packColorUnresolveAttachment(size_t colorIndexGL);
void removeColorUnresolveAttachment(size_t colorIndexGL);
size_t hash() const; size_t hash() const;
...@@ -153,6 +156,14 @@ class alignas(4) RenderPassDesc final ...@@ -153,6 +156,14 @@ class alignas(4) RenderPassDesc final
{ {
return mColorResolveAttachmentMask.test(colorIndexGL); return mColorResolveAttachmentMask.test(colorIndexGL);
} }
gl::DrawBufferMask getColorUnresolveAttachmentMask() const
{
return mColorUnresolveAttachmentMask;
}
bool hasColorUnresolveAttachment(size_t colorIndexGL) const
{
return mColorUnresolveAttachmentMask.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.
...@@ -193,9 +204,11 @@ class alignas(4) RenderPassDesc final ...@@ -193,9 +204,11 @@ class alignas(4) RenderPassDesc final
// (mAttachmentFormats is one element too large, so there are 8 bits there to take). // (mAttachmentFormats is one element too large, so there are 8 bits there to take).
gl::DrawBufferMask mColorResolveAttachmentMask; gl::DrawBufferMask mColorResolveAttachmentMask;
// TODO(syoussefi): to be used to determine which attachments are multisampled-render-to-texture // Whether each color attachment with a corresponding resolve attachment should be initialized
// that need load. http://anglebug.com/4881 // with said resolve attachment in an initial subpass. This is an optimization to avoid
ANGLE_MAYBE_UNUSED uint8_t padding; // loadOp=LOAD on the implicit multisampled image used with multisampled-render-to-texture
// render targets. This operation is referred to as "unresolve".
gl::DrawBufferMask mColorUnresolveAttachmentMask;
// 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,
...@@ -318,8 +331,11 @@ static_assert(kVertexInputAttributesSize == 96, "Size mismatch"); ...@@ -318,8 +331,11 @@ static_assert(kVertexInputAttributesSize == 96, "Size mismatch");
struct RasterizationStateBits final struct RasterizationStateBits final
{ {
uint32_t depthClampEnable : 4; // Note: Currently only 2 subpasses possible, so there are 5 bits in subpass that can be
uint32_t rasterizationDiscardEnable : 4; // repurposed.
uint32_t subpass : 6;
uint32_t depthClampEnable : 1;
uint32_t rasterizationDiscardEnable : 1;
uint32_t polygonMode : 4; uint32_t polygonMode : 4;
uint32_t cullMode : 4; uint32_t cullMode : 4;
uint32_t frontFace : 4; uint32_t frontFace : 4;
...@@ -607,7 +623,13 @@ class GraphicsPipelineDesc final ...@@ -607,7 +623,13 @@ class GraphicsPipelineDesc final
void setScissor(const VkRect2D &scissor); void setScissor(const VkRect2D &scissor);
void updateScissor(GraphicsPipelineTransitionBits *transition, const VkRect2D &scissor); void updateScissor(GraphicsPipelineTransitionBits *transition, const VkRect2D &scissor);
// Subpass
void resetSubpass(GraphicsPipelineTransitionBits *transition);
void nextSubpass(GraphicsPipelineTransitionBits *transition);
private: private:
void updateSubpass(GraphicsPipelineTransitionBits *transition, uint32_t subpass);
VertexInputAttributes mVertexInputAttribs; VertexInputAttributes mVertexInputAttribs;
RenderPassDesc mRenderPassDesc; RenderPassDesc mRenderPassDesc;
PackedRasterizationAndMultisampleStateInfo mRasterizationAndMultisampleStateInfo; PackedRasterizationAndMultisampleStateInfo mRasterizationAndMultisampleStateInfo;
...@@ -1001,6 +1023,7 @@ class FramebufferDesc ...@@ -1001,6 +1023,7 @@ class FramebufferDesc
void updateColor(uint32_t index, ImageViewSubresourceSerial serial); void updateColor(uint32_t index, ImageViewSubresourceSerial serial);
void updateColorResolve(uint32_t index, ImageViewSubresourceSerial serial); void updateColorResolve(uint32_t index, ImageViewSubresourceSerial serial);
void updateColorUnresolveMask(gl::DrawBufferMask colorUnresolveMask);
void updateDepthStencil(ImageViewSubresourceSerial serial); void updateDepthStencil(ImageViewSubresourceSerial serial);
void updateReadOnlyDepth(bool readOnlyDepth); void updateReadOnlyDepth(bool readOnlyDepth);
size_t hash() const; size_t hash() const;
...@@ -1022,7 +1045,13 @@ class FramebufferDesc ...@@ -1022,7 +1045,13 @@ class FramebufferDesc
// Note: this is an exclusive index. If there is one index it will be "1". // Note: this is an exclusive index. If there is one index it will be "1".
uint16_t mMaxIndex; uint16_t mMaxIndex;
uint16_t mReadOnlyDepth; uint8_t mReadOnlyDepth;
// If the render pass contains an initial subpass to unresolve a number of attachments, the
// subpass description is derived from the following mask, specifying which attachments need
// to be unresolved.
gl::DrawBufferMask mColorUnresolveAttachmentMask;
FramebufferAttachmentArray<ImageViewSubresourceSerial> mSerials; FramebufferAttachmentArray<ImageViewSubresourceSerial> mSerials;
}; };
......
...@@ -1521,6 +1521,9 @@ TEST_P(VulkanPerformanceCounterTest, DepthStencilTextureClearAndLoad) ...@@ -1521,6 +1521,9 @@ TEST_P(VulkanPerformanceCounterTest, DepthStencilTextureClearAndLoad)
// Tests that multisampled-render-to-texture depth/stencil textures don't ever load data. // Tests that multisampled-render-to-texture depth/stencil textures don't ever load data.
TEST_P(VulkanPerformanceCounterTest, RenderToTextureDepthStencilTextureShouldNotLoad) TEST_P(VulkanPerformanceCounterTest, RenderToTextureDepthStencilTextureShouldNotLoad)
{ {
// http://anglebug.com/5083
ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsVulkan());
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
const rx::vk::PerfCounters &counters = hackANGLE(); const rx::vk::PerfCounters &counters = hackANGLE();
......
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