// // Copyright 2018 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // UtilsVk.cpp: // Implements the UtilsVk class. // #include "libANGLE/renderer/vulkan/UtilsVk.h" #include "common/spirv/spirv_instruction_builder_autogen.h" #include "libANGLE/renderer/vulkan/ContextVk.h" #include "libANGLE/renderer/vulkan/FramebufferVk.h" #include "libANGLE/renderer/vulkan/RenderTargetVk.h" #include "libANGLE/renderer/vulkan/SurfaceVk.h" #include "libANGLE/renderer/vulkan/vk_renderer.h" #include "libANGLE/renderer/vulkan/vk_utils.h" namespace rx { namespace ConvertVertex_comp = vk::InternalShader::ConvertVertex_comp; namespace ImageClear_frag = vk::InternalShader::ImageClear_frag; namespace ImageCopy_frag = vk::InternalShader::ImageCopy_frag; namespace ImageCopyFloat_frag = vk::InternalShader::ImageCopyFloat_frag; namespace CopyImageToBuffer_comp = vk::InternalShader::CopyImageToBuffer_comp; namespace BlitResolve_frag = vk::InternalShader::BlitResolve_frag; namespace Blit3DSrc_frag = vk::InternalShader::Blit3DSrc_frag; namespace BlitResolveStencilNoExport_comp = vk::InternalShader::BlitResolveStencilNoExport_comp; namespace ExportStencil_frag = vk::InternalShader::ExportStencil_frag; namespace ConvertIndexIndirectLineLoop_comp = vk::InternalShader::ConvertIndexIndirectLineLoop_comp; namespace GenerateMipmap_comp = vk::InternalShader::GenerateMipmap_comp; namespace EtcToBc_comp = vk::InternalShader::EtcToBc_comp; namespace spirv = angle::spirv; namespace { constexpr uint32_t kConvertIndexDestinationBinding = 0; constexpr uint32_t kConvertVertexDestinationBinding = 0; constexpr uint32_t kConvertVertexSourceBinding = 1; constexpr uint32_t kImageCopySourceBinding = 0; constexpr uint32_t kCopyImageToBufferSourceBinding = 0; constexpr uint32_t kCopyImageToBufferDestinationBinding = 1; constexpr uint32_t kBlitResolveColorOrDepthBinding = 0; constexpr uint32_t kBlitResolveStencilBinding = 1; constexpr uint32_t kBlitResolveSamplerBinding = 2; constexpr uint32_t kBlitResolveStencilNoExportDestBinding = 0; constexpr uint32_t kBlitResolveStencilNoExportSrcBinding = 1; constexpr uint32_t kBlitResolveStencilNoExportSamplerBinding = 2; constexpr uint32_t kExportStencilInputIndex = 0; constexpr uint32_t kExportStencilBinding = 0; constexpr uint32_t kOverlayDrawTextWidgetsBinding = 0; constexpr uint32_t kOverlayDrawGraphWidgetsBinding = 1; constexpr uint32_t kOverlayDrawFontBinding = 2; constexpr uint32_t kGenerateMipmapDestinationBinding = 0; constexpr uint32_t kGenerateMipmapSourceBinding = 1; constexpr uint32_t kGenerateFragmentShadingRateAttachmentBinding = 0; bool ValidateFloatOneAsUint() { union { uint32_t asUint; float asFloat; } one; one.asUint = gl::Float32One; return one.asFloat == 1.0f; } uint32_t GetConvertVertexFlags(const UtilsVk::ConvertVertexParameters ¶ms) { bool srcIsSint = params.srcFormat->isSint(); bool srcIsUint = params.srcFormat->isUint(); bool srcIsSnorm = params.srcFormat->isSnorm(); bool srcIsUnorm = params.srcFormat->isUnorm(); bool srcIsFixed = params.srcFormat->isFixed; bool srcIsFloat = params.srcFormat->isFloat(); bool srcIsHalfFloat = params.srcFormat->isVertexTypeHalfFloat(); bool dstIsSint = params.dstFormat->isSint(); bool dstIsUint = params.dstFormat->isUint(); bool dstIsSnorm = params.dstFormat->isSnorm(); bool dstIsUnorm = params.dstFormat->isUnorm(); bool dstIsFloat = params.dstFormat->isFloat(); bool dstIsHalfFloat = params.dstFormat->isVertexTypeHalfFloat(); // Assert on the types to make sure the shader supports its. These are based on // ConvertVertex_comp::Conversion values. ASSERT(!dstIsSint || srcIsSint); // If destination is sint, src must be sint too ASSERT(!dstIsUint || srcIsUint); // If destination is uint, src must be uint too ASSERT(!srcIsFixed || dstIsFloat); // If source is fixed, dst must be float // One of each bool set must be true ASSERT(srcIsSint || srcIsUint || srcIsSnorm || srcIsUnorm || srcIsFixed || srcIsFloat); ASSERT(dstIsSint || dstIsUint || dstIsSnorm || dstIsUnorm || dstIsFloat || dstIsHalfFloat); // We currently don't have any big-endian devices in the list of supported platforms. The // shader is capable of supporting big-endian architectures, but the relevant flag (IsBigEndian) // is not added to the build configuration file (to reduce binary size). If necessary, add // IsBigEndian to ConvertVertex.comp.json and select the appropriate flag based on the // endian-ness test here. ASSERT(IsLittleEndian()); uint32_t flags = 0; if (srcIsHalfFloat && dstIsHalfFloat) { // Note that HalfFloat conversion uses the same shader as Uint. flags = ConvertVertex_comp::kUintToUint; } else if ((srcIsSnorm && dstIsSnorm) || (srcIsUnorm && dstIsUnorm)) { // Do snorm->snorm and unorm->unorm copies using the uint->uint shader. Currently only // supported for same-width formats, so it's only used when adding channels. ASSERT(params.srcFormat->redBits == params.dstFormat->redBits); flags = ConvertVertex_comp::kUintToUint; } else if (srcIsSint && dstIsSint) { flags = ConvertVertex_comp::kSintToSint; } else if (srcIsUint && dstIsUint) { flags = ConvertVertex_comp::kUintToUint; } else if (srcIsSint) { flags = ConvertVertex_comp::kSintToFloat; } else if (srcIsUint) { flags = ConvertVertex_comp::kUintToFloat; } else if (srcIsSnorm) { flags = ConvertVertex_comp::kSnormToFloat; } else if (srcIsUnorm) { flags = ConvertVertex_comp::kUnormToFloat; } else if (srcIsFixed) { flags = ConvertVertex_comp::kFixedToFloat; } else if (srcIsFloat) { flags = ConvertVertex_comp::kFloatToFloat; } else { UNREACHABLE(); } return flags; } uint32_t GetImageClearFlags(const angle::Format &format, uint32_t attachmentIndex, bool clearDepth) { constexpr uint32_t kAttachmentFlagStep = ImageClear_frag::kAttachment1 - ImageClear_frag::kAttachment0; static_assert(gl::IMPLEMENTATION_MAX_DRAW_BUFFERS == 8, "ImageClear shader assumes maximum 8 draw buffers"); static_assert( ImageClear_frag::kAttachment0 + 7 * kAttachmentFlagStep == ImageClear_frag::kAttachment7, "ImageClear AttachmentN flag calculation needs correction"); uint32_t flags = ImageClear_frag::kAttachment0 + attachmentIndex * kAttachmentFlagStep; if (format.isSint()) { flags |= ImageClear_frag::kIsSint; } else if (format.isUint()) { flags |= ImageClear_frag::kIsUint; } else { flags |= ImageClear_frag::kIsFloat; } if (clearDepth) { flags |= ImageClear_frag::kClearDepth; } return flags; } uint32_t GetFormatFlags(const angle::Format &format, uint32_t intFlag, uint32_t uintFlag, uint32_t floatFlag) { if (format.isSint()) { return intFlag; } if (format.isUint()) { return uintFlag; } return floatFlag; } uint32_t GetImageCopyFlags(const angle::Format &srcIntendedFormat, const angle::Format &dstIntendedFormat) { uint32_t flags = 0; flags |= GetFormatFlags(srcIntendedFormat, ImageCopy_frag::kSrcIsSint, ImageCopy_frag::kSrcIsUint, ImageCopy_frag::kSrcIsFloat); flags |= GetFormatFlags(dstIntendedFormat, ImageCopy_frag::kDstIsSint, ImageCopy_frag::kDstIsUint, ImageCopy_frag::kDstIsFloat); return flags; } uint32_t GetCopyImageToBufferFlags(const angle::Format &srcFormat) { ASSERT(!srcFormat.isSint() && !srcFormat.isUint()); return CopyImageToBuffer_comp::kSrcIsFloat; } uint32_t GetBlitResolveFlags(bool blitColor, bool blitDepth, bool blitStencil, const angle::Format &intendedFormat) { if (blitColor) { return GetFormatFlags(intendedFormat, BlitResolve_frag::kBlitColorInt, BlitResolve_frag::kBlitColorUint, BlitResolve_frag::kBlitColorFloat); } if (blitDepth) { if (blitStencil) { return BlitResolve_frag::kBlitDepthStencil; } else { return BlitResolve_frag::kBlitDepth; } } else { return BlitResolve_frag::kBlitStencil; } } uint32_t GetConvertIndexIndirectLineLoopFlag(uint32_t indicesBitsWidth) { switch (indicesBitsWidth) { case 8: return ConvertIndexIndirectLineLoop_comp::kIs8Bits; case 16: return ConvertIndexIndirectLineLoop_comp::kIs16Bits; case 32: return ConvertIndexIndirectLineLoop_comp::kIs32Bits; default: UNREACHABLE(); return 0; } } uint32_t GetGenerateMipmapFlags(ContextVk *contextVk, const angle::Format &actualFormat) { uint32_t flags = 0; // Note: If bits-per-component is 8 or 16 and float16 is supported in the shader, use that for // faster math. const bool hasShaderFloat16 = contextVk->getFeatures().supportsShaderFloat16.enabled; if (actualFormat.redBits <= 8) { flags = hasShaderFloat16 ? GenerateMipmap_comp::kIsRGBA8_UseHalf : GenerateMipmap_comp::kIsRGBA8; } else if (actualFormat.redBits <= 16) { flags = hasShaderFloat16 ? GenerateMipmap_comp::kIsRGBA16_UseHalf : GenerateMipmap_comp::kIsRGBA16; } else { flags = GenerateMipmap_comp::kIsRGBA32F; } flags |= UtilsVk::GetGenerateMipmapMaxLevels(contextVk) == UtilsVk::kGenerateMipmapMaxLevels ? GenerateMipmap_comp::kDestSize6 : GenerateMipmap_comp::kDestSize4; return flags; } enum UnresolveColorAttachmentType { kUnresolveTypeUnused = 0, kUnresolveTypeFloat = 1, kUnresolveTypeSint = 2, kUnresolveTypeUint = 3, }; uint32_t GetUnresolveFlags(uint32_t colorAttachmentCount, const gl::DrawBuffersArray &colorSrc, bool unresolveDepth, bool unresolveStencil, gl::DrawBuffersArray *attachmentTypesOut) { uint32_t flags = 0; for (uint32_t attachmentIndex = 0; attachmentIndex < colorAttachmentCount; ++attachmentIndex) { const angle::Format &format = colorSrc[attachmentIndex]->getIntendedFormat(); UnresolveColorAttachmentType type = kUnresolveTypeFloat; if (format.isSint()) { type = kUnresolveTypeSint; } else if (format.isUint()) { type = kUnresolveTypeUint; } (*attachmentTypesOut)[attachmentIndex] = type; // |flags| is comprised of |colorAttachmentCount| values from // |UnresolveColorAttachmentType|, each taking up 2 bits. flags |= type << (2 * attachmentIndex); } // Additionally, two bits are used for depth and stencil unresolve. constexpr uint32_t kDepthUnresolveFlagBit = 2 * gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; constexpr uint32_t kStencilUnresolveFlagBit = kDepthUnresolveFlagBit + 1; if (unresolveDepth) { flags |= 1 << kDepthUnresolveFlagBit; } if (unresolveStencil) { flags |= 1 << kStencilUnresolveFlagBit; } return flags; } uint32_t GetFormatDefaultChannelMask(const angle::Format &intendedImageFormat, const angle::Format &actualImageFormat) { uint32_t mask = 0; // Red can never be introduced due to format emulation (except for luma which is handled // especially) ASSERT(((intendedImageFormat.redBits > 0) == (actualImageFormat.redBits > 0)) || intendedImageFormat.isLUMA()); mask |= intendedImageFormat.greenBits == 0 && actualImageFormat.greenBits > 0 ? 2 : 0; mask |= intendedImageFormat.blueBits == 0 && actualImageFormat.blueBits > 0 ? 4 : 0; mask |= intendedImageFormat.alphaBits == 0 && actualImageFormat.alphaBits > 0 ? 8 : 0; return mask; } // Calculate the transformation offset for blit/resolve. See BlitResolve.frag for details on how // these values are derived. void CalculateBlitOffset(const UtilsVk::BlitResolveParameters ¶ms, float offset[2]) { int srcOffsetFactorX = params.flipX ? -1 : 1; int srcOffsetFactorY = params.flipY ? -1 : 1; offset[0] = params.dstOffset[0] * params.stretch[0] - params.srcOffset[0] * srcOffsetFactorX; offset[1] = params.dstOffset[1] * params.stretch[1] - params.srcOffset[1] * srcOffsetFactorY; } void CalculateResolveOffset(const UtilsVk::BlitResolveParameters ¶ms, int32_t offset[2]) { int srcOffsetFactorX = params.flipX ? -1 : 1; int srcOffsetFactorY = params.flipY ? -1 : 1; // There's no stretching in resolve. offset[0] = params.dstOffset[0] - params.srcOffset[0] * srcOffsetFactorX; offset[1] = params.dstOffset[1] - params.srcOffset[1] * srcOffsetFactorY; } void SetDepthStateForWrite(vk::Renderer *renderer, vk::GraphicsPipelineDesc *desc) { if (!renderer->getFeatures().useDepthTestEnableDynamicState.enabled) { desc->setDepthTestEnabled(VK_TRUE); } if (!renderer->getFeatures().useDepthWriteEnableDynamicState.enabled) { desc->setDepthWriteEnabled(VK_TRUE); } if (!renderer->getFeatures().useDepthCompareOpDynamicState.enabled) { desc->setDepthFunc(VK_COMPARE_OP_ALWAYS); } } void SetDepthStateForUnused(vk::Renderer *renderer, vk::GraphicsPipelineDesc *desc) { if (!renderer->getFeatures().useDepthTestEnableDynamicState.enabled) { desc->setDepthTestEnabled(VK_FALSE); } if (!renderer->getFeatures().useDepthWriteEnableDynamicState.enabled) { desc->setDepthWriteEnabled(VK_FALSE); } } void SetDepthDynamicStateForWrite(vk::Renderer *renderer, vk::RenderPassCommandBuffer *commandBuffer) { if (renderer->getFeatures().useDepthTestEnableDynamicState.enabled) { commandBuffer->setDepthTestEnable(VK_TRUE); } if (renderer->getFeatures().useDepthWriteEnableDynamicState.enabled) { commandBuffer->setDepthWriteEnable(VK_TRUE); } if (renderer->getFeatures().useDepthCompareOpDynamicState.enabled) { commandBuffer->setDepthCompareOp(VK_COMPARE_OP_ALWAYS); } } void SetDepthDynamicStateForUnused(vk::Renderer *renderer, vk::RenderPassCommandBuffer *commandBuffer) { if (renderer->getFeatures().useDepthTestEnableDynamicState.enabled) { commandBuffer->setDepthTestEnable(VK_FALSE); } if (renderer->getFeatures().useDepthWriteEnableDynamicState.enabled) { commandBuffer->setDepthWriteEnable(VK_FALSE); } if (renderer->getFeatures().useDepthCompareOpDynamicState.enabled) { commandBuffer->setDepthCompareOp(VK_COMPARE_OP_ALWAYS); } } // Sets the appropriate settings in the pipeline for either the shader to output stencil, regardless // of whether its done through the reference value or the shader stencil export extension. void SetStencilStateForWrite(vk::Renderer *renderer, vk::GraphicsPipelineDesc *desc) { if (!renderer->getFeatures().useStencilTestEnableDynamicState.enabled) { desc->setStencilTestEnabled(true); } if (!renderer->getFeatures().useStencilOpDynamicState.enabled) { desc->setStencilFrontFuncs(VK_COMPARE_OP_ALWAYS); desc->setStencilBackFuncs(VK_COMPARE_OP_ALWAYS); desc->setStencilFrontOps(VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE); desc->setStencilBackOps(VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE); } } void SetStencilDynamicStateForWrite(vk::Renderer *renderer, vk::RenderPassCommandBuffer *commandBuffer) { if (renderer->getFeatures().useStencilTestEnableDynamicState.enabled) { commandBuffer->setStencilTestEnable(true); } if (renderer->getFeatures().useStencilOpDynamicState.enabled) { commandBuffer->setStencilOp(VK_STENCIL_FACE_FRONT_BIT, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_COMPARE_OP_ALWAYS); commandBuffer->setStencilOp(VK_STENCIL_FACE_BACK_BIT, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_COMPARE_OP_ALWAYS); } } void SetStencilDynamicStateForUnused(vk::Renderer *renderer, vk::RenderPassCommandBuffer *commandBuffer) { if (renderer->getFeatures().useStencilTestEnableDynamicState.enabled) { commandBuffer->setStencilTestEnable(false); } if (renderer->getFeatures().useStencilOpDynamicState.enabled) { commandBuffer->setStencilOp(VK_STENCIL_FACE_FRONT_BIT, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_COMPARE_OP_ALWAYS); commandBuffer->setStencilOp(VK_STENCIL_FACE_BACK_BIT, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_COMPARE_OP_ALWAYS); } commandBuffer->setStencilCompareMask(0x00, 0x00); commandBuffer->setStencilWriteMask(0x00, 0x00); commandBuffer->setStencilReference(0x00, 0x00); } void HandlePrimitiveRestart(ContextVk *contextVk, gl::DrawElementsType glIndexType, GLsizei indexCount, const uint8_t *srcPtr, uint8_t *outPtr) { switch (glIndexType) { case gl::DrawElementsType::UnsignedByte: if (contextVk->getFeatures().supportsIndexTypeUint8.enabled) { CopyLineLoopIndicesWithRestart(indexCount, srcPtr, outPtr); } else { CopyLineLoopIndicesWithRestart(indexCount, srcPtr, outPtr); } break; case gl::DrawElementsType::UnsignedShort: CopyLineLoopIndicesWithRestart(indexCount, srcPtr, outPtr); break; case gl::DrawElementsType::UnsignedInt: CopyLineLoopIndicesWithRestart(indexCount, srcPtr, outPtr); break; default: UNREACHABLE(); } } namespace unresolve { // The unresolve shader looks like the following, based on the number and types of unresolve // attachments. Note that stencil is placed first, to align with the ExportStencil shader, and // simplifying descriptor set creation. // // #version 450 core // #extension GL_ARB_shader_stencil_export : require // // 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 usubpassInput stencilIn; // layout(input_attachment_index = 0, set = 0, binding = 1) uniform subpassInput depthIn; // layout(input_attachment_index = 1, set = 0, binding = 2) uniform subpassInput colorIn0; // layout(input_attachment_index = 2, set = 0, binding = 3) uniform isubpassInput colorIn1; // layout(input_attachment_index = 3, set = 0, binding = 4) uniform usubpassInput colorIn2; // // void main() // { // colorOut0 = subpassLoad(colorIn0); // colorOut1 = subpassLoad(colorIn1); // colorOut2 = subpassLoad(colorIn2); // gl_FragDepth = subpassLoad(depthIn).x; // gl_FragStencilRefARB = int(subpassLoad(stencilIn).x); // } // // This shader compiles to the following SPIR-V: // // OpCapability Shader \ // OpCapability InputAttachment \ // OpCapability StencilExportEXT \ Preamble. Mostly fixed, except // OpExtension "SPV_EXT_shader_stencil_export" \ OpEntryPoint should enumerate // %1 = OpExtInstImport "GLSL.std.450" \ out variables, stencil export // OpMemoryModel Logical GLSL450 / is conditional to stencil // OpEntryPoint Fragment %4 "main" %26 %27 %28 %29 %30 / unresolve, and depth replacing // OpExecutionMode %4 OriginUpperLeft / conditional to depth unresolve. // OpExecutionMode %4 DepthReplacing / // OpSource GLSL 450 / // // OpName %4 "main" \ // OpName %26 "colorOut0" \ // OpName %27 "colorOut1" \ // OpName %28 "colorOut2" \ // OpName %29 "gl_FragDepth" \ Debug information. Not generated here. // OpName %30 "gl_FragStencilRefARB" / // OpName %31 "colorIn0" / // OpName %32 "colorIn1" / // OpName %33 "colorIn2" / // OpName %34 "depthIn" / // OpName %35 "stencilIn" / // // OpDecorate %26 Location 0 \ // OpDecorate %27 Location 1 \ Location decoration of out variables. // OpDecorate %28 Location 2 / // // OpDecorate %29 BuiltIn FragDepth \ Builtin outputs, conditional to depth // OpDecorate %30 BuiltIn FragStencilRefEXT / and stencil unresolve. // // OpDecorate %31 DescriptorSet 0 \ // OpDecorate %31 Binding 2 \ // OpDecorate %31 InputAttachmentIndex 1 \ // OpDecorate %32 DescriptorSet 0 \ // OpDecorate %32 Binding 3 \ // OpDecorate %32 InputAttachmentIndex 2 \ // OpDecorate %33 DescriptorSet 0 \ set, binding and input_attachment // OpDecorate %33 Binding 4 \ decorations of the subpassInput // OpDecorate %33 InputAttachmentIndex 3 / variables. // OpDecorate %34 DescriptorSet 0 / // OpDecorate %34 Binding 1 / // OpDecorate %34 InputAttachmentIndex 0 / // OpDecorate %35 DescriptorSet 0 / // OpDecorate %35 Binding 0 / // OpDecorate %35 InputAttachmentIndex 0 / // // %2 = OpTypeVoid \ Type of main(). Fixed. // %3 = OpTypeFunction %2 / // // %6 = OpTypeFloat 32 \ // %7 = OpTypeVector %6 4 \ // %8 = OpTypePointer Output %7 \ Type declaration for "out vec4" // %9 = OpTypeImage %6 SubpassData 0 0 0 2 Unknown / and "subpassInput". Fixed. // %10 = OpTypePointer UniformConstant %9 / // // %11 = OpTypeInt 32 1 \ // %12 = OpTypeVector %11 4 \ // %13 = OpTypePointer Output %12 \ Type declaration for "out ivec4" // %14 = OpTypeImage %11 SubpassData 0 0 0 2 Unknown / and "isubpassInput". Fixed. // %15 = OpTypePointer UniformConstant %14 / // // %16 = OpTypeInt 32 0 \ // %17 = OpTypeVector %16 4 \ // %18 = OpTypePointer Output %17 \ Type declaration for "out uvec4" // %19 = OpTypeImage %16 SubpassData 0 0 0 2 Unknown / and "usubpassInput". Fixed. // %20 = OpTypePointer UniformConstant %19 / // // %21 = OpTypePointer Output %6 \ Type declaraions for depth and stencil. Fixed. // %22 = OpTypePointer Output %11 / // // %23 = OpConstant %11 0 \ // %24 = OpTypeVector %11 2 \ ivec2(0) for OpImageRead. subpassLoad // %25 = OpConstantComposite %22 %21 %21 / doesn't require coordinates. Fixed. // // %26 = OpVariable %8 Output \ // %27 = OpVariable %13 Output \ // %28 = OpVariable %18 Output \ // %29 = OpVariable %21 Output \ // %30 = OpVariable %22 Output \ Actual "out" and "*subpassInput" // %31 = OpVariable %10 UniformConstant / variable declarations. // %32 = OpVariable %15 UniformConstant / // %33 = OpVariable %20 UniformConstant / // %34 = OpVariable %10 UniformConstant / // %35 = OpVariable %20 UniformConstant / // // %4 = OpFunction %2 None %3 \ Top of main(). Fixed. // %5 = OpLabel / // // %36 = OpLoad %9 %31 \ // %37 = OpImageRead %7 %36 %23 \ colorOut0 = subpassLoad(colorIn0); // OpStore %26 %37 / // // %38 = OpLoad %14 %32 \ // %39 = OpImageRead %12 %38 %23 \ colorOut1 = subpassLoad(colorIn1); // OpStore %27 %39 / // // %40 = OpLoad %19 %33 \ // %41 = OpImageRead %17 %40 %23 \ colorOut2 = subpassLoad(colorIn2); // OpStore %28 %41 / // // %42 = OpLoad %9 %34 \ // %43 = OpImageRead %7 %42 %23 \ gl_FragDepth = subpassLoad(depthIn).x; // %44 = OpCompositeExtract %6 %43 0 / // OpStore %29 %44 / // // %45 = OpLoad %19 %35 \ // %46 = OpImageRead %17 %45 %23 \ // %47 = OpCompositeExtract %16 %46 0 \ gl_FragStencilRefARB = int(subpassLoad(stencilIn).x); // %48 = OpBitcast %11 %47 / // OpStore %30 %48 / // // OpReturn \ Bottom of main(). Fixed. // OpFunctionEnd / // // What makes the generation of this shader manageable is that the majority of it is constant // between the different variations of the shader. The rest are repeating patterns with different // ids or indices. enum { // main() ids kIdExtInstImport = 1, kIdVoid, kIdMainType, kIdMain, kIdMainLabel, // Types for "out vec4" and "subpassInput" kIdFloatType, kIdFloat4Type, kIdFloat4OutType, kIdFloatSubpassImageType, kIdFloatSubpassInputType, // Types for "out ivec4" and "isubpassInput" kIdSIntType, kIdSInt4Type, kIdSInt4OutType, kIdSIntSubpassImageType, kIdSIntSubpassInputType, // Types for "out uvec4" and "usubpassInput" kIdUIntType, kIdUInt4Type, kIdUInt4OutType, kIdUIntSubpassImageType, kIdUIntSubpassInputType, // Types for gl_FragDepth && gl_FragStencilRefARB kIdFloatOutType, kIdSIntOutType, // ivec2(0) constant kIdSIntZero, kIdSInt2Type, kIdSInt2Zero, // Output variable ids kIdColor0Out, kIdDepthOut = kIdColor0Out + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS, kIdStencilOut, // Input variable ids kIdColor0In, kIdDepthIn = kIdColor0In + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS, kIdStencilIn, // Ids for temp variables kIdColor0Load, // 2 temp ids per color unresolve kIdDepthLoad = kIdColor0Load + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS * 2, // 3 temp ids for depth unresolve kIdStencilLoad = kIdDepthLoad + 3, // Total number of ids used // 4 temp ids for stencil unresolve kIdCount = kIdStencilLoad + 4, }; void InsertPreamble(uint32_t colorAttachmentCount, bool unresolveDepth, bool unresolveStencil, angle::spirv::Blob *blobOut) { spirv::WriteCapability(blobOut, spv::CapabilityShader); spirv::WriteCapability(blobOut, spv::CapabilityInputAttachment); if (unresolveStencil) { spirv::WriteCapability(blobOut, spv::CapabilityStencilExportEXT); spirv::WriteExtension(blobOut, "SPV_EXT_shader_stencil_export"); } // OpExtInstImport is actually not needed by this shader. We don't use any instructions from // GLSL.std.450. spirv::WriteMemoryModel(blobOut, spv::AddressingModelLogical, spv::MemoryModelGLSL450); // Create the list of entry point ids, including only the out variables. spirv::IdRefList entryPointIds; for (uint32_t colorIndex = 0; colorIndex < colorAttachmentCount; ++colorIndex) { entryPointIds.push_back(spirv::IdRef(kIdColor0Out + colorIndex)); } if (unresolveDepth) { entryPointIds.push_back(spirv::IdRef(kIdDepthOut)); } if (unresolveStencil) { entryPointIds.push_back(spirv::IdRef(kIdStencilOut)); } spirv::WriteEntryPoint(blobOut, spv::ExecutionModelFragment, spirv::IdRef(kIdMain), "main", entryPointIds); spirv::WriteExecutionMode(blobOut, spirv::IdRef(kIdMain), spv::ExecutionModeOriginUpperLeft, {}); if (unresolveDepth) { spirv::WriteExecutionMode(blobOut, spirv::IdRef(kIdMain), spv::ExecutionModeDepthReplacing, {}); } spirv::WriteSource(blobOut, spv::SourceLanguageGLSL, spirv::LiteralInteger(450), nullptr, nullptr); } void InsertInputDecorations(spirv::IdRef id, uint32_t attachmentIndex, uint32_t binding, angle::spirv::Blob *blobOut) { spirv::WriteDecorate(blobOut, id, spv::DecorationDescriptorSet, {spirv::LiteralInteger(ToUnderlying(DescriptorSetIndex::Internal))}); spirv::WriteDecorate(blobOut, id, spv::DecorationBinding, {spirv::LiteralInteger(binding)}); spirv::WriteDecorate(blobOut, id, spv::DecorationInputAttachmentIndex, {spirv::LiteralInteger(attachmentIndex)}); } void InsertColorDecorations(uint32_t colorIndex, uint32_t colorInputIndexStart, uint32_t colorBindingIndexStart, angle::spirv::Blob *blobOut) { // Decorate the output color attachment with Location spirv::WriteDecorate(blobOut, spirv::IdRef(kIdColor0Out + colorIndex), spv::DecorationLocation, {spirv::LiteralInteger(colorIndex)}); // Decorate the subpasss input color attachment with Set/Binding/InputAttachmentIndex. InsertInputDecorations(spirv::IdRef(kIdColor0In + colorIndex), colorIndex + colorInputIndexStart, colorIndex + colorBindingIndexStart, blobOut); } void InsertDepthStencilDecorations(bool unresolveDepth, bool unresolveStencil, bool supportsShaderStencilExport, uint32_t *nextInputIndex, uint32_t *nextBindingIndex, angle::spirv::Blob *blobOut) { if (unresolveStencil && supportsShaderStencilExport) { // Make sure unresolve desc set is compatible with the ExportStencil shader. ASSERT(*nextInputIndex == kExportStencilInputIndex); ASSERT(*nextBindingIndex == kExportStencilBinding); // Decorate the output stencil attachment with Location spirv::WriteDecorate(blobOut, spirv::IdRef(kIdStencilOut), spv::DecorationBuiltIn, {spirv::LiteralInteger(spv::BuiltInFragStencilRefEXT)}); // Decorate the subpasss input stencil attachment with Set/Binding/InputAttachmentIndex. InsertInputDecorations(spirv::IdRef(kIdStencilIn), *nextInputIndex, *nextBindingIndex, blobOut); // Advance the binding. Note that the depth/stencil attachment has the same input // attachment index (it's the same attachment in the subpass), but different bindings (one // aspect per image view). ++*nextBindingIndex; } if (unresolveDepth) { // Decorate the output depth attachment with Location spirv::WriteDecorate(blobOut, spirv::IdRef(kIdDepthOut), spv::DecorationBuiltIn, {spirv::LiteralInteger(spv::BuiltInFragDepth)}); // Decorate the subpasss input depth attachment with Set/Binding/InputAttachmentIndex. InsertInputDecorations(spirv::IdRef(kIdDepthIn), *nextInputIndex, *nextBindingIndex, blobOut); ++*nextBindingIndex; } if (unresolveDepth || unresolveStencil) { // Even if stencil is taking a special path and is not being unresolved with color and // depth, the input index is still consumed so the ExportStencil shader can operate on it. ++*nextInputIndex; } } void InsertDerivativeTypes(spirv::IdRef baseId, spirv::IdRef vec4Id, spirv::IdRef vec4OutId, spirv::IdRef imageTypeId, spirv::IdRef inputTypeId, angle::spirv::Blob *blobOut) { spirv::WriteTypeVector(blobOut, vec4Id, baseId, spirv::LiteralInteger(4)); spirv::WriteTypePointer(blobOut, vec4OutId, spv::StorageClassOutput, vec4Id); spirv::WriteTypeImage(blobOut, imageTypeId, baseId, spv::DimSubpassData, // Unused with subpass inputs spirv::LiteralInteger(0), // Not arrayed spirv::LiteralInteger(0), // Not multisampled spirv::LiteralInteger(0), // Used without a sampler spirv::LiteralInteger(2), spv::ImageFormatUnknown, nullptr); spirv::WriteTypePointer(blobOut, inputTypeId, spv::StorageClassUniformConstant, imageTypeId); } void InsertCommonTypes(angle::spirv::Blob *blobOut) { // Types to support main(). spirv::WriteTypeVoid(blobOut, spirv::IdRef(kIdVoid)); spirv::WriteTypeFunction(blobOut, spirv::IdRef(kIdMainType), spirv::IdRef(kIdVoid), {}); // Float types spirv::WriteTypeFloat(blobOut, spirv::IdRef(kIdFloatType), spirv::LiteralInteger(32), nullptr); InsertDerivativeTypes(spirv::IdRef(kIdFloatType), spirv::IdRef(kIdFloat4Type), spirv::IdRef(kIdFloat4OutType), spirv::IdRef(kIdFloatSubpassImageType), spirv::IdRef(kIdFloatSubpassInputType), blobOut); // Int types spirv::WriteTypeInt(blobOut, spirv::IdRef(kIdSIntType), spirv::LiteralInteger(32), spirv::LiteralInteger(1)); InsertDerivativeTypes(spirv::IdRef(kIdSIntType), spirv::IdRef(kIdSInt4Type), spirv::IdRef(kIdSInt4OutType), spirv::IdRef(kIdSIntSubpassImageType), spirv::IdRef(kIdSIntSubpassInputType), blobOut); // Unsigned int types spirv::WriteTypeInt(blobOut, spirv::IdRef(kIdUIntType), spirv::LiteralInteger(32), spirv::LiteralInteger(0)); InsertDerivativeTypes(spirv::IdRef(kIdUIntType), spirv::IdRef(kIdUInt4Type), spirv::IdRef(kIdUInt4OutType), spirv::IdRef(kIdUIntSubpassImageType), spirv::IdRef(kIdUIntSubpassInputType), blobOut); // Types to support depth/stencil spirv::WriteTypePointer(blobOut, spirv::IdRef(kIdFloatOutType), spv::StorageClassOutput, spirv::IdRef(kIdFloatType)); spirv::WriteTypePointer(blobOut, spirv::IdRef(kIdSIntOutType), spv::StorageClassOutput, spirv::IdRef(kIdSIntType)); // Constants used to load from subpass inputs spirv::WriteConstant(blobOut, spirv::IdRef(kIdSIntType), spirv::IdRef(kIdSIntZero), spirv::LiteralInteger(0)); spirv::WriteTypeVector(blobOut, spirv::IdRef(kIdSInt2Type), spirv::IdRef(kIdSIntType), spirv::LiteralInteger(2)); spirv::WriteConstantComposite(blobOut, spirv::IdRef(kIdSInt2Type), spirv::IdRef(kIdSInt2Zero), {spirv::IdRef(kIdSIntZero), spirv::IdRef(kIdSIntZero)}); } void InsertVariableDecl(spirv::IdRef outType, spirv::IdRef outId, spirv::IdRef inType, spirv::IdRef inId, angle::spirv::Blob *blobOut) { // Declare both the output and subpass input variables. spirv::WriteVariable(blobOut, outType, outId, spv::StorageClassOutput, nullptr); spirv::WriteVariable(blobOut, inType, inId, spv::StorageClassUniformConstant, nullptr); } void InsertColorVariableDecl(uint32_t colorIndex, UnresolveColorAttachmentType type, angle::spirv::Blob *blobOut) { // Find the correct types for color variable declarations. spirv::IdRef outType(kIdFloat4OutType); spirv::IdRef outId(kIdColor0Out + colorIndex); spirv::IdRef inType(kIdFloatSubpassInputType); spirv::IdRef inId(kIdColor0In + colorIndex); switch (type) { case kUnresolveTypeSint: outType = spirv::IdRef(kIdSInt4OutType); inType = spirv::IdRef(kIdSIntSubpassInputType); break; case kUnresolveTypeUint: outType = spirv::IdRef(kIdUInt4OutType); inType = spirv::IdRef(kIdUIntSubpassInputType); break; default: break; } InsertVariableDecl(outType, outId, inType, inId, blobOut); } void InsertDepthStencilVariableDecl(bool unresolveDepth, bool unresolveStencil, angle::spirv::Blob *blobOut) { if (unresolveDepth) { InsertVariableDecl(spirv::IdRef(kIdFloatOutType), spirv::IdRef(kIdDepthOut), spirv::IdRef(kIdFloatSubpassInputType), spirv::IdRef(kIdDepthIn), blobOut); } if (unresolveStencil) { InsertVariableDecl(spirv::IdRef(kIdSIntOutType), spirv::IdRef(kIdStencilOut), spirv::IdRef(kIdUIntSubpassInputType), spirv::IdRef(kIdStencilIn), blobOut); } } void InsertTopOfMain(angle::spirv::Blob *blobOut) { spirv::WriteFunction(blobOut, spirv::IdRef(kIdVoid), spirv::IdRef(kIdMain), spv::FunctionControlMaskNone, spirv::IdRef(kIdMainType)); spirv::WriteLabel(blobOut, spirv::IdRef(kIdMainLabel)); } void InsertColorUnresolveLoadStore(uint32_t colorIndex, UnresolveColorAttachmentType type, angle::spirv::Blob *blobOut) { spirv::IdRef loadResult(kIdColor0Load + colorIndex * 2); spirv::IdRef imageReadResult(loadResult + 1); // Find the correct types for load/store. spirv::IdRef loadType(kIdFloatSubpassImageType); spirv::IdRef readType(kIdFloat4Type); spirv::IdRef inId(kIdColor0In + colorIndex); spirv::IdRef outId(kIdColor0Out + colorIndex); switch (type) { case kUnresolveTypeSint: loadType = spirv::IdRef(kIdSIntSubpassImageType); readType = spirv::IdRef(kIdSInt4Type); break; case kUnresolveTypeUint: loadType = spirv::IdRef(kIdUIntSubpassImageType); readType = spirv::IdRef(kIdUInt4Type); break; default: break; } // Load the subpass input image, read from it, and store in output. spirv::WriteLoad(blobOut, loadType, loadResult, inId, nullptr); spirv::WriteImageRead(blobOut, readType, imageReadResult, loadResult, spirv::IdRef(kIdSInt2Zero), nullptr, {}); spirv::WriteStore(blobOut, outId, imageReadResult, nullptr); } void InsertDepthStencilUnresolveLoadStore(bool unresolveDepth, bool unresolveStencil, angle::spirv::Blob *blobOut) { if (unresolveDepth) { spirv::IdRef loadResult(kIdDepthLoad); spirv::IdRef imageReadResult(loadResult + 1); spirv::IdRef extractResult(imageReadResult + 1); spirv::IdRef loadType(kIdFloatSubpassImageType); spirv::IdRef readType(kIdFloat4Type); spirv::IdRef inId(kIdDepthIn); spirv::IdRef outId(kIdDepthOut); // Load the subpass input image, read from it, select .x, and store in output. spirv::WriteLoad(blobOut, loadType, loadResult, inId, nullptr); spirv::WriteImageRead(blobOut, readType, imageReadResult, loadResult, spirv::IdRef(kIdSInt2Zero), nullptr, {}); spirv::WriteCompositeExtract(blobOut, spirv::IdRef(kIdFloatType), extractResult, imageReadResult, {spirv::LiteralInteger(0)}); spirv::WriteStore(blobOut, outId, extractResult, nullptr); } if (unresolveStencil) { spirv::IdRef loadResult(kIdStencilLoad); spirv::IdRef imageReadResult(loadResult + 1); spirv::IdRef extractResult(imageReadResult + 1); spirv::IdRef bitcastResult(extractResult + 1); spirv::IdRef loadType(kIdUIntSubpassImageType); spirv::IdRef readType(kIdUInt4Type); spirv::IdRef inId(kIdStencilIn); spirv::IdRef outId(kIdStencilOut); // Load the subpass input image, read from it, select .x, and store in output. There's a // bitcast involved since the stencil subpass input has unsigned type, while // gl_FragStencilRefARB is signed! spirv::WriteLoad(blobOut, loadType, loadResult, inId, nullptr); spirv::WriteImageRead(blobOut, readType, imageReadResult, loadResult, spirv::IdRef(kIdSInt2Zero), nullptr, {}); spirv::WriteCompositeExtract(blobOut, spirv::IdRef(kIdUIntType), extractResult, imageReadResult, {spirv::LiteralInteger(0)}); spirv::WriteBitcast(blobOut, spirv::IdRef(kIdSIntType), bitcastResult, extractResult); spirv::WriteStore(blobOut, outId, bitcastResult, nullptr); } } void InsertBottomOfMain(angle::spirv::Blob *blobOut) { spirv::WriteReturn(blobOut); spirv::WriteFunctionEnd(blobOut); } angle::spirv::Blob MakeFragShader( uint32_t colorAttachmentCount, gl::DrawBuffersArray &colorAttachmentTypes, bool unresolveDepth, bool unresolveStencil, bool supportsShaderStencilExport) { const bool unresolveStencilWithShaderExport = unresolveStencil && supportsShaderStencilExport; angle::spirv::Blob code; // Reserve a sensible amount of memory. A single-attachment shader is 169 words. code.reserve(169); // Header spirv::WriteSpirvHeader(&code, spirv::kVersion_1_0, kIdCount); // The preamble InsertPreamble(colorAttachmentCount, unresolveDepth, unresolveStencilWithShaderExport, &code); // Depth stencil decorations uint32_t colorInputIndexStart = 0; uint32_t colorBindingIndexStart = 0; InsertDepthStencilDecorations(unresolveDepth, unresolveStencil, supportsShaderStencilExport, &colorInputIndexStart, &colorBindingIndexStart, &code); // Color attachment decorations for (uint32_t colorIndex = 0; colorIndex < colorAttachmentCount; ++colorIndex) { InsertColorDecorations(colorIndex, colorInputIndexStart, colorBindingIndexStart, &code); } // Common types InsertCommonTypes(&code); // Attachment declarations for (uint32_t colorIndex = 0; colorIndex < colorAttachmentCount; ++colorIndex) { InsertColorVariableDecl(colorIndex, colorAttachmentTypes[colorIndex], &code); } InsertDepthStencilVariableDecl(unresolveDepth, unresolveStencilWithShaderExport, &code); // Top of main InsertTopOfMain(&code); // Load and store for each attachment for (uint32_t colorIndex = 0; colorIndex < colorAttachmentCount; ++colorIndex) { InsertColorUnresolveLoadStore(colorIndex, colorAttachmentTypes[colorIndex], &code); } InsertDepthStencilUnresolveLoadStore(unresolveDepth, unresolveStencilWithShaderExport, &code); // Bottom of main InsertBottomOfMain(&code); return code; } } // namespace unresolve angle::Result GetUnresolveFrag( vk::Context *context, uint32_t colorAttachmentCount, gl::DrawBuffersArray &colorAttachmentTypes, bool unresolveDepth, bool unresolveStencil, vk::ShaderModulePtr *shader) { if (*shader) { ASSERT((*shader)->valid()); return angle::Result::Continue; } angle::spirv::Blob shaderCode = unresolve::MakeFragShader( colorAttachmentCount, colorAttachmentTypes, unresolveDepth, unresolveStencil, context->getFeatures().supportsShaderStencilExport.enabled); ASSERT(spirv::Validate(shaderCode)); // Create shader lazily. Access will need to be locked for multi-threading. return vk::InitShaderModule(context, shader, shaderCode.data(), shaderCode.size() * 4); } gl::DrawBufferMask MakeColorBufferMask(uint32_t colorAttachmentIndexGL) { gl::DrawBufferMask mask; mask.set(colorAttachmentIndexGL); return mask; } void UpdateColorAccess(ContextVk *contextVk, gl::DrawBufferMask colorAttachmentMask, gl::DrawBufferMask colorEnabledMask) { vk::RenderPassCommandBufferHelper *renderPassCommands = &contextVk->getStartedRenderPassCommands(); // Explicitly mark a color write because we are modifying the color buffer. vk::PackedAttachmentIndex colorIndexVk(0); for (size_t colorIndexGL : colorAttachmentMask) { if (colorEnabledMask.test(colorIndexGL)) { renderPassCommands->onColorAccess(colorIndexVk, vk::ResourceAccess::ReadWrite); } ++colorIndexVk; } } void UpdateDepthStencilAccess(ContextVk *contextVk, bool depthWrite, bool stencilWrite) { vk::RenderPassCommandBufferHelper *renderPassCommands = &contextVk->getStartedRenderPassCommands(); if (depthWrite) { // Explicitly mark a depth write because we are modifying the depth buffer. renderPassCommands->onDepthAccess(vk::ResourceAccess::ReadWrite); // Because we may have changed the depth access mode, update read only depth mode. renderPassCommands->updateDepthReadOnlyMode(contextVk->getDepthStencilAttachmentFlags()); } if (stencilWrite) { // Explicitly mark a stencil write because we are modifying the stencil buffer. renderPassCommands->onStencilAccess(vk::ResourceAccess::ReadWrite); // Because we may have changed the stencil access mode, update read only stencil mode. renderPassCommands->updateStencilReadOnlyMode(contextVk->getDepthStencilAttachmentFlags()); } } void ResetDynamicState(ContextVk *contextVk, vk::RenderPassCommandBuffer *commandBuffer) { // Reset dynamic state that might affect UtilsVk. Mark all dynamic state dirty for simplicity. // Ideally, only dynamic state that is changed by UtilsVk will be marked dirty but, until such // time as extensive transition tests are written, this approach is less bug-prone. // Notes: the following dynamic state doesn't apply to UtilsVk functions: // // - line width: UtilsVk doesn't use line primitives // - depth bias: UtilsVk doesn't enable depth bias // - blend constants: UtilsVk doesn't enable blending // - logic op: UtilsVk doesn't enable logic op // // The following dynamic state is always set by UtilsVk when effective: // // - depth write mask: UtilsVk sets this when enabling depth test // - depth compare op: UtilsVk sets this when enabling depth test // - stencil compare mask: UtilsVk sets this when enabling stencil test // - stencil write mask: UtilsVk sets this when enabling stencil test // - stencil reference: UtilsVk sets this when enabling stencil test // - stencil func: UtilsVk sets this when enabling stencil test // - stencil ops: UtilsVk sets this when enabling stencil test vk::Renderer *renderer = contextVk->getRenderer(); // Reset all other dynamic state, since it can affect UtilsVk functions: if (renderer->getFeatures().useCullModeDynamicState.enabled) { commandBuffer->setCullMode(VK_CULL_MODE_NONE); } if (renderer->getFeatures().useFrontFaceDynamicState.enabled) { commandBuffer->setFrontFace(VK_FRONT_FACE_COUNTER_CLOCKWISE); } if (renderer->getFeatures().useDepthTestEnableDynamicState.enabled) { commandBuffer->setDepthTestEnable(VK_FALSE); } if (renderer->getFeatures().useStencilTestEnableDynamicState.enabled) { commandBuffer->setStencilTestEnable(VK_FALSE); } if (renderer->getFeatures().useRasterizerDiscardEnableDynamicState.enabled) { commandBuffer->setRasterizerDiscardEnable(VK_FALSE); } if (renderer->getFeatures().useDepthBiasEnableDynamicState.enabled) { commandBuffer->setDepthBiasEnable(VK_FALSE); } if (renderer->getFeatures().usePrimitiveRestartEnableDynamicState.enabled) { commandBuffer->setPrimitiveRestartEnable(VK_FALSE); } if (contextVk->getFeatures().supportsFragmentShadingRate.enabled) { VkExtent2D fragmentSize = {1, 1}; VkFragmentShadingRateCombinerOpKHR shadingRateCombinerOp[2] = { VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR, VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR}; commandBuffer->setFragmentShadingRate(&fragmentSize, shadingRateCombinerOp); } if (renderer->getFeatures().supportsLogicOpDynamicState.enabled) { commandBuffer->setLogicOp(VK_LOGIC_OP_COPY); } if (contextVk->getFeatures().supportsVertexInputDynamicState.enabled) { commandBuffer->setVertexInput(0, nullptr, 0, nullptr); } // Let ContextVk know that it should refresh all dynamic state. contextVk->invalidateAllDynamicState(); } } // namespace UtilsVk::ConvertVertexShaderParams::ConvertVertexShaderParams() = default; UtilsVk::ImageCopyShaderParams::ImageCopyShaderParams() = default; uint32_t UtilsVk::GetGenerateMipmapMaxLevels(ContextVk *contextVk) { vk::Renderer *renderer = contextVk->getRenderer(); uint32_t maxPerStageDescriptorStorageImages = renderer->getPhysicalDeviceProperties().limits.maxPerStageDescriptorStorageImages; // Vulkan requires that there be support for at least 4 storage images per stage. constexpr uint32_t kMinimumStorageImagesLimit = 4; ASSERT(maxPerStageDescriptorStorageImages >= kMinimumStorageImagesLimit); // If fewer than max-levels are supported, use 4 levels (which is the minimum required number // of storage image bindings). return maxPerStageDescriptorStorageImages < kGenerateMipmapMaxLevels ? kMinimumStorageImagesLimit : kGenerateMipmapMaxLevels; } UtilsVk::UtilsVk() = default; UtilsVk::~UtilsVk() = default; void UtilsVk::destroy(ContextVk *contextVk) { vk::Renderer *renderer = contextVk->getRenderer(); VkDevice device = renderer->getDevice(); for (Function f : angle::AllEnums()) { for (auto &descriptorSetLayout : mDescriptorSetLayouts[f]) { descriptorSetLayout.reset(); } mPipelineLayouts[f].reset(); mDescriptorPools[f].destroy(device); } for (auto &item : mImageCopyWithSamplerPipelineLayouts) { const vk::SamplerDesc &samplerDesc = item.first; for (auto &descriptorSetLayout : mImageCopyWithSamplerDescriptorSetLayouts[samplerDesc]) { descriptorSetLayout.reset(); } mImageCopyWithSamplerPipelineLayouts[samplerDesc].reset(); mImageCopyWithSamplerDescriptorPools[samplerDesc].destroy(device); } for (ComputeShaderProgramAndPipelines &programAndPipelines : mConvertIndex) { programAndPipelines.program.destroy(renderer); for (vk::PipelineHelper &pipeline : programAndPipelines.pipelines) { pipeline.destroy(device); } } for (ComputeShaderProgramAndPipelines &programAndPipelines : mConvertIndirectLineLoop) { programAndPipelines.program.destroy(renderer); for (vk::PipelineHelper &pipeline : programAndPipelines.pipelines) { pipeline.destroy(device); } } for (ComputeShaderProgramAndPipelines &programAndPipelines : mConvertIndexIndirectLineLoop) { programAndPipelines.program.destroy(renderer); for (vk::PipelineHelper &pipeline : programAndPipelines.pipelines) { pipeline.destroy(device); } } for (ComputeShaderProgramAndPipelines &programAndPipelines : mConvertVertex) { programAndPipelines.program.destroy(renderer); for (vk::PipelineHelper &pipeline : programAndPipelines.pipelines) { pipeline.destroy(device); } } mImageClearVSOnly.program.destroy(renderer); mImageClearVSOnly.pipelines.destroy(contextVk); for (GraphicsShaderProgramAndPipelines &programAndPipelines : mImageClear) { programAndPipelines.program.destroy(renderer); programAndPipelines.pipelines.destroy(contextVk); } for (GraphicsShaderProgramAndPipelines &programAndPipelines : mImageCopy) { programAndPipelines.program.destroy(renderer); programAndPipelines.pipelines.destroy(contextVk); } mImageCopyFloat.program.destroy(renderer); mImageCopyFloat.pipelines.destroy(contextVk); for (auto &iter : mImageCopyWithSampler) { GraphicsShaderProgramAndPipelines &programAndPipelines = iter.second; programAndPipelines.program.destroy(renderer); programAndPipelines.pipelines.destroy(contextVk); } for (ComputeShaderProgramAndPipelines &programAndPipelines : mCopyImageToBuffer) { programAndPipelines.program.destroy(renderer); for (vk::PipelineHelper &pipeline : programAndPipelines.pipelines) { pipeline.destroy(device); } } for (GraphicsShaderProgramAndPipelines &programAndPipelines : mBlitResolve) { programAndPipelines.program.destroy(renderer); programAndPipelines.pipelines.destroy(contextVk); } for (GraphicsShaderProgramAndPipelines &programAndPipelines : mBlit3DSrc) { programAndPipelines.program.destroy(renderer); programAndPipelines.pipelines.destroy(contextVk); } for (ComputeShaderProgramAndPipelines &programAndPipelines : mBlitResolveStencilNoExport) { programAndPipelines.program.destroy(renderer); for (vk::PipelineHelper &pipeline : programAndPipelines.pipelines) { pipeline.destroy(device); } } mExportStencil.program.destroy(renderer); mExportStencil.pipelines.destroy(contextVk); mOverlayDraw.program.destroy(renderer); mOverlayDraw.pipelines.destroy(contextVk); for (ComputeShaderProgramAndPipelines &programAndPipelines : mGenerateMipmap) { programAndPipelines.program.destroy(renderer); for (vk::PipelineHelper &pipeline : programAndPipelines.pipelines) { pipeline.destroy(device); } } for (ComputeShaderProgramAndPipelines &programAndPipelines : mEtcToBc) { programAndPipelines.program.destroy(renderer); for (vk::PipelineHelper &pipeline : programAndPipelines.pipelines) { pipeline.destroy(device); } } for (auto &programIter : mUnresolve) { GraphicsShaderProgramAndPipelines &programAndPipelines = programIter.second; programAndPipelines.program.destroy(renderer); programAndPipelines.pipelines.destroy(contextVk); } mUnresolve.clear(); mUnresolveFragShaders.clear(); mPointSampler.destroy(device); mLinearSampler.destroy(device); mGenerateFragmentShadingRateAttachment.program.destroy(renderer); for (vk::PipelineHelper &pipeline : mGenerateFragmentShadingRateAttachment.pipelines) { pipeline.destroy(device); } } angle::Result UtilsVk::ensureResourcesInitialized(ContextVk *contextVk, Function function, VkDescriptorPoolSize *setSizes, size_t setSizesCount, size_t pushConstantsSize) { vk::DescriptorSetLayoutDesc descriptorSetDesc; bool isCompute = function >= Function::ComputeStartIndex; VkShaderStageFlags descStages = isCompute ? VK_SHADER_STAGE_COMPUTE_BIT : VK_SHADER_STAGE_FRAGMENT_BIT; if (function == Function::OverlayDraw) { descStages |= VK_SHADER_STAGE_VERTEX_BIT; } uint32_t currentBinding = 0; for (size_t i = 0; i < setSizesCount; ++i) { descriptorSetDesc.addBinding(currentBinding, setSizes[i].type, setSizes[i].descriptorCount, descStages, nullptr); ++currentBinding; } ANGLE_TRY(contextVk->getDescriptorSetLayoutCache().getDescriptorSetLayout( contextVk, descriptorSetDesc, &mDescriptorSetLayouts[function][DescriptorSetIndex::Internal])); vk::DescriptorSetLayoutBindingVector bindingVector; descriptorSetDesc.unpackBindings(&bindingVector); std::vector descriptorPoolSizes; for (const VkDescriptorSetLayoutBinding &binding : bindingVector) { if (binding.descriptorCount > 0) { VkDescriptorPoolSize poolSize = {}; poolSize.type = binding.descriptorType; poolSize.descriptorCount = binding.descriptorCount; descriptorPoolSizes.emplace_back(poolSize); } } if (!descriptorPoolSizes.empty()) { ANGLE_TRY(mDescriptorPools[function].init( contextVk, descriptorPoolSizes.data(), descriptorPoolSizes.size(), *mDescriptorSetLayouts[function][DescriptorSetIndex::Internal])); } // Corresponding pipeline layouts: vk::PipelineLayoutDesc pipelineLayoutDesc; pipelineLayoutDesc.updateDescriptorSetLayout(DescriptorSetIndex::Internal, descriptorSetDesc); if (pushConstantsSize) { pipelineLayoutDesc.updatePushConstantRange(descStages, 0, static_cast(pushConstantsSize)); } ANGLE_TRY(contextVk->getPipelineLayoutCache().getPipelineLayout(contextVk, pipelineLayoutDesc, mDescriptorSetLayouts[function], &mPipelineLayouts[function])); return angle::Result::Continue; } angle::Result UtilsVk::ensureConvertIndexResourcesInitialized(ContextVk *contextVk) { if (mPipelineLayouts[Function::ConvertIndexBuffer]) { return angle::Result::Continue; } VkDescriptorPoolSize setSizes[2] = { {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, }; return ensureResourcesInitialized(contextVk, Function::ConvertIndexBuffer, setSizes, ArraySize(setSizes), sizeof(ConvertIndexShaderParams)); } angle::Result UtilsVk::ensureConvertIndexIndirectResourcesInitialized(ContextVk *contextVk) { if (mPipelineLayouts[Function::ConvertIndexIndirectBuffer]) { return angle::Result::Continue; } VkDescriptorPoolSize setSizes[4] = { {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, // dst index buffer {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, // source index buffer {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, // src indirect buffer {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, // dst indirect buffer }; return ensureResourcesInitialized(contextVk, Function::ConvertIndexIndirectBuffer, setSizes, ArraySize(setSizes), sizeof(ConvertIndexIndirectShaderParams)); } angle::Result UtilsVk::ensureConvertIndexIndirectLineLoopResourcesInitialized(ContextVk *contextVk) { if (mPipelineLayouts[Function::ConvertIndexIndirectLineLoopBuffer]) { return angle::Result::Continue; } VkDescriptorPoolSize setSizes[4] = { {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, // cmd buffer {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, // dst cmd buffer {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, // source index buffer {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, // dst index buffer }; return ensureResourcesInitialized(contextVk, Function::ConvertIndexIndirectLineLoopBuffer, setSizes, ArraySize(setSizes), sizeof(ConvertIndexIndirectLineLoopShaderParams)); } angle::Result UtilsVk::ensureConvertIndirectLineLoopResourcesInitialized(ContextVk *contextVk) { if (mPipelineLayouts[Function::ConvertIndirectLineLoopBuffer]) { return angle::Result::Continue; } VkDescriptorPoolSize setSizes[3] = { {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, // cmd buffer {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, // dst cmd buffer {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, // dst index buffer }; return ensureResourcesInitialized(contextVk, Function::ConvertIndirectLineLoopBuffer, setSizes, ArraySize(setSizes), sizeof(ConvertIndirectLineLoopShaderParams)); } angle::Result UtilsVk::ensureConvertVertexResourcesInitialized(ContextVk *contextVk) { if (mPipelineLayouts[Function::ConvertVertexBuffer]) { return angle::Result::Continue; } VkDescriptorPoolSize setSizes[2] = { {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, }; return ensureResourcesInitialized(contextVk, Function::ConvertVertexBuffer, setSizes, ArraySize(setSizes), sizeof(ConvertVertexShaderParams)); } angle::Result UtilsVk::ensureImageClearResourcesInitialized(ContextVk *contextVk) { if (mPipelineLayouts[Function::ImageClear]) { return angle::Result::Continue; } // The shader does not use any descriptor sets. return ensureResourcesInitialized(contextVk, Function::ImageClear, nullptr, 0, sizeof(ImageClearShaderParams)); } angle::Result UtilsVk::ensureImageCopyResourcesInitialized(ContextVk *contextVk) { if (mPipelineLayouts[Function::ImageCopy]) { return angle::Result::Continue; } VkDescriptorPoolSize setSizes[1] = { {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1}, }; return ensureResourcesInitialized(contextVk, Function::ImageCopy, setSizes, ArraySize(setSizes), sizeof(ImageCopyShaderParams)); } angle::Result UtilsVk::ensureImageCopyResourcesInitializedWithSampler( ContextVk *contextVk, const vk::SamplerDesc &samplerDesc) { if (mImageCopyWithSamplerPipelineLayouts[samplerDesc]) { return angle::Result::Continue; } vk::SharedSamplerPtr sampler; ANGLE_TRY( contextVk->getRenderer()->getSamplerCache().getSampler(contextVk, samplerDesc, &sampler)); vk::DescriptorSetLayoutDesc descriptorSetDesc; descriptorSetDesc.addBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, &sampler->get()); ANGLE_TRY(contextVk->getDescriptorSetLayoutCache().getDescriptorSetLayout( contextVk, descriptorSetDesc, &mImageCopyWithSamplerDescriptorSetLayouts[samplerDesc][DescriptorSetIndex::Internal])); VkDescriptorPoolSize setSizes[1] = { // A single YCbCr sampler may consume up to 3 descriptors. {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3}, }; ANGLE_TRY(mImageCopyWithSamplerDescriptorPools[samplerDesc].init( contextVk, setSizes, 1, *mImageCopyWithSamplerDescriptorSetLayouts[samplerDesc][DescriptorSetIndex::Internal])); vk::PipelineLayoutDesc pipelineLayoutDesc; pipelineLayoutDesc.updateDescriptorSetLayout(DescriptorSetIndex::Internal, descriptorSetDesc); pipelineLayoutDesc.updatePushConstantRange(VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(ImageCopyShaderParams)); ANGLE_TRY(contextVk->getPipelineLayoutCache().getPipelineLayout( contextVk, pipelineLayoutDesc, mImageCopyWithSamplerDescriptorSetLayouts[samplerDesc], &mImageCopyWithSamplerPipelineLayouts[samplerDesc])); return angle::Result::Continue; } angle::Result UtilsVk::ensureCopyImageToBufferResourcesInitialized(ContextVk *contextVk) { if (mPipelineLayouts[Function::CopyImageToBuffer]) { return angle::Result::Continue; } VkDescriptorPoolSize setSizes[2] = { {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, }; return ensureResourcesInitialized(contextVk, Function::CopyImageToBuffer, setSizes, ArraySize(setSizes), sizeof(CopyImageToBufferShaderParams)); } angle::Result UtilsVk::ensureBlitResolveResourcesInitialized(ContextVk *contextVk) { if (!mPipelineLayouts[Function::BlitResolve]) { VkDescriptorPoolSize setSizes[3] = { {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1}, {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1}, {VK_DESCRIPTOR_TYPE_SAMPLER, 1}, }; ANGLE_TRY(ensureResourcesInitialized(contextVk, Function::BlitResolve, setSizes, ArraySize(setSizes), sizeof(BlitResolveShaderParams))); } return ensureSamplersInitialized(contextVk); } angle::Result UtilsVk::ensureBlitResolveStencilNoExportResourcesInitialized(ContextVk *contextVk) { if (!mPipelineLayouts[Function::BlitResolveStencilNoExport]) { VkDescriptorPoolSize setSizes[3] = { {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}, {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1}, {VK_DESCRIPTOR_TYPE_SAMPLER, 1}, }; ANGLE_TRY(ensureResourcesInitialized(contextVk, Function::BlitResolveStencilNoExport, setSizes, ArraySize(setSizes), sizeof(BlitResolveStencilNoExportShaderParams))); } return ensureSamplersInitialized(contextVk); } angle::Result UtilsVk::ensureExportStencilResourcesInitialized(ContextVk *contextVk) { if (mPipelineLayouts[Function::ExportStencil]) { return angle::Result::Continue; } VkDescriptorPoolSize setSizes[1] = { {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1}, }; return ensureResourcesInitialized(contextVk, Function::ExportStencil, setSizes, ArraySize(setSizes), sizeof(ExportStencilShaderParams)); } angle::Result UtilsVk::ensureOverlayDrawResourcesInitialized(ContextVk *contextVk) { if (!mPipelineLayouts[Function::OverlayDraw]) { VkDescriptorPoolSize setSizes[3] = { {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1}, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1}, {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1}, }; ANGLE_TRY(ensureResourcesInitialized(contextVk, Function::OverlayDraw, setSizes, ArraySize(setSizes), sizeof(OverlayDrawShaderParams))); } return ensureSamplersInitialized(contextVk); } angle::Result UtilsVk::ensureGenerateMipmapResourcesInitialized(ContextVk *contextVk) { if (mPipelineLayouts[Function::GenerateMipmap]) { return angle::Result::Continue; } VkDescriptorPoolSize setSizes[2] = { {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, GetGenerateMipmapMaxLevels(contextVk)}, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1}, }; return ensureResourcesInitialized(contextVk, Function::GenerateMipmap, setSizes, ArraySize(setSizes), sizeof(GenerateMipmapShaderParams)); } angle::Result UtilsVk::ensureTransCodeEtcToBcResourcesInitialized(ContextVk *contextVk) { if (mPipelineLayouts[Function::TransCodeEtcToBc]) { return angle::Result::Continue; } VkDescriptorPoolSize setSizes[2] = { {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1}, {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1}, }; return ensureResourcesInitialized(contextVk, Function::TransCodeEtcToBc, setSizes, ArraySize(setSizes), sizeof(EtcToBcShaderParams)); } angle::Result UtilsVk::ensureUnresolveResourcesInitialized(ContextVk *contextVk, Function function, uint32_t attachmentCount) { ASSERT(static_cast(function) - static_cast(Function::Unresolve1Attachment) == attachmentCount - 1); if (mPipelineLayouts[function]) { return angle::Result::Continue; } vk::FramebufferAttachmentArray 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 = {}; samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerInfo.flags = 0; samplerInfo.magFilter = VK_FILTER_NEAREST; samplerInfo.minFilter = VK_FILTER_NEAREST; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerInfo.mipLodBias = 0.0f; samplerInfo.anisotropyEnable = VK_FALSE; samplerInfo.maxAnisotropy = 1; samplerInfo.compareEnable = VK_FALSE; samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; samplerInfo.minLod = 0; samplerInfo.maxLod = 0; samplerInfo.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK; samplerInfo.unnormalizedCoordinates = VK_FALSE; if (!mPointSampler.valid()) { ANGLE_VK_TRY(contextVk, mPointSampler.init(contextVk->getDevice(), samplerInfo)); } samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR; if (!mLinearSampler.valid()) { ANGLE_VK_TRY(contextVk, mLinearSampler.init(contextVk->getDevice(), samplerInfo)); } return angle::Result::Continue; } angle::Result UtilsVk::ensureGenerateFragmentShadingRateResourcesInitialized(ContextVk *contextVk) { if (mGenerateFragmentShadingRateAttachment.program.valid(gl::ShaderType::Compute)) { return angle::Result::Continue; } VkDescriptorPoolSize setSizes[1] = { {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1}, }; return ensureResourcesInitialized(contextVk, Function::GenerateFragmentShadingRate, setSizes, ArraySize(setSizes), sizeof(GenerateFragmentShadingRateParameters)); } angle::Result UtilsVk::setupComputeProgram( ContextVk *contextVk, Function function, const vk::ShaderModulePtr &csShader, ComputeShaderProgramAndPipelines *programAndPipelines, const VkDescriptorSet descriptorSet, const void *pushConstants, size_t pushConstantsSize, vk::OutsideRenderPassCommandBufferHelper *commandBufferHelper) { vk::Renderer *renderer = contextVk->getRenderer(); ASSERT(function >= Function::ComputeStartIndex); const vk::PipelineLayoutPtr &pipelineLayout = mPipelineLayouts[function]; if (!programAndPipelines->program.valid(gl::ShaderType::Compute)) { programAndPipelines->program.setShader(gl::ShaderType::Compute, csShader); } vk::PipelineHelper *pipeline; vk::PipelineCacheAccess pipelineCache; ANGLE_TRY(renderer->getPipelineCache(contextVk, &pipelineCache)); ANGLE_TRY(programAndPipelines->program.getOrCreateComputePipeline( contextVk, &programAndPipelines->pipelines, &pipelineCache, *pipelineLayout, vk::GetComputePipelineOptions(contextVk->pipelineRobustness(), contextVk->pipelineProtectedAccess()), PipelineSource::Utils, &pipeline, nullptr, nullptr)); commandBufferHelper->retainResource(pipeline); vk::OutsideRenderPassCommandBuffer *commandBuffer = &commandBufferHelper->getCommandBuffer(); commandBuffer->bindComputePipeline(pipeline->getPipeline()); contextVk->invalidateComputePipelineBinding(); if (descriptorSet != VK_NULL_HANDLE) { commandBuffer->bindDescriptorSets(*pipelineLayout, VK_PIPELINE_BIND_POINT_COMPUTE, DescriptorSetIndex::Internal, 1, &descriptorSet, 0, nullptr); contextVk->invalidateComputeDescriptorSet(DescriptorSetIndex::Internal); } if (pushConstants) { commandBuffer->pushConstants(*pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, static_cast(pushConstantsSize), pushConstants); } return angle::Result::Continue; } angle::Result UtilsVk::setupGraphicsProgramWithLayout( ContextVk *contextVk, const vk::PipelineLayout &pipelineLayout, const vk::ShaderModulePtr &vsShader, const vk::ShaderModulePtr &fsShader, GraphicsShaderProgramAndPipelines *programAndPipelines, const vk::GraphicsPipelineDesc *pipelineDesc, const VkDescriptorSet descriptorSet, const void *pushConstants, size_t pushConstantsSize, vk::RenderPassCommandBuffer *commandBuffer) { vk::Renderer *renderer = contextVk->getRenderer(); if (!programAndPipelines->program.valid(gl::ShaderType::Vertex)) { programAndPipelines->program.setShader(gl::ShaderType::Vertex, vsShader); if (fsShader) { programAndPipelines->program.setShader(gl::ShaderType::Fragment, fsShader); } } // This value is not used but is passed to getGraphicsPipeline to avoid a nullptr check. vk::PipelineCacheAccess pipelineCache; ANGLE_TRY(renderer->getPipelineCache(contextVk, &pipelineCache)); // Pull in a compatible RenderPass. const vk::RenderPass *compatibleRenderPass = nullptr; ANGLE_TRY(contextVk->getCompatibleRenderPass(pipelineDesc->getRenderPassDesc(), &compatibleRenderPass)); const vk::GraphicsPipelineDesc *descPtr; vk::PipelineHelper *helper; if (!programAndPipelines->pipelines.getPipeline(*pipelineDesc, &descPtr, &helper)) { ANGLE_TRY(programAndPipelines->program.createGraphicsPipeline( contextVk, &programAndPipelines->pipelines, &pipelineCache, *compatibleRenderPass, pipelineLayout, PipelineSource::Utils, *pipelineDesc, {}, &descPtr, &helper)); } contextVk->getStartedRenderPassCommands().retainResource(helper); commandBuffer->bindGraphicsPipeline(helper->getPipeline()); contextVk->invalidateGraphicsPipelineBinding(); if (descriptorSet != VK_NULL_HANDLE) { commandBuffer->bindDescriptorSets(pipelineLayout, VK_PIPELINE_BIND_POINT_GRAPHICS, DescriptorSetIndex::Internal, 1, &descriptorSet, 0, nullptr); contextVk->invalidateGraphicsDescriptorSet(DescriptorSetIndex::Internal); } if (pushConstants) { commandBuffer->pushConstants(pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, static_cast(pushConstantsSize), pushConstants); } ResetDynamicState(contextVk, commandBuffer); return angle::Result::Continue; } angle::Result UtilsVk::setupGraphicsProgram(ContextVk *contextVk, Function function, const vk::ShaderModulePtr &vsShader, const vk::ShaderModulePtr &fsShader, GraphicsShaderProgramAndPipelines *programAndPipelines, const vk::GraphicsPipelineDesc *pipelineDesc, const VkDescriptorSet descriptorSet, const void *pushConstants, size_t pushConstantsSize, vk::RenderPassCommandBuffer *commandBuffer) { ASSERT(function < Function::ComputeStartIndex); return setupGraphicsProgramWithLayout( contextVk, *mPipelineLayouts[function], vsShader, fsShader, programAndPipelines, pipelineDesc, descriptorSet, pushConstants, pushConstantsSize, commandBuffer); } angle::Result UtilsVk::convertIndexBuffer(ContextVk *contextVk, vk::BufferHelper *dst, vk::BufferHelper *src, const ConvertIndexParameters ¶ms) { ANGLE_TRY(ensureConvertIndexResourcesInitialized(contextVk)); vk::CommandBufferAccess access; access.onBufferComputeShaderRead(src); access.onBufferComputeShaderWrite(dst); vk::OutsideRenderPassCommandBufferHelper *commandBufferHelper; vk::OutsideRenderPassCommandBuffer *commandBuffer; ANGLE_TRY(contextVk->getOutsideRenderPassCommandBufferHelper(access, &commandBufferHelper)); commandBuffer = &commandBufferHelper->getCommandBuffer(); VkDescriptorSet descriptorSet; ANGLE_TRY(allocateDescriptorSet(contextVk, commandBufferHelper, Function::ConvertIndexBuffer, &descriptorSet)); std::array buffers = {{ {dst->getBuffer().getHandle(), dst->getOffset(), dst->getSize()}, {src->getBuffer().getHandle(), src->getOffset(), src->getSize()}, }}; VkWriteDescriptorSet writeInfo = {}; writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfo.dstSet = descriptorSet; writeInfo.dstBinding = kConvertIndexDestinationBinding; writeInfo.descriptorCount = 2; writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; writeInfo.pBufferInfo = buffers.data(); vkUpdateDescriptorSets(contextVk->getDevice(), 1, &writeInfo, 0, nullptr); ConvertIndexShaderParams shaderParams = {params.srcOffset, params.dstOffset >> 2, params.maxIndex, 0}; uint32_t flags = 0; if (contextVk->getState().isPrimitiveRestartEnabled()) { flags |= vk::InternalShader::ConvertIndex_comp::kIsPrimitiveRestartEnabled; } vk::ShaderModulePtr shader; ANGLE_TRY(contextVk->getShaderLibrary().getConvertIndex_comp(contextVk, flags, &shader)); ANGLE_TRY(setupComputeProgram(contextVk, Function::ConvertIndexBuffer, shader, &mConvertIndex[flags], descriptorSet, &shaderParams, sizeof(ConvertIndexShaderParams), commandBufferHelper)); constexpr uint32_t kInvocationsPerGroup = 64; constexpr uint32_t kInvocationsPerIndex = 2; const uint32_t kIndexCount = params.maxIndex; const uint32_t kGroupCount = UnsignedCeilDivide(kIndexCount * kInvocationsPerIndex, kInvocationsPerGroup); commandBuffer->dispatch(kGroupCount, 1, 1); return angle::Result::Continue; } angle::Result UtilsVk::convertIndexIndirectBuffer(ContextVk *contextVk, vk::BufferHelper *srcIndirectBuf, vk::BufferHelper *srcIndexBuf, vk::BufferHelper *dstIndirectBuf, vk::BufferHelper *dstIndexBuf, const ConvertIndexIndirectParameters ¶ms) { ANGLE_TRY(ensureConvertIndexIndirectResourcesInitialized(contextVk)); vk::CommandBufferAccess access; access.onBufferComputeShaderRead(srcIndirectBuf); access.onBufferComputeShaderRead(srcIndexBuf); access.onBufferComputeShaderWrite(dstIndirectBuf); access.onBufferComputeShaderWrite(dstIndexBuf); vk::OutsideRenderPassCommandBufferHelper *commandBufferHelper; vk::OutsideRenderPassCommandBuffer *commandBuffer; ANGLE_TRY(contextVk->getOutsideRenderPassCommandBufferHelper(access, &commandBufferHelper)); commandBuffer = &commandBufferHelper->getCommandBuffer(); VkDescriptorSet descriptorSet; ANGLE_TRY(allocateDescriptorSet(contextVk, commandBufferHelper, Function::ConvertIndexIndirectBuffer, &descriptorSet)); std::array buffers = {{ {dstIndexBuf->getBuffer().getHandle(), dstIndexBuf->getOffset(), dstIndexBuf->getSize()}, {srcIndexBuf->getBuffer().getHandle(), srcIndexBuf->getOffset(), srcIndexBuf->getSize()}, {srcIndirectBuf->getBuffer().getHandle(), srcIndirectBuf->getOffset(), srcIndirectBuf->getSize()}, {dstIndirectBuf->getBuffer().getHandle(), dstIndirectBuf->getOffset(), dstIndirectBuf->getSize()}, }}; VkWriteDescriptorSet writeInfo = {}; writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfo.dstSet = descriptorSet; writeInfo.dstBinding = kConvertIndexDestinationBinding; writeInfo.descriptorCount = 4; writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; writeInfo.pBufferInfo = buffers.data(); vkUpdateDescriptorSets(contextVk->getDevice(), 1, &writeInfo, 0, nullptr); ConvertIndexIndirectShaderParams shaderParams = { params.srcIndirectBufOffset >> 2, params.srcIndexBufOffset, params.dstIndexBufOffset >> 2, params.maxIndex, params.dstIndirectBufOffset >> 2}; uint32_t flags = vk::InternalShader::ConvertIndex_comp::kIsIndirect; if (contextVk->getState().isPrimitiveRestartEnabled()) { flags |= vk::InternalShader::ConvertIndex_comp::kIsPrimitiveRestartEnabled; } vk::ShaderModulePtr shader; ANGLE_TRY(contextVk->getShaderLibrary().getConvertIndex_comp(contextVk, flags, &shader)); ANGLE_TRY(setupComputeProgram(contextVk, Function::ConvertIndexIndirectBuffer, shader, &mConvertIndex[flags], descriptorSet, &shaderParams, sizeof(ConvertIndexIndirectShaderParams), commandBufferHelper)); constexpr uint32_t kInvocationsPerGroup = 64; constexpr uint32_t kInvocationsPerIndex = 2; const uint32_t kIndexCount = params.maxIndex; const uint32_t kGroupCount = UnsignedCeilDivide(kIndexCount * kInvocationsPerIndex, kInvocationsPerGroup); commandBuffer->dispatch(kGroupCount, 1, 1); return angle::Result::Continue; } angle::Result UtilsVk::convertLineLoopIndexIndirectBuffer( ContextVk *contextVk, vk::BufferHelper *srcIndirectBuffer, vk::BufferHelper *srcIndexBuffer, vk::BufferHelper *dstIndirectBuffer, vk::BufferHelper *dstIndexBuffer, const ConvertLineLoopIndexIndirectParameters ¶ms) { ANGLE_TRY(ensureConvertIndexIndirectLineLoopResourcesInitialized(contextVk)); vk::CommandBufferAccess access; access.onBufferComputeShaderRead(srcIndirectBuffer); access.onBufferComputeShaderRead(srcIndexBuffer); access.onBufferComputeShaderWrite(dstIndirectBuffer); access.onBufferComputeShaderWrite(dstIndexBuffer); vk::OutsideRenderPassCommandBufferHelper *commandBufferHelper; vk::OutsideRenderPassCommandBuffer *commandBuffer; ANGLE_TRY(contextVk->getOutsideRenderPassCommandBufferHelper(access, &commandBufferHelper)); commandBuffer = &commandBufferHelper->getCommandBuffer(); VkDescriptorSet descriptorSet; ANGLE_TRY(allocateDescriptorSet(contextVk, commandBufferHelper, Function::ConvertIndexIndirectLineLoopBuffer, &descriptorSet)); std::array buffers = {{ {dstIndexBuffer->getBuffer().getHandle(), dstIndexBuffer->getOffset(), dstIndexBuffer->getSize()}, {srcIndexBuffer->getBuffer().getHandle(), srcIndexBuffer->getOffset(), srcIndexBuffer->getSize()}, {srcIndirectBuffer->getBuffer().getHandle(), srcIndirectBuffer->getOffset(), srcIndirectBuffer->getSize()}, {dstIndirectBuffer->getBuffer().getHandle(), dstIndirectBuffer->getOffset(), dstIndirectBuffer->getSize()}, }}; VkWriteDescriptorSet writeInfo = {}; writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfo.dstSet = descriptorSet; writeInfo.dstBinding = kConvertIndexDestinationBinding; writeInfo.descriptorCount = 4; writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; writeInfo.pBufferInfo = buffers.data(); vkUpdateDescriptorSets(contextVk->getDevice(), 1, &writeInfo, 0, nullptr); ConvertIndexIndirectLineLoopShaderParams shaderParams = { params.indirectBufferOffset >> 2, params.dstIndirectBufferOffset >> 2, params.srcIndexBufferOffset, params.dstIndexBufferOffset >> 2, contextVk->getState().isPrimitiveRestartEnabled()}; uint32_t flags = GetConvertIndexIndirectLineLoopFlag(params.indicesBitsWidth); vk::ShaderModulePtr shader; ANGLE_TRY(contextVk->getShaderLibrary().getConvertIndexIndirectLineLoop_comp(contextVk, flags, &shader)); ANGLE_TRY(setupComputeProgram(contextVk, Function::ConvertIndexIndirectLineLoopBuffer, shader, &mConvertIndexIndirectLineLoop[flags], descriptorSet, &shaderParams, sizeof(ConvertIndexIndirectLineLoopShaderParams), commandBufferHelper)); commandBuffer->dispatch(1, 1, 1); return angle::Result::Continue; } angle::Result UtilsVk::convertLineLoopArrayIndirectBuffer( ContextVk *contextVk, vk::BufferHelper *srcIndirectBuffer, vk::BufferHelper *dstIndirectBuffer, vk::BufferHelper *dstIndexBuffer, const ConvertLineLoopArrayIndirectParameters ¶ms) { ANGLE_TRY(ensureConvertIndirectLineLoopResourcesInitialized(contextVk)); vk::CommandBufferAccess access; access.onBufferComputeShaderRead(srcIndirectBuffer); access.onBufferComputeShaderWrite(dstIndirectBuffer); access.onBufferComputeShaderWrite(dstIndexBuffer); vk::OutsideRenderPassCommandBufferHelper *commandBufferHelper; vk::OutsideRenderPassCommandBuffer *commandBuffer; ANGLE_TRY(contextVk->getOutsideRenderPassCommandBufferHelper(access, &commandBufferHelper)); commandBuffer = &commandBufferHelper->getCommandBuffer(); VkDescriptorSet descriptorSet; ANGLE_TRY(allocateDescriptorSet(contextVk, commandBufferHelper, Function::ConvertIndirectLineLoopBuffer, &descriptorSet)); std::array buffers = {{ {srcIndirectBuffer->getBuffer().getHandle(), srcIndirectBuffer->getOffset(), srcIndirectBuffer->getSize()}, {dstIndirectBuffer->getBuffer().getHandle(), dstIndirectBuffer->getOffset(), dstIndirectBuffer->getSize()}, {dstIndexBuffer->getBuffer().getHandle(), dstIndexBuffer->getOffset(), dstIndexBuffer->getSize()}, }}; VkWriteDescriptorSet writeInfo = {}; writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfo.dstSet = descriptorSet; writeInfo.dstBinding = kConvertIndexDestinationBinding; writeInfo.descriptorCount = 3; writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; writeInfo.pBufferInfo = buffers.data(); vkUpdateDescriptorSets(contextVk->getDevice(), 1, &writeInfo, 0, nullptr); ConvertIndirectLineLoopShaderParams shaderParams = {params.indirectBufferOffset >> 2, params.dstIndirectBufferOffset >> 2, params.dstIndexBufferOffset >> 2}; uint32_t flags = 0; vk::ShaderModulePtr shader; ANGLE_TRY( contextVk->getShaderLibrary().getConvertIndirectLineLoop_comp(contextVk, flags, &shader)); ANGLE_TRY(setupComputeProgram(contextVk, Function::ConvertIndirectLineLoopBuffer, shader, &mConvertIndirectLineLoop[flags], descriptorSet, &shaderParams, sizeof(ConvertIndirectLineLoopShaderParams), commandBufferHelper)); commandBuffer->dispatch(1, 1, 1); return angle::Result::Continue; } // Used to clear a layer of a renderable texture in part or whole (EXT_clear_texture). angle::Result UtilsVk::clearTexture(ContextVk *contextVk, vk::ImageHelper *dst, ClearTextureParameters ¶ms) { const angle::Format &dstActualFormat = dst->getActualFormat(); bool isDepthOrStencil = dstActualFormat.hasDepthOrStencilBits(); bool isFormatDS = dstActualFormat.hasDepthAndStencilBits(); vk::DeviceScoped destView(contextVk->getDevice()); const gl::TextureType destViewType = vk::Get2DTextureType(1, dst->getSamples()); ANGLE_TRY(dst->initLayerImageView(contextVk, destViewType, params.aspectFlags, gl::SwizzleState(), &destView.get(), params.level, 1, params.layer, 1)); gl::Rectangle renderArea = {}; renderArea.x = params.clearArea.x; renderArea.y = params.clearArea.y; renderArea.width = params.clearArea.width; renderArea.height = params.clearArea.height; vk::RenderPassDesc renderPassDesc; renderPassDesc.setSamples(dst->getSamples()); if (!isDepthOrStencil) { renderPassDesc.packColorAttachment(0, dstActualFormat.id); } else { renderPassDesc.packDepthStencilAttachment(dstActualFormat.id); } vk::RenderPassCommandBuffer *commandBuffer; vk::ImageLayout imageLayout = isDepthOrStencil ? vk::ImageLayout::DepthWriteStencilWrite : vk::ImageLayout::ColorWrite; ANGLE_TRY(startRenderPass(contextVk, dst, &destView.get(), renderPassDesc, renderArea, params.aspectFlags, ¶ms.clearValue, vk::RenderPassSource::InternalUtils, &commandBuffer)); // If the format contains both depth and stencil, the barrier aspect mask for the image should // include both bits. contextVk->onImageRenderPassWrite( dst->toGLLevel(params.level), params.layer, 1, isFormatDS ? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT : params.aspectFlags, imageLayout, dst); vk::ImageView destViewObject = destView.release(); contextVk->addGarbage(&destViewObject); // Close the render pass for this temporary framebuffer. If the render pass is not immediately // closed and the render area grows due to scissor change, the clear area unexpectedly changes. // This can be avoided if the scissor code takes LOAD_OP_CLEAR into account before deciding to // grow the render pass's render area. return contextVk->flushCommandsAndEndRenderPass( RenderPassClosureReason::TemporaryForClearTexture); } angle::Result UtilsVk::convertVertexBuffer( ContextVk *contextVk, vk::BufferHelper *dst, vk::BufferHelper *src, const ConvertVertexParameters ¶ms, const OffsetAndVertexCounts &additionalOffsetVertexCounts) { vk::CommandBufferAccess access; access.onBufferComputeShaderRead(src); access.onBufferComputeShaderWrite(dst); vk::OutsideRenderPassCommandBufferHelper *commandBufferHelper; ANGLE_TRY(contextVk->getOutsideRenderPassCommandBufferHelper(access, &commandBufferHelper)); ConvertVertexShaderParams shaderParams; shaderParams.Ns = params.srcFormat->channelCount; shaderParams.Bs = params.srcFormat->pixelBytes / params.srcFormat->channelCount; shaderParams.Ss = static_cast(params.srcStride); shaderParams.Nd = params.dstFormat->channelCount; shaderParams.Bd = params.dstFormat->pixelBytes / params.dstFormat->channelCount; shaderParams.Sd = shaderParams.Nd * shaderParams.Bd; // The component size is expected to either be 1, 2 or 4 bytes. ASSERT(4 % shaderParams.Bs == 0); ASSERT(4 % shaderParams.Bd == 0); shaderParams.Es = 4 / shaderParams.Bs; shaderParams.Ed = 4 / shaderParams.Bd; // Total number of output components is simply the number of vertices by number of components in // each. shaderParams.componentCount = static_cast(params.vertexCount * shaderParams.Nd); // Total number of 4-byte outputs is the number of components divided by how many components can // fit in a 4-byte value. Note that this value is also the invocation size of the shader. shaderParams.outputCount = UnsignedCeilDivide(shaderParams.componentCount, shaderParams.Ed); shaderParams.srcOffset = static_cast(params.srcOffset); shaderParams.dstOffset = static_cast(params.dstOffset); bool isSrcA2BGR10 = params.srcFormat->vertexAttribType == gl::VertexAttribType::UnsignedInt2101010 || params.srcFormat->vertexAttribType == gl::VertexAttribType::Int2101010; bool isSrcRGB10A2 = params.srcFormat->vertexAttribType == gl::VertexAttribType::UnsignedInt1010102 || params.srcFormat->vertexAttribType == gl::VertexAttribType::Int1010102; shaderParams.isSrcHDR = isSrcA2BGR10 || isSrcRGB10A2; shaderParams.isSrcA2BGR10 = isSrcA2BGR10; uint32_t flags = GetConvertVertexFlags(params); // See GLES3.0 section 2.9.1 Transferring Array Elements const uint32_t srcValueBits = shaderParams.isSrcHDR ? 2 : shaderParams.Bs * 8; const uint32_t srcValueMask = srcValueBits == 32 ? 0xFFFFFFFFu : angle::BitMask(srcValueBits); switch (flags) { case ConvertVertex_comp::kSintToSint: case ConvertVertex_comp::kSintToFloat: case ConvertVertex_comp::kUintToFloat: // For integers, alpha should take a value of 1. shaderParams.srcEmulatedAlpha = 1; break; case ConvertVertex_comp::kUintToUint: // For integers, alpha should take a value of 1. However, uint->uint is also used to // add channels to RGB snorm, unorm and half formats. if (params.dstFormat->isSnorm()) { // See case ConvertVertex_comp::kSnormToFloat below. shaderParams.srcEmulatedAlpha = srcValueMask >> 1; } else if (params.dstFormat->isUnorm()) { // See case ConvertVertex_comp::kUnormToFloat below. shaderParams.srcEmulatedAlpha = srcValueMask; } else if (params.dstFormat->isVertexTypeHalfFloat()) { shaderParams.srcEmulatedAlpha = gl::Float16One; } else { shaderParams.srcEmulatedAlpha = 1; } break; case ConvertVertex_comp::kSnormToFloat: // The largest signed number with as many bits as the alpha channel of the source is // 0b011...1 which is srcValueMask >> 1 shaderParams.srcEmulatedAlpha = srcValueMask >> 1; break; case ConvertVertex_comp::kUnormToFloat: // The largest unsigned number with as many bits as the alpha channel of the source is // 0b11...1 which is srcValueMask shaderParams.srcEmulatedAlpha = srcValueMask; break; case ConvertVertex_comp::kFixedToFloat: // 1.0 in fixed point is 0x10000 shaderParams.srcEmulatedAlpha = 0x10000; break; case ConvertVertex_comp::kFloatToFloat: ASSERT(ValidateFloatOneAsUint()); shaderParams.srcEmulatedAlpha = gl::Float32One; break; default: UNREACHABLE(); } return convertVertexBufferImpl(contextVk, dst, src, flags, commandBufferHelper, shaderParams, additionalOffsetVertexCounts); } angle::Result UtilsVk::convertVertexBufferImpl( ContextVk *contextVk, vk::BufferHelper *dst, vk::BufferHelper *src, uint32_t flags, vk::OutsideRenderPassCommandBufferHelper *commandBufferHelper, const ConvertVertexShaderParams &shaderParams, const OffsetAndVertexCounts &additionalOffsetVertexCounts) { ANGLE_TRY(ensureConvertVertexResourcesInitialized(contextVk)); vk::OutsideRenderPassCommandBuffer *commandBuffer; commandBuffer = &commandBufferHelper->getCommandBuffer(); VkDescriptorSet descriptorSet; ANGLE_TRY(allocateDescriptorSet(contextVk, commandBufferHelper, Function::ConvertVertexBuffer, &descriptorSet)); // ConvertVertexBuffer writes whole 4 bytes to dstOffset. Caller must ensure dstOffset is // aligned on 4 bytes boundary. ASSERT(dst->getOffset() % 4 == 0); VkWriteDescriptorSet writeInfo = {}; VkDescriptorBufferInfo buffers[2] = { {dst->getBuffer().getHandle(), dst->getOffset(), dst->getSize()}, {src->getBuffer().getHandle(), src->getOffset(), src->getSize()}, }; static_assert(kConvertVertexDestinationBinding + 1 == kConvertVertexSourceBinding, "Update write info"); writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfo.dstSet = descriptorSet; writeInfo.dstBinding = kConvertVertexDestinationBinding; writeInfo.descriptorCount = 2; writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; writeInfo.pBufferInfo = buffers; vkUpdateDescriptorSets(contextVk->getDevice(), 1, &writeInfo, 0, nullptr); vk::ShaderModulePtr shader; ANGLE_TRY(contextVk->getShaderLibrary().getConvertVertex_comp(contextVk, flags, &shader)); ANGLE_TRY(setupComputeProgram(contextVk, Function::ConvertVertexBuffer, shader, &mConvertVertex[flags], descriptorSet, &shaderParams, sizeof(shaderParams), commandBufferHelper)); commandBuffer->dispatch(UnsignedCeilDivide(shaderParams.outputCount, 64), 1, 1); if (!additionalOffsetVertexCounts.empty()) { ConvertVertexShaderParams constants = shaderParams; for (const OffsetAndVertexCount &offsetAndVertexCount : additionalOffsetVertexCounts) { // Total number of output components is simply the number of vertices by number of // components in each. constants.componentCount = static_cast(offsetAndVertexCount.vertexCount * shaderParams.Nd); // Total number of 4-byte outputs is the number of components divided by how many // components can fit in a 4-byte value. Note that this value is also the invocation // size of the shader. constants.outputCount = UnsignedCeilDivide(constants.componentCount, shaderParams.Ed); constants.srcOffset = static_cast(offsetAndVertexCount.srcOffset); constants.dstOffset = static_cast(offsetAndVertexCount.dstOffset); commandBuffer->pushConstants(*mPipelineLayouts[Function::ConvertVertexBuffer], VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(constants), &constants); // Since multiple compute dispatch all convert from the same srcBuffer and write to the // same dstBuffer, even if the ranges overlap, they should end up with writing the same // values, thus no barrier is needed here. commandBuffer->dispatch(UnsignedCeilDivide(constants.outputCount, 64), 1, 1); } } return angle::Result::Continue; } angle::Result UtilsVk::startRenderPass(ContextVk *contextVk, vk::ImageHelper *image, const vk::ImageView *imageView, const vk::RenderPassDesc &renderPassDesc, const gl::Rectangle &renderArea, const VkImageAspectFlags aspectFlags, const VkClearValue *clearValue, vk::RenderPassSource renderPassSource, vk::RenderPassCommandBuffer **commandBufferOut) { ASSERT(aspectFlags == VK_IMAGE_ASPECT_COLOR_BIT || (aspectFlags & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0); vk::ImageLayout imageLayout = aspectFlags == VK_IMAGE_ASPECT_COLOR_BIT ? vk::ImageLayout::ColorWrite : vk::ImageLayout::DepthWriteStencilWrite; vk::Framebuffer framebuffer; vk::Framebuffer framebufferHandle; vk::RenderPassFramebuffer renderPassFramebuffer; const uint32_t framebufferWidth = renderArea.x + renderArea.width; const uint32_t framebufferHeight = renderArea.y + renderArea.height; const uint32_t framebufferLayers = 1; vk::ImagelessFramebuffer imageless = vk::ImagelessFramebuffer::Yes; if (!contextVk->getFeatures().preferDynamicRendering.enabled) { imageless = vk::ImagelessFramebuffer::No; const vk::RenderPass *compatibleRenderPass = nullptr; ANGLE_TRY(contextVk->getCompatibleRenderPass(renderPassDesc, &compatibleRenderPass)); VkFramebufferCreateInfo framebufferInfo = {}; // Minimize the framebuffer coverage to only cover up to the render area. framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebufferInfo.flags = 0; framebufferInfo.renderPass = compatibleRenderPass->getHandle(); framebufferInfo.attachmentCount = 1; framebufferInfo.pAttachments = imageView->ptr(); framebufferInfo.width = framebufferWidth; framebufferInfo.height = framebufferHeight; framebufferInfo.layers = framebufferLayers; ANGLE_VK_TRY(contextVk, framebuffer.init(contextVk->getDevice(), framebufferInfo)); framebufferHandle.setHandle(framebuffer.getHandle()); } renderPassFramebuffer.setFramebuffer( contextVk, std::move(framebufferHandle), {imageView->getHandle()}, framebufferWidth, framebufferHeight, framebufferLayers, imageless, renderPassSource); // If a clear value has been provided, the load op is set to clear. vk::AttachmentOpsArray renderPassAttachmentOps; vk::PackedClearValuesArray clearValues; VkClearValue attachmentClearValue = {}; if (clearValue == nullptr) { renderPassAttachmentOps.initWithLoadStore(vk::kAttachmentIndexZero, imageLayout, imageLayout); } else { attachmentClearValue = *clearValue; renderPassAttachmentOps.setLayouts(vk::kAttachmentIndexZero, imageLayout, imageLayout); renderPassAttachmentOps.setClearOp(vk::kAttachmentIndexZero); renderPassAttachmentOps.setClearStencilOp(vk::kAttachmentIndexZero); } if (aspectFlags == VK_IMAGE_ASPECT_COLOR_BIT) { clearValues.storeColor(vk::kAttachmentIndexZero, attachmentClearValue); } else { clearValues.storeDepthStencil(vk::kAttachmentIndexZero, attachmentClearValue); } ANGLE_TRY(contextVk->beginNewRenderPass( std::move(renderPassFramebuffer), renderArea, renderPassDesc, renderPassAttachmentOps, vk::PackedAttachmentCount(1), vk::kAttachmentIndexInvalid, clearValues, commandBufferOut)); contextVk->addGarbage(&framebuffer); return angle::Result::Continue; } angle::Result UtilsVk::clearFramebuffer(ContextVk *contextVk, FramebufferVk *framebuffer, const ClearFramebufferParameters ¶ms) { vk::Renderer *renderer = contextVk->getRenderer(); ANGLE_TRY(ensureImageClearResourcesInitialized(contextVk)); const gl::Rectangle &scissoredRenderArea = params.clearArea; vk::RenderPassCommandBuffer *commandBuffer; // Start a new render pass if not already started if (contextVk->hasActiveRenderPass() && contextVk->hasStartedRenderPassWithQueueSerial(framebuffer->getLastRenderPassQueueSerial())) { vk::RenderPassCommandBufferHelper *renderPassCommands = &contextVk->getStartedRenderPassCommands(); renderPassCommands->growRenderArea(contextVk, scissoredRenderArea); commandBuffer = &renderPassCommands->getCommandBuffer(); } else { // Deferred clears should be handled already. ASSERT(!framebuffer->hasDeferredClears()); ANGLE_TRY(contextVk->startRenderPass(scissoredRenderArea, &commandBuffer, nullptr)); } UpdateColorAccess(contextVk, framebuffer->getState().getColorAttachmentsMask(), MakeColorBufferMask(params.colorAttachmentIndexGL)); UpdateDepthStencilAccess(contextVk, params.clearDepth, params.clearStencil); ImageClearShaderParams shaderParams; shaderParams.clearValue = params.colorClearValue; shaderParams.clearDepth = params.depthStencilClearValue.depth; vk::GraphicsPipelineDesc pipelineDesc; pipelineDesc.initDefaults(contextVk, vk::GraphicsPipelineSubset::Complete, contextVk->pipelineRobustness(), contextVk->pipelineProtectedAccess()); pipelineDesc.setColorWriteMasks(0, gl::DrawBufferMask(), gl::DrawBufferMask()); pipelineDesc.setSingleColorWriteMask(params.colorAttachmentIndexGL, params.colorMaskFlags); pipelineDesc.setRasterizationSamples(framebuffer->getSamples()); pipelineDesc.setRenderPassDesc(framebuffer->getRenderPassDesc()); // Clears can be done on a currently open render pass, so make sure the correct subpass index is // used. pipelineDesc.setSubpass(contextVk->getCurrentSubpassIndex()); // Clear depth by enabling depth clamping and setting the viewport depth range to the clear // value if possible. Otherwise use the shader to export depth. const bool supportsDepthClamp = renderer->getPhysicalDeviceFeatures().depthClamp == VK_TRUE; if (params.clearDepth) { SetDepthStateForWrite(renderer, &pipelineDesc); if (supportsDepthClamp) { // Note: this path requires the depthClamp Vulkan feature. pipelineDesc.setDepthClampEnabled(true); } } // Clear stencil by enabling stencil write with the right mask. if (params.clearStencil) { SetStencilStateForWrite(renderer, &pipelineDesc); } vk::ShaderLibrary &shaderLibrary = contextVk->getShaderLibrary(); vk::ShaderModulePtr vertexShader; vk::ShaderModulePtr fragmentShader; GraphicsShaderProgramAndPipelines *imageClearProgramAndPipelines = &mImageClearVSOnly; ANGLE_TRY(shaderLibrary.getFullScreenTri_vert(contextVk, 0, &vertexShader)); if (params.clearColor) { const uint32_t flags = GetImageClearFlags(*params.colorFormat, params.colorAttachmentIndexGL, params.clearDepth && !supportsDepthClamp); ANGLE_TRY(shaderLibrary.getImageClear_frag(contextVk, flags, &fragmentShader)); imageClearProgramAndPipelines = &mImageClear[flags]; } // Make sure transform feedback is paused. Needs to be done before binding the pipeline as // that's not allowed in Vulkan. const bool isTransformFeedbackActiveUnpaused = contextVk->getStartedRenderPassCommands().isTransformFeedbackActiveUnpaused(); contextVk->pauseTransformFeedbackIfActiveUnpaused(); ANGLE_TRY(setupGraphicsProgram(contextVk, Function::ImageClear, vertexShader, fragmentShader, imageClearProgramAndPipelines, &pipelineDesc, VK_NULL_HANDLE, &shaderParams, sizeof(shaderParams), commandBuffer)); // Set dynamic state VkViewport viewport; gl::Rectangle completeRenderArea = framebuffer->getRotatedCompleteRenderArea(contextVk); bool invertViewport = contextVk->isViewportFlipEnabledForDrawFBO(); bool clipSpaceOriginUpperLeft = contextVk->getState().getClipOrigin() == gl::ClipOrigin::UpperLeft; // Set depth range to clear value. If clearing depth, the vertex shader depth output is clamped // to this value, thus clearing the depth buffer to the desired clear value. const float clearDepthValue = params.depthStencilClearValue.depth; gl_vk::GetViewport(completeRenderArea, clearDepthValue, clearDepthValue, invertViewport, clipSpaceOriginUpperLeft, completeRenderArea.height, &viewport); commandBuffer->setViewport(0, 1, &viewport); const VkRect2D scissor = gl_vk::GetRect(params.clearArea); commandBuffer->setScissor(0, 1, &scissor); if (params.clearDepth) { SetDepthDynamicStateForWrite(renderer, commandBuffer); } else { SetDepthDynamicStateForUnused(renderer, commandBuffer); } if (params.clearStencil) { constexpr uint8_t kCompareMask = 0xFF; const uint8_t clearStencilValue = static_cast(params.depthStencilClearValue.stencil); commandBuffer->setStencilCompareMask(kCompareMask, kCompareMask); commandBuffer->setStencilWriteMask(params.stencilMask, params.stencilMask); commandBuffer->setStencilReference(clearStencilValue, clearStencilValue); SetStencilDynamicStateForWrite(renderer, commandBuffer); } else { SetStencilDynamicStateForUnused(renderer, commandBuffer); } ASSERT(contextVk->hasStartedRenderPassWithQueueSerial( framebuffer->getLastRenderPassQueueSerial())); // Make sure this draw call doesn't count towards occlusion query results. contextVk->pauseRenderPassQueriesIfActive(); commandBuffer->draw(3, 0); ANGLE_TRY(contextVk->resumeRenderPassQueriesIfActive()); // If transform feedback was active, we can't pause and resume it in the same render pass // because we can't insert a memory barrier for the counter buffers. In that case, break the // render pass. if (isTransformFeedbackActiveUnpaused) { ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass( RenderPassClosureReason::XfbResumeAfterDrawBasedClear)); } return angle::Result::Continue; } angle::Result UtilsVk::clearImage(ContextVk *contextVk, vk::ImageHelper *dst, const ClearImageParameters ¶ms) { vk::Renderer *renderer = contextVk->getRenderer(); ANGLE_TRY(ensureImageClearResourcesInitialized(contextVk)); const angle::Format &dstActualFormat = dst->getActualFormat(); // Currently, this function is only used to clear emulated channels of color images. ASSERT(!dstActualFormat.hasDepthOrStencilBits()); // TODO: currently this function is only implemented for images that are drawable. If needed, // for images that are not drawable, the following algorithm can be used. // // - Copy image to temp buffer // - Use convertVertexBufferImpl to overwrite the alpha channel // - Copy the result back to the image // // Note that the following check is not enough; if the image is AHB-imported, then the draw path // cannot be taken if AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER hasn't been specified, even if the // format is renderable. // // http://anglebug.com/42264676 if (!vk::FormatHasNecessaryFeature(renderer, dstActualFormat.id, dst->getTilingMode(), VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { UNIMPLEMENTED(); return angle::Result::Continue; } vk::DeviceScoped destView(contextVk->getDevice()); const gl::TextureType destViewType = vk::Get2DTextureType(1, dst->getSamples()); ANGLE_TRY(dst->initLayerImageView(contextVk, destViewType, VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(), &destView.get(), params.dstMip, 1, params.dstLayer, 1)); const gl::Rectangle &renderArea = params.clearArea; ImageClearShaderParams shaderParams; shaderParams.clearValue = params.colorClearValue; shaderParams.clearDepth = 0; vk::RenderPassDesc renderPassDesc; renderPassDesc.setSamples(dst->getSamples()); renderPassDesc.packColorAttachment(0, dstActualFormat.id); vk::GraphicsPipelineDesc pipelineDesc; pipelineDesc.initDefaults(contextVk, vk::GraphicsPipelineSubset::Complete, contextVk->pipelineRobustness(), contextVk->pipelineProtectedAccess()); pipelineDesc.setSingleColorWriteMask(0, params.colorMaskFlags); pipelineDesc.setRasterizationSamples(dst->getSamples()); pipelineDesc.setRenderPassDesc(renderPassDesc); vk::RenderPassCommandBuffer *commandBuffer; ANGLE_TRY(startRenderPass(contextVk, dst, &destView.get(), renderPassDesc, renderArea, VK_IMAGE_ASPECT_COLOR_BIT, nullptr, vk::RenderPassSource::InternalUtils, &commandBuffer)); UpdateColorAccess(contextVk, MakeColorBufferMask(0), MakeColorBufferMask(0)); contextVk->onImageRenderPassWrite(dst->toGLLevel(params.dstMip), params.dstLayer, 1, VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::ColorWrite, dst); const uint32_t flags = GetImageClearFlags(dstActualFormat, 0, false); vk::ShaderLibrary &shaderLibrary = contextVk->getShaderLibrary(); vk::ShaderModulePtr vertexShader; vk::ShaderModulePtr fragmentShader; ANGLE_TRY(shaderLibrary.getFullScreenTri_vert(contextVk, 0, &vertexShader)); ANGLE_TRY(shaderLibrary.getImageClear_frag(contextVk, flags, &fragmentShader)); ANGLE_TRY(setupGraphicsProgram(contextVk, Function::ImageClear, vertexShader, fragmentShader, &mImageClear[flags], &pipelineDesc, VK_NULL_HANDLE, &shaderParams, sizeof(shaderParams), commandBuffer)); // Set dynamic state VkViewport viewport; gl_vk::GetViewport(renderArea, 0.0f, 1.0f, false, false, dst->getExtents().height, &viewport); commandBuffer->setViewport(0, 1, &viewport); VkRect2D scissor = gl_vk::GetRect(renderArea); commandBuffer->setScissor(0, 1, &scissor); SetDepthDynamicStateForUnused(renderer, commandBuffer); SetStencilDynamicStateForUnused(renderer, commandBuffer); // Note: this utility creates its own framebuffer, thus bypassing ContextVk::startRenderPass. // As such, occlusion queries are not enabled. commandBuffer->draw(3, 0); vk::ImageView destViewObject = destView.release(); contextVk->addGarbage(&destViewObject); // Close the render pass for this temporary framebuffer. return contextVk->flushCommandsAndEndRenderPass( RenderPassClosureReason::TemporaryForImageClear); } angle::Result UtilsVk::colorBlitResolve(ContextVk *contextVk, FramebufferVk *framebuffer, vk::ImageHelper *src, const vk::ImageView *srcView, const BlitResolveParameters ¶ms) { // The views passed to this function are already retained, so a render pass cannot be already // open. Otherwise, this function closes the render pass, which may incur a vkQueueSubmit and // then the views are used in a new command buffer without having been retained for it. // http://crbug.com/1272266#c22 // // Note that depth/stencil views for blit are not derived from a |Resource| class and are // retained differently. ASSERT(!contextVk->hasActiveRenderPass()); return blitResolveImpl(contextVk, framebuffer, src, srcView, nullptr, nullptr, params); } angle::Result UtilsVk::depthStencilBlitResolve(ContextVk *contextVk, FramebufferVk *framebuffer, vk::ImageHelper *src, const vk::ImageView *srcDepthView, const vk::ImageView *srcStencilView, const BlitResolveParameters ¶ms) { return blitResolveImpl(contextVk, framebuffer, src, nullptr, srcDepthView, srcStencilView, params); } angle::Result UtilsVk::blitResolveImpl(ContextVk *contextVk, FramebufferVk *framebuffer, vk::ImageHelper *src, const vk::ImageView *srcColorView, const vk::ImageView *srcDepthView, const vk::ImageView *srcStencilView, const BlitResolveParameters ¶ms) { // Possible ways to resolve color are: // // - vkCmdResolveImage: This is by far the easiest method, but lacks the ability to flip // images during resolve. // - Manual resolve: A shader can read all samples from input, average them and output. // - Using subpass resolve attachment: A shader can transform the sample colors from source to // destination coordinates and the subpass resolve would finish the job. // // The first method is unable to handle flipping, so it's not generally applicable. The last // method would have been great were we able to modify the last render pass that rendered into // source, but still wouldn't be able to handle flipping. The second method is implemented in // this function for complete control. // Possible ways to resolve depth/stencil are: // // - Manual resolve: A shader can read a samples from input and choose that for output. // - Using subpass resolve attachment through VkSubpassDescriptionDepthStencilResolveKHR: This // requires an extension that's not very well supported. // // The first method is implemented in this function. // Possible ways to blit color, depth or stencil are: // // - vkCmdBlitImage: This function works if the source and destination formats have the blit // feature. // - Manual blit: A shader can sample from the source image and write it to the destination. // // The first method has a serious shortcoming. GLES allows blit parameters to exceed the // source or destination boundaries. The actual blit is clipped to these limits, but the // scaling applied is determined solely by the input areas. Vulkan requires the blit parameters // to be within the source and destination bounds. This makes it hard to keep the scaling // constant. // // The second method is implemented in this function, which shares code with the resolve method. vk::Renderer *renderer = contextVk->getRenderer(); ANGLE_TRY(ensureBlitResolveResourcesInitialized(contextVk)); bool isResolve = src->getSamples() > 1; BlitResolveShaderParams shaderParams; // Note: adjustments made for pre-rotatation in FramebufferVk::blit() affect these // Calculate*Offset() functions. if (isResolve) { CalculateResolveOffset(params, shaderParams.offset.resolve); } else { CalculateBlitOffset(params, shaderParams.offset.blit); } shaderParams.stretch[0] = params.stretch[0]; shaderParams.stretch[1] = params.stretch[1]; shaderParams.invSrcExtent[0] = 1.0f / params.srcExtents[0]; shaderParams.invSrcExtent[1] = 1.0f / params.srcExtents[1]; shaderParams.srcLayer = params.srcLayer; shaderParams.samples = src->getSamples(); shaderParams.invSamples = 1.0f / shaderParams.samples; shaderParams.outputMask = framebuffer->getState().getEnabledDrawBuffers().bits(); shaderParams.flipX = params.flipX; shaderParams.flipY = params.flipY; shaderParams.rotateXY = 0; // Potentially make adjustments for pre-rotation. Depending on the angle some of the // shaderParams need to be adjusted. switch (params.rotation) { case SurfaceRotation::Identity: case SurfaceRotation::Rotated90Degrees: break; case SurfaceRotation::Rotated180Degrees: case SurfaceRotation::Rotated270Degrees: if (isResolve) { // Align the offset with minus 1, or the sample position near the edge will be // wrong. shaderParams.offset.resolve[0] += params.rotatedOffsetFactor[0] - 1; shaderParams.offset.resolve[1] += params.rotatedOffsetFactor[1] - 1; } else { shaderParams.offset.blit[0] += params.rotatedOffsetFactor[0]; shaderParams.offset.blit[1] += params.rotatedOffsetFactor[1]; } break; default: UNREACHABLE(); break; } shaderParams.rotateXY = IsRotatedAspectRatio(params.rotation); bool blitColor = srcColorView != nullptr; bool blitDepth = srcDepthView != nullptr; bool blitStencil = srcStencilView != nullptr; // Either color is blitted/resolved or depth/stencil, but not both. ASSERT(blitColor != (blitDepth || blitStencil)); // Linear sampling is only valid with color blitting. ASSERT((blitColor && !isResolve) || !params.linear); uint32_t flags = GetBlitResolveFlags(blitColor, blitDepth, blitStencil, src->getIntendedFormat()); flags |= src->getLayerCount() > 1 ? BlitResolve_frag::kSrcIsArray : 0; flags |= isResolve ? BlitResolve_frag::kIsResolve : 0; Function function = Function::BlitResolve; // Note: a different shader is used for 3D color blits, but otherwise the desc sets, parameters // etc are identical. const bool isSrc3D = src->getType() == VK_IMAGE_TYPE_3D; ASSERT(!isSrc3D || (blitColor && !isResolve)); if (isSrc3D) { flags = GetFormatFlags(src->getIntendedFormat(), Blit3DSrc_frag::kBlitInt, Blit3DSrc_frag::kBlitUint, Blit3DSrc_frag::kBlitFloat); } vk::GraphicsPipelineDesc pipelineDesc; pipelineDesc.initDefaults(contextVk, vk::GraphicsPipelineSubset::Complete, contextVk->pipelineRobustness(), contextVk->pipelineProtectedAccess()); if (blitColor) { constexpr VkColorComponentFlags kAllColorComponents = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; pipelineDesc.setColorWriteMasks( gl::BlendStateExt::ColorMaskStorage::GetReplicatedValue( kAllColorComponents, gl::BlendStateExt::ColorMaskStorage::GetMask( framebuffer->getRenderPassDesc().colorAttachmentRange())), framebuffer->getEmulatedAlphaAttachmentMask(), ~gl::DrawBufferMask()); } else { pipelineDesc.setColorWriteMasks(0, gl::DrawBufferMask(), gl::DrawBufferMask()); } pipelineDesc.setRenderPassDesc(framebuffer->getRenderPassDesc()); if (blitDepth) { SetDepthStateForWrite(renderer, &pipelineDesc); } if (blitStencil) { SetStencilStateForWrite(renderer, &pipelineDesc); } // All deferred clear must have been flushed, otherwise it will conflict with params.blitArea. ASSERT(!framebuffer->hasDeferredClears()); vk::RenderPassCommandBuffer *commandBuffer; ANGLE_TRY(framebuffer->startNewRenderPass(contextVk, params.blitArea, &commandBuffer, nullptr)); VkDescriptorSet descriptorSet; ANGLE_TRY(allocateDescriptorSet(contextVk, &contextVk->getStartedRenderPassCommands(), Function::BlitResolve, &descriptorSet)); // Pick layout consistent with GetImageReadLayout() to avoid unnecessary layout change. vk::ImageLayout srcImagelayout = src->isDepthOrStencil() ? vk::ImageLayout::DepthReadStencilReadFragmentShaderRead : vk::ImageLayout::FragmentShaderReadOnly; contextVk->onImageRenderPassRead(src->getAspectFlags(), srcImagelayout, src); UpdateColorAccess(contextVk, framebuffer->getState().getColorAttachmentsMask(), framebuffer->getState().getEnabledDrawBuffers()); UpdateDepthStencilAccess(contextVk, blitDepth, blitStencil); VkDescriptorImageInfo imageInfos[2] = {}; if (blitColor) { imageInfos[0].imageView = srcColorView->getHandle(); imageInfos[0].imageLayout = src->getCurrentLayout(renderer); } if (blitDepth) { imageInfos[0].imageView = srcDepthView->getHandle(); imageInfos[0].imageLayout = src->getCurrentLayout(renderer); } if (blitStencil) { imageInfos[1].imageView = srcStencilView->getHandle(); imageInfos[1].imageLayout = src->getCurrentLayout(renderer); } VkDescriptorImageInfo samplerInfo = {}; samplerInfo.sampler = params.linear ? mLinearSampler.getHandle() : mPointSampler.getHandle(); VkWriteDescriptorSet writeInfos[3] = {}; writeInfos[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfos[0].dstSet = descriptorSet; writeInfos[0].dstBinding = kBlitResolveColorOrDepthBinding; writeInfos[0].descriptorCount = 1; writeInfos[0].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; writeInfos[0].pImageInfo = &imageInfos[0]; writeInfos[1] = writeInfos[0]; writeInfos[1].dstBinding = kBlitResolveStencilBinding; writeInfos[1].pImageInfo = &imageInfos[1]; writeInfos[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfos[2].dstSet = descriptorSet; writeInfos[2].dstBinding = kBlitResolveSamplerBinding; writeInfos[2].descriptorCount = 1; writeInfos[2].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; writeInfos[2].pImageInfo = &samplerInfo; // If resolving color, there's one write info; index 0 // If resolving depth, write info index 0 must be written // If resolving stencil, write info index 1 must also be written // // Note again that resolving color and depth/stencil are mutually exclusive here. uint32_t writeInfoOffset = blitDepth || blitColor ? 0 : 1; uint32_t writeInfoCount = blitColor + blitDepth + blitStencil; vkUpdateDescriptorSets(contextVk->getDevice(), writeInfoCount, writeInfos + writeInfoOffset, 0, nullptr); vkUpdateDescriptorSets(contextVk->getDevice(), 1, &writeInfos[2], 0, nullptr); vk::ShaderLibrary &shaderLibrary = contextVk->getShaderLibrary(); vk::ShaderModulePtr vertexShader; vk::ShaderModulePtr fragmentShader; ANGLE_TRY(shaderLibrary.getFullScreenTri_vert(contextVk, 0, &vertexShader)); if (isSrc3D) { ANGLE_TRY(shaderLibrary.getBlit3DSrc_frag(contextVk, flags, &fragmentShader)); } else { ANGLE_TRY(shaderLibrary.getBlitResolve_frag(contextVk, flags, &fragmentShader)); } ANGLE_TRY(setupGraphicsProgram(contextVk, function, vertexShader, fragmentShader, isSrc3D ? &mBlit3DSrc[flags] : &mBlitResolve[flags], &pipelineDesc, descriptorSet, &shaderParams, sizeof(shaderParams), commandBuffer)); // Set dynamic state VkViewport viewport; gl::Rectangle completeRenderArea = framebuffer->getRotatedCompleteRenderArea(contextVk); gl_vk::GetViewport(completeRenderArea, 0.0f, 1.0f, false, false, completeRenderArea.height, &viewport); commandBuffer->setViewport(0, 1, &viewport); VkRect2D scissor = gl_vk::GetRect(params.blitArea); commandBuffer->setScissor(0, 1, &scissor); if (blitDepth) { SetDepthDynamicStateForWrite(renderer, commandBuffer); } else { SetDepthDynamicStateForUnused(renderer, commandBuffer); } if (blitStencil) { constexpr uint8_t kCompleteMask = 0xFF; constexpr uint8_t kUnusedReference = 0x00; commandBuffer->setStencilCompareMask(kCompleteMask, kCompleteMask); commandBuffer->setStencilWriteMask(kCompleteMask, kCompleteMask); commandBuffer->setStencilReference(kUnusedReference, kUnusedReference); SetStencilDynamicStateForWrite(renderer, commandBuffer); } else { SetStencilDynamicStateForUnused(renderer, commandBuffer); } // Note: this utility starts the render pass directly, thus bypassing // ContextVk::startRenderPass. As such, occlusion queries are not enabled. commandBuffer->draw(3, 0); // Don't allow this render pass to be reactivated by the user's draw call due to test flakiness // on win/intel bot. contextVk->disableRenderPassReactivation(); return angle::Result::Continue; } angle::Result UtilsVk::stencilBlitResolveNoShaderExport(ContextVk *contextVk, FramebufferVk *framebuffer, vk::ImageHelper *src, const vk::ImageView *srcStencilView, const BlitResolveParameters ¶ms) { vk::Renderer *renderer = contextVk->getRenderer(); // When VK_EXT_shader_stencil_export is not available, stencil is blitted/resolved into a // temporary buffer which is then copied into the stencil aspect of the image. ANGLE_TRY(ensureBlitResolveStencilNoExportResourcesInitialized(contextVk)); bool isResolve = src->getSamples() > 1; // Create a temporary buffer to blit/resolve stencil into. vk::RendererScoped blitBuffer(renderer); uint32_t bufferRowLengthInUints = UnsignedCeilDivide(params.blitArea.width, sizeof(uint32_t)); VkDeviceSize bufferSize = bufferRowLengthInUints * sizeof(uint32_t) * params.blitArea.height; ANGLE_TRY(contextVk->initBufferAllocation( &blitBuffer.get(), renderer->getDeviceLocalMemoryTypeIndex(), static_cast(bufferSize), renderer->getDefaultBufferAlignment(), BufferUsageType::Static)); BlitResolveStencilNoExportShaderParams shaderParams; // Note: adjustments made for pre-rotatation in FramebufferVk::blit() affect these // Calculate*Offset() functions. if (isResolve) { CalculateResolveOffset(params, shaderParams.offset.resolve); } else { CalculateBlitOffset(params, shaderParams.offset.blit); } shaderParams.stretch[0] = params.stretch[0]; shaderParams.stretch[1] = params.stretch[1]; shaderParams.invSrcExtent[0] = 1.0f / params.srcExtents[0]; shaderParams.invSrcExtent[1] = 1.0f / params.srcExtents[1]; shaderParams.srcLayer = params.srcLayer; shaderParams.srcWidth = params.srcExtents[0]; shaderParams.dstPitch = bufferRowLengthInUints; shaderParams.blitArea[0] = params.blitArea.x; shaderParams.blitArea[1] = params.blitArea.y; shaderParams.blitArea[2] = params.blitArea.width; shaderParams.blitArea[3] = params.blitArea.height; shaderParams.flipX = params.flipX; shaderParams.flipY = params.flipY; shaderParams.rotateXY = 0; // Potentially make adjustments for pre-rotatation. Depending on the angle some of the // shaderParams need to be adjusted. switch (params.rotation) { case SurfaceRotation::Identity: case SurfaceRotation::Rotated90Degrees: break; case SurfaceRotation::Rotated180Degrees: case SurfaceRotation::Rotated270Degrees: if (isResolve) { // Align the offset with minus 1, or the sample position near the edge will be // wrong. shaderParams.offset.resolve[0] += params.rotatedOffsetFactor[0] - 1; shaderParams.offset.resolve[1] += params.rotatedOffsetFactor[1] - 1; } else { shaderParams.offset.blit[0] += params.rotatedOffsetFactor[0]; shaderParams.offset.blit[1] += params.rotatedOffsetFactor[1]; } break; default: UNREACHABLE(); break; } shaderParams.rotateXY = IsRotatedAspectRatio(params.rotation); // Linear sampling is only valid with color blitting. ASSERT(!params.linear); uint32_t flags = src->getLayerCount() > 1 ? BlitResolveStencilNoExport_comp::kSrcIsArray : 0; flags |= isResolve ? BlitResolve_frag::kIsResolve : 0; RenderTargetVk *depthStencilRenderTarget = framebuffer->getDepthStencilRenderTarget(); ASSERT(depthStencilRenderTarget != nullptr); vk::ImageHelper *depthStencilImage = &depthStencilRenderTarget->getImageForWrite(); // Change layouts prior to computation. vk::CommandBufferAccess access; access.onImageComputeShaderRead(src->getAspectFlags(), src); access.onImageTransferWrite(depthStencilRenderTarget->getLevelIndex(), 1, depthStencilRenderTarget->getLayerIndex(), 1, depthStencilImage->getAspectFlags(), depthStencilImage); access.onBufferComputeShaderWrite(&blitBuffer.get()); VkDescriptorSet descriptorSet; vk::OutsideRenderPassCommandBufferHelper *commandBufferHelper; vk::OutsideRenderPassCommandBuffer *commandBuffer; ANGLE_TRY(contextVk->getOutsideRenderPassCommandBufferHelper(access, &commandBufferHelper)); commandBuffer = &commandBufferHelper->getCommandBuffer(); ANGLE_TRY(allocateDescriptorSet(contextVk, commandBufferHelper, Function::BlitResolveStencilNoExport, &descriptorSet)); // Blit/resolve stencil into the buffer. VkDescriptorImageInfo imageInfo = {}; imageInfo.imageView = srcStencilView->getHandle(); imageInfo.imageLayout = src->getCurrentLayout(renderer); VkDescriptorBufferInfo bufferInfo = {}; bufferInfo.buffer = blitBuffer.get().getBuffer().getHandle(); bufferInfo.offset = blitBuffer.get().getOffset(); bufferInfo.range = blitBuffer.get().getSize(); VkDescriptorImageInfo samplerInfo = {}; samplerInfo.sampler = params.linear ? mLinearSampler.getHandle() : mPointSampler.getHandle(); VkWriteDescriptorSet writeInfos[3] = {}; writeInfos[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfos[0].dstSet = descriptorSet; writeInfos[0].dstBinding = kBlitResolveStencilNoExportDestBinding; writeInfos[0].descriptorCount = 1; writeInfos[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; writeInfos[0].pBufferInfo = &bufferInfo; writeInfos[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfos[1].dstSet = descriptorSet; writeInfos[1].dstBinding = kBlitResolveStencilNoExportSrcBinding; writeInfos[1].descriptorCount = 1; writeInfos[1].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; writeInfos[1].pImageInfo = &imageInfo; writeInfos[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfos[2].dstSet = descriptorSet; writeInfos[2].dstBinding = kBlitResolveStencilNoExportSamplerBinding; writeInfos[2].descriptorCount = 1; writeInfos[2].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; writeInfos[2].pImageInfo = &samplerInfo; vkUpdateDescriptorSets(contextVk->getDevice(), 3, writeInfos, 0, nullptr); vk::ShaderModulePtr shader; ANGLE_TRY(contextVk->getShaderLibrary().getBlitResolveStencilNoExport_comp(contextVk, flags, &shader)); ANGLE_TRY(setupComputeProgram(contextVk, Function::BlitResolveStencilNoExport, shader, &mBlitResolveStencilNoExport[flags], descriptorSet, &shaderParams, sizeof(shaderParams), commandBufferHelper)); commandBuffer->dispatch(UnsignedCeilDivide(bufferRowLengthInUints, 8), UnsignedCeilDivide(params.blitArea.height, 8), 1); // Add a barrier prior to copy. VkMemoryBarrier memoryBarrier = {}; memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; commandBuffer->memoryBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, memoryBarrier); // Copy the resulting buffer into dst. VkBufferImageCopy region = {}; region.bufferOffset = blitBuffer.get().getOffset(); region.bufferRowLength = bufferRowLengthInUints * sizeof(uint32_t); region.bufferImageHeight = params.blitArea.height; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; region.imageSubresource.mipLevel = depthStencilImage->toVkLevel(depthStencilRenderTarget->getLevelIndex()).get(); region.imageSubresource.baseArrayLayer = depthStencilRenderTarget->getLayerIndex(); region.imageSubresource.layerCount = 1; region.imageOffset.x = params.blitArea.x; region.imageOffset.y = params.blitArea.y; region.imageOffset.z = 0; region.imageExtent.width = params.blitArea.width; region.imageExtent.height = params.blitArea.height; region.imageExtent.depth = 1; commandBuffer->copyBufferToImage(blitBuffer.get().getBuffer().getHandle(), depthStencilImage->getImage(), depthStencilImage->getCurrentLayout(renderer), 1, ®ion); return angle::Result::Continue; } angle::Result UtilsVk::copyImage(ContextVk *contextVk, vk::ImageHelper *dst, const vk::ImageView *destView, vk::ImageHelper *src, const vk::ImageView *srcView, const CopyImageParameters ¶ms) { vk::Renderer *renderer = contextVk->getRenderer(); // The views passed to this function are already retained, so a render pass cannot be already // open. Otherwise, this function closes the render pass, which may incur a vkQueueSubmit and // then the views are used in a new command buffer without having been retained for it. // http://crbug.com/1272266#c22 ASSERT(!contextVk->hasActiveRenderPass()); const angle::Format &srcIntendedFormat = src->getIntendedFormat(); const angle::Format &dstIntendedFormat = dst->getIntendedFormat(); const bool isYUV = src->getYcbcrConversionDesc().valid(); const bool isSrcMultisampled = params.srcSampleCount > 1; vk::SamplerDesc samplerDesc; if (isYUV) { samplerDesc = vk::SamplerDesc(contextVk, gl::SamplerState(), false, &src->getYcbcrConversionDesc(), srcIntendedFormat.id); ANGLE_TRY(ensureImageCopyResourcesInitializedWithSampler(contextVk, samplerDesc)); } else { ANGLE_TRY(ensureImageCopyResourcesInitialized(contextVk)); } ImageCopyShaderParams shaderParams; shaderParams.flipX = 0; shaderParams.flipY = params.srcFlipY || params.dstFlipY; shaderParams.premultiplyAlpha = params.srcPremultiplyAlpha; shaderParams.unmultiplyAlpha = params.srcUnmultiplyAlpha; shaderParams.dstHasLuminance = dstIntendedFormat.luminanceBits > 0; shaderParams.dstIsAlpha = dstIntendedFormat.isLUMA() && dstIntendedFormat.alphaBits > 0; shaderParams.dstDefaultChannelsMask = GetFormatDefaultChannelMask(dst->getIntendedFormat(), dst->getActualFormat()); shaderParams.srcMip = params.srcMip; shaderParams.srcLayer = params.srcLayer; shaderParams.srcSampleCount = params.srcSampleCount; shaderParams.srcOffset[0] = params.srcOffset[0]; shaderParams.srcOffset[1] = params.srcOffset[1]; shaderParams.dstOffset[0] = params.dstOffset[0]; shaderParams.dstOffset[1] = params.dstOffset[1]; shaderParams.rotateXY = 0; shaderParams.srcIsSRGB = params.srcColorEncoding == GL_SRGB; shaderParams.dstIsSRGB = params.dstColorEncoding == GL_SRGB; // If both src and dst are sRGB, and there is no alpha multiplication/division necessary, then // the shader can work with sRGB data and pretend they are linear. if (shaderParams.srcIsSRGB && shaderParams.dstIsSRGB && !shaderParams.premultiplyAlpha && !shaderParams.unmultiplyAlpha) { shaderParams.srcIsSRGB = false; shaderParams.dstIsSRGB = false; } ASSERT(!(params.srcFlipY && params.dstFlipY)); if (params.srcFlipY) { // If viewport is flipped, the shader expects srcOffset[1] to have the // last row's index instead of the first's. shaderParams.srcOffset[1] = params.srcHeight - params.srcOffset[1] - 1; } else if (params.dstFlipY) { // If image is flipped during copy, the shader uses the same code path as above, // with srcOffset being set to the last row's index instead of the first's. shaderParams.srcOffset[1] = params.srcOffset[1] + params.srcExtents[1] - 1; } switch (params.srcRotation) { case SurfaceRotation::Identity: break; case SurfaceRotation::Rotated90Degrees: shaderParams.rotateXY = 1; break; case SurfaceRotation::Rotated180Degrees: shaderParams.flipX = true; ASSERT(shaderParams.flipY); shaderParams.flipY = false; shaderParams.srcOffset[0] += params.srcExtents[0]; shaderParams.srcOffset[1] -= params.srcExtents[1]; break; case SurfaceRotation::Rotated270Degrees: shaderParams.flipX = true; ASSERT(!shaderParams.flipY); shaderParams.flipY = true; shaderParams.srcOffset[0] += params.srcExtents[0]; shaderParams.srcOffset[1] += params.srcExtents[1]; shaderParams.rotateXY = 1; break; default: UNREACHABLE(); break; } vk::RenderPassDesc renderPassDesc; renderPassDesc.setSamples(dst->getSamples()); renderPassDesc.packColorAttachment(0, dst->getActualFormatID()); vk::GraphicsPipelineDesc pipelineDesc; pipelineDesc.initDefaults(contextVk, vk::GraphicsPipelineSubset::Complete, contextVk->pipelineRobustness(), contextVk->pipelineProtectedAccess()); pipelineDesc.setRenderPassDesc(renderPassDesc); pipelineDesc.setRasterizationSamples(dst->getSamples()); gl::Rectangle renderArea; renderArea.x = params.dstOffset[0]; renderArea.y = params.dstOffset[1]; renderArea.width = params.srcExtents[0]; renderArea.height = params.srcExtents[1]; if ((params.srcRotation == SurfaceRotation::Rotated90Degrees) || (params.srcRotation == SurfaceRotation::Rotated270Degrees)) { // The surface is rotated 90/270 degrees. This changes the aspect ratio of the surface. std::swap(renderArea.width, renderArea.height); } vk::RenderPassCommandBuffer *commandBuffer; ANGLE_TRY(startRenderPass(contextVk, dst, destView, renderPassDesc, renderArea, VK_IMAGE_ASPECT_COLOR_BIT, nullptr, vk::RenderPassSource::InternalUtils, &commandBuffer)); VkDescriptorSet descriptorSet; if (isYUV) { ANGLE_TRY(allocateDescriptorSetForImageCopyWithSampler( contextVk, &contextVk->getStartedRenderPassCommands(), samplerDesc, &descriptorSet)); } else { ANGLE_TRY(allocateDescriptorSet(contextVk, &contextVk->getStartedRenderPassCommands(), Function::ImageCopy, &descriptorSet)); } UpdateColorAccess(contextVk, MakeColorBufferMask(0), MakeColorBufferMask(0)); // Change source layout inside render pass. contextVk->onImageRenderPassRead(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::FragmentShaderReadOnly, src); contextVk->onImageRenderPassWrite(params.dstMip, params.dstLayer, 1, VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::ColorWrite, dst); VkDescriptorImageInfo imageInfo = {}; imageInfo.imageView = srcView->getHandle(); imageInfo.imageLayout = src->getCurrentLayout(renderer); VkWriteDescriptorSet writeInfo = {}; writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfo.dstSet = descriptorSet; writeInfo.dstBinding = kImageCopySourceBinding; writeInfo.descriptorCount = 1; writeInfo.descriptorType = isYUV ? VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER : VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; writeInfo.pImageInfo = &imageInfo; vkUpdateDescriptorSets(contextVk->getDevice(), 1, &writeInfo, 0, nullptr); vk::ShaderLibrary &shaderLibrary = contextVk->getShaderLibrary(); vk::ShaderModulePtr vertexShader; vk::ShaderModulePtr fragmentShader; ANGLE_TRY(shaderLibrary.getFullScreenTri_vert(contextVk, 0, &vertexShader)); if (isYUV) { ASSERT(src->getType() == VK_IMAGE_TYPE_2D); ANGLE_TRY(shaderLibrary.getImageCopyFloat_frag(contextVk, ImageCopyFloat_frag::kSrcIsYUV, &fragmentShader)); ANGLE_TRY(setupGraphicsProgramWithLayout( contextVk, *mImageCopyWithSamplerPipelineLayouts[samplerDesc], vertexShader, fragmentShader, &mImageCopyWithSampler[samplerDesc], &pipelineDesc, descriptorSet, &shaderParams, sizeof(shaderParams), commandBuffer)); } else if (isSrcMultisampled) { ANGLE_TRY(shaderLibrary.getImageCopyFloat_frag(contextVk, ImageCopyFloat_frag::kSrcIs2DMS, &fragmentShader)); ANGLE_TRY(setupGraphicsProgram(contextVk, Function::ImageCopy, vertexShader, fragmentShader, &mImageCopyFloat, &pipelineDesc, descriptorSet, &shaderParams, sizeof(shaderParams), commandBuffer)); } else { uint32_t flags = GetImageCopyFlags(srcIntendedFormat, dstIntendedFormat); if (src->getType() == VK_IMAGE_TYPE_3D) { flags |= ImageCopy_frag::kSrcIs3D; } else if (src->getLayerCount() > 1) { flags |= ImageCopy_frag::kSrcIs2DArray; } else { ASSERT(src->getType() == VK_IMAGE_TYPE_2D); flags |= ImageCopy_frag::kSrcIs2D; } ANGLE_TRY(shaderLibrary.getImageCopy_frag(contextVk, flags, &fragmentShader)); ANGLE_TRY(setupGraphicsProgram(contextVk, Function::ImageCopy, vertexShader, fragmentShader, &mImageCopy[flags], &pipelineDesc, descriptorSet, &shaderParams, sizeof(shaderParams), commandBuffer)); } // Set dynamic state VkViewport viewport; gl_vk::GetViewport(renderArea, 0.0f, 1.0f, false, false, dst->getExtents().height, &viewport); commandBuffer->setViewport(0, 1, &viewport); VkRect2D scissor = gl_vk::GetRect(renderArea); commandBuffer->setScissor(0, 1, &scissor); SetDepthDynamicStateForUnused(renderer, commandBuffer); SetStencilDynamicStateForUnused(renderer, commandBuffer); // Note: this utility creates its own framebuffer, thus bypassing ContextVk::startRenderPass. // As such, occlusion queries are not enabled. commandBuffer->draw(3, 0); // Close the render pass for this temporary framebuffer. return contextVk->flushCommandsAndEndRenderPass(RenderPassClosureReason::TemporaryForImageCopy); } angle::Result UtilsVk::copyImageBits(ContextVk *contextVk, vk::ImageHelper *dst, vk::ImageHelper *src, const CopyImageBitsParameters ¶ms) { vk::Renderer *renderer = contextVk->getRenderer(); // This function is used to copy the bit representation of an image to another, and is used to // support EXT_copy_image when a format is emulated. Currently, only RGB->RGBA emulation is // possible, and so this function is tailored to this specific kind of emulation. // // The copy can be done with various degrees of efficiency: // // - If the UINT reinterpretation format for src supports SAMPLED usage, texels can be read // directly from that. Otherwise vkCmdCopyImageToBuffer can be used and data then read from // the buffer. // - If the UINT reinterpretation format for dst supports STORAGE usage, texels can be written // directly to that. Otherwise conversion can be done to a buffer and then // vkCmdCopyBufferToImage used. // // This requires four different shaders. For simplicity, this function unconditionally copies // src to a temp buffer, transforms to another temp buffer and copies to the dst. No known // applications use EXT_copy_image on RGB formats, so no further optimization is currently // necessary. // // The conversion between buffers can be done with ConvertVertex.comp in UintToUint mode, so no // new shader is necessary. The srcEmulatedAlpha parameter is used to make sure the destination // alpha value is correct, if dst is RGBA. // This path should only be necessary for when RGBA is used as fallback for RGB. No other // format which can be used with EXT_copy_image has a fallback. ASSERT(src->getIntendedFormat().blueBits > 0 && src->getIntendedFormat().alphaBits == 0); ASSERT(dst->getIntendedFormat().blueBits > 0 && dst->getIntendedFormat().alphaBits == 0); const angle::Format &srcImageFormat = src->getActualFormat(); const angle::Format &dstImageFormat = dst->getActualFormat(); // Create temporary buffers. vk::RendererScoped srcBuffer(renderer); vk::RendererScoped dstBuffer(renderer); const uint32_t srcPixelBytes = srcImageFormat.pixelBytes; const uint32_t dstPixelBytes = dstImageFormat.pixelBytes; const uint32_t totalPixelCount = params.copyExtents[0] * params.copyExtents[1] * params.copyExtents[2]; // Note that buffer sizes are rounded up a multiple of uint size, as that the granularity in // which the compute shader accesses these buffers. const VkDeviceSize srcBufferSize = roundUpPow2(srcPixelBytes * totalPixelCount, sizeof(uint32_t)); const VkDeviceSize dstBufferSize = roundUpPow2(dstPixelBytes * totalPixelCount, sizeof(uint32_t)); VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.flags = 0; bufferInfo.size = srcBufferSize; bufferInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; bufferInfo.queueFamilyIndexCount = 0; bufferInfo.pQueueFamilyIndices = nullptr; ANGLE_TRY(srcBuffer.get().init(contextVk, bufferInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); bufferInfo.size = dstBufferSize; bufferInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT; ANGLE_TRY(dstBuffer.get().init(contextVk, bufferInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); bool isSrc3D = src->getType() == VK_IMAGE_TYPE_3D; bool isDst3D = dst->getType() == VK_IMAGE_TYPE_3D; // Change layouts prior to computation. vk::CommandBufferAccess access; access.onImageTransferRead(src->getAspectFlags(), src); access.onImageTransferWrite(params.dstLevel, 1, isDst3D ? 0 : params.dstOffset[2], isDst3D ? 1 : params.copyExtents[2], VK_IMAGE_ASPECT_COLOR_BIT, dst); // srcBuffer is the destination of copyImageToBuffer() below. access.onBufferTransferWrite(&srcBuffer.get()); access.onBufferComputeShaderWrite(&dstBuffer.get()); vk::OutsideRenderPassCommandBufferHelper *commandBufferHelper; vk::OutsideRenderPassCommandBuffer *commandBuffer; ANGLE_TRY(contextVk->getOutsideRenderPassCommandBufferHelper(access, &commandBufferHelper)); commandBuffer = &commandBufferHelper->getCommandBuffer(); // Copy src into buffer, completely packed. VkBufferImageCopy srcRegion = {}; srcRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; srcRegion.imageSubresource.mipLevel = src->toVkLevel(params.srcLevel).get(); srcRegion.imageSubresource.baseArrayLayer = isSrc3D ? 0 : params.srcOffset[2]; srcRegion.imageSubresource.layerCount = isSrc3D ? 1 : params.copyExtents[2]; srcRegion.imageOffset.x = params.srcOffset[0]; srcRegion.imageOffset.y = params.srcOffset[1]; srcRegion.imageOffset.z = isSrc3D ? params.srcOffset[2] : 0; srcRegion.imageExtent.width = params.copyExtents[0]; srcRegion.imageExtent.height = params.copyExtents[1]; srcRegion.imageExtent.depth = isSrc3D ? params.copyExtents[2] : 1; commandBuffer->copyImageToBuffer(src->getImage(), src->getCurrentLayout(renderer), srcBuffer.get().getBuffer().getHandle(), 1, &srcRegion); // Add a barrier prior to dispatch call. VkMemoryBarrier memoryBarrier = {}; memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; commandBuffer->memoryBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, memoryBarrier); // Set up ConvertVertex shader to convert between the formats. Only the following three cases // are possible: // // - RGB -> RGBA: Ns = 3, Ss = src.pixelBytes, // Nd = 4, Sd = dst.pixelBytes, use srcEmulatedAlpha // // - RGBA -> RGBA: Ns = 3, Ss = src.pixelBytes, // Nd = 4, Sd = dst.pixelBytes, use srcEmulatedAlpha // // - RGBA -> RGB: Ns = 3, Ss = src.pixelBytes, // Nd = 3, Sd = dst.pixelBytes // // The trick here is with RGBA -> RGBA, where Ns is specified as 3, so that the emulated alpha // from source is not taken (as uint), but rather one is provided such that the destination // alpha would contain the correct emulated alpha. // ConvertVertexShaderParams shaderParams; shaderParams.Ns = 3; shaderParams.Bs = srcImageFormat.pixelBytes / srcImageFormat.channelCount; shaderParams.Ss = srcImageFormat.pixelBytes; shaderParams.Nd = dstImageFormat.channelCount; shaderParams.Bd = dstImageFormat.pixelBytes / dstImageFormat.channelCount; shaderParams.Sd = shaderParams.Nd * shaderParams.Bd; // The component size is expected to either be 1, 2 or 4 bytes. ASSERT(4 % shaderParams.Bs == 0); ASSERT(4 % shaderParams.Bd == 0); shaderParams.Es = 4 / shaderParams.Bs; shaderParams.Ed = 4 / shaderParams.Bd; // Total number of output components is simply the number of pixels by number of components in // each. shaderParams.componentCount = totalPixelCount * shaderParams.Nd; // Total number of 4-byte outputs is the number of components divided by how many components can // fit in a 4-byte value. Note that this value is also the invocation size of the shader. shaderParams.outputCount = UnsignedCeilDivide(shaderParams.componentCount, shaderParams.Ed); shaderParams.srcOffset = 0; shaderParams.dstOffset = 0; shaderParams.isSrcHDR = 0; shaderParams.isSrcA2BGR10 = 0; // Due to the requirements of EXT_copy_image, the channel size of src and dst must be // identical. Usage of srcEmulatedAlpha relies on this as it's used to output an alpha value in // dst through the source. ASSERT(shaderParams.Bs == shaderParams.Bd); // The following RGB formats are allowed in EXT_copy_image: // // - RGB32F, RGB32UI, RGB32I // - RGB16F, RGB16UI, RGB16I // - RGB8, RGB8_SNORM, SRGB8, RGB8UI, RGB8I // // The value of emulated alpha is: // // - 1 for all RGB*I and RGB*UI formats // - bit representation of 1.0f for RGB32F // - bit representation of half-float 1.0f for RGB16F // - 0xFF for RGB8 and SRGB8 // - 0x7F for RGB8_SNORM if (dstImageFormat.isInt()) { shaderParams.srcEmulatedAlpha = 1; } else if (dstImageFormat.isUnorm()) { ASSERT(shaderParams.Bd == 1); shaderParams.srcEmulatedAlpha = 0xFF; } else if (dstImageFormat.isSnorm()) { ASSERT(shaderParams.Bd == 1); shaderParams.srcEmulatedAlpha = 0x7F; } else if (shaderParams.Bd == 2) { ASSERT(dstImageFormat.isFloat()); shaderParams.srcEmulatedAlpha = gl::Float16One; } else if (shaderParams.Bd == 4) { ASSERT(dstImageFormat.isFloat()); ASSERT(ValidateFloatOneAsUint()); shaderParams.srcEmulatedAlpha = gl::Float32One; } else { UNREACHABLE(); } // Use UintToUint conversion to preserve the bit pattern during transfer. const uint32_t flags = ConvertVertex_comp::kUintToUint; ANGLE_TRY(convertVertexBufferImpl(contextVk, &dstBuffer.get(), &srcBuffer.get(), flags, commandBufferHelper, shaderParams, {})); // Add a barrier prior to copy. memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; commandBuffer->memoryBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, memoryBarrier); // Copy buffer into dst. It's completely packed. VkBufferImageCopy dstRegion = {}; dstRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; dstRegion.imageSubresource.mipLevel = dst->toVkLevel(params.dstLevel).get(); dstRegion.imageSubresource.baseArrayLayer = isDst3D ? 0 : params.dstOffset[2]; dstRegion.imageSubresource.layerCount = isDst3D ? 1 : params.copyExtents[2]; dstRegion.imageOffset.x = params.dstOffset[0]; dstRegion.imageOffset.y = params.dstOffset[1]; dstRegion.imageOffset.z = isDst3D ? params.dstOffset[2] : 0; dstRegion.imageExtent.width = params.copyExtents[0]; dstRegion.imageExtent.height = params.copyExtents[1]; dstRegion.imageExtent.depth = isDst3D ? params.copyExtents[2] : 1; commandBuffer->copyBufferToImage(dstBuffer.get().getBuffer().getHandle(), dst->getImage(), dst->getCurrentLayout(renderer), 1, &dstRegion); return angle::Result::Continue; } angle::Result UtilsVk::copyImageToBuffer(ContextVk *contextVk, vk::BufferHelper *dst, vk::ImageHelper *src, const CopyImageToBufferParameters ¶ms) { vk::Renderer *renderer = contextVk->getRenderer(); ANGLE_TRY(ensureCopyImageToBufferResourcesInitialized(contextVk)); const angle::Format &srcFormat = src->getActualFormat(); ASSERT(params.outputOffset % sizeof(uint32_t) == 0); ASSERT(params.outputPitch % sizeof(uint32_t) == 0); CopyImageToBufferShaderParams shaderParams; shaderParams.srcOffset[0] = params.srcOffset[0]; shaderParams.srcOffset[1] = params.srcOffset[1]; shaderParams.srcDepth = params.srcLayer; shaderParams.reverseRowOrder = params.reverseRowOrder; shaderParams.size[0] = params.size[0]; shaderParams.size[1] = params.size[1]; shaderParams.outputOffset = static_cast(params.outputOffset / sizeof(uint32_t)); shaderParams.outputPitch = params.outputPitch / sizeof(uint32_t); shaderParams.isDstSnorm = params.outputFormat->isSnorm(); gl::SwizzleState swizzle; if (params.outputFormat->isBGRA()) { swizzle.swizzleRed = GL_BLUE; swizzle.swizzleBlue = GL_RED; } uint32_t flags = GetCopyImageToBufferFlags(srcFormat); gl::TextureType textureType; if (src->getType() == VK_IMAGE_TYPE_3D) { flags |= CopyImageToBuffer_comp::kSrcIs3D; textureType = gl::TextureType::_3D; } else { flags |= CopyImageToBuffer_comp::kSrcIs2D; textureType = gl::TextureType::_2D; } // Don't decode to linear colorspace when copying an image angle::FormatID imageFormat = src->getActualFormatID(); angle::FormatID linearFormat = src->getActualFormat().isSRGB ? ConvertToLinear(imageFormat) : imageFormat; ASSERT(linearFormat != angle::FormatID::NONE); vk::DeviceScoped srcView(contextVk->getDevice()); ANGLE_TRY(src->initReinterpretedLayerImageView( contextVk, textureType, src->getAspectFlags(), swizzle, &srcView.get(), params.srcMip, 1, textureType == gl::TextureType::_2D ? params.srcLayer : 0, 1, VK_IMAGE_USAGE_SAMPLED_BIT, linearFormat)); vk::CommandBufferAccess access; access.onImageComputeShaderRead(src->getAspectFlags(), src); access.onBufferComputeShaderWrite(dst); vk::OutsideRenderPassCommandBufferHelper *commandBufferHelper; ANGLE_TRY(contextVk->getOutsideRenderPassCommandBufferHelper(access, &commandBufferHelper)); vk::OutsideRenderPassCommandBuffer *commandBuffer; commandBuffer = &commandBufferHelper->getCommandBuffer(); VkDescriptorSet descriptorSet; ANGLE_TRY(allocateDescriptorSet(contextVk, commandBufferHelper, Function::CopyImageToBuffer, &descriptorSet)); VkDescriptorImageInfo imageInfo = {}; imageInfo.imageView = srcView.get().getHandle(); imageInfo.imageLayout = src->getCurrentLayout(renderer); VkDescriptorBufferInfo bufferInfo = {}; bufferInfo.buffer = dst->getBuffer().getHandle(); bufferInfo.offset = dst->getOffset(); bufferInfo.range = dst->getSize(); VkWriteDescriptorSet writeInfo[2] = {}; writeInfo[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfo[0].dstSet = descriptorSet; writeInfo[0].dstBinding = kCopyImageToBufferSourceBinding; writeInfo[0].descriptorCount = 1; writeInfo[0].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; writeInfo[0].pImageInfo = &imageInfo; writeInfo[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfo[1].dstSet = descriptorSet; writeInfo[1].dstBinding = kCopyImageToBufferDestinationBinding; writeInfo[1].descriptorCount = 1; writeInfo[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; writeInfo[1].pBufferInfo = &bufferInfo; vkUpdateDescriptorSets(contextVk->getDevice(), 2, writeInfo, 0, nullptr); vk::ShaderModulePtr shader; ANGLE_TRY(contextVk->getShaderLibrary().getCopyImageToBuffer_comp(contextVk, flags, &shader)); ANGLE_TRY(setupComputeProgram(contextVk, Function::CopyImageToBuffer, shader, &mCopyImageToBuffer[flags], descriptorSet, &shaderParams, sizeof(shaderParams), commandBufferHelper)); commandBuffer->dispatch(UnsignedCeilDivide(params.size[0], 8), UnsignedCeilDivide(params.size[1], 8), 1); vk::ImageView srcViewObject = srcView.release(); contextVk->addGarbage(&srcViewObject); return angle::Result::Continue; } angle::Result UtilsVk::copyRgbToRgba(ContextVk *contextVk, const angle::Format &srcFormat, vk::BufferHelper *srcBuffer, uint32_t srcOffset, uint32_t pixelCount, vk::BufferHelper *dstBuffer) { vk::OutsideRenderPassCommandBufferHelper *commandBufferHelper; vk::CommandBufferAccess access; access.onBufferComputeShaderRead(srcBuffer); access.onBufferComputeShaderWrite(dstBuffer); ANGLE_TRY(contextVk->getOutsideRenderPassCommandBufferHelper(access, &commandBufferHelper)); rx::UtilsVk::ConvertVertexShaderParams shaderParams; shaderParams.Ns = 3; // src channels shaderParams.Bs = 4; // src bytes per channel shaderParams.Ss = 12; // src stride shaderParams.Nd = 4; // dest channels shaderParams.Bd = 4; // dest bytes per channel shaderParams.Sd = 16; // dest stride shaderParams.Es = 4 / shaderParams.Bs; shaderParams.Ed = 4 / shaderParams.Bd; // Total number of output components is simply the number of pixels by number of components in // each. shaderParams.componentCount = pixelCount * shaderParams.Nd; // Total number of 4-byte outputs is the number of components divided by how many components can // fit in a 4-byte value. Note that this value is also the invocation size of the shader. shaderParams.outputCount = UnsignedCeilDivide(shaderParams.componentCount, shaderParams.Ed); shaderParams.srcOffset = srcOffset; shaderParams.dstOffset = 0; shaderParams.isSrcHDR = 0; shaderParams.isSrcA2BGR10 = 0; uint32_t flags = 0; switch (srcFormat.id) { case angle::FormatID::R32G32B32_UINT: flags = ConvertVertex_comp::kUintToUint; shaderParams.srcEmulatedAlpha = 1; break; case angle::FormatID::R32G32B32_SINT: flags = ConvertVertex_comp::kSintToSint; shaderParams.srcEmulatedAlpha = 1; break; case angle::FormatID::R32G32B32_FLOAT: flags = ConvertVertex_comp::kFloatToFloat; shaderParams.srcEmulatedAlpha = gl::Float32One; break; default: UNREACHABLE(); } return convertVertexBufferImpl(contextVk, dstBuffer, srcBuffer, flags, commandBufferHelper, shaderParams, {}); } uint32_t GetEtcToBcFlags(const angle::Format &format) { switch (format.id) { case angle::FormatID::ETC1_R8G8B8_UNORM_BLOCK: case angle::FormatID::ETC2_R8G8B8_UNORM_BLOCK: case angle::FormatID::ETC2_R8G8B8_SRGB_BLOCK: case angle::FormatID::ETC2_R8G8B8A1_SRGB_BLOCK: case angle::FormatID::ETC2_R8G8B8A1_UNORM_BLOCK: case angle::FormatID::ETC2_R8G8B8A8_UNORM_BLOCK: case angle::FormatID::ETC2_R8G8B8A8_SRGB_BLOCK: case angle::FormatID::ETC1_LOSSY_DECODE_R8G8B8_UNORM_BLOCK: return EtcToBc_comp::kEtcRgba8ToBC3; case angle::FormatID::EAC_R11_SNORM_BLOCK: case angle::FormatID::EAC_R11_UNORM_BLOCK: case angle::FormatID::EAC_R11G11_SNORM_BLOCK: case angle::FormatID::EAC_R11G11_UNORM_BLOCK: return EtcToBc_comp::kEtcRg11ToBC5; default: UNREACHABLE(); return EtcToBc_comp::kEtcRgba8ToBC3; } } angle::FormatID GetCompactibleUINTFormat(const angle::Format &format) { ASSERT(format.pixelBytes == 8 || format.pixelBytes == 16); return format.pixelBytes != 8 ? angle::FormatID::R32G32B32A32_UINT : angle::FormatID::R32G32_UINT; } angle::Result UtilsVk::transCodeEtcToBc(ContextVk *contextVk, vk::BufferHelper *srcBuffer, vk::ImageHelper *dstImage, const VkBufferImageCopy *copyRegion) { ANGLE_TRY(ensureTransCodeEtcToBcResourcesInitialized(contextVk)); vk::Renderer *renderer = contextVk->getRenderer(); const angle::Format &intendedFormat = dstImage->getIntendedFormat(); vk::ContextScoped bufferViewHelper(contextVk); const gl::InternalFormat &info = gl::GetSizedInternalFormatInfo(intendedFormat.glInternalFormat); // According to GLES spec. Etc texture don't support 3D texture type. ASSERT(copyRegion->bufferRowLength % info.compressedBlockWidth == 0 && copyRegion->bufferImageHeight % info.compressedBlockHeight == 0 && copyRegion->imageExtent.depth == 1); ASSERT(dstImage->getType() != VK_IMAGE_TYPE_1D && dstImage->getType() != VK_IMAGE_TYPE_3D); GLuint sliceTexels = (copyRegion->bufferRowLength / info.compressedBlockWidth) * (copyRegion->bufferImageHeight / info.compressedBlockHeight); GLuint sliceSize = sliceTexels * intendedFormat.pixelBytes; GLuint texBufferSize = sliceSize * copyRegion->imageSubresource.layerCount; // Make sure the texture buffer size not out of limit. // Usually the limit is more than 128M. ASSERT( texBufferSize < static_cast(renderer->getPhysicalDeviceProperties().limits.maxTexelBufferElements)); const vk::BufferView *srcBufferView = nullptr; bufferViewHelper.get().init(renderer, 0, texBufferSize); ANGLE_TRY(bufferViewHelper.get().getView( contextVk, *srcBuffer, copyRegion->bufferOffset, renderer->getFormat(GetCompactibleUINTFormat(intendedFormat)), &srcBufferView)); vk::LevelIndex dstLevel = gl::LevelIndexWrapper(copyRegion->imageSubresource.mipLevel); vk::OutsideRenderPassCommandBufferHelper *commandBufferHelper; ANGLE_TRY(contextVk->getOutsideRenderPassCommandBufferHelper({}, &commandBufferHelper)); const angle::Format &format = dstImage->getIntendedFormat(); uint32_t flags = GetEtcToBcFlags(format); vk::ShaderModulePtr shader; ANGLE_TRY(contextVk->getShaderLibrary().getEtcToBc_comp(contextVk, flags, &shader)); vk::OutsideRenderPassCommandBuffer *commandBuffer; commandBuffer = &commandBufferHelper->getCommandBuffer(); // For BC format, shader need width and height to be multiple of four. uint32_t width = rx::roundUpPow2(copyRegion->imageExtent.width, 4u); uint32_t height = rx::roundUpPow2(copyRegion->imageExtent.height, 4u); // push constants data EtcToBcShaderParams shaderParams = {}; shaderParams.offsetX = static_cast(copyRegion->imageOffset.x); shaderParams.offsetY = static_cast(copyRegion->imageOffset.y); shaderParams.texelOffset = 0; shaderParams.width = width; shaderParams.height = height; shaderParams.alphaBits = format.alphaBits; shaderParams.isSigned = format.isSnorm(); shaderParams.isEacRg = format.channelCount == 2; // EAC_RG11 VkBufferView bufferView = srcBufferView->getHandle(); VkWriteDescriptorSet writeDescriptorSet[2] = {}; writeDescriptorSet[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSet[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; writeDescriptorSet[0].dstBinding = 0; writeDescriptorSet[0].pBufferInfo = nullptr; writeDescriptorSet[0].descriptorCount = 1; writeDescriptorSet[0].pTexelBufferView = &bufferView; VkDescriptorImageInfo imageInfo = {}; imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; writeDescriptorSet[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSet[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; writeDescriptorSet[1].dstBinding = 1; writeDescriptorSet[1].pImageInfo = &imageInfo; writeDescriptorSet[1].descriptorCount = 1; // Due to limitation VUID-VkImageViewCreateInfo-image-07072, we have to copy layer by layer. for (uint32_t i = 0; i < copyRegion->imageSubresource.layerCount; ++i) { vk::DeviceScoped scopedImageView(contextVk->getDevice()); ANGLE_TRY(dstImage->initReinterpretedLayerImageView( contextVk, gl::TextureType::_2D, VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(), &scopedImageView.get(), dstLevel, 1, copyRegion->imageSubresource.baseArrayLayer + i, 1, VK_IMAGE_USAGE_STORAGE_BIT, GetCompactibleUINTFormat(intendedFormat))); imageInfo.imageView = scopedImageView.get().getHandle(); VkDescriptorSet descriptorSet; ANGLE_TRY(allocateDescriptorSet(contextVk, commandBufferHelper, Function::TransCodeEtcToBc, &descriptorSet)); writeDescriptorSet[0].dstSet = descriptorSet; writeDescriptorSet[1].dstSet = descriptorSet; vkUpdateDescriptorSets(contextVk->getDevice(), 2, writeDescriptorSet, 0, nullptr); ANGLE_TRY(setupComputeProgram(contextVk, Function::TransCodeEtcToBc, shader, &mEtcToBc[flags], descriptorSet, &shaderParams, sizeof(shaderParams), commandBufferHelper)); // Work group size is 8 x 8 x 1 commandBuffer->dispatch(UnsignedCeilDivide(width, 8), UnsignedCeilDivide(height, 8), 1); // Release temporary views vk::ImageView imageView = scopedImageView.release(); contextVk->addGarbage(&imageView); shaderParams.texelOffset += sliceTexels; } // Retain buffer view commandBufferHelper->retainResource(&bufferViewHelper.get()); return angle::Result::Continue; } angle::Result UtilsVk::generateMipmap(ContextVk *contextVk, vk::ImageHelper *src, const vk::ImageView *srcLevelZeroView, vk::ImageHelper *dst, const GenerateMipmapDestLevelViews &destLevelViews, const vk::Sampler &sampler, const GenerateMipmapParameters ¶ms) { vk::Renderer *renderer = contextVk->getRenderer(); ANGLE_TRY(ensureGenerateMipmapResourcesInitialized(contextVk)); const gl::Extents &srcExtents = src->getLevelExtents(vk::LevelIndex(params.srcLevel)); ASSERT(srcExtents.depth == 1); // Each workgroup processes a 64x64 tile of the image. constexpr uint32_t kPixelWorkgroupRatio = 64; const uint32_t workGroupX = UnsignedCeilDivide(srcExtents.width, kPixelWorkgroupRatio); const uint32_t workGroupY = UnsignedCeilDivide(srcExtents.height, kPixelWorkgroupRatio); GenerateMipmapShaderParams shaderParams; shaderParams.invSrcExtent[0] = 1.0f / srcExtents.width; shaderParams.invSrcExtent[1] = 1.0f / srcExtents.height; shaderParams.levelCount = params.dstLevelCount; uint32_t flags = GetGenerateMipmapFlags(contextVk, src->getActualFormat()); vk::OutsideRenderPassCommandBufferHelper *commandBufferHelper; ANGLE_TRY(contextVk->getOutsideRenderPassCommandBufferHelper({}, &commandBufferHelper)); VkDescriptorSet descriptorSet; ANGLE_TRY(allocateDescriptorSet(contextVk, commandBufferHelper, Function::GenerateMipmap, &descriptorSet)); VkDescriptorImageInfo destImageInfos[kGenerateMipmapMaxLevels] = {}; for (uint32_t level = 0; level < kGenerateMipmapMaxLevels; ++level) { destImageInfos[level].imageView = destLevelViews[level]->getHandle(); destImageInfos[level].imageLayout = dst->getCurrentLayout(renderer); } VkDescriptorImageInfo srcImageInfo = {}; srcImageInfo.imageView = srcLevelZeroView->getHandle(); srcImageInfo.imageLayout = src->getCurrentLayout(renderer); srcImageInfo.sampler = sampler.getHandle(); VkWriteDescriptorSet writeInfos[2] = {}; writeInfos[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfos[0].dstSet = descriptorSet; writeInfos[0].dstBinding = kGenerateMipmapDestinationBinding; writeInfos[0].descriptorCount = GetGenerateMipmapMaxLevels(contextVk); writeInfos[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; writeInfos[0].pImageInfo = destImageInfos; writeInfos[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfos[1].dstSet = descriptorSet; writeInfos[1].dstBinding = kGenerateMipmapSourceBinding; writeInfos[1].descriptorCount = 1; writeInfos[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeInfos[1].pImageInfo = &srcImageInfo; vkUpdateDescriptorSets(contextVk->getDevice(), 2, writeInfos, 0, nullptr); vk::ShaderModulePtr shader; ANGLE_TRY(contextVk->getShaderLibrary().getGenerateMipmap_comp(contextVk, flags, &shader)); // Note: onImageRead/onImageWrite is expected to be called by the caller. This avoids inserting // barriers between calls for each layer of the image. vk::OutsideRenderPassCommandBuffer *commandBuffer; commandBuffer = &commandBufferHelper->getCommandBuffer(); ANGLE_TRY(setupComputeProgram(contextVk, Function::GenerateMipmap, shader, &mGenerateMipmap[flags], descriptorSet, &shaderParams, sizeof(shaderParams), commandBufferHelper)); commandBuffer->dispatch(workGroupX, workGroupY, 1); return angle::Result::Continue; } angle::Result UtilsVk::generateMipmapWithDraw(ContextVk *contextVk, vk::ImageHelper *image, const angle::FormatID actualFormatID, const bool isMipmapFiltered) { // This function only supports - // 1. color formats that support color attachment feature // 2. image is of type VK_IMAGE_TYPE_2D and is not MSAA ASSERT(image); ASSERT(image->getType() == VK_IMAGE_TYPE_2D && image->getSamples() == 1); const angle::Format &actualFormat = angle::Format::Get(actualFormatID); ASSERT(!actualFormat.hasDepthOrStencilBits()); // TODO: the following check is not enough; if the image is AHB-imported, then the draw path // cannot be taken if AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER hasn't been specified, even if the // format is renderable. vk::Renderer *renderer = contextVk->getRenderer(); ASSERT(vk::FormatHasNecessaryFeature(renderer, actualFormat.id, image->getTilingMode(), VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)); // Ensure required resources are initialized ANGLE_TRY(ensureBlitResolveResourcesInitialized(contextVk)); uint32_t layerCount = image->getLayerCount(); uint32_t levelCount = image->getLevelCount(); GLint sampleCount = image->getSamples(); gl::LevelIndex baseLevelGL = image->getFirstAllocatedLevel(); vk::LevelIndex baseLevelVK = image->toVkLevel(baseLevelGL); vk::LevelIndex maxLevelVK = baseLevelVK + (levelCount - 1); // Transition entire image to color attachment layout vk::CommandBufferAccess access; access.onImageDrawMipmapGenerationWrite(baseLevelGL, levelCount, 0, layerCount, VK_IMAGE_ASPECT_COLOR_BIT, image); vk::OutsideRenderPassCommandBuffer *outsideCommandBuffer; ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &outsideCommandBuffer)); gl::TextureType textureType = vk::Get2DTextureType(layerCount, sampleCount); gl::SwizzleState swizzle = {}; VkImageUsageFlags imageUsageFlags = image->getUsage(); // Setup shaders for draw uint32_t flags = GetBlitResolveFlags(true, false, false, actualFormat); flags |= layerCount > 1 ? BlitResolve_frag::kSrcIsArray : 0; Function function = Function::BlitResolve; vk::ShaderLibrary &shaderLibrary = contextVk->getShaderLibrary(); vk::ShaderModulePtr vertexShader; vk::ShaderModulePtr fragmentShader; ANGLE_TRY(shaderLibrary.getFullScreenTri_vert(contextVk, 0, &vertexShader)); ANGLE_TRY(shaderLibrary.getBlitResolve_frag(contextVk, flags, &fragmentShader)); // Setup blit shader parameters BlitResolveShaderParams shaderParams; shaderParams.offset.blit[0] = 0.0f; shaderParams.offset.blit[1] = 0.0f; shaderParams.stretch[0] = 1.0f; shaderParams.stretch[1] = 1.0f; shaderParams.samples = 1; shaderParams.invSamples = 1.0f; shaderParams.outputMask = 1; shaderParams.flipX = 0; shaderParams.flipY = 0; shaderParams.rotateXY = 0; // Setup pipeline for draw vk::RenderPassDesc renderPassDesc; renderPassDesc.setSamples(sampleCount); renderPassDesc.packColorAttachment(0, actualFormatID); vk::GraphicsPipelineDesc pipelineDesc; pipelineDesc.initDefaults(contextVk, vk::GraphicsPipelineSubset::Complete, contextVk->pipelineRobustness(), contextVk->pipelineProtectedAccess()); pipelineDesc.setSingleColorWriteMask(0, (VkColorComponentFlagBits::VK_COLOR_COMPONENT_R_BIT | VkColorComponentFlagBits::VK_COLOR_COMPONENT_G_BIT | VkColorComponentFlagBits::VK_COLOR_COMPONENT_B_BIT | VkColorComponentFlagBits::VK_COLOR_COMPONENT_A_BIT)); pipelineDesc.setRasterizationSamples(sampleCount); pipelineDesc.setRenderPassDesc(renderPassDesc); // Setup write descriptors VkDescriptorImageInfo imageInfos = {}; imageInfos.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkDescriptorImageInfo samplerInfo = {}; samplerInfo.sampler = isMipmapFiltered ? mLinearSampler.getHandle() : mPointSampler.getHandle(); VkWriteDescriptorSet writeInfos[2] = {}; writeInfos[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfos[0].dstBinding = kBlitResolveColorOrDepthBinding; writeInfos[0].descriptorCount = 1; writeInfos[0].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; writeInfos[0].pImageInfo = &imageInfos; writeInfos[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfos[1].dstBinding = kBlitResolveSamplerBinding; writeInfos[1].descriptorCount = 1; writeInfos[1].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; writeInfos[1].pImageInfo = &samplerInfo; // Setup for read barrier VkImageMemoryBarrier barrier = {}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.image = image->getImage().getHandle(); barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.layerCount = layerCount; barrier.subresourceRange.baseMipLevel = baseLevelVK.get(); barrier.subresourceRange.levelCount = 1; barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; // Sample from mipLevel N and draw to mipLevel N+1 for (vk::LevelIndex srcLevelVk = baseLevelVK; srcLevelVk < maxLevelVK;) { // Transition "srcLevel" of all layers to shader read only optimal layout outsideCommandBuffer = nullptr; ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer({}, &outsideCommandBuffer)); barrier.subresourceRange.baseMipLevel = srcLevelVk.get(); outsideCommandBuffer->imageBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, barrier); // Update render area vk::LevelIndex dstLevelVk = srcLevelVk + 1; ASSERT(dstLevelVk <= baseLevelVK + levelCount); gl::Extents extents = image->getLevelExtents(dstLevelVk); gl::Rectangle renderArea = gl::Rectangle(0, 0, extents.width, extents.height); VkViewport viewport; gl_vk::GetViewport(renderArea, 0.0f, 1.0f, false, false, renderArea.height, &viewport); VkRect2D scissor = gl_vk::GetRect(renderArea); shaderParams.invSrcExtent[0] = 1.0f / renderArea.width; shaderParams.invSrcExtent[1] = 1.0f / renderArea.height; // mipLevel N --> mipLevel N+1 must be done for each layer for (uint32_t currentLayer = 0; currentLayer < layerCount; currentLayer++) { // Create image views for currentLayer's srcLevelVk and dstLevelVk vk::ImageView srcImageView; ANGLE_TRY(image->initReinterpretedLayerImageView( contextVk, textureType, image->getAspectFlags(), swizzle, &srcImageView, srcLevelVk, 1, currentLayer, 1, imageUsageFlags, actualFormatID)); vk::ImageView dstImageView; ANGLE_TRY(image->initReinterpretedLayerImageView( contextVk, textureType, image->getAspectFlags(), swizzle, &dstImageView, dstLevelVk, 1, currentLayer, 1, imageUsageFlags, actualFormatID)); vk::RenderPassCommandBuffer *commandBuffer = nullptr; ANGLE_TRY(startRenderPass(contextVk, image, &dstImageView, renderPassDesc, renderArea, VK_IMAGE_ASPECT_COLOR_BIT, nullptr, vk::RenderPassSource::InternalUtils, &commandBuffer)); UpdateColorAccess(contextVk, MakeColorBufferMask(0), MakeColorBufferMask(0)); VkDescriptorSet descriptorSet; ANGLE_TRY(allocateDescriptorSet(contextVk, &contextVk->getStartedRenderPassCommands(), Function::BlitResolve, &descriptorSet)); // Update write descriptor info writeInfos[0].dstSet = descriptorSet; writeInfos[1].dstSet = descriptorSet; imageInfos.imageView = srcImageView.getHandle(); vkUpdateDescriptorSets(contextVk->getDevice(), 2, writeInfos, 0, nullptr); // Update layer index and create pipeline shaderParams.srcLayer = currentLayer; ANGLE_TRY(setupGraphicsProgram(contextVk, function, vertexShader, fragmentShader, &mBlitResolve[flags], &pipelineDesc, descriptorSet, &shaderParams, sizeof(shaderParams), commandBuffer)); // Set dynamic state commandBuffer->setViewport(0, 1, &viewport); commandBuffer->setScissor(0, 1, &scissor); SetDepthDynamicStateForUnused(renderer, commandBuffer); SetStencilDynamicStateForUnused(renderer, commandBuffer); // Note: this utility creates its own framebuffer, thus bypassing // ContextVk::startRenderPass. As such, occlusion queries are not enabled. commandBuffer->draw(3, 0); contextVk->addGarbage(&srcImageView); contextVk->addGarbage(&dstImageView); } // Close the render pass for this temporary framebuffer. ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass( RenderPassClosureReason::GenerateMipmapWithDraw)); srcLevelVk = dstLevelVk; } // Transition the last mipLevel to shader read only optimal layout outsideCommandBuffer = nullptr; ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer({}, &outsideCommandBuffer)); barrier.subresourceRange.baseMipLevel = maxLevelVK.get(); outsideCommandBuffer->imageBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, barrier); // Update image's layout related state to shader read only optimal layout image->setCurrentImageLayout(renderer, vk::ImageLayout::FragmentShaderReadOnly); return angle::Result::Continue; } angle::Result UtilsVk::unresolve(ContextVk *contextVk, const FramebufferVk *framebuffer, const UnresolveParameters ¶ms) { vk::Renderer *renderer = contextVk->getRenderer(); // Get attachment count and pointers to resolve images and views. gl::DrawBuffersArray colorSrc = {}; gl::DrawBuffersArray colorSrcView = {}; const vk::ImageView *depthSrcView = nullptr; const vk::ImageView *stencilSrcView = nullptr; // 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.unresolveColorMask) { RenderTargetVk *colorRenderTarget = framebuffer->getColorDrawRenderTarget(colorIndexGL); ASSERT(colorRenderTarget->hasResolveAttachment()); ASSERT(colorRenderTarget->isImageTransient()); colorSrc[colorIndexVk.get()] = &colorRenderTarget->getResolveImageForRenderPass(); ANGLE_TRY( colorRenderTarget->getResolveImageView(contextVk, &colorSrcView[colorIndexVk.get()])); ++colorIndexVk; } if (params.unresolveDepth || params.unresolveStencil) { RenderTargetVk *depthStencilRenderTarget = framebuffer->getDepthStencilRenderTarget(); ASSERT(depthStencilRenderTarget->hasResolveAttachment()); ASSERT(depthStencilRenderTarget->isImageTransient()); if (params.unresolveDepth) { ANGLE_TRY(depthStencilRenderTarget->getResolveDepthOrStencilImageView( contextVk, VK_IMAGE_ASPECT_DEPTH_BIT, &depthSrcView)); } if (params.unresolveStencil) { ANGLE_TRY(depthStencilRenderTarget->getResolveDepthOrStencilImageView( contextVk, VK_IMAGE_ASPECT_STENCIL_BIT, &stencilSrcView)); } } vk::GraphicsPipelineDesc pipelineDesc; pipelineDesc.initDefaults(contextVk, vk::GraphicsPipelineSubset::Complete, contextVk->pipelineRobustness(), contextVk->pipelineProtectedAccess()); pipelineDesc.setRasterizationSamples(framebuffer->getSamples()); pipelineDesc.setRenderPassDesc(framebuffer->getRenderPassDesc()); vk::RenderPassCommandBuffer *commandBuffer = &contextVk->getStartedRenderPassCommands().getCommandBuffer(); vk::ShaderLibrary &shaderLibrary = contextVk->getShaderLibrary(); vk::ShaderModulePtr vertexShader; ANGLE_TRY(shaderLibrary.getFullScreenTri_vert(contextVk, 0, &vertexShader)); // Set dynamic state VkViewport viewport; gl::Rectangle completeRenderArea = framebuffer->getRotatedCompleteRenderArea(contextVk); bool invertViewport = contextVk->isViewportFlipEnabledForDrawFBO(); bool clipSpaceOriginUpperLeft = contextVk->getState().getClipOrigin() == gl::ClipOrigin::UpperLeft; gl_vk::GetViewport(completeRenderArea, 0.0f, 1.0f, invertViewport, clipSpaceOriginUpperLeft, completeRenderArea.height, &viewport); commandBuffer->setViewport(0, 1, &viewport); VkRect2D scissor = gl_vk::GetRect(completeRenderArea); commandBuffer->setScissor(0, 1, &scissor); // When VK_EXT_shader_stencil_export is enabled, the draw call can directly read from the // stencil buffer and export it. When disabled, a special path is taken after the main // unresolve draw call. const bool unresolveStencilWithShaderExport = params.unresolveStencil && contextVk->getFeatures().supportsShaderStencilExport.enabled; const uint32_t colorAttachmentCount = colorIndexVk.get(); const uint32_t depthStencilBindingCount = (params.unresolveDepth ? 1 : 0) + (unresolveStencilWithShaderExport ? 1 : 0); const uint32_t totalBindingCount = colorAttachmentCount + depthStencilBindingCount; if (totalBindingCount > 0) { const Function function = static_cast( static_cast(Function::Unresolve1Attachment) + totalBindingCount - 1); ANGLE_TRY(ensureUnresolveResourcesInitialized(contextVk, function, totalBindingCount)); if (params.unresolveDepth) { SetDepthStateForWrite(renderer, &pipelineDesc); } if (unresolveStencilWithShaderExport) { SetStencilStateForWrite(renderer, &pipelineDesc); } VkDescriptorSet descriptorSet; ANGLE_TRY(allocateDescriptorSet(contextVk, &contextVk->getStartedRenderPassCommands(), function, &descriptorSet)); vk::FramebufferAttachmentArray inputImageInfo = {}; uint32_t inputBindingIndex = 0; if (unresolveStencilWithShaderExport) { inputImageInfo[inputBindingIndex].imageView = stencilSrcView->getHandle(); inputImageInfo[inputBindingIndex].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; ++inputBindingIndex; } if (params.unresolveDepth) { inputImageInfo[inputBindingIndex].imageView = depthSrcView->getHandle(); inputImageInfo[inputBindingIndex].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; ++inputBindingIndex; } for (uint32_t attachmentIndex = 0; attachmentIndex < colorAttachmentCount; ++attachmentIndex) { inputImageInfo[inputBindingIndex].imageView = colorSrcView[attachmentIndex]->getHandle(); inputImageInfo[inputBindingIndex].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; ++inputBindingIndex; } VkWriteDescriptorSet writeInfo = {}; writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfo.dstSet = descriptorSet; writeInfo.dstBinding = 0; writeInfo.descriptorCount = totalBindingCount; writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; writeInfo.pImageInfo = inputImageInfo.data(); vkUpdateDescriptorSets(contextVk->getDevice(), 1, &writeInfo, 0, nullptr); gl::DrawBuffersArray colorAttachmentTypes; uint32_t flags = GetUnresolveFlags(colorAttachmentCount, colorSrc, params.unresolveDepth, unresolveStencilWithShaderExport, &colorAttachmentTypes); vk::ShaderModulePtr &fragmentShader = mUnresolveFragShaders[flags]; ANGLE_TRY(GetUnresolveFrag(contextVk, colorAttachmentCount, colorAttachmentTypes, params.unresolveDepth, params.unresolveStencil, &fragmentShader)); ANGLE_TRY(setupGraphicsProgram(contextVk, function, vertexShader, fragmentShader, &mUnresolve[flags], &pipelineDesc, descriptorSet, nullptr, 0, commandBuffer)); if (params.unresolveDepth) { SetDepthDynamicStateForWrite(renderer, commandBuffer); } else { SetDepthDynamicStateForUnused(renderer, commandBuffer); } if (unresolveStencilWithShaderExport) { constexpr uint8_t kCompleteMask = 0xFF; constexpr uint8_t kUnusedReference = 0x00; commandBuffer->setStencilCompareMask(kCompleteMask, kCompleteMask); commandBuffer->setStencilWriteMask(kCompleteMask, kCompleteMask); commandBuffer->setStencilReference(kUnusedReference, kUnusedReference); SetStencilDynamicStateForWrite(renderer, commandBuffer); } else { SetStencilDynamicStateForUnused(renderer, commandBuffer); } // This draw call is made before ContextVk gets a chance to start the occlusion query. As // such, occlusion queries are not enabled. commandBuffer->draw(3, 0); } // If stencil needs to be unresolved, but stencil export is not supported, set each bit of // stencil by adjusting the mask and controlling the output with `discard;` in the shader. This // requires that the stencil is cleared to 0 beforehand. if (params.unresolveStencil && !unresolveStencilWithShaderExport) { ANGLE_TRY(ensureExportStencilResourcesInitialized(contextVk)); // Disable color and depth output, and only let stencil through. pipelineDesc.setColorWriteMasks(0, gl::DrawBufferMask(), gl::DrawBufferMask()); SetDepthStateForUnused(renderer, &pipelineDesc); SetStencilStateForWrite(renderer, &pipelineDesc); vk::ShaderModulePtr exportStencilShader; ANGLE_TRY(shaderLibrary.getExportStencil_frag(contextVk, 0, &exportStencilShader)); // A new descriptor set is needed to match the layout of the ExportStencil program. VkDescriptorSet exportStencilDescriptorSet; ANGLE_TRY(allocateDescriptorSet(contextVk, &contextVk->getStartedRenderPassCommands(), Function::ExportStencil, &exportStencilDescriptorSet)); VkDescriptorImageInfo stencilImageInfo = {}; stencilImageInfo.imageView = stencilSrcView->getHandle(); stencilImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkWriteDescriptorSet stencilWriteInfo = {}; stencilWriteInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; stencilWriteInfo.dstSet = exportStencilDescriptorSet; stencilWriteInfo.dstBinding = 0; stencilWriteInfo.descriptorCount = 1; stencilWriteInfo.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; stencilWriteInfo.pImageInfo = &stencilImageInfo; vkUpdateDescriptorSets(contextVk->getDevice(), 1, &stencilWriteInfo, 0, nullptr); ANGLE_TRY(setupGraphicsProgram(contextVk, Function::ExportStencil, vertexShader, exportStencilShader, &mExportStencil, &pipelineDesc, exportStencilDescriptorSet, nullptr, 0, commandBuffer)); SetDepthDynamicStateForUnused(renderer, commandBuffer); SetStencilDynamicStateForWrite(renderer, commandBuffer); constexpr uint8_t kCompareMask = 0xFF; constexpr uint8_t kReference = 0xFF; commandBuffer->setStencilCompareMask(kCompareMask, kCompareMask); commandBuffer->setStencilReference(kReference, kReference); // Set each bit in a different draw call. This is not terribly efficient, but manages to // keep the transient multisampled stencil data on tile and avoids having to write it back // to memory / allocate memory for it. for (uint32_t bit = 0; bit < 8; ++bit) { const uint32_t writeMask = 1u << bit; commandBuffer->setStencilWriteMask(writeMask, writeMask); ExportStencilShaderParams shaderParams; shaderParams.bit = bit; commandBuffer->pushConstants( *mPipelineLayouts[Function::ExportStencil], VK_SHADER_STAGE_FRAGMENT_BIT, 0, /*static_cast*/ (sizeof(shaderParams)), &shaderParams); commandBuffer->draw(3, 0); } } return angle::Result::Continue; } angle::Result UtilsVk::drawOverlay(ContextVk *contextVk, vk::BufferHelper *textWidgetsBuffer, vk::BufferHelper *graphWidgetsBuffer, vk::ImageHelper *font, const vk::ImageView *fontView, vk::ImageHelper *dst, const vk::ImageView *destView, const OverlayDrawParameters ¶ms) { vk::Renderer *renderer = contextVk->getRenderer(); ANGLE_TRY(ensureOverlayDrawResourcesInitialized(contextVk)); OverlayDrawShaderParams shaderParams; shaderParams.viewportSize[0] = dst->getExtents().width; shaderParams.viewportSize[1] = dst->getExtents().height; shaderParams.isText = false; shaderParams.rotateXY = params.rotateXY; if (params.rotateXY) { std::swap(shaderParams.viewportSize[0], shaderParams.viewportSize[1]); } ASSERT(dst->getLevelCount() == 1 && dst->getLayerCount() == 1 && dst->getFirstAllocatedLevel() == gl::LevelIndex(0)); vk::RenderPassDesc renderPassDesc; renderPassDesc.setSamples(1); renderPassDesc.packColorAttachment(0, dst->getActualFormatID()); vk::GraphicsPipelineDesc pipelineDesc; pipelineDesc.initDefaults(contextVk, vk::GraphicsPipelineSubset::Complete, contextVk->pipelineRobustness(), contextVk->pipelineProtectedAccess()); pipelineDesc.setRenderPassDesc(renderPassDesc); pipelineDesc.setTopology(gl::PrimitiveMode::TriangleStrip); pipelineDesc.setSingleBlend(0, true, VK_BLEND_OP_ADD, VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA); gl::Rectangle renderArea; renderArea.x = 0; renderArea.y = 0; renderArea.width = shaderParams.viewportSize[0]; renderArea.height = shaderParams.viewportSize[1]; // A potential optimization is to reuse the already open render pass if it belongs to the // swapchain. vk::RenderPassCommandBuffer *commandBuffer; ANGLE_TRY(startRenderPass(contextVk, dst, destView, renderPassDesc, renderArea, VK_IMAGE_ASPECT_COLOR_BIT, nullptr, vk::RenderPassSource::DefaultFramebuffer, &commandBuffer)); vk::RenderPassCommandBufferHelper *commandBufferHelper = &contextVk->getStartedRenderPassCommands(); VkDescriptorSet descriptorSet; ANGLE_TRY(allocateDescriptorSet(contextVk, commandBufferHelper, Function::OverlayDraw, &descriptorSet)); UpdateColorAccess(contextVk, MakeColorBufferMask(0), MakeColorBufferMask(0)); commandBufferHelper->retainResource(textWidgetsBuffer); commandBufferHelper->retainResource(graphWidgetsBuffer); contextVk->onImageRenderPassRead(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::FragmentShaderReadOnly, font); contextVk->onImageRenderPassWrite(gl::LevelIndex(0), 0, 1, VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::ColorWrite, dst); VkDescriptorImageInfo imageInfo = {}; imageInfo.imageView = fontView->getHandle(); imageInfo.imageLayout = font->getCurrentLayout(renderer); VkDescriptorBufferInfo bufferInfos[2] = {}; bufferInfos[0].buffer = textWidgetsBuffer->getBuffer().getHandle(); bufferInfos[0].offset = textWidgetsBuffer->getOffset(); bufferInfos[0].range = textWidgetsBuffer->getSize(); bufferInfos[1].buffer = graphWidgetsBuffer->getBuffer().getHandle(); bufferInfos[1].offset = graphWidgetsBuffer->getOffset(); bufferInfos[1].range = graphWidgetsBuffer->getSize(); VkWriteDescriptorSet writeInfos[3] = {}; writeInfos[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfos[0].dstSet = descriptorSet; writeInfos[0].dstBinding = kOverlayDrawTextWidgetsBinding; writeInfos[0].descriptorCount = 1; writeInfos[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writeInfos[0].pBufferInfo = &bufferInfos[0]; writeInfos[1] = writeInfos[0]; writeInfos[1].dstBinding = kOverlayDrawGraphWidgetsBinding; writeInfos[1].pBufferInfo = &bufferInfos[1]; writeInfos[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfos[2].dstSet = descriptorSet; writeInfos[2].dstBinding = kOverlayDrawFontBinding; writeInfos[2].descriptorCount = 1; writeInfos[2].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; writeInfos[2].pImageInfo = &imageInfo; vkUpdateDescriptorSets(contextVk->getDevice(), 3, writeInfos, 0, nullptr); vk::ShaderLibrary &shaderLibrary = contextVk->getShaderLibrary(); vk::ShaderModulePtr vertexShader; vk::ShaderModulePtr fragmentShader; ANGLE_TRY(shaderLibrary.getOverlayDraw_vert(contextVk, 0, &vertexShader)); ANGLE_TRY(shaderLibrary.getOverlayDraw_frag(contextVk, 0, &fragmentShader)); ANGLE_TRY(setupGraphicsProgram(contextVk, Function::OverlayDraw, vertexShader, fragmentShader, &mOverlayDraw, &pipelineDesc, descriptorSet, nullptr, 0, commandBuffer)); // Set dynamic state VkViewport viewport; gl_vk::GetViewport(renderArea, 0.0f, 1.0f, false, false, dst->getExtents().height, &viewport); commandBuffer->setViewport(0, 1, &viewport); VkRect2D scissor = gl_vk::GetRect(renderArea); commandBuffer->setScissor(0, 1, &scissor); SetDepthDynamicStateForUnused(renderer, commandBuffer); SetStencilDynamicStateForUnused(renderer, commandBuffer); // Draw all the graph widgets. if (params.graphWidgetCount > 0) { shaderParams.isText = false; commandBuffer->pushConstants(*mPipelineLayouts[Function::OverlayDraw], VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(shaderParams), &shaderParams); commandBuffer->drawInstanced(4, params.graphWidgetCount, 0); } // Draw all the text widgets. if (params.textWidgetCount > 0) { shaderParams.isText = true; commandBuffer->pushConstants(*mPipelineLayouts[Function::OverlayDraw], VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(shaderParams), &shaderParams); commandBuffer->drawInstanced(4, params.textWidgetCount, 0); } // Overlay is always drawn as the last render pass before present. Automatically move the // layout to PresentSrc. contextVk->onColorDraw(gl::LevelIndex(0), 0, 1, dst, nullptr, {}, vk::PackedAttachmentIndex(0)); if (contextVk->getFeatures().supportsPresentation.enabled && !contextVk->getFeatures().preferDynamicRendering.enabled) { contextVk->getStartedRenderPassCommands().setImageOptimizeForPresent(dst); contextVk->finalizeImageLayout(dst, {}); } // Close the render pass for this temporary framebuffer. return contextVk->flushCommandsAndEndRenderPass( RenderPassClosureReason::TemporaryForOverlayDraw); } angle::Result UtilsVk::generateFragmentShadingRate( ContextVk *contextVk, vk::ImageHelper *shadingRateAttachmentImageHelper, vk::ImageViewHelper *shadingRateAttachmentImageViewHelper, const GenerateFragmentShadingRateParameters &shadingRateParameters) { ANGLE_TRY(ensureGenerateFragmentShadingRateResourcesInitialized(contextVk)); // Each workgroup processes an 8x8 tile of the image. constexpr uint32_t kPixelWorkgroupSize = 8; const uint32_t workGroupX = UnsignedCeilDivide(shadingRateParameters.attachmentWidth, kPixelWorkgroupSize); const uint32_t workGroupY = UnsignedCeilDivide(shadingRateParameters.attachmentHeight, kPixelWorkgroupSize); // Setup compute shader vk::OutsideRenderPassCommandBufferHelper *commandBufferHelper; vk::CommandBufferAccess access = {}; // Fragment shading rate image will always have 1 layer. access.onImageComputeShaderWrite(shadingRateAttachmentImageHelper->getFirstAllocatedLevel(), shadingRateAttachmentImageHelper->getLevelCount(), 0, shadingRateAttachmentImageHelper->getLayerCount(), shadingRateAttachmentImageHelper->getAspectFlags(), shadingRateAttachmentImageHelper); ANGLE_TRY(contextVk->getOutsideRenderPassCommandBufferHelper(access, &commandBufferHelper)); VkDescriptorSet descriptorSet; ANGLE_TRY(allocateDescriptorSet(contextVk, commandBufferHelper, Function::GenerateFragmentShadingRate, &descriptorSet)); VkDescriptorImageInfo destShadingRateImage = {}; destShadingRateImage.imageView = shadingRateAttachmentImageViewHelper->getFragmentShadingRateImageView().getHandle(); destShadingRateImage.imageLayout = shadingRateAttachmentImageHelper->getCurrentLayout(contextVk->getRenderer()); destShadingRateImage.sampler = mPointSampler.getHandle(); VkWriteDescriptorSet writeInfos[1] = {}; writeInfos[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfos[0].dstSet = descriptorSet; writeInfos[0].dstBinding = kGenerateFragmentShadingRateAttachmentBinding; writeInfos[0].descriptorCount = 1; writeInfos[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; writeInfos[0].pImageInfo = &destShadingRateImage; vkUpdateDescriptorSets(contextVk->getDevice(), 1, writeInfos, 0, nullptr); vk::ShaderModulePtr computeShader; ANGLE_TRY(contextVk->getShaderLibrary().getGenerateFragmentShadingRate_comp(contextVk, 0, &computeShader)); // Record the command vk::OutsideRenderPassCommandBuffer *commandBuffer; commandBuffer = &commandBufferHelper->getCommandBuffer(); ANGLE_TRY(setupComputeProgram(contextVk, Function::GenerateFragmentShadingRate, computeShader, &mGenerateFragmentShadingRateAttachment, descriptorSet, &shadingRateParameters, sizeof(shadingRateParameters), commandBufferHelper)); commandBuffer->dispatch(workGroupX, workGroupY, 1); return angle::Result::Continue; } angle::Result UtilsVk::allocateDescriptorSetWithLayout( ContextVk *contextVk, vk::CommandBufferHelperCommon *commandBufferHelper, vk::DynamicDescriptorPool &descriptorPool, const vk::DescriptorSetLayout &descriptorSetLayout, VkDescriptorSet *descriptorSetOut) { vk::DescriptorSetPointer descriptorSet; ANGLE_TRY(descriptorPool.allocateDescriptorSet(contextVk, descriptorSetLayout, &descriptorSet)); // Retain the individual descriptorSet to the command buffer. commandBufferHelper->retainResource(descriptorSet.get()); *descriptorSetOut = descriptorSet->getDescriptorSet(); return angle::Result::Continue; } angle::Result UtilsVk::allocateDescriptorSet(ContextVk *contextVk, vk::CommandBufferHelperCommon *commandBufferHelper, Function function, VkDescriptorSet *descriptorSetOut) { return allocateDescriptorSetWithLayout( contextVk, commandBufferHelper, mDescriptorPools[function], *mDescriptorSetLayouts[function][DescriptorSetIndex::Internal], descriptorSetOut); } angle::Result UtilsVk::allocateDescriptorSetForImageCopyWithSampler( ContextVk *contextVk, vk::CommandBufferHelperCommon *commandBufferHelper, const vk::SamplerDesc &samplerDesc, VkDescriptorSet *descriptorSetOut) { return allocateDescriptorSetWithLayout( contextVk, commandBufferHelper, mImageCopyWithSamplerDescriptorPools[samplerDesc], *mImageCopyWithSamplerDescriptorSetLayouts[samplerDesc][DescriptorSetIndex::Internal], descriptorSetOut); } UtilsVk::ClearFramebufferParameters::ClearFramebufferParameters() : clearColor(false), clearDepth(false), clearStencil(false), stencilMask(0), colorMaskFlags(0), colorAttachmentIndexGL(0), colorFormat(nullptr), colorClearValue{}, depthStencilClearValue{} {} // LineLoopHelper implementation. LineLoopHelper::LineLoopHelper(vk::Renderer *renderer) {} LineLoopHelper::~LineLoopHelper() = default; angle::Result LineLoopHelper::getIndexBufferForDrawArrays(ContextVk *contextVk, uint32_t clampedVertexCount, GLint firstVertex, vk::BufferHelper **bufferOut) { size_t allocateBytes = sizeof(uint32_t) * (static_cast(clampedVertexCount) + 1); ANGLE_TRY(contextVk->initBufferForVertexConversion(&mDynamicIndexBuffer, allocateBytes, vk::MemoryHostVisibility::Visible)); vk::BufferHelper *indexBuffer = mDynamicIndexBuffer.getBuffer(); uint32_t *indices = reinterpret_cast(indexBuffer->getMappedMemory()); // Note: there could be an overflow in this addition. uint32_t unsignedFirstVertex = static_cast(firstVertex); uint32_t vertexCount = (clampedVertexCount + unsignedFirstVertex); for (uint32_t vertexIndex = unsignedFirstVertex; vertexIndex < vertexCount; vertexIndex++) { *indices++ = vertexIndex; } *indices = unsignedFirstVertex; // Since we are not using the VK_MEMORY_PROPERTY_HOST_COHERENT_BIT flag when creating the // device memory in the StreamingBuffer, we always need to make sure we flush it after // writing. ANGLE_TRY(indexBuffer->flush(contextVk->getRenderer())); *bufferOut = indexBuffer; return angle::Result::Continue; } angle::Result LineLoopHelper::getIndexBufferForElementArrayBuffer(ContextVk *contextVk, BufferVk *elementArrayBufferVk, gl::DrawElementsType glIndexType, int indexCount, intptr_t elementArrayOffset, vk::BufferHelper **bufferOut, uint32_t *indexCountOut) { if (glIndexType == gl::DrawElementsType::UnsignedByte || contextVk->getState().isPrimitiveRestartEnabled()) { ANGLE_TRACE_EVENT0("gpu.angle", "LineLoopHelper::getIndexBufferForElementArrayBuffer"); void *srcDataMapping = nullptr; ANGLE_TRY(elementArrayBufferVk->mapImpl(contextVk, GL_MAP_READ_BIT, &srcDataMapping)); ANGLE_TRY(streamIndices(contextVk, glIndexType, indexCount, static_cast(srcDataMapping) + elementArrayOffset, bufferOut, indexCountOut)); ANGLE_TRY(elementArrayBufferVk->unmapImpl(contextVk)); return angle::Result::Continue; } *indexCountOut = indexCount + 1; size_t unitSize = contextVk->getVkIndexTypeSize(glIndexType); size_t allocateBytes = unitSize * (indexCount + 1) + 1; ANGLE_TRY(contextVk->initBufferForVertexConversion(&mDynamicIndexBuffer, allocateBytes, vk::MemoryHostVisibility::Visible)); vk::BufferHelper *indexBuffer = mDynamicIndexBuffer.getBuffer(); vk::BufferHelper *sourceBuffer = &elementArrayBufferVk->getBuffer(); VkDeviceSize sourceOffset = static_cast(elementArrayOffset) + sourceBuffer->getOffset(); uint64_t unitCount = static_cast(indexCount); angle::FixedVector copies = { {sourceOffset, indexBuffer->getOffset(), unitCount * unitSize}, {sourceOffset, indexBuffer->getOffset() + unitCount * unitSize, unitSize}, }; vk::CommandBufferAccess access; access.onBufferTransferWrite(indexBuffer); access.onBufferTransferRead(sourceBuffer); vk::OutsideRenderPassCommandBuffer *commandBuffer; ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &commandBuffer)); commandBuffer->copyBuffer(sourceBuffer->getBuffer(), indexBuffer->getBuffer(), static_cast(copies.size()), copies.data()); ANGLE_TRY(indexBuffer->flush(contextVk->getRenderer())); *bufferOut = indexBuffer; return angle::Result::Continue; } angle::Result LineLoopHelper::streamIndices(ContextVk *contextVk, gl::DrawElementsType glIndexType, GLsizei indexCount, const uint8_t *srcPtr, vk::BufferHelper **bufferOut, uint32_t *indexCountOut) { size_t unitSize = contextVk->getVkIndexTypeSize(glIndexType); uint32_t numOutIndices = indexCount + 1; if (contextVk->getState().isPrimitiveRestartEnabled()) { numOutIndices = GetLineLoopWithRestartIndexCount(glIndexType, indexCount, srcPtr); } *indexCountOut = numOutIndices; ANGLE_TRY(contextVk->initBufferForVertexConversion( &mDynamicIndexBuffer, unitSize * numOutIndices, vk::MemoryHostVisibility::Visible)); vk::BufferHelper *indexBuffer = mDynamicIndexBuffer.getBuffer(); uint8_t *indices = indexBuffer->getMappedMemory(); if (contextVk->getState().isPrimitiveRestartEnabled()) { HandlePrimitiveRestart(contextVk, glIndexType, indexCount, srcPtr, indices); } else { if (contextVk->shouldConvertUint8VkIndexType(glIndexType)) { // If vulkan doesn't support uint8 index types, we need to emulate it. VkIndexType indexType = contextVk->getVkIndexType(glIndexType); ASSERT(indexType == VK_INDEX_TYPE_UINT16); uint16_t *indicesDst = reinterpret_cast(indices); for (int i = 0; i < indexCount; i++) { indicesDst[i] = srcPtr[i]; } indicesDst[indexCount] = srcPtr[0]; } else { memcpy(indices, srcPtr, unitSize * indexCount); memcpy(indices + unitSize * indexCount, srcPtr, unitSize); } } ANGLE_TRY(indexBuffer->flush(contextVk->getRenderer())); *bufferOut = indexBuffer; return angle::Result::Continue; } angle::Result LineLoopHelper::streamIndicesIndirect(ContextVk *contextVk, gl::DrawElementsType glIndexType, vk::BufferHelper *srcIndexBuffer, vk::BufferHelper *srcIndirectBuffer, VkDeviceSize indirectBufferOffset, vk::BufferHelper **dstIndexBufferOut, vk::BufferHelper **dstIndirectBufferOut) { size_t unitSize = contextVk->getVkIndexTypeSize(glIndexType); size_t allocateBytes = static_cast(srcIndexBuffer->getSize() + unitSize); if (contextVk->getState().isPrimitiveRestartEnabled()) { // If primitive restart, new index buffer is 135% the size of the original index buffer. The // smallest lineloop with primitive restart is 3 indices (point 1, point 2 and restart // value) when converted to linelist becomes 4 vertices. Expansion of 4/3. Any larger // lineloops would have less overhead and require less extra space. Any incomplete // primitives can be dropped or left incomplete and thus not increase the size of the // destination index buffer. Since we don't know the number of indices being used we'll use // the size of the index buffer as allocated as the index count. size_t numInputIndices = static_cast(srcIndexBuffer->getSize() / unitSize); size_t numNewInputIndices = ((numInputIndices * 4) / 3) + 1; allocateBytes = static_cast(numNewInputIndices * unitSize); } // Allocate buffer for results ANGLE_TRY(contextVk->initBufferForVertexConversion(&mDynamicIndexBuffer, allocateBytes, vk::MemoryHostVisibility::Visible)); ANGLE_TRY(contextVk->initBufferForVertexConversion(&mDynamicIndirectBuffer, sizeof(VkDrawIndexedIndirectCommand), vk::MemoryHostVisibility::Visible)); vk::BufferHelper *dstIndexBuffer = mDynamicIndexBuffer.getBuffer(); vk::BufferHelper *dstIndirectBuffer = mDynamicIndirectBuffer.getBuffer(); // Copy relevant section of the source into destination at allocated offset. Note that the // offset returned by allocate() above is in bytes. As is the indices offset pointer. UtilsVk::ConvertLineLoopIndexIndirectParameters params = {}; params.indirectBufferOffset = static_cast(indirectBufferOffset); params.dstIndirectBufferOffset = 0; params.srcIndexBufferOffset = 0; params.dstIndexBufferOffset = 0; params.indicesBitsWidth = static_cast(unitSize * 8); ANGLE_TRY(contextVk->getUtils().convertLineLoopIndexIndirectBuffer( contextVk, srcIndirectBuffer, srcIndexBuffer, dstIndirectBuffer, dstIndexBuffer, params)); mDynamicIndexBuffer.clearDirty(); mDynamicIndirectBuffer.clearDirty(); *dstIndexBufferOut = dstIndexBuffer; *dstIndirectBufferOut = dstIndirectBuffer; return angle::Result::Continue; } angle::Result LineLoopHelper::streamArrayIndirect(ContextVk *contextVk, size_t vertexCount, vk::BufferHelper *arrayIndirectBuffer, VkDeviceSize arrayIndirectBufferOffset, vk::BufferHelper **dstIndexBufferOut, vk::BufferHelper **dstIndexIndirectBufferOut) { auto unitSize = sizeof(uint32_t); size_t allocateBytes = static_cast((vertexCount + 1) * unitSize); ANGLE_TRY(contextVk->initBufferForVertexConversion(&mDynamicIndexBuffer, allocateBytes, vk::MemoryHostVisibility::Visible)); ANGLE_TRY(contextVk->initBufferForVertexConversion(&mDynamicIndirectBuffer, sizeof(VkDrawIndexedIndirectCommand), vk::MemoryHostVisibility::Visible)); vk::BufferHelper *dstIndexBuffer = mDynamicIndexBuffer.getBuffer(); vk::BufferHelper *dstIndirectBuffer = mDynamicIndirectBuffer.getBuffer(); // Copy relevant section of the source into destination at allocated offset. Note that the // offset returned by allocate() above is in bytes. As is the indices offset pointer. UtilsVk::ConvertLineLoopArrayIndirectParameters params = {}; params.indirectBufferOffset = static_cast(arrayIndirectBufferOffset); params.dstIndirectBufferOffset = 0; params.dstIndexBufferOffset = 0; ANGLE_TRY(contextVk->getUtils().convertLineLoopArrayIndirectBuffer( contextVk, arrayIndirectBuffer, dstIndirectBuffer, dstIndexBuffer, params)); mDynamicIndexBuffer.clearDirty(); mDynamicIndirectBuffer.clearDirty(); *dstIndexBufferOut = dstIndexBuffer; *dstIndexIndirectBufferOut = dstIndirectBuffer; return angle::Result::Continue; } void LineLoopHelper::release(ContextVk *contextVk) { mDynamicIndexBuffer.release(contextVk->getRenderer()); mDynamicIndirectBuffer.release(contextVk->getRenderer()); } void LineLoopHelper::destroy(vk::Renderer *renderer) { mDynamicIndexBuffer.destroy(renderer); mDynamicIndirectBuffer.destroy(renderer); } } // namespace rx