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;
......
......@@ -11,6 +11,7 @@
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/FramebufferVk.h"
#include "libANGLE/renderer/vulkan/GlslangWrapperVk.h"
#include "libANGLE/renderer/vulkan/RenderTargetVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
......@@ -268,6 +269,44 @@ uint32_t GetGenerateMipmapFlags(ContextVk *contextVk, const vk::Format &format)
return flags;
}
enum UnresolveAttachmentType
{
kUnresolveTypeUnused = 0,
kUnresolveTypeFloat = 1,
kUnresolveTypeSint = 2,
kUnresolveTypeUint = 3,
};
uint32_t GetUnresolveFlags(uint32_t attachmentCount,
const gl::DrawBuffersArray<vk::ImageHelper *> &src,
gl::DrawBuffersArray<UnresolveAttachmentType> *attachmentTypesOut)
{
uint32_t flags = 0;
for (uint32_t attachmentIndex = 0; attachmentIndex < attachmentCount; ++attachmentIndex)
{
const angle::Format &format = src[attachmentIndex]->getFormat().intendedFormat();
UnresolveAttachmentType type = kUnresolveTypeFloat;
if (format.isSint())
{
type = kUnresolveTypeSint;
}
else if (format.isUint())
{
type = kUnresolveTypeUint;
}
(*attachmentTypesOut)[attachmentIndex] = type;
// |flags| is comprised of |attachmentCount| values from |UnresolveAttachmentType|, each
// taking up 2 bits.
flags |= type << (2 * attachmentIndex);
}
return flags;
}
uint32_t GetFormatDefaultChannelMask(const vk::Format &format)
{
uint32_t mask = 0;
......@@ -305,6 +344,81 @@ void CalculateResolveOffset(const UtilsVk::BlitResolveParameters &params, int32_
offset[0] = params.destOffset[0] - params.srcOffset[0] * srcOffsetFactorX;
offset[1] = params.destOffset[1] - params.srcOffset[1] * srcOffsetFactorY;
}
// Creates a shader that looks like the following, based on the number and types of unresolve
// attachments.
//
// #version 450 core
//
// layout(location = 0) out vec4 colorOut0;
// layout(location = 1) out ivec4 colorOut1;
// layout(location = 2) out uvec4 colorOut2;
// layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput colorIn0;
// layout(input_attachment_index = 1, set = 0, binding = 1) uniform isubpassInput colorIn1;
// layout(input_attachment_index = 2, set = 0, binding = 2) uniform usubpassInput colorIn2;
//
// void main()
// {
// colorOut0 = subpassLoad(colorIn0);
// colorOut1 = subpassLoad(colorIn1);
// colorOut2 = subpassLoad(colorIn2);
// }
angle::Result MakeUnresolveFragShader(
vk::Context *context,
uint32_t attachmentCount,
gl::DrawBuffersArray<UnresolveAttachmentType> &attachmentTypes,
SpirvBlob *spirvBlobOut)
{
std::ostringstream source;
source << "#version 450 core\n";
for (uint32_t attachmentIndex = 0; attachmentIndex < attachmentCount; ++attachmentIndex)
{
const UnresolveAttachmentType type = attachmentTypes[attachmentIndex];
ASSERT(type != kUnresolveTypeUnused);
const char *prefix =
type == kUnresolveTypeUint ? "u" : type == kUnresolveTypeSint ? "i" : "";
source << "layout(location=" << attachmentIndex << ") out " << prefix << "vec4 colorOut"
<< attachmentIndex << ";\n";
source << "layout(input_attachment_index=" << attachmentIndex
<< ", set=0, binding=" << attachmentIndex << ") uniform " << prefix
<< "subpassInput colorIn" << attachmentIndex << ";\n";
}
source << "void main(){\n";
for (uint32_t attachmentIndex = 0; attachmentIndex < attachmentCount; ++attachmentIndex)
{
source << "colorOut" << attachmentIndex << " = subpassLoad(colorIn" << attachmentIndex
<< ");\n";
}
source << "}\n";
return GlslangWrapperVk::CompileShaderOneOff(context, gl::ShaderType::Fragment, source.str(),
spirvBlobOut);
}
angle::Result GetUnresolveFrag(vk::Context *context,
uint32_t attachmentCount,
gl::DrawBuffersArray<UnresolveAttachmentType> &attachmentTypes,
vk::RefCounted<vk::ShaderAndSerial> *shader)
{
if (shader->get().valid())
{
return angle::Result::Continue;
}
SpirvBlob shaderCode;
ANGLE_TRY(MakeUnresolveFragShader(context, attachmentCount, attachmentTypes, &shaderCode));
// Create shader lazily. Access will need to be locked for multi-threading.
return vk::InitShaderAndSerial(context, &shader->get(), shaderCode.data(),
shaderCode.size() * 4);
}
} // namespace
const uint32_t UtilsVk::kGenerateMipmapMaxLevels;
......@@ -395,6 +509,20 @@ void UtilsVk::destroy(RendererVk *renderer)
program.destroy(device);
}
for (auto &programIter : mUnresolvePrograms)
{
vk::ShaderProgramHelper &program = programIter.second;
program.destroy(device);
}
mUnresolvePrograms.clear();
for (auto &shaderIter : mUnresolveFragShaders)
{
vk::RefCounted<vk::ShaderAndSerial> &shader = shaderIter.second;
shader.get().destroy(device);
}
mUnresolveFragShaders.clear();
mPointSampler.destroy(device);
mLinearSampler.destroy(device);
}
......@@ -650,6 +778,26 @@ angle::Result UtilsVk::ensureGenerateMipmapResourcesInitialized(ContextVk *conte
ArraySize(setSizes), sizeof(GenerateMipmapShaderParams));
}
angle::Result UtilsVk::ensureUnresolveResourcesInitialized(ContextVk *contextVk,
Function function,
uint32_t attachmentCount)
{
ASSERT(static_cast<uint32_t>(function) -
static_cast<uint32_t>(Function::Unresolve1Attachment) ==
attachmentCount - 1);
if (mPipelineLayouts[function].valid())
{
return angle::Result::Continue;
}
gl::DrawBuffersArray<VkDescriptorPoolSize> setSizes;
std::fill(setSizes.begin(), setSizes.end(),
VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1});
return ensureResourcesInitialized(contextVk, function, setSizes.data(), attachmentCount, 0);
}
angle::Result UtilsVk::ensureSamplersInitialized(ContextVk *contextVk)
{
VkSamplerCreateInfo samplerInfo = {};
......@@ -1945,6 +2093,96 @@ angle::Result UtilsVk::generateMipmap(ContextVk *contextVk,
return angle::Result::Continue;
}
angle::Result UtilsVk::unresolve(ContextVk *contextVk,
const FramebufferVk *framebuffer,
const UnresolveParameters &params)
{
// Get attachment count and pointers to resolve images and views.
gl::DrawBuffersArray<vk::ImageHelper *> src;
gl::DrawBuffersArray<const vk::ImageView *> srcView;
ASSERT(params.unresolveMask.any());
// The subpass that initializes the multisampled-render-to-texture attachments packs the
// attachments that need to be unresolved, so the attachment indices of this subpass are not the
// same. See InitializeUnresolveSubpass for details.
vk::PackedAttachmentIndex colorIndexVk(0);
for (size_t colorIndexGL : params.unresolveMask)
{
RenderTargetVk *colorRenderTarget = framebuffer->getColorDrawRenderTarget(colorIndexGL);
ASSERT(colorRenderTarget->hasResolveAttachment());
ASSERT(colorRenderTarget->isImageTransient());
src[colorIndexVk.get()] = &colorRenderTarget->getResolveImageForRenderPass();
ANGLE_TRY(colorRenderTarget->getResolveImageView(contextVk, &srcView[colorIndexVk.get()]));
++colorIndexVk;
}
const uint32_t attachmentCount = colorIndexVk.get();
const Function function = static_cast<Function>(
static_cast<uint32_t>(Function::Unresolve1Attachment) + attachmentCount - 1);
ANGLE_TRY(ensureUnresolveResourcesInitialized(contextVk, function, attachmentCount));
vk::GraphicsPipelineDesc pipelineDesc;
pipelineDesc.initDefaults();
pipelineDesc.setCullMode(VK_CULL_MODE_NONE);
pipelineDesc.setRasterizationSamples(framebuffer->getSamples());
pipelineDesc.setRenderPassDesc(framebuffer->getRenderPassDesc());
// Note: depth test is disabled by default so this should be unnecessary, but works around an
// Intel bug on windows. http://anglebug.com/3348
pipelineDesc.setDepthWriteEnabled(false);
VkViewport viewport;
gl::Rectangle completeRenderArea = framebuffer->getRotatedCompleteRenderArea(contextVk);
bool invertViewport = contextVk->isViewportFlipEnabledForDrawFBO();
gl_vk::GetViewport(completeRenderArea, 0.0f, 1.0f, invertViewport, completeRenderArea.height,
&viewport);
pipelineDesc.setViewport(viewport);
pipelineDesc.setScissor(gl_vk::GetRect(completeRenderArea));
VkDescriptorSet descriptorSet;
vk::RefCountedDescriptorPoolBinding descriptorPoolBinding;
ANGLE_TRY(allocateDescriptorSet(contextVk, function, &descriptorPoolBinding, &descriptorSet));
gl::DrawBuffersArray<VkDescriptorImageInfo> inputImageInfo = {};
for (uint32_t attachmentIndex = 0; attachmentIndex < attachmentCount; ++attachmentIndex)
{
inputImageInfo[attachmentIndex].imageView = srcView[attachmentIndex]->getHandle();
inputImageInfo[attachmentIndex].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
VkWriteDescriptorSet writeInfo = {};
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.dstSet = descriptorSet;
writeInfo.dstBinding = 0;
writeInfo.descriptorCount = attachmentCount;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
writeInfo.pImageInfo = inputImageInfo.data();
vkUpdateDescriptorSets(contextVk->getDevice(), 1, &writeInfo, 0, nullptr);
gl::DrawBuffersArray<UnresolveAttachmentType> attachmentTypes;
uint32_t flags = GetUnresolveFlags(attachmentCount, src, &attachmentTypes);
vk::ShaderLibrary &shaderLibrary = contextVk->getShaderLibrary();
vk::RefCounted<vk::ShaderAndSerial> *vertexShader = nullptr;
vk::RefCounted<vk::ShaderAndSerial> *fragmentShader = &mUnresolveFragShaders[flags];
ANGLE_TRY(shaderLibrary.getFullScreenQuad_vert(contextVk, 0, &vertexShader));
ANGLE_TRY(GetUnresolveFrag(contextVk, attachmentCount, attachmentTypes, fragmentShader));
vk::CommandBuffer *commandBuffer =
&contextVk->getStartedRenderPassCommands().getCommandBuffer();
ANGLE_TRY(setupProgram(contextVk, function, fragmentShader, vertexShader,
&mUnresolvePrograms[flags], &pipelineDesc, descriptorSet, nullptr, 0,
commandBuffer));
commandBuffer->draw(6, 0);
return angle::Result::Continue;
}
angle::Result UtilsVk::cullOverlayWidgets(ContextVk *contextVk,
vk::BufferHelper *enabledWidgetsBuffer,
vk::ImageHelper *dest,
......
......@@ -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;
};
......
......@@ -163,7 +163,9 @@ void UnpackAttachmentDesc(VkAttachmentDescription *desc,
ConvertImageLayoutToVkImageLayout(static_cast<ImageLayout>(ops.finalLayout));
}
void UnpackResolveAttachmentDesc(VkAttachmentDescription *desc, const vk::Format &format)
void UnpackResolveAttachmentDesc(VkAttachmentDescription *desc,
const vk::Format &format,
bool usedAsInputAttachment)
{
// We would only need this flag for duplicated attachments. Apply it conservatively. In
// practice it's unlikely any application would use the same image as multiple resolve
......@@ -179,8 +181,12 @@ void UnpackResolveAttachmentDesc(VkAttachmentDescription *desc, const vk::Format
// Resolve attachments always have a sample count of 1. For simplicity, unlikely cases where
// the resolve framebuffer is immediately invalidated or cleared are ignored. Therefore, loadOp
// and storeOp can be fixed to DONT_CARE and STORE respectively.
//
// That said, if the corresponding color attachment needs to take its initial value from the
// resolve attachment (i.e. needs to be unresolved), loadOp needs to be set to LOAD.
desc->samples = VK_SAMPLE_COUNT_1_BIT;
desc->loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc->loadOp =
usedAsInputAttachment ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc->storeOp = VK_ATTACHMENT_STORE_OP_STORE;
desc->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc->stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
......@@ -226,6 +232,213 @@ void SetPipelineShaderStageInfo(const VkStructureType type,
shaderStage->pSpecializationInfo = &specializationInfo;
}
// Defines a subpass that uses the resolve attachments as input attachments to initialize color
// attachments that need to be "unresolved" at the start of the render pass. The subpass will
// only contain color attachments that need to be unresolved to simplify the shader that performs
// the operations.
void InitializeUnresolveSubpass(
const RenderPassDesc &desc,
const gl::DrawBuffersVector<VkAttachmentReference> &drawSubpassColorAttachmentRefs,
const gl::DrawBuffersVector<VkAttachmentReference> &drawSubpassResolveAttachmentRefs,
const VkAttachmentReference &depthStencilAttachmentRef,
gl::DrawBuffersVector<VkAttachmentReference> *unresolveColorAttachmentRefs,
gl::DrawBuffersVector<VkAttachmentReference> *unresolveInputAttachmentRefs,
gl::DrawBuffersVector<uint32_t> *unresolvePreserveAttachmentRefs,
VkSubpassDescription *subpassDesc)
{
for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL)
{
// Assume the GL Framebuffer has the following attachments enabled:
//
// GL Color 0
// GL Color 3
// GL Color 4
// GL Color 6
// GL Color 7
// GL Depth/Stencil
//
// Additionally, assume Color 0, 4 and 6 are multisampled-render-to-texture (or for any
// other reason) have corresponding resolve attachments. Furthermore, say Color 4 and 6
// require an initial unresolve operation.
//
// In the above example, the render pass is created with the following attachments:
//
// RP Attachment[0] <- corresponding to GL Color 0
// RP Attachment[1] <- corresponding to GL Color 3
// RP Attachment[2] <- corresponding to GL Color 4
// RP Attachment[3] <- corresponding to GL Color 6
// RP Attachment[4] <- corresponding to GL Color 7
// RP Attachment[5] <- corresponding to GL Depth/Stencil
// RP Attachment[6] <- corresponding to resolve attachment of GL Color 0
// RP Attachment[7] <- corresponding to resolve attachment of GL Color 4
// RP Attachment[8] <- corresponding to resolve attachment of GL Color 6
//
// The subpass that takes the application draw calls has the following attachments, creating
// the mapping from the Vulkan attachment indices (i.e. RP attachment indices) to GL indices
// as indicated by the GL shaders:
//
// Subpass[1] Color[0] -> RP Attachment[0]
// Subpass[1] Color[1] -> VK_ATTACHMENT_UNUSED
// Subpass[1] Color[2] -> VK_ATTACHMENT_UNUSED
// Subpass[1] Color[3] -> RP Attachment[1]
// Subpass[1] Color[4] -> RP Attachment[2]
// Subpass[1] Color[5] -> VK_ATTACHMENT_UNUSED
// Subpass[1] Color[6] -> RP Attachment[3]
// Subpass[1] Color[7] -> RP Attachment[4]
// Subpass[1] Depth/Stencil -> RP Attachment[5]
// Subpass[1] Resolve[0] -> RP Attachment[6]
// Subpass[1] Resolve[1] -> VK_ATTACHMENT_UNUSED
// Subpass[1] Resolve[2] -> VK_ATTACHMENT_UNUSED
// Subpass[1] Resolve[3] -> VK_ATTACHMENT_UNUSED
// Subpass[1] Resolve[4] -> RP Attachment[7]
// Subpass[1] Resolve[5] -> VK_ATTACHMENT_UNUSED
// Subpass[1] Resolve[6] -> RP Attachment[8]
// Subpass[1] Resolve[7] -> VK_ATTACHMENT_UNUSED
//
// The initial subpass that's created here is (remember that in the above example Color 4
// and 6 need to be unresolved):
//
// Subpass[0] Input[0] -> RP Attachment[7] = Subpass[1] Resolve[4]
// Subpass[0] Input[1] -> RP Attachment[8] = Subpass[1] Resolve[6]
// Subpass[0] Color[0] -> RP Attachment[2] = Subpass[1] Color[4]
// Subpass[0] Color[1] -> RP Attachment[3] = Subpass[1] Color[6]
//
// The trick here therefore is to use the color attachment refs already created for the
// application draw subpass indexed with colorIndexGL.
//
// As an additional note, the attachments that are not used in the unresolve subpass must be
// preserved. That is color attachments and the depth/stencil attachment if any. Resolve
// attachments are rewritten by the next subpass, so they don't need to be preserved. Note
// that there's no need to preserve a color attachment whose loadOp is DONT_CARE. For
// simplicity, we preserve those as well. The driver would ideally avoid preserving
// attachments with loadOp=DONT_CARE.
//
// With the above example:
//
// Subpass[0] Preserve[0] -> RP Attachment[0] = Subpass[1] Color[0]
// Subpass[0] Preserve[1] -> RP Attachment[1] = Subpass[1] Color[3]
// Subpass[0] Preserve[2] -> RP Attachment[4] = Subpass[1] Color[7]
// Subpass[0] Preserve[3] -> RP Attachment[5] = Subpass[1] Depth/Stencil
//
// Again, the color attachment refs already created for the application draw subpass can be
// used indexed with colorIndexGL.
if (!desc.hasColorUnresolveAttachment(colorIndexGL))
{
if (desc.isColorAttachmentEnabled(colorIndexGL))
{
unresolvePreserveAttachmentRefs->push_back(
drawSubpassColorAttachmentRefs[colorIndexGL].attachment);
}
continue;
}
ASSERT(desc.isColorAttachmentEnabled(colorIndexGL));
ASSERT(desc.hasColorResolveAttachment(colorIndexGL));
ASSERT(drawSubpassColorAttachmentRefs[colorIndexGL].attachment != VK_ATTACHMENT_UNUSED);
ASSERT(drawSubpassResolveAttachmentRefs[colorIndexGL].attachment != VK_ATTACHMENT_UNUSED);
unresolveColorAttachmentRefs->push_back(drawSubpassColorAttachmentRefs[colorIndexGL]);
unresolveInputAttachmentRefs->push_back(drawSubpassResolveAttachmentRefs[colorIndexGL]);
// Note the input attachment layout should be shader read-only. The subpass dependency
// will take care of transitioning the layout of the resolve attachment to color attachment
// automatically.
unresolveInputAttachmentRefs->back().layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
if (depthStencilAttachmentRef.attachment != VK_ATTACHMENT_UNUSED)
{
// There's no need to preserve the depth attachment if loadOp=DONT_CARE, but we do for
// simplicity.
unresolvePreserveAttachmentRefs->push_back(depthStencilAttachmentRef.attachment);
}
ASSERT(!unresolveColorAttachmentRefs->empty());
ASSERT(unresolveColorAttachmentRefs->size() == unresolveInputAttachmentRefs->size());
const uint32_t attachmentCount = static_cast<uint32_t>(unresolveColorAttachmentRefs->size());
subpassDesc->flags = 0;
subpassDesc->pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDesc->inputAttachmentCount = attachmentCount;
subpassDesc->pInputAttachments = unresolveInputAttachmentRefs->data();
subpassDesc->colorAttachmentCount = attachmentCount;
subpassDesc->pColorAttachments = unresolveColorAttachmentRefs->data();
subpassDesc->pResolveAttachments = nullptr;
subpassDesc->pDepthStencilAttachment = nullptr;
subpassDesc->preserveAttachmentCount =
static_cast<uint32_t>(unresolvePreserveAttachmentRefs->size());
subpassDesc->pPreserveAttachments = unresolvePreserveAttachmentRefs->data();
}
// There is normally one subpass, and occasionally another for the unresolve operation.
constexpr size_t kSubpassFastVectorSize = 2;
using SubpassList = angle::FastVector<VkSubpassDescription, kSubpassFastVectorSize>;
void InitializeUnresolveSubpassDependencies(const SubpassList &subpassDesc,
std::vector<VkSubpassDependency> *subpassDependencies)
{
ASSERT(subpassDesc.size() >= 2);
// The unresolve subpass is the first subpass. The application draw subpass is the last
// subpass.
const uint32_t srcSubpass = 0;
const uint32_t dstSubpass = static_cast<uint32_t>(subpassDesc.size() - 1);
// A subpass dependency is needed between the unresolve and draw subpasses. There are two
// hazards here:
//
// - Subpass 0 writes to color attachments, subpass 1 writes to the same color attachments.
// This is a WaW hazard (color write -> color write) similar to when two subsequent render
// passes write to the same images.
// - Subpass 0 reads from resolve attachments, subpass 1 writes to the same resolve attachments.
// This is a WaR hazard (fragment shader read -> color write) which only requires an execution
// barrier.
//
// Note: the DEPENDENCY_BY_REGION flag is necessary to create a "framebuffer-local" dependency,
// as opposed to "framebuffer-global". The latter is effectively a render pass break. The
// former creates a dependency per framebuffer region. If dependency scopes correspond to
// attachments with:
//
// - Same sample count: dependency is at sample granularity
// - Different sample count: dependency is at pixel granularity
//
// The latter is clarified by the spec as such:
//
// > Practically, the pixel vs sample granularity dependency means that if an input attachment
// > has a different number of samples than the pipeline's rasterizationSamples, then a fragment
// > can access any sample in the input attachment's pixel even if it only uses
// > framebuffer-local dependencies.
//
// The dependency for the first hazard above (color write -> color write) is on same-sample
// attachments, so it will not allow the use of input attachments as required by the unresolve
// subpass. As a result, even though the second hazard seems to be subsumed by the first (its
// src stage is earlier and its dst stage is the same), a separate dependency is created for it
// just to obtain a pixel granularity dependency.
subpassDependencies->emplace_back();
VkSubpassDependency *dependency = &subpassDependencies->back();
dependency->srcSubpass = srcSubpass;
dependency->dstSubpass = dstSubpass;
dependency->srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency->dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency->srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependency->dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependency->dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
subpassDependencies->emplace_back();
dependency = &subpassDependencies->back();
dependency->srcSubpass = srcSubpass;
dependency->dstSubpass = dstSubpass;
dependency->srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependency->dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency->srcAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
dependency->dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependency->dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
}
angle::Result InitializeRenderPassFromDesc(vk::Context *context,
const RenderPassDesc &desc,
const AttachmentOpsArray &ops,
......@@ -327,38 +540,70 @@ angle::Result InitializeRenderPassFromDesc(vk::Context *context,
colorResolveAttachmentRefs.push_back(colorRef);
UnpackResolveAttachmentDesc(&attachmentDescs[attachmentCount.get()], format);
UnpackResolveAttachmentDesc(&attachmentDescs[attachmentCount.get()], format,
desc.hasColorUnresolveAttachment(colorIndexGL));
++attachmentCount;
}
VkSubpassDescription subpassDesc = {};
SubpassList subpassDesc;
subpassDesc.flags = 0;
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDesc.inputAttachmentCount = 0;
subpassDesc.pInputAttachments = nullptr;
subpassDesc.colorAttachmentCount = static_cast<uint32_t>(colorAttachmentRefs.size());
subpassDesc.pColorAttachments = colorAttachmentRefs.data();
subpassDesc.pResolveAttachments = attachmentCount.get() > nonResolveAttachmentCount
// If any attachment needs to be unresolved, create an initial subpass for this purpose. Note
// that the following arrays are used in initializing a VkSubpassDescription in subpassDesc,
// which is in turn used in VkRenderPassCreateInfo below. That is why they are declared in the
// same scope.
gl::DrawBuffersVector<VkAttachmentReference> unresolveColorAttachmentRefs;
gl::DrawBuffersVector<VkAttachmentReference> unresolveInputAttachmentRefs;
gl::DrawBuffersVector<uint32_t> unresolvePreserveAttachmentRefs;
if (desc.getColorUnresolveAttachmentMask().any())
{
subpassDesc.push_back({});
InitializeUnresolveSubpass(desc, colorAttachmentRefs, colorResolveAttachmentRefs,
depthStencilAttachmentRef, &unresolveColorAttachmentRefs,
&unresolveInputAttachmentRefs, &unresolvePreserveAttachmentRefs,
&subpassDesc.back());
}
subpassDesc.push_back({});
VkSubpassDescription *applicationSubpass = &subpassDesc.back();
applicationSubpass->flags = 0;
applicationSubpass->pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
applicationSubpass->inputAttachmentCount = 0;
applicationSubpass->pInputAttachments = nullptr;
applicationSubpass->colorAttachmentCount = static_cast<uint32_t>(colorAttachmentRefs.size());
applicationSubpass->pColorAttachments = colorAttachmentRefs.data();
applicationSubpass->pResolveAttachments = attachmentCount.get() > nonResolveAttachmentCount
? colorResolveAttachmentRefs.data()
: nullptr;
subpassDesc.pDepthStencilAttachment =
applicationSubpass->pDepthStencilAttachment =
(depthStencilAttachmentRef.attachment != VK_ATTACHMENT_UNUSED ? &depthStencilAttachmentRef
: nullptr);
subpassDesc.preserveAttachmentCount = 0;
subpassDesc.pPreserveAttachments = nullptr;
applicationSubpass->preserveAttachmentCount = 0;
applicationSubpass->pPreserveAttachments = nullptr;
std::vector<VkSubpassDependency> subpassDependencies;
if (desc.getColorUnresolveAttachmentMask().any())
{
InitializeUnresolveSubpassDependencies(subpassDesc, &subpassDependencies);
}
VkRenderPassCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
createInfo.flags = 0;
createInfo.attachmentCount = attachmentCount.get();
createInfo.pAttachments = attachmentDescs.data();
createInfo.subpassCount = 1;
createInfo.pSubpasses = &subpassDesc;
createInfo.subpassCount = static_cast<uint32_t>(subpassDesc.size());
createInfo.pSubpasses = subpassDesc.data();
createInfo.dependencyCount = 0;
createInfo.pDependencies = nullptr;
if (!subpassDependencies.empty())
{
createInfo.dependencyCount = static_cast<uint32_t>(subpassDependencies.size());
createInfo.pDependencies = subpassDependencies.data();
}
ANGLE_VK_TRY(context, renderPass->init(context->getDevice(), createInfo));
return angle::Result::Continue;
}
......@@ -566,6 +811,16 @@ void RenderPassDesc::removeColorResolveAttachment(size_t colorIndexGL)
mColorResolveAttachmentMask.reset(colorIndexGL);
}
void RenderPassDesc::packColorUnresolveAttachment(size_t colorIndexGL)
{
mColorUnresolveAttachmentMask.set(colorIndexGL);
}
void RenderPassDesc::removeColorUnresolveAttachment(size_t colorIndexGL)
{
mColorUnresolveAttachmentMask.reset(colorIndexGL);
}
RenderPassDesc &RenderPassDesc::operator=(const RenderPassDesc &other)
{
memcpy(this, &other, sizeof(RenderPassDesc));
......@@ -658,6 +913,7 @@ void GraphicsPipelineDesc::initDefaults()
SetBitField(packedAttrib.offset, 0);
}
mRasterizationAndMultisampleStateInfo.bits.subpass = 0;
mRasterizationAndMultisampleStateInfo.bits.depthClampEnable = 0;
mRasterizationAndMultisampleStateInfo.bits.rasterizationDiscardEnable = 0;
SetBitField(mRasterizationAndMultisampleStateInfo.bits.polygonMode, VK_POLYGON_MODE_FILL);
......@@ -1014,6 +1270,15 @@ angle::Result GraphicsPipelineDesc::initializePipeline(
blendState.attachmentCount = static_cast<uint32_t>(mRenderPassDesc.colorAttachmentRange());
blendState.pAttachments = blendAttachmentState.data();
// If this graphics pipeline is for the unresolve operation, correct the color attachment count
// for that subpass.
if (mRenderPassDesc.getColorUnresolveAttachmentMask().any() &&
mRasterizationAndMultisampleStateInfo.bits.subpass == 0)
{
blendState.attachmentCount =
static_cast<uint32_t>(mRenderPassDesc.getColorUnresolveAttachmentMask().count());
}
for (int i = 0; i < 4; i++)
{
blendState.blendConstants[i] = inputAndBlend.blendConstants[i];
......@@ -1064,7 +1329,7 @@ angle::Result GraphicsPipelineDesc::initializePipeline(
createInfo.pDynamicState = nullptr;
createInfo.layout = pipelineLayout.getHandle();
createInfo.renderPass = compatibleRenderPass.getHandle();
createInfo.subpass = 0;
createInfo.subpass = mRasterizationAndMultisampleStateInfo.bits.subpass;
createInfo.basePipelineHandle = VK_NULL_HANDLE;
createInfo.basePipelineIndex = 0;
......@@ -1540,6 +1805,26 @@ void GraphicsPipelineDesc::updateScissor(GraphicsPipelineTransitionBits *transit
transition->set(ANGLE_GET_TRANSITION_BIT(mScissor, extent.height));
}
void GraphicsPipelineDesc::updateSubpass(GraphicsPipelineTransitionBits *transition,
uint32_t subpass)
{
if (mRasterizationAndMultisampleStateInfo.bits.subpass != subpass)
{
SetBitField(mRasterizationAndMultisampleStateInfo.bits.subpass, subpass);
transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits));
}
}
void GraphicsPipelineDesc::resetSubpass(GraphicsPipelineTransitionBits *transition)
{
updateSubpass(transition, 0);
}
void GraphicsPipelineDesc::nextSubpass(GraphicsPipelineTransitionBits *transition)
{
updateSubpass(transition, mRasterizationAndMultisampleStateInfo.bits.subpass + 1);
}
void GraphicsPipelineDesc::updateRenderPassDesc(GraphicsPipelineTransitionBits *transition,
const RenderPassDesc &renderPassDesc)
{
......@@ -1897,6 +2182,11 @@ void FramebufferDesc::updateColorResolve(uint32_t index, ImageViewSubresourceSer
update(kFramebufferDescResolveIndexOffset + index, serial);
}
void FramebufferDesc::updateColorUnresolveMask(gl::DrawBufferMask colorUnresolveMask)
{
mColorUnresolveAttachmentMask = colorUnresolveMask;
}
void FramebufferDesc::updateDepthStencil(ImageViewSubresourceSerial serial)
{
update(kFramebufferDescDepthStencilIndex, serial);
......@@ -1909,24 +2199,22 @@ void FramebufferDesc::updateReadOnlyDepth(bool readOnlyDepth)
size_t FramebufferDesc::hash() const
{
return angle::ComputeGenericHash(&mSerials, sizeof(mSerials[0]) * (mMaxIndex + 1));
return angle::ComputeGenericHash(&mSerials, sizeof(mSerials[0]) * mMaxIndex) ^
mColorUnresolveAttachmentMask.bits();
}
void FramebufferDesc::reset()
{
mMaxIndex = 0;
mReadOnlyDepth = false;
mColorUnresolveAttachmentMask.reset();
memset(&mSerials, 0, sizeof(mSerials));
}
bool FramebufferDesc::operator==(const FramebufferDesc &other) const
{
if (mMaxIndex != other.mMaxIndex)
{
return false;
}
if (mReadOnlyDepth != other.mReadOnlyDepth)
if (mMaxIndex != other.mMaxIndex || mReadOnlyDepth != other.mReadOnlyDepth ||
mColorUnresolveAttachmentMask != other.mColorUnresolveAttachmentMask)
{
return false;
}
......
......@@ -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;
};
......
......@@ -119,9 +119,17 @@ class MultisampledRenderToTextureTest : public ANGLETest
ASSERT_GL_NO_ERROR();
}
struct GLType
{
GLint internalFormat;
GLenum format;
GLenum type;
};
void createAndAttachColorAttachment(bool useRenderbuffer,
GLsizei size,
GLenum renderbufferTarget,
const GLType *glType,
GLTexture *textureOut,
GLRenderbuffer *renderbufferOut);
void createAndAttachDepthStencilAttachment(bool useRenderbuffer,
......@@ -132,6 +140,7 @@ class MultisampledRenderToTextureTest : public ANGLETest
void copyTexImageTestCommon(bool useRenderbuffer);
void copyTexSubImageTestCommon(bool useRenderbuffer);
void drawCopyThenBlendCommon(bool useRenderbuffer);
void clearDrawCopyThenBlendSameProgramCommon(bool useRenderbuffer);
void clearThenBlendCommon(bool useRenderbuffer);
void depthStencilClearThenDrawCommon(bool useRenderbuffer);
......@@ -153,6 +162,7 @@ class MultisampledRenderToTextureES31Test : public MultisampledRenderToTextureTe
{
protected:
void blitFramebufferAttachment1Common(bool useRenderbuffer);
void drawCopyThenBlendAllAttachmentsMixed(bool useRenderbuffer);
};
// Checking against invalid parameters for RenderbufferStorageMultisampleEXT.
......@@ -443,20 +453,29 @@ void MultisampledRenderToTextureTest::createAndAttachColorAttachment(
bool useRenderbuffer,
GLsizei size,
GLenum renderbufferTarget,
const GLType *glType,
GLTexture *textureOut,
GLRenderbuffer *renderbufferOut)
{
GLenum internalFormat = glType ? glType->internalFormat : GL_RGBA;
GLenum format = glType ? glType->format : GL_RGBA;
GLenum type = glType ? glType->type : GL_UNSIGNED_BYTE;
if (useRenderbuffer)
{
if (internalFormat == GL_RGBA)
{
internalFormat = GL_RGBA8;
}
glBindRenderbuffer(GL_RENDERBUFFER, *renderbufferOut);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_RGBA8, size, size);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, internalFormat, size, size);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, renderbufferTarget, GL_RENDERBUFFER,
*renderbufferOut);
}
else
{
glBindTexture(GL_TEXTURE_2D, *textureOut);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, size, size, 0, format, type, nullptr);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, renderbufferTarget, GL_TEXTURE_2D,
*textureOut, 0, 4);
}
......@@ -498,7 +517,7 @@ void MultisampledRenderToTextureTest::colorAttachmentMultisampleDrawTestCommon(b
constexpr GLsizei kSize = 6;
GLTexture texture;
GLRenderbuffer renderbuffer;
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, &texture,
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr, &texture,
&renderbuffer);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
......@@ -634,7 +653,7 @@ void MultisampledRenderToTextureES3Test::readPixelsTestCommon(bool useRenderbuff
constexpr GLsizei kSize = 6;
GLTexture texture;
GLRenderbuffer renderbuffer;
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, &texture,
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr, &texture,
&renderbuffer);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
......@@ -690,7 +709,7 @@ void MultisampledRenderToTextureTest::copyTexImageTestCommon(bool useRenderbuffe
GLTexture texture;
GLRenderbuffer renderbuffer;
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, &texture,
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr, &texture,
&renderbuffer);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
......@@ -740,7 +759,7 @@ void MultisampledRenderToTextureTest::copyTexSubImageTestCommon(bool useRenderbu
// Create color attachment for copyFBO0
GLTexture texture;
GLRenderbuffer renderbuffer;
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, &texture,
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr, &texture,
&renderbuffer);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
......@@ -750,7 +769,7 @@ void MultisampledRenderToTextureTest::copyTexSubImageTestCommon(bool useRenderbu
// Create color attachment for copyFBO1
GLTexture texture1;
GLRenderbuffer renderbuffer1;
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, &texture1,
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr, &texture1,
&renderbuffer1);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
......@@ -829,8 +848,8 @@ void MultisampledRenderToTextureES3Test::blitFramebufferTestCommon(bool useRende
GLTexture textureMS;
GLRenderbuffer renderbufferMS;
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, &textureMS,
&renderbufferMS);
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
&textureMS, &renderbufferMS);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Clear depth to 0.5 and color to green.
......@@ -948,8 +967,8 @@ void MultisampledRenderToTextureTest::drawCopyThenBlendCommon(bool useRenderbuff
// Create multisampled framebuffer to draw into
GLTexture textureMS;
GLRenderbuffer renderbufferMS;
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, &textureMS,
&renderbufferMS);
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
&textureMS, &renderbufferMS);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Draw red into the multisampled color buffer.
......@@ -990,8 +1009,8 @@ void MultisampledRenderToTextureTest::drawCopyThenBlendCommon(bool useRenderbuff
// For completeness, verify that the texture used as copy target is red.
ASSERT_GL_NO_ERROR();
const GLColor expectedCopyResult(255, 0, 0, 255);
verifyResults(texture, expectedCopyResult, kSize, 0, 0, kSize, kSize);
const GLColor kExpectedCopyResult(255, 0, 0, 255);
verifyResults(texture, kExpectedCopyResult, kSize, 0, 0, kSize, kSize);
ASSERT_GL_NO_ERROR();
}
......@@ -1010,6 +1029,99 @@ TEST_P(MultisampledRenderToTextureTest, RenderbufferDrawCopyThenBlend)
drawCopyThenBlendCommon(true);
}
void MultisampledRenderToTextureTest::clearDrawCopyThenBlendSameProgramCommon(bool useRenderbuffer)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
constexpr GLsizei kSize = 64;
setupCopyTexProgram();
GLFramebuffer fboMS;
glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
// Create multisampled framebuffer to draw into
GLTexture textureMS;
GLRenderbuffer renderbufferMS;
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
&textureMS, &renderbufferMS);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Draw red into the multisampled color buffer.
ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
glUseProgram(drawColor);
GLint colorUniformLocation =
glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
// Clear the framebuffer.
glClearColor(0.1f, 0.9f, 0.2f, 0.8f);
glClear(GL_COLOR_BUFFER_BIT);
// Then draw into it.
glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Create a texture and copy into it.
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Draw again into the framebuffer, this time blending. This tests that the framebuffer's data,
// residing in the single-sampled texture, is available to the multisampled intermediate image
// for blending.
// Blend half-transparent green into the multisampled color buffer.
glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 0.5f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Verify that the texture is now yellow
const GLColor kExpected(127, 127, 0, 191);
EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
// Once again, clear and draw so the program is used again in the way it was first used.
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_BLEND);
glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor::blue, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, GLColor::blue, 1);
EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, GLColor::blue, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, GLColor::blue, 1);
// For completeness, verify that the texture used as copy target is red.
ASSERT_GL_NO_ERROR();
const GLColor kExpectedCopyResult(255, 0, 0, 255);
verifyResults(texture, kExpectedCopyResult, kSize, 0, 0, kSize, kSize);
ASSERT_GL_NO_ERROR();
}
// Clear&Draw, copy, then blend with same program. The copy will make sure an implicit resolve
// happens. The second draw should retain the data written by the first draw command ("unresolve"
// operation). The same program is used for the first and second draw calls, and the fact that the
// attachment is cleared or unresolved should not cause issues. In the Vulkan backend, the program
// will be used in different subpass indices, so two graphics pipelines should be created for it.
TEST_P(MultisampledRenderToTextureTest, ClearDrawCopyThenBlendSameProgram)
{
clearDrawCopyThenBlendSameProgramCommon(false);
}
// Same as ClearDrawCopyThenBlendSameProgram but with renderbuffers
TEST_P(MultisampledRenderToTextureTest, RenderbufferClearDrawCopyThenBlendSameProgram)
{
clearDrawCopyThenBlendSameProgramCommon(true);
}
void MultisampledRenderToTextureTest::clearThenBlendCommon(bool useRenderbuffer)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
......@@ -1023,8 +1135,8 @@ void MultisampledRenderToTextureTest::clearThenBlendCommon(bool useRenderbuffer)
// Create multisampled framebuffer to draw into
GLTexture textureMS;
GLRenderbuffer renderbufferMS;
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, &textureMS,
&renderbufferMS);
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
&textureMS, &renderbufferMS);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Clear the framebuffer.
......@@ -1081,8 +1193,8 @@ void MultisampledRenderToTextureTest::depthStencilClearThenDrawCommon(bool useRe
// Create framebuffer to draw into, with both color and depth attachments.
GLTexture textureMS;
GLRenderbuffer renderbufferMS;
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, &textureMS,
&renderbufferMS);
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
&textureMS, &renderbufferMS);
GLTexture dsTextureMS;
GLRenderbuffer dsRenderbufferMS;
......@@ -1159,8 +1271,8 @@ void MultisampledRenderToTextureES3Test::colorAttachment1Common(bool useRenderbu
// Create multisampled framebuffer to draw into, use color attachment 1
GLTexture textureMS;
GLRenderbuffer renderbufferMS;
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT1, &textureMS,
&renderbufferMS);
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT1, nullptr,
&textureMS, &renderbufferMS);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Setup program to render into attachment 1.
......@@ -1206,8 +1318,8 @@ void MultisampledRenderToTextureES3Test::colorAttachment1Common(bool useRenderbu
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
// For completeness, verify that the texture used as copy target is red.
const GLColor expectedCopyResult(255, 0, 0, 255);
verifyResults(texture, expectedCopyResult, kSize, 0, 0, kSize, kSize);
const GLColor kExpectedCopyResult(255, 0, 0, 255);
verifyResults(texture, kExpectedCopyResult, kSize, 0, 0, kSize, kSize);
ASSERT_GL_NO_ERROR();
......@@ -1250,13 +1362,13 @@ void MultisampledRenderToTextureES3Test::colorAttachments0And3Common(bool useRen
// Create multisampled framebuffer to draw into, use color attachment 1
GLTexture textureMS0;
GLRenderbuffer renderbufferMS0;
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, &textureMS0,
&renderbufferMS0);
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
&textureMS0, &renderbufferMS0);
GLTexture textureMS3;
GLRenderbuffer renderbufferMS3;
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT3, &textureMS3,
&renderbufferMS3);
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT3, nullptr,
&textureMS3, &renderbufferMS3);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
......@@ -1310,8 +1422,8 @@ void MultisampledRenderToTextureES3Test::colorAttachments0And3Common(bool useRen
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
// For completeness, verify that the texture used as copy target is red.
const GLColor expectedCopyResult(255, 0, 0, 255);
verifyResults(texture, expectedCopyResult, kSize, 0, 0, kSize, kSize);
const GLColor kExpectedCopyResult(255, 0, 0, 255);
verifyResults(texture, kExpectedCopyResult, kSize, 0, 0, kSize, kSize);
ASSERT_GL_NO_ERROR();
......@@ -1480,8 +1592,8 @@ TEST_P(MultisampledRenderToTextureES31Test, MixedMultisampledAndMultisampledRend
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
// For completeness, verify that the texture used as copy target is red.
const GLColor expectedCopyResult(255, 0, 0, 255);
verifyResults(texture, expectedCopyResult, kSize, 0, 0, kSize, kSize);
const GLColor kExpectedCopyResult(255, 0, 0, 255);
verifyResults(texture, kExpectedCopyResult, kSize, 0, 0, kSize, kSize);
ASSERT_GL_NO_ERROR();
......@@ -1508,8 +1620,8 @@ void MultisampledRenderToTextureES31Test::blitFramebufferAttachment1Common(bool
GLTexture textureMS1;
GLRenderbuffer renderbufferMS1;
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT1, &textureMS1,
&renderbufferMS1);
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT1, nullptr,
&textureMS1, &renderbufferMS1);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Setup program to render into attachments 0 and 1.
......@@ -1594,8 +1706,8 @@ void MultisampledRenderToTextureES3Test::blitFramebufferMixedColorAndDepthCommon
GLTexture textureMS;
GLRenderbuffer renderbufferMS;
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, &textureMS,
&renderbufferMS);
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
&textureMS, &renderbufferMS);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Clear depth to 0.5 and color to red.
......@@ -1825,6 +1937,181 @@ TEST_P(MultisampledRenderToTextureTest, DrawMultisampledDifferentSamples)
ASSERT_GL_NO_ERROR();
}
void MultisampledRenderToTextureES31Test::drawCopyThenBlendAllAttachmentsMixed(bool useRenderbuffer)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
// The Vulkan backend doesn't appropriately disable blend for integer attachments. This causes
// an assertion failure within swiftshader. http://anglebug.com/4583
ANGLE_SKIP_TEST_IF(isSwiftshader());
GLint maxDrawBuffers = 0;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
// At least 4 draw buffers per GLES3.0 spec.
ASSERT_GE(maxDrawBuffers, 4);
// Maximum 8 draw buffers exposed by ANGLE.
constexpr GLint kImplMaxDrawBuffers = 8;
maxDrawBuffers = std::min(maxDrawBuffers, kImplMaxDrawBuffers);
constexpr const char *kDecl[kImplMaxDrawBuffers] = {
"layout(location = 0) out vec4 out0;", "layout(location = 1) out ivec4 out1;",
"layout(location = 2) out uvec4 out2;", "layout(location = 3) out vec4 out3;",
"layout(location = 4) out uvec4 out4;", "layout(location = 5) out ivec4 out5;",
"layout(location = 6) out ivec4 out6;", "layout(location = 7) out vec4 out7;",
};
constexpr GLType kGLType[kImplMaxDrawBuffers] = {
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT},
{GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT}, {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE},
{GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT}, {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT},
{GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE},
};
constexpr const char *kAssign1[kImplMaxDrawBuffers] = {
"out0 = vec4(1.0f, 0.0f, 0.0f, 1.0f);",
"out1 = ivec4(-19, 13, 123456, -654321);",
"out2 = uvec4(98765, 43210, 2, 0);",
"out3 = vec4(0.0f, 1.0f, 0.0f, 1.0f);",
"out4 = uvec4(10101010, 2345, 0, 991);",
"out5 = ivec4(615243, -948576, -222, 111);",
"out6 = ivec4(-8127931, -1392781, 246810, 1214161820);",
"out7 = vec4(0.0f, 0.0f, 1.0f, 1.0f);",
};
constexpr const char *kAssign2[kImplMaxDrawBuffers] = {
"out0 = vec4(0.0f, 1.0f, 0.0f, 0.5f);",
"out1 = ivec4(0, 0, 0, 0);",
"out2 = uvec4(0, 0, 0, 0);",
"out3 = vec4(0.0f, 0.0f, 1.0f, 0.5f);",
"out4 = uvec4(0, 0, 0, 0);",
"out5 = ivec4(0, 0, 0, 0);",
"out6 = ivec4(0, 0, 0, 0);",
"out7 = vec4(1.0f, 0.0f, 0.0f, 0.5f);",
};
// Generate the shaders, [0] for first draw and [1] for second.
std::stringstream fsStr[2];
for (unsigned int index = 0; index < 2; ++index)
{
fsStr[index] << R"(#version 300 es
precision highp float;
)";
for (GLint drawBuffer = 0; drawBuffer < maxDrawBuffers; ++drawBuffer)
{
fsStr[index] << kDecl[drawBuffer] << "\n";
}
fsStr[index] << R"(void main()
{
)";
const char *const *assign = index == 0 ? kAssign1 : kAssign2;
for (GLint drawBuffer = 0; drawBuffer < maxDrawBuffers; ++drawBuffer)
{
fsStr[index] << assign[drawBuffer] << "\n";
}
fsStr[index] << "}\n";
}
constexpr GLsizei kSize = 64;
setupCopyTexProgram();
// Create multisampled framebuffer to draw into
GLFramebuffer fboMS;
glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
GLTexture textureMS[kImplMaxDrawBuffers];
GLRenderbuffer renderbufferMS[kImplMaxDrawBuffers];
for (GLint drawBuffer = 0; drawBuffer < maxDrawBuffers; ++drawBuffer)
{
createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0 + drawBuffer,
&kGLType[drawBuffer], &textureMS[drawBuffer],
&renderbufferMS[drawBuffer]);
}
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Setup programs
ANGLE_GL_PROGRAM(drawProg, essl3_shaders::vs::Simple(), fsStr[0].str().c_str());
ANGLE_GL_PROGRAM(blendProg, essl3_shaders::vs::Simple(), fsStr[1].str().c_str());
constexpr GLenum kDrawBuffers[] = {
GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3,
GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5, GL_COLOR_ATTACHMENT6, GL_COLOR_ATTACHMENT7};
glDrawBuffers(maxDrawBuffers, kDrawBuffers);
ASSERT_GL_NO_ERROR();
// Draw into the multisampled color buffers.
glUseProgram(drawProg);
drawQuad(drawProg, essl3_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Create a texture and copy from one of them.
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
ASSERT_GL_NO_ERROR();
// Blend color buffers.
glUseProgram(blendProg);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
drawQuad(blendProg, essl3_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Verify texture colors.
glReadBuffer(GL_COLOR_ATTACHMENT0);
const GLColor kExpected0(127, 127, 0, 191);
EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected0, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected0, 1);
EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected0, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected0, 1);
glReadBuffer(GL_COLOR_ATTACHMENT3);
const GLColor kExpected3(0, 127, 127, 191);
EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected3, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected3, 1);
EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected3, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected3, 1);
if (maxDrawBuffers > 7)
{
glReadBuffer(GL_COLOR_ATTACHMENT7);
const GLColor kExpected7(127, 0, 127, 191);
EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected7, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected7, 1);
EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected7, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected7, 1);
}
// For completeness, verify that the texture used as copy target is red.
const GLColor kExpectedCopyResult(255, 0, 0, 255);
verifyResults(texture, kExpectedCopyResult, kSize, 0, 0, kSize, kSize);
ASSERT_GL_NO_ERROR();
}
// Draw, copy, then blend with 8 mixed format attachments. The copy will make sure an implicit
// resolve happens. Regardless, the following draw should retain the data written by the first draw
// command.
TEST_P(MultisampledRenderToTextureES31Test, DrawCopyThenBlendAllAttachmentsMixed)
{
drawCopyThenBlendAllAttachmentsMixed(false);
}
// Same as DrawCopyThenBlendAllAttachmentsMixed but with renderbuffers.
TEST_P(MultisampledRenderToTextureES31Test, RenderbufferDrawCopyThenBlendAllAttachmentsMixed)
{
drawCopyThenBlendAllAttachmentsMixed(true);
}
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND_ES31(MultisampledRenderToTextureTest);
ANGLE_INSTANTIATE_TEST_ES3(MultisampledRenderToTextureES3Test);
ANGLE_INSTANTIATE_TEST_ES31(MultisampledRenderToTextureES31Test);
......
......@@ -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