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()
EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);
// EShMessages messages = EShMsgDefault;
TBuiltInResource builtInResources(glslang::DefaultTBuiltInResource);
const TBuiltInResource builtInResources(glslang::DefaultTBuiltInResource);
glslang::TShader dummyShader(EShLangVertex);
const char *kShaderString = R"(#version 450 core
......@@ -902,6 +902,52 @@ constexpr gl::ShaderMap<EShLanguage> kShLanguageMap = {
{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)
void ValidateSpirvMessage(spv_message_level_t level,
const char *source,
......@@ -2160,9 +2206,6 @@ angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<SpirvBlob> *spirvBlobsOut)
{
// Enable SPIR-V and Vulkan rules when parsing GLSL
EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);
TBuiltInResource builtInResources(glslang::DefaultTBuiltInResource);
GetBuiltInResourcesFromCaps(glCaps, &builtInResources);
......@@ -2186,33 +2229,11 @@ angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback,
continue;
}
ANGLE_TRACE_EVENT0("gpu.angle", "GlslangGetShaderSpirvCode TShader::parse");
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);
ANGLE_TRY(CompileShader(callback, builtInResources, shaderType, shaderSources[shaderType],
shaders[shaderType], &program));
}
bool linkResult = program.link(messages);
if (!linkResult)
{
ERR() << "Internal error linking Vulkan shaders:\n" << program.getInfoLog() << "\n";
ANGLE_GLSLANG_CHECK(callback, false, GlslangError::InvalidShader);
}
ANGLE_TRY(LinkProgram(callback, &program));
for (const gl::ShaderType shaderType : linkedShaderStages)
{
......@@ -2228,4 +2249,23 @@ angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback,
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
......@@ -136,6 +136,11 @@ angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<SpirvBlob> *spirvBlobsOut);
angle::Result GlslangCompileShaderOneOff(const GlslangErrorCallback &callback,
gl::ShaderType shaderType,
const std::string &shaderSource,
SpirvBlob *spirvBlobOut);
} // namespace rx
#endif // LIBANGLE_RENDERER_GLSLANG_WRAPPER_UTILS_H_
......@@ -3046,8 +3046,7 @@ angle::Result ContextVk::syncState(const gl::Context *context,
&mGraphicsPipelineTransition, depthStencilState, drawFramebuffer);
mGraphicsPipelineDesc->updateStencilBackWriteMask(
&mGraphicsPipelineTransition, depthStencilState, drawFramebuffer);
mGraphicsPipelineDesc->updateRenderPassDesc(&mGraphicsPipelineTransition,
mDrawFramebuffer->getRenderPassDesc());
onDrawFramebufferRenderPassDescChange(mDrawFramebuffer);
break;
}
case gl::State::DIRTY_BIT_RENDERBUFFER_BINDING:
......@@ -3413,17 +3412,22 @@ void ContextVk::invalidateDriverUniforms()
void ContextVk::onDrawFramebufferChange(FramebufferVk *framebufferVk)
{
const vk::RenderPassDesc &renderPassDesc = framebufferVk->getRenderPassDesc();
// Ensure that the RenderPass description is updated.
invalidateCurrentGraphicsPipeline();
// Ensure that the pipeline description is updated.
if (mGraphicsPipelineDesc->getRasterizationSamples() !=
static_cast<uint32_t>(framebufferVk->getSamples()))
{
mGraphicsPipelineDesc->updateRasterizationSamples(&mGraphicsPipelineTransition,
framebufferVk->getSamples());
}
mGraphicsPipelineDesc->updateRenderPassDesc(&mGraphicsPipelineTransition, renderPassDesc);
onDrawFramebufferRenderPassDescChange(framebufferVk);
}
void ContextVk::onDrawFramebufferRenderPassDescChange(FramebufferVk *framebufferVk)
{
invalidateCurrentGraphicsPipeline();
mGraphicsPipelineDesc->updateRenderPassDesc(&mGraphicsPipelineTransition,
framebufferVk->getRenderPassDesc());
}
void ContextVk::invalidateCurrentTransformFeedbackBuffers()
......@@ -4577,6 +4581,9 @@ angle::Result ContextVk::beginNewRenderPass(
mRenderPassCommands->beginRenderPass(framebuffer, renderArea, renderPassDesc,
renderPassAttachmentOps, depthStencilAttachmentIndex,
clearValues, commandBufferOut);
// Restart at subpass 0.
mGraphicsPipelineDesc->resetSubpass(&mGraphicsPipelineTransition);
return angle::Result::Continue;
}
......@@ -4610,6 +4617,16 @@ angle::Result ContextVk::startRenderPass(gl::Rectangle renderArea,
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)
{
if (mRenderPassCommandBuffer != nullptr)
......
......@@ -358,6 +358,7 @@ class ContextVk : public ContextImpl, public vk::Context
void invalidateDefaultAttribute(size_t attribIndex);
void invalidateDefaultAttributes(const gl::AttributesMask &dirtyMask);
void onDrawFramebufferChange(FramebufferVk *framebufferVk);
void onDrawFramebufferRenderPassDescChange(FramebufferVk *framebufferVk);
void onHostVisibleBufferWrite() { mIsAnyHostVisibleBufferWritten = true; }
void invalidateCurrentTransformFeedbackBuffers();
......@@ -586,6 +587,7 @@ class ContextVk : public ContextImpl, public vk::Context
egl::ContextPriority getContextPriority() const override { return mContextPriority; }
angle::Result startRenderPass(gl::Rectangle renderArea, vk::CommandBuffer **commandBufferOut);
void startNextSubpass();
angle::Result flushCommandsAndEndRenderPass();
angle::Result syncExternalMemory();
......
......@@ -1348,46 +1348,6 @@ angle::Result FramebufferVk::resolveColorWithCommand(ContextVk *contextVk,
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
{
// 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,
ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass());
}
// Notify the ContextVk to update the pipeline desc.
updateRenderPassDesc();
// Notify the ContextVk to update the pipeline desc.
FramebufferVk *currentDrawFramebuffer = vk::GetImpl(context->getState().getDrawFramebuffer());
if (currentDrawFramebuffer == this)
{
......@@ -2225,14 +2185,12 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
const gl::Rectangle &renderArea,
vk::CommandBuffer **commandBufferOut)
{
vk::Framebuffer *framebuffer = nullptr;
ANGLE_TRY(getFramebuffer(contextVk, &framebuffer, nullptr));
ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass());
// Initialize RenderPass info.
vk::AttachmentOpsArray renderPassAttachmentOps;
vk::PackedClearValuesArray packedClearValues;
gl::DrawBufferMask previousUnresolveMask = mRenderPassDesc.getColorUnresolveAttachmentMask();
// Color attachments.
const auto &colorRenderTargets = mRenderTargetCache.getColors();
......@@ -2274,22 +2232,29 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
VK_ATTACHMENT_STORE_OP_DONT_CARE);
// If there's a resolve attachment, and loadOp needs to be LOAD, the multisampled attachment
// needs to take its value from the resolve attachment. In this case, there's no choice
// but to blit from the resolve image into the multisampled one. It's expected that
// application code results in a clear of the framebuffer at the start of the renderpass, so
// the multisampled image is truely transient.
// needs to take its value from the resolve attachment. In this case, an initial subpass is
// added for this very purpose which uses the resolve attachment as input attachment. As a
// result, loadOp of the multisampled attachment can remain DONT_CARE.
//
// Note that this only needs to be done if the multisampled image and the resolve attachment
// come from the same source. When optimizing glBlitFramebuffer for example, this is not
// the case. isImageTransient() indicates whether this should happen.
//
// TODO: In an ideal world, this could be done in a subpass so that the multisampled data
// always ever stays on a tiled renderer's tile and no memory backing is allocated for it.
// http://anglebug.com/4881
if (colorRenderTarget->hasResolveAttachment() && colorRenderTarget->isImageTransient() &&
renderPassAttachmentOps[colorIndexVk].loadOp == VK_ATTACHMENT_LOAD_OP_LOAD)
// come from the same source. isImageTransient() indicates whether this should happen.
if (colorRenderTarget->hasResolveAttachment() && colorRenderTarget->isImageTransient())
{
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;
......@@ -2377,12 +2342,29 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
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,
renderPassAttachmentOps, depthStencilAttachmentIndex,
packedClearValues, commandBufferOut));
// Transition the images to the correct layout (through onColorDraw) after the
// resolve-to-multisampled copies are done.
// Transition the images to the correct layout (through onColorDraw).
for (size_t colorIndexGL : mState.getColorAttachmentsMask())
{
RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndexGL];
......@@ -2398,6 +2380,18 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
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;
}
......
......@@ -163,11 +163,6 @@ class FramebufferVk : public FramebufferImpl
const UtilsVk::BlitResolveParameters &params,
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,
gl::DrawBufferMask clearColorBuffers,
bool clearDepth,
......
......@@ -97,4 +97,15 @@ angle::Result GlslangWrapperVk::TransformSpirV(
removeEarlyFragmentTestsOptimization, removeDebugInfo, variableInfoMap, initialSpirvBlob,
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
......@@ -49,6 +49,11 @@ class GlslangWrapperVk
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const SpirvBlob &initialSpirvBlob,
SpirvBlob *shaderCodeOut);
static angle::Result CompileShaderOneOff(vk::Context *context,
gl::ShaderType shaderType,
const std::string &shaderSource,
SpirvBlob *spirvBlobOut);
};
} // namespace rx
......
......@@ -81,17 +81,19 @@ angle::Result RenderbufferVk::setStorageImpl(const gl::Context *context,
const bool isDepthStencilFormat = textureFormat.hasDepthOrStencilBits();
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
// 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
const bool isRenderToTexture =
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;
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
bool isAdreno540 = mPhysicalDeviceProperties.deviceID == angle::kDeviceID_Adreno540;
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,
!(IsApple() && isSwiftShader));
!(IsApple() && isSwiftShader) && !(isIntel && IsWindows()));
ANGLE_FEATURE_CONDITION(&mFeatures, preferredLargeHeapBlockSize4MB, !isQualcomm);
......
......@@ -1266,7 +1266,8 @@ angle::Result TextureVk::ensureImageAllocated(ContextVk *contextVk, const vk::Fo
else if (renderer->hasImageFormatFeatureBits(format.vkImageFormat,
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;
......
......@@ -164,6 +164,11 @@ class UtilsVk : angle::NonCopyable
uint32_t destLevelCount;
};
struct UnresolveParameters
{
gl::DrawBufferMask unresolveMask;
};
// Based on the maximum number of levels in GenerateMipmap.comp.
static constexpr uint32_t kGenerateMipmapMaxLevels = 6;
static uint32_t GetGenerateMipmapMaxLevels(ContextVk *contextVk);
......@@ -238,6 +243,10 @@ class UtilsVk : angle::NonCopyable
const vk::Sampler &sampler,
const GenerateMipmapParameters &params);
angle::Result unresolve(ContextVk *contextVk,
const FramebufferVk *framebuffer,
const UnresolveParameters &params);
// Overlay utilities.
angle::Result cullOverlayWidgets(ContextVk *contextVk,
vk::BufferHelper *enabledWidgetsBuffer,
......@@ -397,21 +406,30 @@ class UtilsVk : angle::NonCopyable
ImageClear = 0,
ImageCopy = 1,
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
ComputeStartIndex = 3, // Special value to separate draw and dispatch functions.
ConvertIndexBuffer = 3,
ConvertVertexBuffer = 4,
BlitResolveStencilNoExport = 5,
OverlayCull = 6,
OverlayDraw = 7,
ConvertIndexIndirectBuffer = 8,
ConvertIndexIndirectLineLoopBuffer = 9,
ConvertIndirectLineLoopBuffer = 10,
GenerateMipmap = 11,
InvalidEnum = 12,
EnumCount = 12,
ComputeStartIndex = 11, // Special value to separate draw and dispatch functions.
ConvertIndexBuffer = 11,
ConvertVertexBuffer = 12,
BlitResolveStencilNoExport = 13,
OverlayCull = 14,
OverlayDraw = 15,
ConvertIndexIndirectBuffer = 16,
ConvertIndexIndirectLineLoopBuffer = 17,
ConvertIndirectLineLoopBuffer = 18,
GenerateMipmap = 19,
InvalidEnum = 20,
EnumCount = 20,
};
// Common function that creates the pipeline for the specified function, binds it and prepares
......@@ -455,6 +473,9 @@ class UtilsVk : angle::NonCopyable
angle::Result ensureOverlayCullResourcesInitialized(ContextVk *contextVk);
angle::Result ensureOverlayDrawResourcesInitialized(ContextVk *contextVk);
angle::Result ensureGenerateMipmapResourcesInitialized(ContextVk *contextVk);
angle::Result ensureUnresolveResourcesInitialized(ContextVk *contextVk,
Function function,
uint32_t attachmentIndex);
angle::Result ensureSamplersInitialized(ContextVk *context);
......@@ -502,6 +523,11 @@ class UtilsVk : angle::NonCopyable
vk::ShaderProgramHelper
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 mLinearSampler;
};
......
......@@ -136,6 +136,9 @@ class alignas(4) RenderPassDesc final
// Remove the resolve attachment. Used when optimizing blit through resolve attachment to
// temporarily pack a resolve attachment and then remove it.
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;
......@@ -153,6 +156,14 @@ class alignas(4) RenderPassDesc final
{
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
// color attachments.
......@@ -193,9 +204,11 @@ class alignas(4) RenderPassDesc final
// (mAttachmentFormats is one element too large, so there are 8 bits there to take).
gl::DrawBufferMask mColorResolveAttachmentMask;
// TODO(syoussefi): to be used to determine which attachments are multisampled-render-to-texture
// that need load. http://anglebug.com/4881
ANGLE_MAYBE_UNUSED uint8_t padding;
// Whether each color attachment with a corresponding resolve attachment should be initialized
// with said resolve attachment in an initial subpass. This is an optimization to avoid
// 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
// attachment formats follow the last enabled color attachment. When creating a render pass,
......@@ -318,8 +331,11 @@ static_assert(kVertexInputAttributesSize == 96, "Size mismatch");
struct RasterizationStateBits final
{
uint32_t depthClampEnable : 4;
uint32_t rasterizationDiscardEnable : 4;
// Note: Currently only 2 subpasses possible, so there are 5 bits in subpass that can be
// repurposed.
uint32_t subpass : 6;
uint32_t depthClampEnable : 1;
uint32_t rasterizationDiscardEnable : 1;
uint32_t polygonMode : 4;
uint32_t cullMode : 4;
uint32_t frontFace : 4;
......@@ -607,7 +623,13 @@ class GraphicsPipelineDesc final
void setScissor(const VkRect2D &scissor);
void updateScissor(GraphicsPipelineTransitionBits *transition, const VkRect2D &scissor);
// Subpass
void resetSubpass(GraphicsPipelineTransitionBits *transition);
void nextSubpass(GraphicsPipelineTransitionBits *transition);
private:
void updateSubpass(GraphicsPipelineTransitionBits *transition, uint32_t subpass);
VertexInputAttributes mVertexInputAttribs;
RenderPassDesc mRenderPassDesc;
PackedRasterizationAndMultisampleStateInfo mRasterizationAndMultisampleStateInfo;
......@@ -1001,6 +1023,7 @@ class FramebufferDesc
void updateColor(uint32_t index, ImageViewSubresourceSerial serial);
void updateColorResolve(uint32_t index, ImageViewSubresourceSerial serial);
void updateColorUnresolveMask(gl::DrawBufferMask colorUnresolveMask);
void updateDepthStencil(ImageViewSubresourceSerial serial);
void updateReadOnlyDepth(bool readOnlyDepth);
size_t hash() const;
......@@ -1022,7 +1045,13 @@ class FramebufferDesc
// Note: this is an exclusive index. If there is one index it will be "1".
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;
};
......
......@@ -1521,6 +1521,9 @@ TEST_P(VulkanPerformanceCounterTest, DepthStencilTextureClearAndLoad)
// Tests that multisampled-render-to-texture depth/stencil textures don't ever load data.
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"));
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