| // |
| // 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. |
| // |
| // vk_cache_utils.cpp: |
| // Contains the classes for the Pipeline State Object cache as well as the RenderPass cache. |
| // Also contains the structures for the packed descriptions for the RenderPass and Pipeline. |
| // |
| |
| #include "libANGLE/renderer/vulkan/vk_cache_utils.h" |
| |
| #include "common/aligned_memory.h" |
| #include "common/vulkan/vk_google_filtering_precision.h" |
| #include "libANGLE/BlobCache.h" |
| #include "libANGLE/VertexAttribute.h" |
| #include "libANGLE/renderer/vulkan/DisplayVk.h" |
| #include "libANGLE/renderer/vulkan/FramebufferVk.h" |
| #include "libANGLE/renderer/vulkan/ProgramVk.h" |
| #include "libANGLE/renderer/vulkan/RendererVk.h" |
| #include "libANGLE/renderer/vulkan/TextureVk.h" |
| #include "libANGLE/renderer/vulkan/VertexArrayVk.h" |
| #include "libANGLE/renderer/vulkan/vk_format_utils.h" |
| #include "libANGLE/renderer/vulkan/vk_helpers.h" |
| |
| #include <type_traits> |
| |
| namespace rx |
| { |
| constexpr bool kDumpPipelineCacheGraph = false; |
| |
| namespace vk |
| { |
| |
| namespace |
| { |
| static_assert(static_cast<uint32_t>(RenderPassLoadOp::Load) == VK_ATTACHMENT_LOAD_OP_LOAD, |
| "ConvertRenderPassLoadOpToVkLoadOp must be updated"); |
| static_assert(static_cast<uint32_t>(RenderPassLoadOp::Clear) == VK_ATTACHMENT_LOAD_OP_CLEAR, |
| "ConvertRenderPassLoadOpToVkLoadOp must be updated"); |
| static_assert(static_cast<uint32_t>(RenderPassLoadOp::DontCare) == VK_ATTACHMENT_LOAD_OP_DONT_CARE, |
| "ConvertRenderPassLoadOpToVkLoadOp must be updated"); |
| static_assert(static_cast<uint32_t>(RenderPassLoadOp::None) == 3, |
| "ConvertRenderPassLoadOpToVkLoadOp must be updated"); |
| |
| static_assert(static_cast<uint32_t>(RenderPassStoreOp::Store) == VK_ATTACHMENT_STORE_OP_STORE, |
| "ConvertRenderPassStoreOpToVkStoreOp must be updated"); |
| static_assert(static_cast<uint32_t>(RenderPassStoreOp::DontCare) == |
| VK_ATTACHMENT_STORE_OP_DONT_CARE, |
| "ConvertRenderPassStoreOpToVkStoreOp must be updated"); |
| static_assert(static_cast<uint32_t>(RenderPassStoreOp::None) == 2, |
| "ConvertRenderPassStoreOpToVkStoreOp must be updated"); |
| |
| constexpr uint16_t kMinSampleShadingScale = angle::BitMask<uint16_t>(11); |
| |
| VkAttachmentLoadOp ConvertRenderPassLoadOpToVkLoadOp(RenderPassLoadOp loadOp) |
| { |
| return loadOp == RenderPassLoadOp::None ? VK_ATTACHMENT_LOAD_OP_NONE_EXT |
| : static_cast<VkAttachmentLoadOp>(loadOp); |
| } |
| VkAttachmentStoreOp ConvertRenderPassStoreOpToVkStoreOp(RenderPassStoreOp storeOp) |
| { |
| return storeOp == RenderPassStoreOp::None ? VK_ATTACHMENT_STORE_OP_NONE_EXT |
| : static_cast<VkAttachmentStoreOp>(storeOp); |
| } |
| |
| uint8_t PackGLBlendOp(GLenum blendOp) |
| { |
| switch (blendOp) |
| { |
| case GL_FUNC_ADD: |
| return static_cast<uint8_t>(VK_BLEND_OP_ADD); |
| case GL_FUNC_SUBTRACT: |
| return static_cast<uint8_t>(VK_BLEND_OP_SUBTRACT); |
| case GL_FUNC_REVERSE_SUBTRACT: |
| return static_cast<uint8_t>(VK_BLEND_OP_REVERSE_SUBTRACT); |
| case GL_MIN: |
| return static_cast<uint8_t>(VK_BLEND_OP_MIN); |
| case GL_MAX: |
| return static_cast<uint8_t>(VK_BLEND_OP_MAX); |
| case GL_MULTIPLY_KHR: |
| return static_cast<uint8_t>(VK_BLEND_OP_MULTIPLY_EXT - VK_BLEND_OP_ZERO_EXT); |
| case GL_SCREEN_KHR: |
| return static_cast<uint8_t>(VK_BLEND_OP_SCREEN_EXT - VK_BLEND_OP_ZERO_EXT); |
| case GL_OVERLAY_KHR: |
| return static_cast<uint8_t>(VK_BLEND_OP_OVERLAY_EXT - VK_BLEND_OP_ZERO_EXT); |
| case GL_DARKEN_KHR: |
| return static_cast<uint8_t>(VK_BLEND_OP_DARKEN_EXT - VK_BLEND_OP_ZERO_EXT); |
| case GL_LIGHTEN_KHR: |
| return static_cast<uint8_t>(VK_BLEND_OP_LIGHTEN_EXT - VK_BLEND_OP_ZERO_EXT); |
| case GL_COLORDODGE_KHR: |
| return static_cast<uint8_t>(VK_BLEND_OP_COLORDODGE_EXT - VK_BLEND_OP_ZERO_EXT); |
| case GL_COLORBURN_KHR: |
| return static_cast<uint8_t>(VK_BLEND_OP_COLORBURN_EXT - VK_BLEND_OP_ZERO_EXT); |
| case GL_HARDLIGHT_KHR: |
| return static_cast<uint8_t>(VK_BLEND_OP_HARDLIGHT_EXT - VK_BLEND_OP_ZERO_EXT); |
| case GL_SOFTLIGHT_KHR: |
| return static_cast<uint8_t>(VK_BLEND_OP_SOFTLIGHT_EXT - VK_BLEND_OP_ZERO_EXT); |
| case GL_DIFFERENCE_KHR: |
| return static_cast<uint8_t>(VK_BLEND_OP_DIFFERENCE_EXT - VK_BLEND_OP_ZERO_EXT); |
| case GL_EXCLUSION_KHR: |
| return static_cast<uint8_t>(VK_BLEND_OP_EXCLUSION_EXT - VK_BLEND_OP_ZERO_EXT); |
| case GL_HSL_HUE_KHR: |
| return static_cast<uint8_t>(VK_BLEND_OP_HSL_HUE_EXT - VK_BLEND_OP_ZERO_EXT); |
| case GL_HSL_SATURATION_KHR: |
| return static_cast<uint8_t>(VK_BLEND_OP_HSL_SATURATION_EXT - VK_BLEND_OP_ZERO_EXT); |
| case GL_HSL_COLOR_KHR: |
| return static_cast<uint8_t>(VK_BLEND_OP_HSL_COLOR_EXT - VK_BLEND_OP_ZERO_EXT); |
| case GL_HSL_LUMINOSITY_KHR: |
| return static_cast<uint8_t>(VK_BLEND_OP_HSL_LUMINOSITY_EXT - VK_BLEND_OP_ZERO_EXT); |
| default: |
| UNREACHABLE(); |
| return 0; |
| } |
| } |
| |
| VkBlendOp UnpackBlendOp(uint8_t packedBlendOp) |
| { |
| if (packedBlendOp <= VK_BLEND_OP_MAX) |
| { |
| return static_cast<VkBlendOp>(packedBlendOp); |
| } |
| return static_cast<VkBlendOp>(packedBlendOp + VK_BLEND_OP_ZERO_EXT); |
| } |
| |
| uint8_t PackGLBlendFactor(GLenum blendFactor) |
| { |
| switch (blendFactor) |
| { |
| case GL_ZERO: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_ZERO); |
| case GL_ONE: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE); |
| case GL_SRC_COLOR: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC_COLOR); |
| case GL_DST_COLOR: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_DST_COLOR); |
| case GL_ONE_MINUS_SRC_COLOR: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR); |
| case GL_SRC_ALPHA: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC_ALPHA); |
| case GL_ONE_MINUS_SRC_ALPHA: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA); |
| case GL_DST_ALPHA: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_DST_ALPHA); |
| case GL_ONE_MINUS_DST_ALPHA: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA); |
| case GL_ONE_MINUS_DST_COLOR: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR); |
| case GL_SRC_ALPHA_SATURATE: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC_ALPHA_SATURATE); |
| case GL_CONSTANT_COLOR: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_CONSTANT_COLOR); |
| case GL_CONSTANT_ALPHA: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_CONSTANT_ALPHA); |
| case GL_ONE_MINUS_CONSTANT_COLOR: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR); |
| case GL_ONE_MINUS_CONSTANT_ALPHA: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA); |
| case GL_SRC1_COLOR_EXT: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC1_COLOR); |
| case GL_SRC1_ALPHA_EXT: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC1_ALPHA); |
| case GL_ONE_MINUS_SRC1_COLOR_EXT: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR); |
| case GL_ONE_MINUS_SRC1_ALPHA_EXT: |
| return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA); |
| default: |
| UNREACHABLE(); |
| return 0; |
| } |
| } |
| |
| void UnpackAttachmentDesc(VkAttachmentDescription *desc, |
| angle::FormatID formatID, |
| uint8_t samples, |
| const PackedAttachmentOpsDesc &ops) |
| { |
| desc->flags = 0; |
| desc->format = GetVkFormatFromFormatID(formatID); |
| desc->samples = gl_vk::GetSamples(samples); |
| desc->loadOp = ConvertRenderPassLoadOpToVkLoadOp(static_cast<RenderPassLoadOp>(ops.loadOp)); |
| desc->storeOp = |
| ConvertRenderPassStoreOpToVkStoreOp(static_cast<RenderPassStoreOp>(ops.storeOp)); |
| desc->stencilLoadOp = |
| ConvertRenderPassLoadOpToVkLoadOp(static_cast<RenderPassLoadOp>(ops.stencilLoadOp)); |
| desc->stencilStoreOp = |
| ConvertRenderPassStoreOpToVkStoreOp(static_cast<RenderPassStoreOp>(ops.stencilStoreOp)); |
| desc->initialLayout = |
| ConvertImageLayoutToVkImageLayout(static_cast<ImageLayout>(ops.initialLayout)); |
| desc->finalLayout = |
| ConvertImageLayoutToVkImageLayout(static_cast<ImageLayout>(ops.finalLayout)); |
| } |
| |
| void UnpackColorResolveAttachmentDesc(VkAttachmentDescription *desc, |
| angle::FormatID formatID, |
| bool usedAsInputAttachment, |
| bool isInvalidated) |
| { |
| desc->flags = 0; |
| desc->format = GetVkFormatFromFormatID(formatID); |
| |
| // This function is for color resolve attachments. |
| const angle::Format &angleFormat = angle::Format::Get(formatID); |
| ASSERT(angleFormat.depthBits == 0 && angleFormat.stencilBits == 0); |
| |
| // Resolve attachments always have a sample count of 1. |
| // |
| // If the corresponding color attachment needs to take its initial value from the resolve |
| // attachment (i.e. needs to be unresolved), loadOp needs to be set to LOAD, otherwise it should |
| // be DONT_CARE as it gets overwritten during resolve. |
| // |
| // storeOp should be STORE. If the attachment is invalidated, it is set to DONT_CARE. |
| desc->samples = VK_SAMPLE_COUNT_1_BIT; |
| desc->loadOp = |
| usedAsInputAttachment ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
| desc->storeOp = isInvalidated ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE; |
| desc->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
| desc->stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
| desc->initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| desc->finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| } |
| |
| void UnpackDepthStencilResolveAttachmentDesc(VkAttachmentDescription *desc, |
| angle::FormatID formatID, |
| bool usedAsDepthInputAttachment, |
| bool usedAsStencilInputAttachment, |
| bool isDepthInvalidated, |
| bool isStencilInvalidated) |
| { |
| // There cannot be simultaneous usages of the depth/stencil resolve image, as depth/stencil |
| // resolve currently only comes from depth/stencil renderbuffers. |
| desc->flags = 0; |
| desc->format = GetVkFormatFromFormatID(formatID); |
| |
| // This function is for depth/stencil resolve attachment. |
| const angle::Format &angleFormat = angle::Format::Get(formatID); |
| ASSERT(angleFormat.depthBits != 0 || angleFormat.stencilBits != 0); |
| |
| // Missing aspects are folded in is*Invalidated parameters, so no need to double check. |
| ASSERT(angleFormat.depthBits > 0 || isDepthInvalidated); |
| ASSERT(angleFormat.stencilBits > 0 || isStencilInvalidated); |
| |
| // Similarly to color resolve attachments, sample count is 1, loadOp is LOAD or DONT_CARE based |
| // on whether unresolve is required, and storeOp is STORE or DONT_CARE based on whether the |
| // attachment is invalidated or the aspect exists. |
| desc->samples = VK_SAMPLE_COUNT_1_BIT; |
| desc->loadOp = |
| usedAsDepthInputAttachment ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
| desc->storeOp = |
| isDepthInvalidated ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE; |
| desc->stencilLoadOp = |
| usedAsStencilInputAttachment ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
| desc->stencilStoreOp = |
| isStencilInvalidated ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE; |
| desc->initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
| desc->finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
| } |
| |
| void UnpackStencilState(const PackedStencilOpState &packedState, VkStencilOpState *stateOut) |
| { |
| stateOut->failOp = static_cast<VkStencilOp>(packedState.ops.fail); |
| stateOut->passOp = static_cast<VkStencilOp>(packedState.ops.pass); |
| stateOut->depthFailOp = static_cast<VkStencilOp>(packedState.ops.depthFail); |
| stateOut->compareOp = static_cast<VkCompareOp>(packedState.ops.compare); |
| stateOut->compareMask = 0; |
| stateOut->writeMask = 0; |
| stateOut->reference = 0; |
| } |
| |
| void UnpackBlendAttachmentState(const PackedColorBlendAttachmentState &packedState, |
| VkPipelineColorBlendAttachmentState *stateOut) |
| { |
| stateOut->srcColorBlendFactor = static_cast<VkBlendFactor>(packedState.srcColorBlendFactor); |
| stateOut->dstColorBlendFactor = static_cast<VkBlendFactor>(packedState.dstColorBlendFactor); |
| stateOut->colorBlendOp = UnpackBlendOp(packedState.colorBlendOp); |
| stateOut->srcAlphaBlendFactor = static_cast<VkBlendFactor>(packedState.srcAlphaBlendFactor); |
| stateOut->dstAlphaBlendFactor = static_cast<VkBlendFactor>(packedState.dstAlphaBlendFactor); |
| stateOut->alphaBlendOp = UnpackBlendOp(packedState.alphaBlendOp); |
| } |
| |
| void SetPipelineShaderStageInfo(const VkStructureType type, |
| const VkShaderStageFlagBits stage, |
| const VkShaderModule module, |
| const VkSpecializationInfo &specializationInfo, |
| VkPipelineShaderStageCreateInfo *shaderStage) |
| { |
| shaderStage->sType = type; |
| shaderStage->flags = 0; |
| shaderStage->stage = stage; |
| shaderStage->module = module; |
| shaderStage->pName = "main"; |
| shaderStage->pSpecializationInfo = &specializationInfo; |
| } |
| |
| // Defines a subpass that uses the resolve attachments as input attachments to initialize color and |
| // depth/stencil attachments that need to be "unresolved" at the start of the render pass. The |
| // subpass will only contain the attachments that need to be unresolved to simplify the shader that |
| // performs the operations. |
| void InitializeUnresolveSubpass( |
| const RenderPassDesc &desc, |
| const gl::DrawBuffersVector<VkAttachmentReference> &drawSubpassColorAttachmentRefs, |
| const gl::DrawBuffersVector<VkAttachmentReference> &drawSubpassResolveAttachmentRefs, |
| const VkAttachmentReference &depthStencilAttachmentRef, |
| const VkAttachmentReference2KHR &depthStencilResolveAttachmentRef, |
| gl::DrawBuffersVector<VkAttachmentReference> *unresolveColorAttachmentRefs, |
| VkAttachmentReference *unresolveDepthStencilAttachmentRef, |
| FramebufferAttachmentsVector<VkAttachmentReference> *unresolveInputAttachmentRefs, |
| FramebufferAttachmentsVector<uint32_t> *unresolvePreserveAttachmentRefs, |
| VkSubpassDescription *subpassDesc) |
| { |
| for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL) |
| { |
| // Assume the GL Framebuffer has the following attachments enabled: |
| // |
| // GL Color 0 |
| // GL Color 3 |
| // GL Color 4 |
| // GL Color 6 |
| // GL Color 7 |
| // GL Depth/Stencil |
| // |
| // Additionally, assume Color 0, 4 and 6 are multisampled-render-to-texture (or for any |
| // other reason) have corresponding resolve attachments. Furthermore, say Color 4 and 6 |
| // require an initial unresolve operation. |
| // |
| // In the above example, the render pass is created with the following attachments: |
| // |
| // RP Attachment[0] <- corresponding to GL Color 0 |
| // RP Attachment[1] <- corresponding to GL Color 3 |
| // RP Attachment[2] <- corresponding to GL Color 4 |
| // RP Attachment[3] <- corresponding to GL Color 6 |
| // RP Attachment[4] <- corresponding to GL Color 7 |
| // RP Attachment[5] <- corresponding to GL Depth/Stencil |
| // RP Attachment[6] <- corresponding to resolve attachment of GL Color 0 |
| // RP Attachment[7] <- corresponding to resolve attachment of GL Color 4 |
| // RP Attachment[8] <- corresponding to resolve attachment of GL Color 6 |
| // |
| // If the depth/stencil attachment is to be resolved, the following attachment would also be |
| // present: |
| // |
| // RP Attachment[9] <- corresponding to resolve attachment of GL Depth/Stencil |
| // |
| // The subpass that takes the application draw calls has the following attachments, creating |
| // the mapping from the Vulkan attachment indices (i.e. RP attachment indices) to GL indices |
| // as indicated by the GL shaders: |
| // |
| // Subpass[1] Color[0] -> RP Attachment[0] |
| // Subpass[1] Color[1] -> VK_ATTACHMENT_UNUSED |
| // Subpass[1] Color[2] -> VK_ATTACHMENT_UNUSED |
| // Subpass[1] Color[3] -> RP Attachment[1] |
| // Subpass[1] Color[4] -> RP Attachment[2] |
| // Subpass[1] Color[5] -> VK_ATTACHMENT_UNUSED |
| // Subpass[1] Color[6] -> RP Attachment[3] |
| // Subpass[1] Color[7] -> RP Attachment[4] |
| // Subpass[1] Depth/Stencil -> RP Attachment[5] |
| // Subpass[1] Resolve[0] -> RP Attachment[6] |
| // Subpass[1] Resolve[1] -> VK_ATTACHMENT_UNUSED |
| // Subpass[1] Resolve[2] -> VK_ATTACHMENT_UNUSED |
| // Subpass[1] Resolve[3] -> VK_ATTACHMENT_UNUSED |
| // Subpass[1] Resolve[4] -> RP Attachment[7] |
| // Subpass[1] Resolve[5] -> VK_ATTACHMENT_UNUSED |
| // Subpass[1] Resolve[6] -> RP Attachment[8] |
| // Subpass[1] Resolve[7] -> VK_ATTACHMENT_UNUSED |
| // |
| // With depth/stencil resolve attachment: |
| // |
| // Subpass[1] Depth/Stencil Resolve -> RP Attachment[9] |
| // |
| // The initial subpass that's created here is (remember that in the above example Color 4 |
| // and 6 need to be unresolved): |
| // |
| // Subpass[0] Input[0] -> RP Attachment[7] = Subpass[1] Resolve[4] |
| // Subpass[0] Input[1] -> RP Attachment[8] = Subpass[1] Resolve[6] |
| // Subpass[0] Color[0] -> RP Attachment[2] = Subpass[1] Color[4] |
| // Subpass[0] Color[1] -> RP Attachment[3] = Subpass[1] Color[6] |
| // |
| // The trick here therefore is to use the color attachment refs already created for the |
| // application draw subpass indexed with colorIndexGL. |
| // |
| // If depth/stencil needs to be unresolved: |
| // |
| // Subpass[0] Input[2] -> RP Attachment[9] = Subpass[1] Depth/Stencil Resolve |
| // Subpass[0] Color[2] -> RP Attachment[5] = Subpass[1] Depth/Stencil |
| // |
| // As an additional note, the attachments that are not used in the unresolve subpass must be |
| // preserved. That is color attachments and the depth/stencil attachment if any. Resolve |
| // attachments are rewritten by the next subpass, so they don't need to be preserved. Note |
| // that there's no need to preserve attachments whose loadOp is DONT_CARE. For simplicity, |
| // we preserve those as well. The driver would ideally avoid preserving attachments with |
| // loadOp=DONT_CARE. |
| // |
| // With the above example: |
| // |
| // Subpass[0] Preserve[0] -> RP Attachment[0] = Subpass[1] Color[0] |
| // Subpass[0] Preserve[1] -> RP Attachment[1] = Subpass[1] Color[3] |
| // Subpass[0] Preserve[2] -> RP Attachment[4] = Subpass[1] Color[7] |
| // |
| // If depth/stencil is not unresolved: |
| // |
| // Subpass[0] Preserve[3] -> RP Attachment[5] = Subpass[1] Depth/Stencil |
| // |
| // Again, the color attachment refs already created for the application draw subpass can be |
| // used indexed with colorIndexGL. |
| |
| if (!desc.hasColorUnresolveAttachment(colorIndexGL)) |
| { |
| if (desc.isColorAttachmentEnabled(colorIndexGL)) |
| { |
| unresolvePreserveAttachmentRefs->push_back( |
| drawSubpassColorAttachmentRefs[colorIndexGL].attachment); |
| } |
| continue; |
| } |
| ASSERT(desc.isColorAttachmentEnabled(colorIndexGL)); |
| ASSERT(desc.hasColorResolveAttachment(colorIndexGL)); |
| ASSERT(drawSubpassColorAttachmentRefs[colorIndexGL].attachment != VK_ATTACHMENT_UNUSED); |
| ASSERT(drawSubpassResolveAttachmentRefs[colorIndexGL].attachment != VK_ATTACHMENT_UNUSED); |
| |
| unresolveColorAttachmentRefs->push_back(drawSubpassColorAttachmentRefs[colorIndexGL]); |
| unresolveInputAttachmentRefs->push_back(drawSubpassResolveAttachmentRefs[colorIndexGL]); |
| |
| // Note the input attachment layout should be shader read-only. The subpass dependency |
| // will take care of transitioning the layout of the resolve attachment to color attachment |
| // automatically. |
| unresolveInputAttachmentRefs->back().layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
| } |
| |
| if (desc.hasDepthStencilUnresolveAttachment()) |
| { |
| ASSERT(desc.hasDepthStencilAttachment()); |
| ASSERT(desc.hasDepthStencilResolveAttachment()); |
| |
| *unresolveDepthStencilAttachmentRef = depthStencilAttachmentRef; |
| |
| VkAttachmentReference unresolveDepthStencilInputAttachmentRef = {}; |
| unresolveDepthStencilInputAttachmentRef.attachment = |
| depthStencilResolveAttachmentRef.attachment; |
| unresolveDepthStencilInputAttachmentRef.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
| |
| unresolveInputAttachmentRefs->push_back(unresolveDepthStencilInputAttachmentRef); |
| } |
| else if (desc.hasDepthStencilAttachment()) |
| { |
| // Preserve the depth/stencil attachment if not unresolved. Again, there's no need to |
| // preserve this attachment if loadOp=DONT_CARE, but we do for simplicity. |
| unresolvePreserveAttachmentRefs->push_back(depthStencilAttachmentRef.attachment); |
| } |
| |
| ASSERT(!unresolveColorAttachmentRefs->empty() || |
| unresolveDepthStencilAttachmentRef->attachment != VK_ATTACHMENT_UNUSED); |
| ASSERT(unresolveColorAttachmentRefs->size() + |
| (desc.hasDepthStencilUnresolveAttachment() ? 1 : 0) == |
| unresolveInputAttachmentRefs->size()); |
| |
| subpassDesc->flags = 0; |
| subpassDesc->pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
| subpassDesc->inputAttachmentCount = static_cast<uint32_t>(unresolveInputAttachmentRefs->size()); |
| subpassDesc->pInputAttachments = unresolveInputAttachmentRefs->data(); |
| subpassDesc->colorAttachmentCount = static_cast<uint32_t>(unresolveColorAttachmentRefs->size()); |
| subpassDesc->pColorAttachments = unresolveColorAttachmentRefs->data(); |
| subpassDesc->pResolveAttachments = nullptr; |
| subpassDesc->pDepthStencilAttachment = unresolveDepthStencilAttachmentRef; |
| subpassDesc->preserveAttachmentCount = |
| static_cast<uint32_t>(unresolvePreserveAttachmentRefs->size()); |
| subpassDesc->pPreserveAttachments = unresolvePreserveAttachmentRefs->data(); |
| } |
| |
| // There is normally one subpass, and occasionally another for the unresolve operation. |
| constexpr size_t kSubpassFastVectorSize = 2; |
| template <typename T> |
| using SubpassVector = angle::FastVector<T, kSubpassFastVectorSize>; |
| |
| void InitializeUnresolveSubpassDependencies(const SubpassVector<VkSubpassDescription> &subpassDesc, |
| bool unresolveColor, |
| bool unresolveDepthStencil, |
| std::vector<VkSubpassDependency> *subpassDependencies) |
| { |
| ASSERT(subpassDesc.size() >= 2); |
| ASSERT(unresolveColor || unresolveDepthStencil); |
| |
| // The unresolve subpass is the first subpass. The application draw subpass is the next one. |
| constexpr uint32_t kUnresolveSubpassIndex = 0; |
| constexpr uint32_t kDrawSubpassIndex = 1; |
| |
| // A subpass dependency is needed between the unresolve and draw subpasses. There are two |
| // hazards here: |
| // |
| // - Subpass 0 writes to color/depth/stencil attachments, subpass 1 writes to the same |
| // attachments. This is a WaW hazard (color/depth/stencil write -> color/depth/stencil write) |
| // similar to when two subsequent render passes write to the same images. |
| // - Subpass 0 reads from resolve attachments, subpass 1 writes to the same resolve attachments. |
| // This is a WaR hazard (fragment shader read -> color write) which only requires an execution |
| // barrier. |
| // |
| // Note: the DEPENDENCY_BY_REGION flag is necessary to create a "framebuffer-local" dependency, |
| // as opposed to "framebuffer-global". The latter is effectively a render pass break. The |
| // former creates a dependency per framebuffer region. If dependency scopes correspond to |
| // attachments with: |
| // |
| // - Same sample count: dependency is at sample granularity |
| // - Different sample count: dependency is at pixel granularity |
| // |
| // The latter is clarified by the spec as such: |
| // |
| // > Practically, the pixel vs sample granularity dependency means that if an input attachment |
| // > has a different number of samples than the pipeline's rasterizationSamples, then a fragment |
| // > can access any sample in the input attachment's pixel even if it only uses |
| // > framebuffer-local dependencies. |
| // |
| // The dependency for the first hazard above (attachment write -> attachment write) is on |
| // same-sample attachments, so it will not allow the use of input attachments as required by the |
| // unresolve subpass. As a result, even though the second hazard seems to be subsumed by the |
| // first (its src stage is earlier and its dst stage is the same), a separate dependency is |
| // created for it just to obtain a pixel granularity dependency. |
| // |
| // Note: depth/stencil resolve is considered to be done in the color write stage: |
| // |
| // > Moving to the next subpass automatically performs any multisample resolve operations in the |
| // > subpass being ended. End-of-subpass multisample resolves are treated as color attachment |
| // > writes for the purposes of synchronization. This applies to resolve operations for both |
| // > color and depth/stencil attachments. That is, they are considered to execute in the |
| // > VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT pipeline stage and their writes are |
| // > synchronized with VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT. |
| |
| subpassDependencies->emplace_back(); |
| VkSubpassDependency *dependency = &subpassDependencies->back(); |
| |
| constexpr VkPipelineStageFlags kColorWriteStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
| constexpr VkPipelineStageFlags kColorReadWriteStage = |
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
| constexpr VkAccessFlags kColorWriteFlags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| constexpr VkAccessFlags kColorReadWriteFlags = |
| kColorWriteFlags | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; |
| |
| constexpr VkPipelineStageFlags kDepthStencilWriteStage = |
| VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; |
| constexpr VkPipelineStageFlags kDepthStencilReadWriteStage = |
| VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; |
| constexpr VkAccessFlags kDepthStencilWriteFlags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; |
| constexpr VkAccessFlags kDepthStencilReadWriteFlags = |
| kDepthStencilWriteFlags | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; |
| |
| VkPipelineStageFlags attachmentWriteStages = 0; |
| VkPipelineStageFlags attachmentReadWriteStages = 0; |
| VkAccessFlags attachmentWriteFlags = 0; |
| VkAccessFlags attachmentReadWriteFlags = 0; |
| |
| if (unresolveColor) |
| { |
| attachmentWriteStages |= kColorWriteStage; |
| attachmentReadWriteStages |= kColorReadWriteStage; |
| attachmentWriteFlags |= kColorWriteFlags; |
| attachmentReadWriteFlags |= kColorReadWriteFlags; |
| } |
| |
| if (unresolveDepthStencil) |
| { |
| attachmentWriteStages |= kDepthStencilWriteStage; |
| attachmentReadWriteStages |= kDepthStencilReadWriteStage; |
| attachmentWriteFlags |= kDepthStencilWriteFlags; |
| attachmentReadWriteFlags |= kDepthStencilReadWriteFlags; |
| } |
| |
| dependency->srcSubpass = kUnresolveSubpassIndex; |
| dependency->dstSubpass = kDrawSubpassIndex; |
| dependency->srcStageMask = attachmentWriteStages; |
| dependency->dstStageMask = attachmentReadWriteStages; |
| dependency->srcAccessMask = attachmentWriteFlags; |
| dependency->dstAccessMask = attachmentReadWriteFlags; |
| dependency->dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; |
| |
| subpassDependencies->emplace_back(); |
| dependency = &subpassDependencies->back(); |
| |
| // Note again that depth/stencil resolve is considered to be done in the color output stage. |
| dependency->srcSubpass = kUnresolveSubpassIndex; |
| dependency->dstSubpass = kDrawSubpassIndex; |
| dependency->srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; |
| dependency->dstStageMask = kColorWriteStage; |
| dependency->srcAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; |
| dependency->dstAccessMask = kColorWriteFlags; |
| dependency->dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; |
| } |
| |
| // glFramebufferFetchBarrierEXT and glBlendBarrierKHR require a pipeline barrier to be inserted in |
| // the render pass. This requires a subpass self-dependency. |
| // |
| // For framebuffer fetch: |
| // |
| // srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
| // dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
| // srcAccess = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
| // dstAccess = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
| // |
| // For advanced blend: |
| // |
| // srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
| // dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
| // srcAccess = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
| // dstAccess = VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT |
| // |
| // Subpass dependencies cannot be added after the fact at the end of the render pass due to render |
| // pass compatibility rules. ANGLE specifies a subpass self-dependency with the above stage/access |
| // masks in preparation of potential framebuffer fetch and advanced blend barriers. This is known |
| // not to add any overhead on any hardware we have been able to gather information from. |
| void InitializeDefaultSubpassSelfDependencies(vk::Context *context, |
| const RenderPassDesc &desc, |
| uint32_t subpassIndex, |
| std::vector<VkSubpassDependency> *subpassDependencies) |
| { |
| subpassDependencies->emplace_back(); |
| VkSubpassDependency *dependency = &subpassDependencies->back(); |
| |
| dependency->srcSubpass = subpassIndex; |
| dependency->dstSubpass = subpassIndex; |
| dependency->srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
| dependency->dstStageMask = |
| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
| dependency->srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| dependency->dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; |
| if (context->getRenderer()->getFeatures().supportsBlendOperationAdvanced.enabled) |
| { |
| dependency->dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT; |
| } |
| dependency->dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; |
| if (desc.viewCount() > 0) |
| { |
| dependency->dependencyFlags |= VK_DEPENDENCY_VIEW_LOCAL_BIT; |
| } |
| } |
| |
| void ToAttachmentDesciption2(const VkAttachmentDescription &desc, |
| VkAttachmentDescription2KHR *desc2Out) |
| { |
| *desc2Out = {}; |
| desc2Out->sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR; |
| desc2Out->flags = desc.flags; |
| desc2Out->format = desc.format; |
| desc2Out->samples = desc.samples; |
| desc2Out->loadOp = desc.loadOp; |
| desc2Out->storeOp = desc.storeOp; |
| desc2Out->stencilLoadOp = desc.stencilLoadOp; |
| desc2Out->stencilStoreOp = desc.stencilStoreOp; |
| desc2Out->initialLayout = desc.initialLayout; |
| desc2Out->finalLayout = desc.finalLayout; |
| } |
| |
| void ToAttachmentReference2(const VkAttachmentReference &ref, |
| VkImageAspectFlags aspectMask, |
| VkAttachmentReference2KHR *ref2Out) |
| { |
| *ref2Out = {}; |
| ref2Out->sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; |
| ref2Out->attachment = ref.attachment; |
| ref2Out->layout = ref.layout; |
| ref2Out->aspectMask = aspectMask; |
| } |
| |
| void ToSubpassDescription2(const VkSubpassDescription &desc, |
| const FramebufferAttachmentsVector<VkAttachmentReference2KHR> &inputRefs, |
| const gl::DrawBuffersVector<VkAttachmentReference2KHR> &colorRefs, |
| const gl::DrawBuffersVector<VkAttachmentReference2KHR> &resolveRefs, |
| const VkAttachmentReference2KHR &depthStencilRef, |
| uint32_t viewMask, |
| VkSubpassDescription2KHR *desc2Out) |
| { |
| *desc2Out = {}; |
| desc2Out->sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR; |
| desc2Out->flags = desc.flags; |
| desc2Out->pipelineBindPoint = desc.pipelineBindPoint; |
| desc2Out->viewMask = viewMask; |
| desc2Out->inputAttachmentCount = static_cast<uint32_t>(inputRefs.size()); |
| desc2Out->pInputAttachments = !inputRefs.empty() ? inputRefs.data() : nullptr; |
| desc2Out->colorAttachmentCount = static_cast<uint32_t>(colorRefs.size()); |
| desc2Out->pColorAttachments = !colorRefs.empty() ? colorRefs.data() : nullptr; |
| desc2Out->pResolveAttachments = !resolveRefs.empty() ? resolveRefs.data() : nullptr; |
| desc2Out->pDepthStencilAttachment = desc.pDepthStencilAttachment ? &depthStencilRef : nullptr; |
| desc2Out->preserveAttachmentCount = desc.preserveAttachmentCount; |
| desc2Out->pPreserveAttachments = desc.pPreserveAttachments; |
| } |
| |
| void ToSubpassDependency2(const VkSubpassDependency &dep, VkSubpassDependency2KHR *dep2Out) |
| { |
| *dep2Out = {}; |
| dep2Out->sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR; |
| dep2Out->srcSubpass = dep.srcSubpass; |
| dep2Out->dstSubpass = dep.dstSubpass; |
| dep2Out->srcStageMask = dep.srcStageMask; |
| dep2Out->dstStageMask = dep.dstStageMask; |
| dep2Out->srcAccessMask = dep.srcAccessMask; |
| dep2Out->dstAccessMask = dep.dstAccessMask; |
| dep2Out->dependencyFlags = dep.dependencyFlags; |
| } |
| |
| angle::Result CreateRenderPass2(Context *context, |
| const VkRenderPassCreateInfo &createInfo, |
| const VkSubpassDescriptionDepthStencilResolve &depthStencilResolve, |
| const VkRenderPassMultiviewCreateInfo &multiviewInfo, |
| bool unresolveDepth, |
| bool unresolveStencil, |
| bool isRenderToTextureThroughExtension, |
| uint8_t renderToTextureSamples, |
| RenderPass *renderPass) |
| { |
| // Convert the attachments to VkAttachmentDescription2. |
| FramebufferAttachmentArray<VkAttachmentDescription2KHR> attachmentDescs; |
| for (uint32_t index = 0; index < createInfo.attachmentCount; ++index) |
| { |
| ToAttachmentDesciption2(createInfo.pAttachments[index], &attachmentDescs[index]); |
| } |
| |
| // Convert subpass attachments to VkAttachmentReference2 and the subpass description to |
| // VkSubpassDescription2. |
| SubpassVector<FramebufferAttachmentsVector<VkAttachmentReference2KHR>> |
| subpassInputAttachmentRefs(createInfo.subpassCount); |
| SubpassVector<gl::DrawBuffersVector<VkAttachmentReference2KHR>> subpassColorAttachmentRefs( |
| createInfo.subpassCount); |
| SubpassVector<gl::DrawBuffersVector<VkAttachmentReference2KHR>> subpassResolveAttachmentRefs( |
| createInfo.subpassCount); |
| SubpassVector<VkAttachmentReference2KHR> subpassDepthStencilAttachmentRefs( |
| createInfo.subpassCount); |
| SubpassVector<VkSubpassDescription2KHR> subpassDescriptions(createInfo.subpassCount); |
| for (uint32_t subpass = 0; subpass < createInfo.subpassCount; ++subpass) |
| { |
| const VkSubpassDescription &desc = createInfo.pSubpasses[subpass]; |
| FramebufferAttachmentsVector<VkAttachmentReference2KHR> &inputRefs = |
| subpassInputAttachmentRefs[subpass]; |
| gl::DrawBuffersVector<VkAttachmentReference2KHR> &colorRefs = |
| subpassColorAttachmentRefs[subpass]; |
| gl::DrawBuffersVector<VkAttachmentReference2KHR> &resolveRefs = |
| subpassResolveAttachmentRefs[subpass]; |
| VkAttachmentReference2KHR &depthStencilRef = subpassDepthStencilAttachmentRefs[subpass]; |
| |
| inputRefs.resize(desc.inputAttachmentCount); |
| colorRefs.resize(desc.colorAttachmentCount); |
| |
| // Convert subpass attachment references. |
| for (uint32_t index = 0; index < desc.inputAttachmentCount; ++index) |
| { |
| VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| if (index >= desc.colorAttachmentCount) |
| { |
| // Set the aspect of the depth/stencil input attachment (of which there can be only |
| // one). |
| ASSERT(index + 1 == desc.inputAttachmentCount); |
| aspectMask = 0; |
| if (unresolveDepth) |
| { |
| aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; |
| } |
| if (unresolveStencil) |
| { |
| aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; |
| } |
| ASSERT(aspectMask != 0); |
| } |
| |
| ToAttachmentReference2(desc.pInputAttachments[index], aspectMask, &inputRefs[index]); |
| } |
| |
| for (uint32_t index = 0; index < desc.colorAttachmentCount; ++index) |
| { |
| ToAttachmentReference2(desc.pColorAttachments[index], VK_IMAGE_ASPECT_COLOR_BIT, |
| &colorRefs[index]); |
| } |
| if (desc.pResolveAttachments) |
| { |
| resolveRefs.resize(desc.colorAttachmentCount); |
| for (uint32_t index = 0; index < desc.colorAttachmentCount; ++index) |
| { |
| ToAttachmentReference2(desc.pResolveAttachments[index], VK_IMAGE_ASPECT_COLOR_BIT, |
| &resolveRefs[index]); |
| } |
| } |
| if (desc.pDepthStencilAttachment) |
| { |
| ToAttachmentReference2(*desc.pDepthStencilAttachment, |
| VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, |
| &depthStencilRef); |
| } |
| |
| // Convert subpass itself. |
| ToSubpassDescription2(desc, inputRefs, colorRefs, resolveRefs, depthStencilRef, |
| multiviewInfo.pViewMasks[subpass], &subpassDescriptions[subpass]); |
| } |
| |
| VkMultisampledRenderToSingleSampledInfoEXT renderToTextureInfo = {}; |
| renderToTextureInfo.sType = VK_STRUCTURE_TYPE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_INFO_EXT; |
| renderToTextureInfo.multisampledRenderToSingleSampledEnable = true; |
| renderToTextureInfo.rasterizationSamples = gl_vk::GetSamples(renderToTextureSamples); |
| renderToTextureInfo.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; |
| renderToTextureInfo.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; |
| |
| // Append the depth/stencil resolve attachment to the pNext chain of last subpass, if any. |
| if (depthStencilResolve.pDepthStencilResolveAttachment != nullptr) |
| { |
| ASSERT(!isRenderToTextureThroughExtension); |
| subpassDescriptions.back().pNext = &depthStencilResolve; |
| } |
| else |
| { |
| RendererVk *renderer = context->getRenderer(); |
| |
| ASSERT(isRenderToTextureThroughExtension); |
| ASSERT(renderer->getFeatures().supportsMultisampledRenderToSingleSampled.enabled); |
| ASSERT(subpassDescriptions.size() == 1); |
| |
| subpassDescriptions.back().pNext = &renderToTextureInfo; |
| } |
| |
| // Convert subpass dependencies to VkSubpassDependency2. |
| std::vector<VkSubpassDependency2KHR> subpassDependencies(createInfo.dependencyCount); |
| for (uint32_t index = 0; index < createInfo.dependencyCount; ++index) |
| { |
| ToSubpassDependency2(createInfo.pDependencies[index], &subpassDependencies[index]); |
| } |
| |
| // Convert CreateInfo itself |
| VkRenderPassCreateInfo2KHR createInfo2 = {}; |
| createInfo2.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR; |
| createInfo2.flags = createInfo.flags; |
| createInfo2.attachmentCount = createInfo.attachmentCount; |
| createInfo2.pAttachments = attachmentDescs.data(); |
| createInfo2.subpassCount = createInfo.subpassCount; |
| createInfo2.pSubpasses = subpassDescriptions.data(); |
| createInfo2.dependencyCount = static_cast<uint32_t>(subpassDependencies.size()); |
| createInfo2.pDependencies = !subpassDependencies.empty() ? subpassDependencies.data() : nullptr; |
| createInfo2.correlatedViewMaskCount = multiviewInfo.correlationMaskCount; |
| createInfo2.pCorrelatedViewMasks = multiviewInfo.pCorrelationMasks; |
| |
| // Initialize the render pass. |
| ANGLE_VK_TRY(context, renderPass->init2(context->getDevice(), createInfo2)); |
| |
| return angle::Result::Continue; |
| } |
| |
| void UpdateRenderPassColorPerfCounters(const VkRenderPassCreateInfo &createInfo, |
| FramebufferAttachmentMask depthStencilAttachmentIndices, |
| RenderPassPerfCounters *countersOut) |
| { |
| for (uint32_t index = 0; index < createInfo.attachmentCount; index++) |
| { |
| if (depthStencilAttachmentIndices.test(index)) |
| { |
| continue; |
| } |
| |
| VkAttachmentLoadOp loadOp = createInfo.pAttachments[index].loadOp; |
| VkAttachmentStoreOp storeOp = createInfo.pAttachments[index].storeOp; |
| countersOut->colorLoadOpClears += loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ? 1 : 0; |
| countersOut->colorLoadOpLoads += loadOp == VK_ATTACHMENT_LOAD_OP_LOAD ? 1 : 0; |
| countersOut->colorLoadOpNones += loadOp == VK_ATTACHMENT_LOAD_OP_NONE_EXT ? 1 : 0; |
| countersOut->colorStoreOpStores += storeOp == VK_ATTACHMENT_STORE_OP_STORE ? 1 : 0; |
| countersOut->colorStoreOpNones += storeOp == VK_ATTACHMENT_STORE_OP_NONE_EXT ? 1 : 0; |
| } |
| } |
| |
| void UpdateSubpassColorPerfCounters(const VkRenderPassCreateInfo &createInfo, |
| const VkSubpassDescription &subpass, |
| RenderPassPerfCounters *countersOut) |
| { |
| // Color resolve counters. |
| if (subpass.pResolveAttachments == nullptr) |
| { |
| return; |
| } |
| |
| for (uint32_t colorSubpassIndex = 0; colorSubpassIndex < subpass.colorAttachmentCount; |
| ++colorSubpassIndex) |
| { |
| uint32_t resolveRenderPassIndex = subpass.pResolveAttachments[colorSubpassIndex].attachment; |
| |
| if (resolveRenderPassIndex == VK_ATTACHMENT_UNUSED) |
| { |
| continue; |
| } |
| |
| ++countersOut->colorAttachmentResolves; |
| } |
| } |
| |
| void UpdateRenderPassDepthStencilPerfCounters(const VkRenderPassCreateInfo &createInfo, |
| size_t renderPassIndex, |
| RenderPassPerfCounters *countersOut) |
| { |
| ASSERT(renderPassIndex != VK_ATTACHMENT_UNUSED); |
| |
| // Depth/stencil ops counters. |
| const VkAttachmentDescription &ds = createInfo.pAttachments[renderPassIndex]; |
| |
| countersOut->depthLoadOpClears += ds.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ? 1 : 0; |
| countersOut->depthLoadOpLoads += ds.loadOp == VK_ATTACHMENT_LOAD_OP_LOAD ? 1 : 0; |
| countersOut->depthLoadOpNones += ds.loadOp == VK_ATTACHMENT_LOAD_OP_NONE_EXT ? 1 : 0; |
| countersOut->depthStoreOpStores += ds.storeOp == VK_ATTACHMENT_STORE_OP_STORE ? 1 : 0; |
| countersOut->depthStoreOpNones += ds.storeOp == VK_ATTACHMENT_STORE_OP_NONE_EXT ? 1 : 0; |
| |
| countersOut->stencilLoadOpClears += ds.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ? 1 : 0; |
| countersOut->stencilLoadOpLoads += ds.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD ? 1 : 0; |
| countersOut->stencilLoadOpNones += ds.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_NONE_EXT ? 1 : 0; |
| countersOut->stencilStoreOpStores += ds.stencilStoreOp == VK_ATTACHMENT_STORE_OP_STORE ? 1 : 0; |
| countersOut->stencilStoreOpNones += |
| ds.stencilStoreOp == VK_ATTACHMENT_STORE_OP_NONE_EXT ? 1 : 0; |
| |
| // Depth/stencil read-only mode. |
| countersOut->readOnlyDepthStencil += |
| ds.finalLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL ? 1 : 0; |
| } |
| |
| void UpdateRenderPassDepthStencilResolvePerfCounters( |
| const VkRenderPassCreateInfo &createInfo, |
| const VkSubpassDescriptionDepthStencilResolve &depthStencilResolve, |
| RenderPassPerfCounters *countersOut) |
| { |
| if (depthStencilResolve.pDepthStencilResolveAttachment == nullptr) |
| { |
| return; |
| } |
| |
| uint32_t resolveRenderPassIndex = |
| depthStencilResolve.pDepthStencilResolveAttachment->attachment; |
| |
| if (resolveRenderPassIndex == VK_ATTACHMENT_UNUSED) |
| { |
| return; |
| } |
| |
| const VkAttachmentDescription &dsResolve = createInfo.pAttachments[resolveRenderPassIndex]; |
| |
| // Resolve depth/stencil ops counters. |
| countersOut->depthLoadOpClears += dsResolve.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ? 1 : 0; |
| countersOut->depthLoadOpLoads += dsResolve.loadOp == VK_ATTACHMENT_LOAD_OP_LOAD ? 1 : 0; |
| countersOut->depthStoreOpStores += |
| dsResolve.storeOp == static_cast<uint16_t>(RenderPassStoreOp::Store) ? 1 : 0; |
| |
| countersOut->stencilLoadOpClears += |
| dsResolve.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ? 1 : 0; |
| countersOut->stencilLoadOpLoads += |
| dsResolve.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD ? 1 : 0; |
| countersOut->stencilStoreOpStores += |
| dsResolve.stencilStoreOp == static_cast<uint16_t>(RenderPassStoreOp::Store) ? 1 : 0; |
| |
| // Depth/stencil resolve counters. |
| countersOut->depthAttachmentResolves += |
| depthStencilResolve.depthResolveMode != VK_RESOLVE_MODE_NONE ? 1 : 0; |
| countersOut->stencilAttachmentResolves += |
| depthStencilResolve.stencilResolveMode != VK_RESOLVE_MODE_NONE ? 1 : 0; |
| } |
| |
| void UpdateRenderPassPerfCounters( |
| const RenderPassDesc &desc, |
| const VkRenderPassCreateInfo &createInfo, |
| const VkSubpassDescriptionDepthStencilResolve &depthStencilResolve, |
| RenderPassPerfCounters *countersOut) |
| { |
| // Accumulate depth/stencil attachment indices in all subpasses to avoid double-counting |
| // counters. |
| FramebufferAttachmentMask depthStencilAttachmentIndices; |
| |
| for (uint32_t subpassIndex = 0; subpassIndex < createInfo.subpassCount; ++subpassIndex) |
| { |
| const VkSubpassDescription &subpass = createInfo.pSubpasses[subpassIndex]; |
| |
| // Color counters. |
| // NOTE: For simplicity, this will accumulate counts for all subpasses in the renderpass. |
| UpdateSubpassColorPerfCounters(createInfo, subpass, countersOut); |
| |
| // Record index of depth/stencil attachment. |
| if (subpass.pDepthStencilAttachment != nullptr) |
| { |
| uint32_t attachmentRenderPassIndex = subpass.pDepthStencilAttachment->attachment; |
| if (attachmentRenderPassIndex != VK_ATTACHMENT_UNUSED) |
| { |
| depthStencilAttachmentIndices.set(attachmentRenderPassIndex); |
| } |
| } |
| } |
| |
| UpdateRenderPassColorPerfCounters(createInfo, depthStencilAttachmentIndices, countersOut); |
| |
| // Depth/stencil counters. Currently, both subpasses use the same depth/stencil attachment (if |
| // any). |
| ASSERT(depthStencilAttachmentIndices.count() <= 1); |
| for (size_t attachmentRenderPassIndex : depthStencilAttachmentIndices) |
| { |
| UpdateRenderPassDepthStencilPerfCounters(createInfo, attachmentRenderPassIndex, |
| countersOut); |
| } |
| |
| UpdateRenderPassDepthStencilResolvePerfCounters(createInfo, depthStencilResolve, countersOut); |
| |
| // Determine unresolve counters from the render pass desc, to avoid making guesses from subpass |
| // count etc. |
| countersOut->colorAttachmentUnresolves += desc.getColorUnresolveAttachmentMask().count(); |
| countersOut->depthAttachmentUnresolves += desc.hasDepthUnresolveAttachment() ? 1 : 0; |
| countersOut->stencilAttachmentUnresolves += desc.hasStencilUnresolveAttachment() ? 1 : 0; |
| } |
| |
| angle::Result InitializeRenderPassFromDesc(ContextVk *contextVk, |
| const RenderPassDesc &desc, |
| const AttachmentOpsArray &ops, |
| RenderPassHelper *renderPassHelper) |
| { |
| constexpr VkAttachmentReference kUnusedAttachment = {VK_ATTACHMENT_UNUSED, |
| VK_IMAGE_LAYOUT_UNDEFINED}; |
| constexpr VkAttachmentReference2 kUnusedAttachment2 = { |
| VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR, nullptr, VK_ATTACHMENT_UNUSED, |
| VK_IMAGE_LAYOUT_UNDEFINED, 0}; |
| |
| const bool needInputAttachments = desc.getFramebufferFetchMode(); |
| const bool isRenderToTextureThroughExtension = |
| desc.isRenderToTexture() && |
| contextVk->getFeatures().supportsMultisampledRenderToSingleSampled.enabled; |
| const bool isRenderToTextureThroughEmulation = |
| desc.isRenderToTexture() && !isRenderToTextureThroughExtension; |
| |
| const uint8_t descSamples = desc.samples(); |
| const uint8_t attachmentSamples = isRenderToTextureThroughExtension ? 1 : descSamples; |
| const uint8_t renderToTextureSamples = isRenderToTextureThroughExtension ? descSamples : 1; |
| |
| // Unpack the packed and split representation into the format required by Vulkan. |
| gl::DrawBuffersVector<VkAttachmentReference> colorAttachmentRefs; |
| gl::DrawBuffersVector<VkAttachmentReference> colorResolveAttachmentRefs; |
| VkAttachmentReference depthStencilAttachmentRef = kUnusedAttachment; |
| VkAttachmentReference2KHR depthStencilResolveAttachmentRef = kUnusedAttachment2; |
| |
| // The list of attachments includes all non-resolve and resolve attachments. |
| FramebufferAttachmentArray<VkAttachmentDescription> attachmentDescs; |
| |
| // Track invalidated attachments so their resolve attachments can be invalidated as well. |
| // Resolve attachments can be removed in that case if the render pass has only one subpass |
| // (which is the case if there are no unresolve attachments). |
| gl::DrawBufferMask isColorInvalidated; |
| bool isDepthInvalidated = false; |
| bool isStencilInvalidated = false; |
| const bool hasUnresolveAttachments = |
| desc.getColorUnresolveAttachmentMask().any() || desc.hasDepthStencilUnresolveAttachment(); |
| const bool canRemoveResolveAttachments = !hasUnresolveAttachments; |
| |
| // Pack color attachments |
| PackedAttachmentIndex attachmentCount(0); |
| for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL) |
| { |
| // Vulkan says: |
| // |
| // > Each element of the pColorAttachments array corresponds to an output location in the |
| // > shader, i.e. if the shader declares an output variable decorated with a Location value |
| // > of X, then it uses the attachment provided in pColorAttachments[X]. |
| // |
| // This means that colorAttachmentRefs is indexed by colorIndexGL. Where the color |
| // attachment is disabled, a reference with VK_ATTACHMENT_UNUSED is given. |
| |
| if (!desc.isColorAttachmentEnabled(colorIndexGL)) |
| { |
| colorAttachmentRefs.push_back(kUnusedAttachment); |
| continue; |
| } |
| |
| angle::FormatID attachmentFormatID = desc[colorIndexGL]; |
| ASSERT(attachmentFormatID != angle::FormatID::NONE); |
| |
| VkAttachmentReference colorRef; |
| colorRef.attachment = attachmentCount.get(); |
| colorRef.layout = needInputAttachments |
| ? VK_IMAGE_LAYOUT_GENERAL |
| : ConvertImageLayoutToVkImageLayout( |
| static_cast<ImageLayout>(ops[attachmentCount].initialLayout)); |
| colorAttachmentRefs.push_back(colorRef); |
| |
| UnpackAttachmentDesc(&attachmentDescs[attachmentCount.get()], attachmentFormatID, |
| attachmentSamples, ops[attachmentCount]); |
| |
| // If this renderpass uses EXT_srgb_write_control, we need to override the format to its |
| // linear counterpart. Formats that cannot be reinterpreted are exempt from this |
| // requirement. |
| angle::FormatID linearFormat = rx::ConvertToLinear(attachmentFormatID); |
| if (linearFormat != angle::FormatID::NONE) |
| { |
| if (desc.getSRGBWriteControlMode() == gl::SrgbWriteControlMode::Linear) |
| { |
| attachmentFormatID = linearFormat; |
| } |
| } |
| attachmentDescs[attachmentCount.get()].format = GetVkFormatFromFormatID(attachmentFormatID); |
| ASSERT(attachmentDescs[attachmentCount.get()].format != VK_FORMAT_UNDEFINED); |
| |
| isColorInvalidated.set(colorIndexGL, ops[attachmentCount].isInvalidated); |
| |
| ++attachmentCount; |
| } |
| |
| // Pack depth/stencil attachment, if any |
| if (desc.hasDepthStencilAttachment()) |
| { |
| uint32_t depthStencilIndexGL = static_cast<uint32_t>(desc.depthStencilAttachmentIndex()); |
| |
| angle::FormatID attachmentFormatID = desc[depthStencilIndexGL]; |
| ASSERT(attachmentFormatID != angle::FormatID::NONE); |
| |
| depthStencilAttachmentRef.attachment = attachmentCount.get(); |
| depthStencilAttachmentRef.layout = ConvertImageLayoutToVkImageLayout( |
| static_cast<ImageLayout>(ops[attachmentCount].initialLayout)); |
| |
| UnpackAttachmentDesc(&attachmentDescs[attachmentCount.get()], attachmentFormatID, |
| attachmentSamples, ops[attachmentCount]); |
| |
| isDepthInvalidated = ops[attachmentCount].isInvalidated; |
| isStencilInvalidated = ops[attachmentCount].isStencilInvalidated; |
| |
| ++attachmentCount; |
| } |
| |
| // Pack color resolve attachments |
| const uint32_t nonResolveAttachmentCount = attachmentCount.get(); |
| for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL) |
| { |
| if (!desc.hasColorResolveAttachment(colorIndexGL)) |
| { |
| colorResolveAttachmentRefs.push_back(kUnusedAttachment); |
| continue; |
| } |
| |
| ASSERT(desc.isColorAttachmentEnabled(colorIndexGL)); |
| |
| angle::FormatID attachmentFormatID = desc[colorIndexGL]; |
| |
| VkAttachmentReference colorRef; |
| colorRef.attachment = attachmentCount.get(); |
| colorRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| |
| // If color attachment is invalidated, try to remove its resolve attachment altogether. |
| if (canRemoveResolveAttachments && isColorInvalidated.test(colorIndexGL)) |
| { |
| colorResolveAttachmentRefs.push_back(kUnusedAttachment); |
| } |
| else |
| { |
| colorResolveAttachmentRefs.push_back(colorRef); |
| } |
| |
| // When multisampled-render-to-texture is used, invalidating an attachment invalidates both |
| // the multisampled and the resolve attachments. Otherwise, the resolve attachment is |
| // independent of the multisampled attachment, and is never invalidated. |
| UnpackColorResolveAttachmentDesc( |
| &attachmentDescs[attachmentCount.get()], attachmentFormatID, |
| desc.hasColorUnresolveAttachment(colorIndexGL), |
| isColorInvalidated.test(colorIndexGL) && isRenderToTextureThroughEmulation); |
| |
| ++attachmentCount; |
| } |
| |
| // Pack depth/stencil resolve attachment, if any |
| if (desc.hasDepthStencilResolveAttachment()) |
| { |
| ASSERT(desc.hasDepthStencilAttachment()); |
| |
| uint32_t depthStencilIndexGL = static_cast<uint32_t>(desc.depthStencilAttachmentIndex()); |
| |
| angle::FormatID attachmentFormatID = desc[depthStencilIndexGL]; |
| const angle::Format &angleFormat = angle::Format::Get(attachmentFormatID); |
| |
| // Treat missing aspect as invalidated for the purpose of the resolve attachment. |
| if (angleFormat.depthBits == 0) |
| { |
| isDepthInvalidated = true; |
| } |
| if (angleFormat.stencilBits == 0) |
| { |
| isStencilInvalidated = true; |
| } |
| |
| depthStencilResolveAttachmentRef.attachment = attachmentCount.get(); |
| depthStencilResolveAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
| depthStencilResolveAttachmentRef.aspectMask = 0; |
| |
| if (!isDepthInvalidated) |
| { |
| depthStencilResolveAttachmentRef.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; |
| } |
| if (!isStencilInvalidated) |
| { |
| depthStencilResolveAttachmentRef.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; |
| } |
| |
| UnpackDepthStencilResolveAttachmentDesc( |
| &attachmentDescs[attachmentCount.get()], attachmentFormatID, |
| desc.hasDepthUnresolveAttachment(), desc.hasStencilUnresolveAttachment(), |
| isDepthInvalidated, isStencilInvalidated); |
| |
| ++attachmentCount; |
| } |
| |
| SubpassVector<VkSubpassDescription> subpassDesc; |
| |
| // If any attachment needs to be unresolved, create an initial subpass for this purpose. Note |
| // that the following arrays are used in initializing a VkSubpassDescription in subpassDesc, |
| // which is in turn used in VkRenderPassCreateInfo below. That is why they are declared in the |
| // same scope. |
| gl::DrawBuffersVector<VkAttachmentReference> unresolveColorAttachmentRefs; |
| VkAttachmentReference unresolveDepthStencilAttachmentRef = kUnusedAttachment; |
| FramebufferAttachmentsVector<VkAttachmentReference> unresolveInputAttachmentRefs; |
| FramebufferAttachmentsVector<uint32_t> unresolvePreserveAttachmentRefs; |
| if (hasUnresolveAttachments) |
| { |
| subpassDesc.push_back({}); |
| InitializeUnresolveSubpass( |
| desc, colorAttachmentRefs, colorResolveAttachmentRefs, depthStencilAttachmentRef, |
| depthStencilResolveAttachmentRef, &unresolveColorAttachmentRefs, |
| &unresolveDepthStencilAttachmentRef, &unresolveInputAttachmentRefs, |
| &unresolvePreserveAttachmentRefs, &subpassDesc.back()); |
| } |
| |
| subpassDesc.push_back({}); |
| VkSubpassDescription *applicationSubpass = &subpassDesc.back(); |
| |
| applicationSubpass->flags = 0; |
| applicationSubpass->pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
| applicationSubpass->inputAttachmentCount = |
| needInputAttachments ? static_cast<uint32_t>(colorAttachmentRefs.size()) : 0; |
| applicationSubpass->pInputAttachments = |
| needInputAttachments ? colorAttachmentRefs.data() : nullptr; |
| applicationSubpass->colorAttachmentCount = static_cast<uint32_t>(colorAttachmentRefs.size()); |
| applicationSubpass->pColorAttachments = colorAttachmentRefs.data(); |
| applicationSubpass->pResolveAttachments = attachmentCount.get() > nonResolveAttachmentCount |
| ? colorResolveAttachmentRefs.data() |
| : nullptr; |
| applicationSubpass->pDepthStencilAttachment = |
| (depthStencilAttachmentRef.attachment != VK_ATTACHMENT_UNUSED ? &depthStencilAttachmentRef |
| : nullptr); |
| applicationSubpass->preserveAttachmentCount = 0; |
| applicationSubpass->pPreserveAttachments = nullptr; |
| |
| // If depth/stencil is to be resolved, add a VkSubpassDescriptionDepthStencilResolve to the |
| // pNext chain of the subpass description. Note that we need a VkSubpassDescription2KHR to have |
| // a pNext pointer. CreateRenderPass2 is called to convert the data structures here to those |
| // specified by VK_KHR_create_renderpass2 for this purpose. |
| VkSubpassDescriptionDepthStencilResolve depthStencilResolve = {}; |
| if (desc.hasDepthStencilResolveAttachment()) |
| { |
| depthStencilResolve.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE; |
| depthStencilResolve.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; |
| depthStencilResolve.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; |
| |
| // If depth/stencil attachment is invalidated, try to remove its resolve attachment |
| // altogether. |
| const bool removeDepthStencilResolve = |
| canRemoveResolveAttachments && isDepthInvalidated && isStencilInvalidated; |
| if (!removeDepthStencilResolve) |
| { |
| depthStencilResolve.pDepthStencilResolveAttachment = &depthStencilResolveAttachmentRef; |
| } |
| } |
| |
| std::vector<VkSubpassDependency> subpassDependencies; |
| if (hasUnresolveAttachments) |
| { |
| InitializeUnresolveSubpassDependencies( |
| subpassDesc, desc.getColorUnresolveAttachmentMask().any(), |
| desc.hasDepthStencilUnresolveAttachment(), &subpassDependencies); |
| } |
| |
| const uint32_t drawSubpassIndex = static_cast<uint32_t>(subpassDesc.size()) - 1; |
| InitializeDefaultSubpassSelfDependencies(contextVk, desc, drawSubpassIndex, |
| &subpassDependencies); |
| |
| VkRenderPassCreateInfo createInfo = {}; |
| createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; |
| createInfo.flags = 0; |
| createInfo.attachmentCount = attachmentCount.get(); |
| createInfo.pAttachments = attachmentDescs.data(); |
| createInfo.subpassCount = static_cast<uint32_t>(subpassDesc.size()); |
| createInfo.pSubpasses = subpassDesc.data(); |
| createInfo.dependencyCount = 0; |
| createInfo.pDependencies = nullptr; |
| |
| if (!subpassDependencies.empty()) |
| { |
| createInfo.dependencyCount = static_cast<uint32_t>(subpassDependencies.size()); |
| createInfo.pDependencies = subpassDependencies.data(); |
| } |
| |
| SubpassVector<uint32_t> viewMasks(subpassDesc.size(), |
| angle::BitMask<uint32_t>(desc.viewCount())); |
| VkRenderPassMultiviewCreateInfo multiviewInfo = {}; |
| multiviewInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO; |
| multiviewInfo.subpassCount = createInfo.subpassCount; |
| multiviewInfo.pViewMasks = viewMasks.data(); |
| |
| if (desc.viewCount() > 0) |
| { |
| // For VR, the views are correlated, so this would be an optimization. However, an |
| // application can also use multiview for example to render to all 6 faces of a cubemap, in |
| // which case the views are actually not so correlated. In the absence of any hints from |
| // the application, we have to decide on one or the other. Since VR is more expensive, the |
| // views are marked as correlated to optimize that use case. |
| multiviewInfo.correlationMaskCount = 1; |
| multiviewInfo.pCorrelationMasks = viewMasks.data(); |
| |
| createInfo.pNext = &multiviewInfo; |
| } |
| |
| // If depth/stencil resolve is used, we need to create the render pass with |
| // vkCreateRenderPass2KHR. Same when using the VK_EXT_multisampled_render_to_single_sampled |
| // extension. |
| if (depthStencilResolve.pDepthStencilResolveAttachment != nullptr || |
| isRenderToTextureThroughExtension) |
| { |
| ANGLE_TRY(CreateRenderPass2(contextVk, createInfo, depthStencilResolve, multiviewInfo, |
| desc.hasDepthUnresolveAttachment(), |
| desc.hasStencilUnresolveAttachment(), |
| isRenderToTextureThroughExtension, renderToTextureSamples, |
| &renderPassHelper->getRenderPass())); |
| } |
| else |
| { |
| ANGLE_VK_TRY(contextVk, |
| renderPassHelper->getRenderPass().init(contextVk->getDevice(), createInfo)); |
| } |
| |
| // Calculate perf counters associated with this render pass, such as load/store ops, unresolve |
| // and resolve operations etc. This information is taken out of the render pass create info. |
| // Depth/stencil resolve attachment uses RenderPass2 structures, so it's passed in separately. |
| UpdateRenderPassPerfCounters(desc, createInfo, depthStencilResolve, |
| &renderPassHelper->getPerfCounters()); |
| |
| return angle::Result::Continue; |
| } |
| |
| void GetRenderPassAndUpdateCounters(ContextVk *contextVk, |
| bool updatePerfCounters, |
| RenderPassHelper *renderPassHelper, |
| RenderPass **renderPassOut) |
| { |
| *renderPassOut = &renderPassHelper->getRenderPass(); |
| if (updatePerfCounters) |
| { |
| angle::VulkanPerfCounters &counters = contextVk->getPerfCounters(); |
| const RenderPassPerfCounters &rpCounters = renderPassHelper->getPerfCounters(); |
| |
| counters.colorLoadOpClears += rpCounters.colorLoadOpClears; |
| counters.colorLoadOpLoads += rpCounters.colorLoadOpLoads; |
| counters.colorLoadOpNones += rpCounters.colorLoadOpNones; |
| counters.colorStoreOpStores += rpCounters.colorStoreOpStores; |
| counters.colorStoreOpNones += rpCounters.colorStoreOpNones; |
| counters.depthLoadOpClears += rpCounters.depthLoadOpClears; |
| counters.depthLoadOpLoads += rpCounters.depthLoadOpLoads; |
| counters.depthLoadOpNones += rpCounters.depthLoadOpNones; |
| counters.depthStoreOpStores += rpCounters.depthStoreOpStores; |
| counters.depthStoreOpNones += rpCounters.depthStoreOpNones; |
| counters.stencilLoadOpClears += rpCounters.stencilLoadOpClears; |
| counters.stencilLoadOpLoads += rpCounters.stencilLoadOpLoads; |
| counters.stencilLoadOpNones += rpCounters.stencilLoadOpNones; |
| counters.stencilStoreOpStores += rpCounters.stencilStoreOpStores; |
| counters.stencilStoreOpNones += rpCounters.stencilStoreOpNones; |
| counters.colorAttachmentUnresolves += rpCounters.colorAttachmentUnresolves; |
| counters.colorAttachmentResolves += rpCounters.colorAttachmentResolves; |
| counters.depthAttachmentUnresolves += rpCounters.depthAttachmentUnresolves; |
| counters.depthAttachmentResolves += rpCounters.depthAttachmentResolves; |
| counters.stencilAttachmentUnresolves += rpCounters.stencilAttachmentUnresolves; |
| counters.stencilAttachmentResolves += rpCounters.stencilAttachmentResolves; |
| counters.readOnlyDepthStencilRenderPasses += rpCounters.readOnlyDepthStencil; |
| } |
| } |
| |
| void InitializeSpecializationInfo( |
| const SpecializationConstants &specConsts, |
| SpecializationConstantMap<VkSpecializationMapEntry> *specializationEntriesOut, |
| VkSpecializationInfo *specializationInfoOut) |
| { |
| // Collect specialization constants. |
| for (const sh::vk::SpecializationConstantId id : |
| angle::AllEnums<sh::vk::SpecializationConstantId>()) |
| { |
| (*specializationEntriesOut)[id].constantID = static_cast<uint32_t>(id); |
| switch (id) |
| { |
| case sh::vk::SpecializationConstantId::SurfaceRotation: |
| (*specializationEntriesOut)[id].offset = |
| offsetof(SpecializationConstants, surfaceRotation); |
| (*specializationEntriesOut)[id].size = sizeof(specConsts.surfaceRotation); |
| break; |
| case sh::vk::SpecializationConstantId::Dither: |
| (*specializationEntriesOut)[id].offset = |
| offsetof(vk::SpecializationConstants, dither); |
| (*specializationEntriesOut)[id].size = sizeof(specConsts.dither); |
| break; |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| } |
| |
| specializationInfoOut->mapEntryCount = static_cast<uint32_t>(specializationEntriesOut->size()); |
| specializationInfoOut->pMapEntries = specializationEntriesOut->data(); |
| specializationInfoOut->dataSize = sizeof(specConsts); |
| specializationInfoOut->pData = &specConsts; |
| } |
| |
| // Adjust border color value according to intended format. |
| gl::ColorGeneric AdjustBorderColor(const angle::ColorGeneric &borderColorGeneric, |
| const angle::Format &format, |
| bool stencilMode) |
| { |
| gl::ColorGeneric adjustedBorderColor = borderColorGeneric; |
| |
| // Handle depth formats |
| if (format.hasDepthOrStencilBits()) |
| { |
| if (stencilMode) |
| { |
| // Stencil component |
| adjustedBorderColor.colorUI.red = gl::clampForBitCount<unsigned int>( |
| borderColorGeneric.colorUI.red, format.stencilBits); |
| } |
| else |
| { |
| // Depth component |
| if (format.isUnorm()) |
| { |
| adjustedBorderColor.colorF.red = gl::clamp01(borderColorGeneric.colorF.red); |
| } |
| } |
| |
| return adjustedBorderColor; |
| } |
| |
| // Handle LUMA formats |
| if (format.isLUMA() && format.alphaBits > 0) |
| { |
| if (format.luminanceBits > 0) |
| { |
| adjustedBorderColor.colorF.green = borderColorGeneric.colorF.alpha; |
| } |
| else |
| { |
| adjustedBorderColor.colorF.red = borderColorGeneric.colorF.alpha; |
| } |
| |
| return adjustedBorderColor; |
| } |
| |
| // Handle all other formats |
| if (format.isSint()) |
| { |
| adjustedBorderColor.colorI.red = |
| gl::clampForBitCount<int>(borderColorGeneric.colorI.red, format.redBits); |
| adjustedBorderColor.colorI.green = |
| gl::clampForBitCount<int>(borderColorGeneric.colorI.green, format.greenBits); |
| adjustedBorderColor.colorI.blue = |
| gl::clampForBitCount<int>(borderColorGeneric.colorI.blue, format.blueBits); |
| adjustedBorderColor.colorI.alpha = |
| gl::clampForBitCount<int>(borderColorGeneric.colorI.alpha, format.alphaBits); |
| } |
| else if (format.isUint()) |
| { |
| adjustedBorderColor.colorUI.red = |
| gl::clampForBitCount<unsigned int>(borderColorGeneric.colorUI.red, format.redBits); |
| adjustedBorderColor.colorUI.green = |
| gl::clampForBitCount<unsigned int>(borderColorGeneric.colorUI.green, format.greenBits); |
| adjustedBorderColor.colorUI.blue = |
| gl::clampForBitCount<unsigned int>(borderColorGeneric.colorUI.blue, format.blueBits); |
| adjustedBorderColor.colorUI.alpha = |
| gl::clampForBitCount<unsigned int>(borderColorGeneric.colorUI.alpha, format.alphaBits); |
| } |
| else if (format.isSnorm()) |
| { |
| // clamp between -1.0f and 1.0f |
| adjustedBorderColor.colorF.red = gl::clamp(borderColorGeneric.colorF.red, -1.0f, 1.0f); |
| adjustedBorderColor.colorF.green = gl::clamp(borderColorGeneric.colorF.green, -1.0f, 1.0f); |
| adjustedBorderColor.colorF.blue = gl::clamp(borderColorGeneric.colorF.blue, -1.0f, 1.0f); |
| adjustedBorderColor.colorF.alpha = gl::clamp(borderColorGeneric.colorF.alpha, -1.0f, 1.0f); |
| } |
| else if (format.isUnorm()) |
| { |
| // clamp between 0.0f and 1.0f |
| adjustedBorderColor.colorF.red = gl::clamp01(borderColorGeneric.colorF.red); |
| adjustedBorderColor.colorF.green = gl::clamp01(borderColorGeneric.colorF.green); |
| adjustedBorderColor.colorF.blue = gl::clamp01(borderColorGeneric.colorF.blue); |
| adjustedBorderColor.colorF.alpha = gl::clamp01(borderColorGeneric.colorF.alpha); |
| } |
| |
| return adjustedBorderColor; |
| } |
| |
| // Utility for setting a value on a packed 4-bit integer array. |
| template <typename SrcT> |
| void Int4Array_Set(uint8_t *arrayBytes, uint32_t arrayIndex, SrcT value) |
| { |
| uint32_t byteIndex = arrayIndex >> 1; |
| ASSERT(value < 16); |
| |
| if ((arrayIndex & 1) == 0) |
| { |
| arrayBytes[byteIndex] &= 0xF0; |
| arrayBytes[byteIndex] |= static_cast<uint8_t>(value); |
| } |
| else |
| { |
| arrayBytes[byteIndex] &= 0x0F; |
| arrayBytes[byteIndex] |= static_cast<uint8_t>(value) << 4; |
| } |
| } |
| |
| // Utility for getting a value from a packed 4-bit integer array. |
| template <typename DestT> |
| DestT Int4Array_Get(const uint8_t *arrayBytes, uint32_t arrayIndex) |
| { |
| uint32_t byteIndex = arrayIndex >> 1; |
| |
| if ((arrayIndex & 1) == 0) |
| { |
| return static_cast<DestT>(arrayBytes[byteIndex] & 0xF); |
| } |
| else |
| { |
| return static_cast<DestT>(arrayBytes[byteIndex] >> 4); |
| } |
| } |
| |
| // When converting a byte number to a transition bit index we can shift instead of divide. |
| constexpr size_t kTransitionByteShift = Log2(kGraphicsPipelineDirtyBitBytes); |
| |
| // When converting a number of bits offset to a transition bit index we can also shift. |
| constexpr size_t kBitsPerByte = 8; |
| constexpr size_t kTransitionBitShift = kTransitionByteShift + Log2(kBitsPerByte); |
| |
| // Helper macro to map from a PipelineDesc struct and field to a dirty bit index. |
| // Uses the 'offsetof' macro to compute the offset 'Member' within the PipelineDesc |
| // and the offset of 'Field' within 'Member'. We can optimize the dirty bit setting by computing |
| // the shifted dirty bit at compile time instead of calling "set". |
| #define ANGLE_GET_TRANSITION_BIT(Member, Field) \ |
| ((offsetof(GraphicsPipelineDesc, Member) + offsetof(decltype(Member), Field)) >> \ |
| kTransitionByteShift) |
| |
| // Indexed dirty bits cannot be entirely computed at compile time since the index is passed to |
| // the update function. |
| #define ANGLE_GET_INDEXED_TRANSITION_BIT(Member, Field, Index, BitWidth) \ |
| (((BitWidth * Index) >> kTransitionBitShift) + ANGLE_GET_TRANSITION_BIT(Member, Field)) |
| |
| constexpr angle::PackedEnumMap<gl::ComponentType, VkFormat> kMismatchedComponentTypeMap = {{ |
| {gl::ComponentType::Float, VK_FORMAT_R32G32B32A32_SFLOAT}, |
| {gl::ComponentType::Int, VK_FORMAT_R32G32B32A32_SINT}, |
| {gl::ComponentType::UnsignedInt, VK_FORMAT_R32G32B32A32_UINT}, |
| }}; |
| |
| constexpr char kDescriptorTypeNameMap[][30] = {"sampler", |
| "combined image sampler", |
| "sampled image", |
| "storage image", |
| "uniform texel buffer", |
| "storage texel buffer", |
| "uniform buffer", |
| "storage buffer", |
| "uniform buffer dynamic", |
| "storage buffer dynamic", |
| "input attachment"}; |
| |
| // Helpers for creating a readable dump of the graphics pipeline graph. Each program generates a |
| // group of nodes. The group's description is the common state among all nodes. Each node contains |
| // the diff with the shared state. Arrows between nodes indicate the GraphicsPipelineTransitionBits |
| // that have caused the transition. State that is 0 is not output for brevity. |
| enum class PipelineState |
| { |
| VertexAttribFormat, |
| VertexAttribDivisor = VertexAttribFormat + gl::MAX_VERTEX_ATTRIBS, |
| VertexAttribOffset = VertexAttribDivisor + gl::MAX_VERTEX_ATTRIBS, |
| VertexAttribStride = VertexAttribOffset + gl::MAX_VERTEX_ATTRIBS, |
| VertexAttribCompressed = VertexAttribStride + gl::MAX_VERTEX_ATTRIBS, |
| RenderPassSamples = VertexAttribCompressed + gl::MAX_VERTEX_ATTRIBS, |
| RenderPassColorAttachmentRange, |
| RenderPassViewCount, |
| RenderPassSrgbWriteControl, |
| RenderPassHasFramebufferFetch, |
| RenderPassIsRenderToTexture, |
| RenderPassResolveDepthStencil, |
| RenderPassUnresolveDepth, |
| RenderPassUnresolveStencil, |
| RenderPassColorResolveMask, |
| RenderPassColorUnresolveMask, |
| RenderPassColorFormat, |
| RenderPassDepthStencilFormat = RenderPassColorFormat + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS, |
| Subpass, |
| Topology, |
| PatchVertices, |
| PrimitiveRestartEnable, |
| CullMode, |
| FrontFace, |
| SurfaceRotation, |
| ViewportNegativeOneToOne, |
| SampleShadingEnable, |
| RasterizationSamples, |
| MinSampleShading, |
| SampleMask, |
| AlphaToCoverageEnable, |
| AlphaToOneEnable, |
| LogicOpEnable, |
| LogicOp, |
| RasterizerDiscardEnable, |
| ColorWriteMask, |
| BlendEnableMask = ColorWriteMask + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS, |
| SrcColorBlendFactor, |
| DstColorBlendFactor = SrcColorBlendFactor + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS, |
| ColorBlendOp = DstColorBlendFactor + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS, |
| SrcAlphaBlendFactor = ColorBlendOp + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS, |
| DstAlphaBlendFactor = SrcAlphaBlendFactor + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS, |
| AlphaBlendOp = DstAlphaBlendFactor + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS, |
| EmulatedDitherControl = AlphaBlendOp + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS, |
| DepthClampEnable, |
| DepthBoundsTest, |
| DepthCompareOp, |
| DepthTest, |
| DepthWrite, |
| StencilTest, |
| DepthBiasEnable, |
| StencilOpFailFront, |
| StencilOpPassFront, |
| StencilOpDepthFailFront, |
| StencilCompareFront, |
| StencilOpFailBack, |
| StencilOpPassBack, |
| StencilOpDepthFailBack, |
| StencilCompareBack, |
| |
| InvalidEnum, |
| EnumCount = InvalidEnum, |
| }; |
| |
| using UnpackedPipelineState = angle::PackedEnumMap<PipelineState, uint32_t>; |
| using PipelineStateBitSet = angle::BitSetArray<angle::EnumSize<PipelineState>()>; |
| |
| void UnpackPipelineState(const vk::GraphicsPipelineDesc &state, UnpackedPipelineState *valuesOut) |
| { |
| const VertexInputAttributes &vertexInputAttribs = state.getVertexInputAttribsForLog(); |
| const RenderPassDesc &renderPassDesc = state.getRenderPassDescForLog(); |
| const PackedInputAssemblyAndRasterizationStateInfo &inputAndRaster = |
| state.getInputAssemblyAndRasterizationStateInfoForLog(); |
| const PackedColorBlendStateInfo &colorBlend = state.getColorBlendStateInfoForLog(); |
| const PackedDither &dither = state.getDitherForLog(); |
| const PackedDynamicState &dynamicState = state.getDynamicStateForLog(); |
| |
| valuesOut->fill(0); |
| |
| uint32_t *vaFormats = &(*valuesOut)[PipelineState::VertexAttribFormat]; |
| uint32_t *vaDivisors = &(*valuesOut)[PipelineState::VertexAttribDivisor]; |
| uint32_t *vaOffsets = &(*valuesOut)[PipelineState::VertexAttribOffset]; |
| uint32_t *vaStrides = &(*valuesOut)[PipelineState::VertexAttribStride]; |
| uint32_t *vaCompressed = &(*valuesOut)[PipelineState::VertexAttribCompressed]; |
| for (uint32_t attribIndex = 0; attribIndex < gl::MAX_VERTEX_ATTRIBS; ++attribIndex) |
| { |
| vaFormats[attribIndex] = vertexInputAttribs.attribs[attribIndex].format; |
| vaDivisors[attribIndex] = vertexInputAttribs.attribs[attribIndex].divisor; |
| vaOffsets[attribIndex] = vertexInputAttribs.attribs[attribIndex].offset; |
| vaStrides[attribIndex] = dynamicState.ds1.vertexStrides[attribIndex]; |
| vaCompressed[attribIndex] = vertexInputAttribs.attribs[attribIndex].compressed; |
| } |
| |
| (*valuesOut)[PipelineState::RenderPassSamples] = renderPassDesc.samples(); |
| (*valuesOut)[PipelineState::RenderPassColorAttachmentRange] = |
| static_cast<uint32_t>(renderPassDesc.colorAttachmentRange()); |
| (*valuesOut)[PipelineState::RenderPassViewCount] = renderPassDesc.viewCount(); |
| (*valuesOut)[PipelineState::RenderPassSrgbWriteControl] = |
| static_cast<uint32_t>(renderPassDesc.getSRGBWriteControlMode()); |
| (*valuesOut)[PipelineState::RenderPassHasFramebufferFetch] = |
| renderPassDesc.getFramebufferFetchMode(); |
| (*valuesOut)[PipelineState::RenderPassIsRenderToTexture] = renderPassDesc.isRenderToTexture(); |
| (*valuesOut)[PipelineState::RenderPassResolveDepthStencil] = |
| renderPassDesc.hasDepthStencilResolveAttachment(); |
| (*valuesOut)[PipelineState::RenderPassUnresolveDepth] = |
| renderPassDesc.hasDepthUnresolveAttachment(); |
| (*valuesOut)[PipelineState::RenderPassUnresolveStencil] = |
| renderPassDesc.hasStencilUnresolveAttachment(); |
| (*valuesOut)[PipelineState::RenderPassColorResolveMask] = |
| renderPassDesc.getColorResolveAttachmentMask().bits(); |
| (*valuesOut)[PipelineState::RenderPassColorUnresolveMask] = |
| renderPassDesc.getColorUnresolveAttachmentMask().bits(); |
| |
| uint32_t *colorFormats = &(*valuesOut)[PipelineState::RenderPassColorFormat]; |
| for (uint32_t colorIndex = 0; colorIndex < renderPassDesc.colorAttachmentRange(); ++colorIndex) |
| { |
| colorFormats[colorIndex] = static_cast<uint32_t>(renderPassDesc[colorIndex]); |
| } |
| (*valuesOut)[PipelineState::RenderPassDepthStencilFormat] = |
| static_cast<uint32_t>(renderPassDesc[renderPassDesc.depthStencilAttachmentIndex()]); |
| |
| (*valuesOut)[PipelineState::Subpass] = inputAndRaster.bits.subpass; |
| (*valuesOut)[PipelineState::Topology] = inputAndRaster.misc.topology; |
| (*valuesOut)[PipelineState::PatchVertices] = inputAndRaster.misc.patchVertices; |
| (*valuesOut)[PipelineState::PrimitiveRestartEnable] = |
| dynamicState.ds1And2.primitiveRestartEnable; |
| (*valuesOut)[PipelineState::CullMode] = dynamicState.ds1And2.cullMode; |
| (*valuesOut)[PipelineState::FrontFace] = dynamicState.ds1And2.frontFace; |
| (*valuesOut)[PipelineState::SurfaceRotation] = inputAndRaster.misc.surfaceRotation; |
| (*valuesOut)[PipelineState::ViewportNegativeOneToOne] = |
| inputAndRaster.misc.viewportNegativeOneToOne; |
| (*valuesOut)[PipelineState::SampleShadingEnable] = inputAndRaster.bits.sampleShadingEnable; |
| (*valuesOut)[PipelineState::MinSampleShading] = inputAndRaster.misc.minSampleShading; |
| (*valuesOut)[PipelineState::RasterizationSamples] = inputAndRaster.bits.rasterizationSamples; |
| (*valuesOut)[PipelineState::SampleMask] = inputAndRaster.sampleMask; |
| (*valuesOut)[PipelineState::AlphaToCoverageEnable] = inputAndRaster.bits.alphaToCoverageEnable; |
| (*valuesOut)[PipelineState::AlphaToOneEnable] = inputAndRaster.bits.alphaToOneEnable; |
| (*valuesOut)[PipelineState::LogicOpEnable] = inputAndRaster.bits.logicOpEnable; |
| (*valuesOut)[PipelineState::LogicOp] = inputAndRaster.bits.logicOp; |
| (*valuesOut)[PipelineState::RasterizerDiscardEnable] = |
| dynamicState.ds1And2.rasterizerDiscardEnable; |
| (*valuesOut)[PipelineState::BlendEnableMask] = inputAndRaster.misc.blendEnableMask; |
| (*valuesOut)[PipelineState::EmulatedDitherControl] = dither.emulatedDitherControl; |
| (*valuesOut)[PipelineState::DepthBoundsTest] = inputAndRaster.misc.depthBoundsTest; |
| (*valuesOut)[PipelineState::DepthClampEnable] = inputAndRaster.bits.depthClampEnable; |
| (*valuesOut)[PipelineState::DepthCompareOp] = dynamicState.ds1And2.depthCompareOp; |
| (*valuesOut)[PipelineState::DepthTest] = dynamicState.ds1And2.depthTest; |
| (*valuesOut)[PipelineState::DepthWrite] = dynamicState.ds1And2.depthWrite; |
| (*valuesOut)[PipelineState::StencilTest] = dynamicState.ds1And2.stencilTest; |
| (*valuesOut)[PipelineState::DepthBiasEnable] = dynamicState.ds1And2.depthBiasEnable; |
| (*valuesOut)[PipelineState::StencilOpFailFront] = dynamicState.ds1.front.ops.fail; |
| (*valuesOut)[PipelineState::StencilOpPassFront] = dynamicState.ds1.front.ops.pass; |
| (*valuesOut)[PipelineState::StencilOpDepthFailFront] = dynamicState.ds1.front.ops.depthFail; |
| (*valuesOut)[PipelineState::StencilCompareFront] = dynamicState.ds1.front.ops.compare; |
| (*valuesOut)[PipelineState::StencilOpFailBack] = dynamicState.ds1.back.ops.fail; |
| (*valuesOut)[PipelineState::StencilOpPassBack] = dynamicState.ds1.back.ops.pass; |
| (*valuesOut)[PipelineState::StencilOpDepthFailBack] = dynamicState.ds1.back.ops.depthFail; |
| (*valuesOut)[PipelineState::StencilCompareBack] = dynamicState.ds1.back.ops.compare; |
| |
| uint32_t *colorWriteMasks = &(*valuesOut)[PipelineState::ColorWriteMask]; |
| uint32_t *srcColorBlendFactors = &(*valuesOut)[PipelineState::SrcColorBlendFactor]; |
| uint32_t *dstColorBlendFactors = &(*valuesOut)[PipelineState::DstColorBlendFactor]; |
| uint32_t *colorBlendOps = &(*valuesOut)[PipelineState::ColorBlendOp]; |
| uint32_t *srcAlphaBlendFactors = &(*valuesOut)[PipelineState::SrcAlphaBlendFactor]; |
| uint32_t *dstAlphaBlendFactors = &(*valuesOut)[PipelineState::DstAlphaBlendFactor]; |
| uint32_t *alphaBlendOps = &(*valuesOut)[PipelineState::AlphaBlendOp]; |
| const gl::DrawBufferMask blendEnableMask(inputAndRaster.misc.blendEnableMask); |
| for (uint32_t colorIndex = 0; colorIndex < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; ++colorIndex) |
| { |
| colorWriteMasks[colorIndex] = |
| Int4Array_Get<VkColorComponentFlags>(colorBlend.colorWriteMaskBits, colorIndex); |
| |
| srcColorBlendFactors[colorIndex] = colorBlend.attachments[colorIndex].srcColorBlendFactor; |
| dstColorBlendFactors[colorIndex] = colorBlend.attachments[colorIndex].dstColorBlendFactor; |
| colorBlendOps[colorIndex] = colorBlend.attachments[colorIndex].colorBlendOp; |
| srcAlphaBlendFactors[colorIndex] = colorBlend.attachments[colorIndex].srcAlphaBlendFactor; |
| dstAlphaBlendFactors[colorIndex] = colorBlend.attachments[colorIndex].dstAlphaBlendFactor; |
| alphaBlendOps[colorIndex] = colorBlend.attachments[colorIndex].alphaBlendOp; |
| } |
| } |
| |
| PipelineStateBitSet GetCommonPipelineState(const std::vector<UnpackedPipelineState> &pipelines) |
| { |
| PipelineStateBitSet commonState; |
| commonState.set(); |
| |
| ASSERT(!pipelines.empty()); |
| const UnpackedPipelineState &firstPipeline = pipelines[0]; |
| |
| for (const UnpackedPipelineState &pipeline : pipelines) |
| { |
| for (size_t stateIndex = 0; stateIndex < firstPipeline.size(); ++stateIndex) |
| { |
| if (pipeline.data()[stateIndex] != firstPipeline.data()[stateIndex]) |
| { |
| commonState.reset(stateIndex); |
| } |
| } |
| } |
| |
| return commonState; |
| } |
| |
| bool IsPipelineState(size_t stateIndex, PipelineState pipelineState, size_t range) |
| { |
| size_t pipelineStateAsInt = static_cast<size_t>(pipelineState); |
| |
| return stateIndex >= pipelineStateAsInt && stateIndex < pipelineStateAsInt + range; |
| } |
| |
| size_t GetPipelineStateSubIndex(size_t stateIndex, PipelineState pipelineState) |
| { |
| return stateIndex - static_cast<size_t>(pipelineState); |
| } |
| |
| PipelineState GetPipelineState(size_t stateIndex, bool *isRangedOut, size_t *subIndexOut) |
| { |
| constexpr PipelineState kRangedStates[] = { |
| PipelineState::VertexAttribFormat, PipelineState::VertexAttribDivisor, |
| PipelineState::VertexAttribOffset, PipelineState::VertexAttribStride, |
| PipelineState::VertexAttribCompressed, PipelineState::RenderPassColorFormat, |
| PipelineState::ColorWriteMask, PipelineState::SrcColorBlendFactor, |
| PipelineState::DstColorBlendFactor, PipelineState::ColorBlendOp, |
| PipelineState::SrcAlphaBlendFactor, PipelineState::DstAlphaBlendFactor, |
| PipelineState::AlphaBlendOp, |
| }; |
| |
| for (PipelineState ps : kRangedStates) |
| { |
| size_t range; |
| switch (ps) |
| { |
| case PipelineState::VertexAttribFormat: |
| case PipelineState::VertexAttribDivisor: |
| case PipelineState::VertexAttribOffset: |
| case PipelineState::VertexAttribStride: |
| case PipelineState::VertexAttribCompressed: |
| range = gl::MAX_VERTEX_ATTRIBS; |
| break; |
| default: |
| range = gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; |
| break; |
| } |
| |
| if (IsPipelineState(stateIndex, ps, range)) |
| { |
| *subIndexOut = GetPipelineStateSubIndex(stateIndex, ps); |
| *isRangedOut = true; |
| return ps; |
| } |
| } |
| |
| *isRangedOut = false; |
| return static_cast<PipelineState>(stateIndex); |
| } |
| |
| void OutputPipelineState(std::ostream &out, size_t stateIndex, uint32_t state) |
| { |
| size_t subIndex = 0; |
| bool isRanged = false; |
| PipelineState pipelineState = GetPipelineState(stateIndex, &isRanged, &subIndex); |
| |
| constexpr angle::PackedEnumMap<PipelineState, const char *> kStateNames = {{ |
| {PipelineState::VertexAttribFormat, "va_format"}, |
| {PipelineState::VertexAttribDivisor, "va_divisor"}, |
| {PipelineState::VertexAttribOffset, "va_offset"}, |
| {PipelineState::VertexAttribStride, "va_stride"}, |
| {PipelineState::VertexAttribCompressed, "va_compressed"}, |
| {PipelineState::RenderPassSamples, "rp_samples"}, |
| {PipelineState::RenderPassColorAttachmentRange, "rp_color_range"}, |
| {PipelineState::RenderPassViewCount, "rp_views"}, |
| {PipelineState::RenderPassSrgbWriteControl, "rp_srgb"}, |
| {PipelineState::RenderPassHasFramebufferFetch, "rp_has_framebuffer_fetch"}, |
| {PipelineState::RenderPassIsRenderToTexture, "rp_is_msrtt"}, |
| {PipelineState::RenderPassResolveDepthStencil, "rp_resolve_depth_stencil"}, |
| {PipelineState::RenderPassUnresolveDepth, "rp_unresolve_depth"}, |
| {PipelineState::RenderPassUnresolveStencil, "rp_unresolve_stencil"}, |
| {PipelineState::RenderPassColorResolveMask, "rp_resolve_color"}, |
| {PipelineState::RenderPassColorUnresolveMask, "rp_unresolve_color"}, |
| {PipelineState::RenderPassColorFormat, "rp_color"}, |
| {PipelineState::RenderPassDepthStencilFormat, "rp_depth_stencil"}, |
| {PipelineState::Subpass, "subpass"}, |
| {PipelineState::Topology, "topology"}, |
| {PipelineState::PatchVertices, "patch_vertices"}, |
| {PipelineState::PrimitiveRestartEnable, "primitive_restart"}, |
| {PipelineState::CullMode, "cull_mode"}, |
| {PipelineState::FrontFace, "front_face"}, |
| {PipelineState::SurfaceRotation, "rotated_surface"}, |
| {PipelineState::ViewportNegativeOneToOne, "viewport_depth_[-1,1]"}, |
| {PipelineState::SampleShadingEnable, "sample_shading"}, |
| {PipelineState::RasterizationSamples, "rasterization_samples"}, |
| {PipelineState::MinSampleShading, "min_sample_shading"}, |
| {PipelineState::SampleMask, "sample_mask"}, |
| {PipelineState::AlphaToCoverageEnable, "alpha_to_coverage"}, |
| {PipelineState::AlphaToOneEnable, "alpha_to_one"}, |
| {PipelineState::LogicOpEnable, "logic_op_enable"}, |
| {PipelineState::LogicOp, "logic_op"}, |
| {PipelineState::RasterizerDiscardEnable, "rasterization_discard"}, |
| {PipelineState::ColorWriteMask, "color_write"}, |
| {PipelineState::BlendEnableMask, "blend_mask"}, |
| {PipelineState::SrcColorBlendFactor, "src_color_blend"}, |
| {PipelineState::DstColorBlendFactor, "dst_color_blend"}, |
| {PipelineState::ColorBlendOp, "color_blend"}, |
| {PipelineState::SrcAlphaBlendFactor, "src_alpha_blend"}, |
| {PipelineState::DstAlphaBlendFactor, "dst_alpha_blend"}, |
| {PipelineState::AlphaBlendOp, "alpha_blend"}, |
| {PipelineState::EmulatedDitherControl, "dither"}, |
| {PipelineState::DepthClampEnable, "depth_clamp"}, |
| {PipelineState::DepthBoundsTest, "depth_bounds_test"}, |
| {PipelineState::DepthCompareOp, "depth_compare"}, |
| {PipelineState::DepthTest, "depth_test"}, |
| {PipelineState::DepthWrite, "depth_write"}, |
| {PipelineState::StencilTest, "stencil_test"}, |
| {PipelineState::DepthBiasEnable, "depth_bias"}, |
| {PipelineState::StencilOpFailFront, "stencil_front_fail"}, |
| {PipelineState::StencilOpPassFront, "stencil_front_pass"}, |
| {PipelineState::StencilOpDepthFailFront, "stencil_front_depth_fail"}, |
| {PipelineState::StencilCompareFront, "stencil_front_compare"}, |
| {PipelineState::StencilOpFailBack, "stencil_back_fail"}, |
| {PipelineState::StencilOpPassBack, "stencil_back_pass"}, |
| {PipelineState::StencilOpDepthFailBack, "stencil_back_depth_fail"}, |
| {PipelineState::StencilCompareBack, "stencil_back_compare"}, |
| }}; |
| |
| out << kStateNames[pipelineState]; |
| if (isRanged) |
| { |
| out << "_" << subIndex; |
| } |
| |
| switch (pipelineState) |
| { |
| // Given that state == 0 produces no output, binary state doesn't require anything but |
| // its name specified, as it being enabled would be implied. |
| case PipelineState::VertexAttribCompressed: |
| case PipelineState::RenderPassSrgbWriteControl: |
| case PipelineState::RenderPassHasFramebufferFetch: |
| case PipelineState::RenderPassIsRenderToTexture: |
| case PipelineState::RenderPassResolveDepthStencil: |
| case PipelineState::RenderPassUnresolveDepth: |
| case PipelineState::RenderPassUnresolveStencil: |
| case PipelineState::PrimitiveRestartEnable: |
| case PipelineState::SurfaceRotation: |
| case PipelineState::ViewportNegativeOneToOne: |
| case PipelineState::SampleShadingEnable: |
| case PipelineState::AlphaToCoverageEnable: |
| case PipelineState::AlphaToOneEnable: |
| case PipelineState::LogicOpEnable: |
| case PipelineState::RasterizerDiscardEnable: |
| case PipelineState::DepthClampEnable: |
| case PipelineState::DepthBoundsTest: |
| case PipelineState::DepthTest: |
| case PipelineState::DepthWrite: |
| case PipelineState::StencilTest: |
| case PipelineState::DepthBiasEnable: |
| break; |
| |
| // Special formatting for some state |
| case PipelineState::Topology: |
| out << "="; |
| switch (state) |
| { |
| case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: |
| out << "points"; |
| break; |
| case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: |
| out << "lines"; |
| break; |
| case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: |
| out << "line_strip"; |
| break; |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: |
| out << "tris"; |
| break; |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: |
| out << "tri_strip"; |
| break; |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN: |
| out << "tri_fan"; |
| break; |
| case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY: |
| out << "lines_with_adj"; |
| break; |
| case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY: |
| out << "line_strip_with_adj"; |
| break; |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY: |
| out << "tris_with_adj"; |
| break; |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY: |
| out << "tri_strip_with_adj"; |
| break; |
| case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST: |
| out << "patches"; |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| break; |
| case PipelineState::CullMode: |
| out << "="; |
| if ((state & VK_CULL_MODE_FRONT_BIT) != 0) |
| { |
| out << "front"; |
| } |
| if (state == VK_CULL_MODE_FRONT_AND_BACK) |
| { |
| out << "+"; |
| } |
| if ((state & VK_CULL_MODE_BACK_BIT) != 0) |
| { |
| out << "back"; |
| } |
| break; |
| case PipelineState::FrontFace: |
| out << "=" << (state == VK_FRONT_FACE_COUNTER_CLOCKWISE ? "ccw" : "cw"); |
| break; |
| case PipelineState::MinSampleShading: |
| out << "=" << (static_cast<float>(state) / kMinSampleShadingScale); |
| break; |
| case PipelineState::SrcColorBlendFactor: |
| case PipelineState::DstColorBlendFactor: |
| case PipelineState::SrcAlphaBlendFactor: |
| case PipelineState::DstAlphaBlendFactor: |
| out << "="; |
| switch (state) |
| { |
| case VK_BLEND_FACTOR_ZERO: |
| out << "0"; |
| break; |
| case VK_BLEND_FACTOR_ONE: |
| out << "1"; |
| break; |
| case VK_BLEND_FACTOR_SRC_COLOR: |
| out << "sc"; |
| break; |
| case VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR: |
| out << "1-sc"; |
| break; |
| case VK_BLEND_FACTOR_DST_COLOR: |
| out << "dc"; |
| break; |
| case VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR: |
| out << "1-dc"; |
| break; |
| case VK_BLEND_FACTOR_SRC_ALPHA: |
| out << "sa"; |
| break; |
| case VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA: |
| out << "1-sa"; |
| break; |
| case VK_BLEND_FACTOR_DST_ALPHA: |
| out << "da"; |
| break; |
| case VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA: |
| out << "1-da"; |
| break; |
| case VK_BLEND_FACTOR_CONSTANT_COLOR: |
| out << "const_color"; |
| break; |
| case VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR: |
| out << "1-const_color"; |
| break; |
| case VK_BLEND_FACTOR_CONSTANT_ALPHA: |
| out << "const_alpha"; |
| break; |
| case VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA: |
| out << "1-const_alpha"; |
| break; |
| case VK_BLEND_FACTOR_SRC_ALPHA_SATURATE: |
| out << "sat(sa)"; |
| break; |
| case VK_BLEND_FACTOR_SRC1_COLOR: |
| out << "sc1"; |
| break; |
| case VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR: |
| out << "1-sc1"; |
| break; |
| case VK_BLEND_FACTOR_SRC1_ALPHA: |
| out << "sa1"; |
| break; |
| case VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA: |
| out << "1-sa1"; |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| break; |
| case PipelineState::ColorBlendOp: |
| case PipelineState::AlphaBlendOp: |
| out << "="; |
| switch (UnpackBlendOp(static_cast<uint8_t>(state))) |
| { |
| case VK_BLEND_OP_ADD: |
| out << "add"; |
| break; |
| case VK_BLEND_OP_SUBTRACT: |
| out << "sub"; |
| break; |
| case VK_BLEND_OP_REVERSE_SUBTRACT: |
| out << "reverse_sub"; |
| break; |
| case VK_BLEND_OP_MIN: |
| out << "min"; |
| break; |
| case VK_BLEND_OP_MAX: |
| out << "max"; |
| break; |
| case VK_BLEND_OP_MULTIPLY_EXT: |
| out << "multiply"; |
| break; |
| case VK_BLEND_OP_SCREEN_EXT: |
| out << "screen"; |
| break; |
| case VK_BLEND_OP_OVERLAY_EXT: |
| out << "overlay"; |
| break; |
| case VK_BLEND_OP_DARKEN_EXT: |
| out << "darken"; |
| break; |
| case VK_BLEND_OP_LIGHTEN_EXT: |
| out << "lighten"; |
| break; |
| case VK_BLEND_OP_COLORDODGE_EXT: |
| out << "dodge"; |
| break; |
| case VK_BLEND_OP_COLORBURN_EXT: |
| out << "burn"; |
| break; |
| case VK_BLEND_OP_HARDLIGHT_EXT: |
| out << "hardlight"; |
| break; |
| case VK_BLEND_OP_SOFTLIGHT_EXT: |
| out << "softlight"; |
| break; |
| case VK_BLEND_OP_DIFFERENCE_EXT: |
| out << "difference"; |
| break; |
| case VK_BLEND_OP_EXCLUSION_EXT: |
| out << "exclusion"; |
| break; |
| case VK_BLEND_OP_HSL_HUE_EXT: |
| out << "hsl_hue"; |
| break; |
| case VK_BLEND_OP_HSL_SATURATION_EXT: |
| out << "hsl_sat"; |
| break; |
| case VK_BLEND_OP_HSL_COLOR_EXT: |
| out << "hsl_color"; |
| break; |
| case VK_BLEND_OP_HSL_LUMINOSITY_EXT: |
| out << "hsl_lum"; |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| break; |
| case PipelineState::DepthCompareOp: |
| case PipelineState::StencilCompareFront: |
| case PipelineState::StencilCompareBack: |
| out << "="; |
| switch (state) |
| { |
| case VK_COMPARE_OP_NEVER: |
| out << "never"; |
| break; |
| case VK_COMPARE_OP_LESS: |
| out << "'<'"; |
| break; |
| case VK_COMPARE_OP_EQUAL: |
| out << "'='"; |
| break; |
| case VK_COMPARE_OP_LESS_OR_EQUAL: |
| out << "'<='"; |
| break; |
| case VK_COMPARE_OP_GREATER: |
| out << "'>'"; |
| break; |
| case VK_COMPARE_OP_NOT_EQUAL: |
| out << "'!='"; |
| break; |
| case VK_COMPARE_OP_GREATER_OR_EQUAL: |
| out << "'>='"; |
| break; |
| case VK_COMPARE_OP_ALWAYS: |
| out << "always"; |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| break; |
| case PipelineState::StencilOpFailFront: |
| case PipelineState::StencilOpPassFront: |
| case PipelineState::StencilOpDepthFailFront: |
| case PipelineState::StencilOpFailBack: |
| case PipelineState::StencilOpPassBack: |
| case PipelineState::StencilOpDepthFailBack: |
| out << "="; |
| switch (state) |
| { |
| case VK_STENCIL_OP_KEEP: |
| out << "keep"; |
| break; |
| case VK_STENCIL_OP_ZERO: |
| out << "0"; |
| break; |
| case VK_STENCIL_OP_REPLACE: |
| out << "replace"; |
| break; |
| case VK_STENCIL_OP_INCREMENT_AND_CLAMP: |
| out << "clamp++"; |
| break; |
| case VK_STENCIL_OP_DECREMENT_AND_CLAMP: |
| out << "clamp--"; |
| break; |
| case VK_STENCIL_OP_INVERT: |
| out << "'~'"; |
| break; |
| case VK_STENCIL_OP_INCREMENT_AND_WRAP: |
| out << "wrap++"; |
| break; |
| case VK_STENCIL_OP_DECREMENT_AND_WRAP: |
| out << "wrap--"; |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| break; |
| |
| // Some state output the value as hex because they are bitmasks |
| case PipelineState::RenderPassColorResolveMask: |
| case PipelineState::RenderPassColorUnresolveMask: |
| case PipelineState::SampleMask: |
| case PipelineState::ColorWriteMask: |
| case PipelineState::BlendEnableMask: |
| case PipelineState::EmulatedDitherControl: |
| out << "=0x" << std::hex << state << std::dec; |
| break; |
| |
| // The rest will simply output the state value |
| default: |
| out << "=" << state; |
| break; |
| } |
| |
| out << "\\n"; |
| } |
| |
| void OutputAllPipelineState(ContextVk *contextVk, |
| std::ostream &out, |
| const UnpackedPipelineState &pipeline, |
| const PipelineStateBitSet &include, |
| bool isCommonState) |
| { |
| const angle::PackedEnumMap<PipelineState, uint32_t> kDefaultState = {{ |
| {PipelineState::VertexAttribFormat, |
| static_cast<uint32_t>(GetCurrentValueFormatID(gl::VertexAttribType::Float))}, |
| {PipelineState::VertexAttribDivisor, 0}, |
| {PipelineState::VertexAttribOffset, 0}, |
| {PipelineState::VertexAttribStride, 0}, |
| {PipelineState::VertexAttribCompressed, 0}, |
| {PipelineState::RenderPassSamples, 1}, |
| {PipelineState::RenderPassColorAttachmentRange, 0}, |
| {PipelineState::RenderPassViewCount, 0}, |
| {PipelineState::RenderPassSrgbWriteControl, 0}, |
| {PipelineState::RenderPassHasFramebufferFetch, 0}, |
| {PipelineState::RenderPassIsRenderToTexture, 0}, |
| {PipelineState::RenderPassResolveDepthStencil, 0}, |
| {PipelineState::RenderPassUnresolveDepth, 0}, |
| {PipelineState::RenderPassUnresolveStencil, 0}, |
| {PipelineState::RenderPassColorResolveMask, 0}, |
| {PipelineState::RenderPassColorUnresolveMask, 0}, |
| {PipelineState::RenderPassColorFormat, 0}, |
| {PipelineState::RenderPassDepthStencilFormat, 0}, |
| {PipelineState::Subpass, 0}, |
| {PipelineState::Topology, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST}, |
| {PipelineState::PatchVertices, 3}, |
| {PipelineState::PrimitiveRestartEnable, 0}, |
| {PipelineState::CullMode, VK_CULL_MODE_NONE}, |
| {PipelineState::FrontFace, VK_FRONT_FACE_COUNTER_CLOCKWISE}, |
| {PipelineState::SurfaceRotation, 0}, |
| {PipelineState::ViewportNegativeOneToOne, |
| contextVk->getFeatures().supportsDepthClipControl.enabled}, |
| {PipelineState::SampleShadingEnable, 0}, |
| {PipelineState::RasterizationSamples, 1}, |
| {PipelineState::MinSampleShading, kMinSampleShadingScale}, |
| {PipelineState::SampleMask, std::numeric_limits<uint16_t>::max()}, |
| {PipelineState::AlphaToCoverageEnable, 0}, |
| {PipelineState::AlphaToOneEnable, 0}, |
| {PipelineState::LogicOpEnable, 0}, |
| {PipelineState::LogicOp, VK_LOGIC_OP_CLEAR}, |
| {PipelineState::RasterizerDiscardEnable, 0}, |
| {PipelineState::ColorWriteMask, 0}, |
| {PipelineState::BlendEnableMask, 0}, |
| {PipelineState::SrcColorBlendFactor, VK_BLEND_FACTOR_ONE}, |
| {PipelineState::DstColorBlendFactor, VK_BLEND_FACTOR_ZERO}, |
| {PipelineState::ColorBlendOp, VK_BLEND_OP_ADD}, |
| {PipelineState::SrcAlphaBlendFactor, VK_BLEND_FACTOR_ONE}, |
| {PipelineState::DstAlphaBlendFactor, VK_BLEND_FACTOR_ZERO}, |
| {PipelineState::AlphaBlendOp, VK_BLEND_OP_ADD}, |
| {PipelineState::EmulatedDitherControl, 0}, |
| {PipelineState::DepthClampEnable, contextVk->getFeatures().depthClamping.enabled}, |
| {PipelineState::DepthBoundsTest, 0}, |
| {PipelineState::DepthCompareOp, VK_COMPARE_OP_LESS}, |
| {PipelineState::DepthTest, 0}, |
| {PipelineState::DepthWrite, 0}, |
| {PipelineState::StencilTest, 0}, |
| {PipelineState::DepthBiasEnable, 0}, |
| {PipelineState::StencilOpFailFront, VK_STENCIL_OP_KEEP}, |
| {PipelineState::StencilOpPassFront, VK_STENCIL_OP_KEEP}, |
| {PipelineState::StencilOpDepthFailFront, VK_STENCIL_OP_KEEP}, |
| {PipelineState::StencilCompareFront, VK_COMPARE_OP_ALWAYS}, |
| {PipelineState::StencilOpFailBack, VK_STENCIL_OP_KEEP}, |
| {PipelineState::StencilOpPassBack, VK_STENCIL_OP_KEEP}, |
| {PipelineState::StencilOpDepthFailBack, VK_STENCIL_OP_KEEP}, |
| {PipelineState::StencilCompareBack, VK_COMPARE_OP_ALWAYS}, |
| }}; |
| |
| bool anyStateOutput = false; |
| for (size_t stateIndex : include) |
| { |
| size_t subIndex = 0; |
| bool isRanged = false; |
| PipelineState pipelineState = GetPipelineState(stateIndex, &isRanged, &subIndex); |
| |
| const uint32_t state = pipeline.data()[stateIndex]; |
| if (state != kDefaultState[pipelineState]) |
| { |
| OutputPipelineState(out, stateIndex, state); |
| anyStateOutput = true; |
| } |
| } |
| |
| if (!isCommonState) |
| { |
| out << "(" << (anyStateOutput ? "+" : "") << "common state)\\n"; |
| } |
| } |
| |
| void DumpPipelineCacheGraph( |
| ContextVk *contextVk, |
| const std::unordered_map<vk::GraphicsPipelineDesc, vk::PipelineHelper> &cache) |
| { |
| std::ostream &out = contextVk->getPipelineCacheGraphStream(); |
| |
| static std::atomic<uint32_t> sCacheSerial(0); |
| angle::HashMap<vk::GraphicsPipelineDesc, uint32_t> descToId; |
| |
| uint32_t cacheSerial = sCacheSerial.fetch_add(1); |
| uint32_t descId = 0; |
| |
| // Unpack pipeline states |
| std::vector<UnpackedPipelineState> pipelines(cache.size()); |
| for (const auto &descAndPipeline : cache) |
| { |
| UnpackPipelineState(descAndPipeline.first, &pipelines[descId++]); |
| } |
| |
| // Extract common state between all pipelines. |
| PipelineStateBitSet commonState = GetCommonPipelineState(pipelines); |
| PipelineStateBitSet nodeState = ~commonState; |
| |
| out << " subgraph cluster_" << cacheSerial << "{\n"; |
| out << " label=\"Program " << cacheSerial << "\\n\\nCommon state:\\n"; |
| OutputAllPipelineState(contextVk, out, pipelines[0], commonState, true); |
| out << "\";\n"; |
| |
| descId = 0; |
| for (const auto &descAndPipeline : cache) |
| { |
| const vk::GraphicsPipelineDesc &desc = descAndPipeline.first; |
| |
| out << " p" << cacheSerial << "_" << descId << "[label=\"Pipeline " << descId << "\\n\\n"; |
| OutputAllPipelineState(contextVk, out, pipelines[descId], nodeState, false); |
| out << "\"]"; |
| |
| switch (descAndPipeline.second.getCacheLookUpFeedback()) |
| { |
| case vk::CacheLookUpFeedback::Hit: |
| // Default is green already |
| break; |
| case vk::CacheLookUpFeedback::Miss: |
| out << "[color=red]"; |
| break; |
| case vk::CacheLookUpFeedback::WarmUpHit: |
| // Default is green already |
| out << "[style=dashed]"; |
| break; |
| case vk::CacheLookUpFeedback::WarmUpMiss: |
| out << "[style=dashed,color=red]"; |
| break; |
| default: |
| // No feedback available |
| break; |
| } |
| |
| out << ";\n"; |
| |
| descToId[desc] = descId++; |
| } |
| for (const auto &descAndPipeline : cache) |
| { |
| const vk::GraphicsPipelineDesc &desc = descAndPipeline.first; |
| const vk::PipelineHelper &pipelineHelper = descAndPipeline.second; |
| const std::vector<GraphicsPipelineTransition> &transitions = |
| pipelineHelper.getTransitions(); |
| |
| for (const GraphicsPipelineTransition &transition : transitions) |
| { |
| #if defined(ANGLE_IS_64_BIT_CPU) |
| const uint64_t transitionBits = transition.bits.bits(); |
| #else |
| const uint64_t transitionBits = |
| static_cast<uint64_t>(transition.bits.bits(1)) << 32 | transition.bits.bits(0); |
| #endif |
| out << " p" << cacheSerial << "_" << descToId[desc] << " -> p" << cacheSerial << "_" |
| << descToId[*transition.desc] << " [label=\"'0x" << std::hex << transitionBits |
| << std::dec << "'\"];\n"; |
| } |
| } |
| out << " }\n"; |
| } |
| } // anonymous namespace |
| |
| // RenderPassDesc implementation. |
| RenderPassDesc::RenderPassDesc() |
| { |
| memset(this, 0, sizeof(RenderPassDesc)); |
| } |
| |
| RenderPassDesc::~RenderPassDesc() = default; |
| |
| RenderPassDesc::RenderPassDesc(const RenderPassDesc &other) |
| { |
| memcpy(this, &other, sizeof(RenderPassDesc)); |
| } |
| |
| void RenderPassDesc::packColorAttachment(size_t colorIndexGL, angle::FormatID formatID) |
| { |
| ASSERT(colorIndexGL < mAttachmentFormats.size()); |
| static_assert(angle::kNumANGLEFormats < std::numeric_limits<uint8_t>::max(), |
| "Too many ANGLE formats to fit in uint8_t"); |
| // Force the user to pack the depth/stencil attachment last. |
| ASSERT(!hasDepthStencilAttachment()); |
| // This function should only be called for enabled GL color attachments. |
| ASSERT(formatID != angle::FormatID::NONE); |
| |
| uint8_t &packedFormat = mAttachmentFormats[colorIndexGL]; |
| SetBitField(packedFormat, formatID); |
| |
| // Set color attachment range such that it covers the range from index 0 through last active |
| // index. This is the reasons why we need depth/stencil to be packed last. |
| SetBitField(mColorAttachmentRange, std::max<size_t>(mColorAttachmentRange, colorIndexGL + 1)); |
| } |
| |
| void RenderPassDesc::packColorAttachmentGap(size_t colorIndexGL) |
| { |
| ASSERT(colorIndexGL < mAttachmentFormats.size()); |
| static_assert(angle::kNumANGLEFormats < std::numeric_limits<uint8_t>::max(), |
| "Too many ANGLE formats to fit in uint8_t"); |
| // Force the user to pack the depth/stencil attachment last. |
| ASSERT(!hasDepthStencilAttachment()); |
| |
| // Use NONE as a flag for gaps in GL color attachments. |
| uint8_t &packedFormat = mAttachmentFormats[colorIndexGL]; |
| SetBitField(packedFormat, angle::FormatID::NONE); |
| } |
| |
| void RenderPassDesc::packDepthStencilAttachment(angle::FormatID formatID) |
| { |
| ASSERT(!hasDepthStencilAttachment()); |
| |
| size_t index = depthStencilAttachmentIndex(); |
| ASSERT(index < mAttachmentFormats.size()); |
| |
| uint8_t &packedFormat = mAttachmentFormats[index]; |
| SetBitField(packedFormat, formatID); |
| } |
| |
| void RenderPassDesc::packColorResolveAttachment(size_t colorIndexGL) |
| { |
| ASSERT(isColorAttachmentEnabled(colorIndexGL)); |
| ASSERT(!mColorResolveAttachmentMask.test(colorIndexGL)); |
| ASSERT(mSamples > 1); |
| mColorResolveAttachmentMask.set(colorIndexGL); |
| } |
| |
| void RenderPassDesc::removeColorResolveAttachment(size_t colorIndexGL) |
| { |
| ASSERT(mColorResolveAttachmentMask.test(colorIndexGL)); |
| mColorResolveAttachmentMask.reset(colorIndexGL); |
| } |
| |
| void RenderPassDesc::packColorUnresolveAttachment(size_t colorIndexGL) |
| { |
| mColorUnresolveAttachmentMask.set(colorIndexGL); |
| } |
| |
| void RenderPassDesc::removeColorUnresolveAttachment(size_t colorIndexGL) |
| { |
| mColorUnresolveAttachmentMask.reset(colorIndexGL); |
| } |
| |
| void RenderPassDesc::packDepthStencilResolveAttachment() |
| { |
| ASSERT(hasDepthStencilAttachment()); |
| ASSERT(!hasDepthStencilResolveAttachment()); |
| |
| mResolveDepthStencil = true; |
| } |
| |
| void RenderPassDesc::packDepthStencilUnresolveAttachment(bool unresolveDepth, bool unresolveStencil) |
| { |
| ASSERT(hasDepthStencilAttachment()); |
| |
| mUnresolveDepth = unresolveDepth; |
| mUnresolveStencil = unresolveStencil; |
| } |
| |
| void RenderPassDesc::removeDepthStencilUnresolveAttachment() |
| { |
| mUnresolveDepth = false; |
| mUnresolveStencil = false; |
| } |
| |
| RenderPassDesc &RenderPassDesc::operator=(const RenderPassDesc &other) |
| { |
| memcpy(this, &other, sizeof(RenderPassDesc)); |
| return *this; |
| } |
| |
| void RenderPassDesc::setWriteControlMode(gl::SrgbWriteControlMode mode) |
| { |
| SetBitField(mSrgbWriteControl, mode); |
| } |
| |
| size_t RenderPassDesc::hash() const |
| { |
| return angle::ComputeGenericHash(*this); |
| } |
| |
| bool RenderPassDesc::isColorAttachmentEnabled(size_t colorIndexGL) const |
| { |
| angle::FormatID formatID = operator[](colorIndexGL); |
| return formatID != angle::FormatID::NONE; |
| } |
| |
| bool RenderPassDesc::hasDepthStencilAttachment() const |
| { |
| angle::FormatID formatID = operator[](depthStencilAttachmentIndex()); |
| return formatID != angle::FormatID::NONE; |
| } |
| |
| size_t RenderPassDesc::attachmentCount() const |
| { |
| size_t colorAttachmentCount = 0; |
| for (size_t i = 0; i < mColorAttachmentRange; ++i) |
| { |
| colorAttachmentCount += isColorAttachmentEnabled(i); |
| } |
| |
| // Note that there are no gaps in depth/stencil attachments. In fact there is a maximum of 1 of |
| // it + 1 for its resolve attachment. |
| size_t depthStencilCount = hasDepthStencilAttachment() ? 1 : 0; |
| size_t depthStencilResolveCount = hasDepthStencilResolveAttachment() ? 1 : 0; |
| return colorAttachmentCount + mColorResolveAttachmentMask.count() + depthStencilCount + |
| depthStencilResolveCount; |
| } |
| |
| bool operator==(const RenderPassDesc &lhs, const RenderPassDesc &rhs) |
| { |
| return (memcmp(&lhs, &rhs, sizeof(RenderPassDesc)) == 0); |
| } |
| |
| // GraphicsPipelineDesc implementation. |
| // Use aligned allocation and free so we can use the alignas keyword. |
| void *GraphicsPipelineDesc::operator new(std::size_t size) |
| { |
| return angle::AlignedAlloc(size, 32); |
| } |
| |
| void GraphicsPipelineDesc::operator delete(void *ptr) |
| { |
| return angle::AlignedFree(ptr); |
| } |
| |
| GraphicsPipelineDesc::GraphicsPipelineDesc() |
| { |
| memset(this, 0, sizeof(GraphicsPipelineDesc)); |
| } |
| |
| GraphicsPipelineDesc::~GraphicsPipelineDesc() = default; |
| |
| GraphicsPipelineDesc::GraphicsPipelineDesc(const GraphicsPipelineDesc &other) |
| { |
| memcpy(this, &other, sizeof(GraphicsPipelineDesc)); |
| } |
| |
| GraphicsPipelineDesc &GraphicsPipelineDesc::operator=(const GraphicsPipelineDesc &other) |
| { |
| memcpy(this, &other, sizeof(GraphicsPipelineDesc)); |
| return *this; |
| } |
| |
| size_t GraphicsPipelineDesc::hash() const |
| { |
| size_t keySize = sizeof(*this); |
| if (mDynamicState.ds1And2.supportsDynamicState1) |
| { |
| keySize -= kPackedDynamicState1Size; |
| |
| if (mDynamicState.ds1And2.supportsDynamicState2) |
| { |
| keySize -= kPackedDynamicState1And2Size; |
| } |
| } |
| return angle::ComputeGenericHash(this, keySize); |
| } |
| |
| bool GraphicsPipelineDesc::operator==(const GraphicsPipelineDesc &other) const |
| { |
| return (memcmp(this, &other, sizeof(GraphicsPipelineDesc)) == 0); |
| } |
| |
| // TODO(jmadill): We should prefer using Packed GLenums. http://anglebug.com/2169 |
| |
| // Initialize PSO states, it is consistent with initial value of gl::State |
| void GraphicsPipelineDesc::initDefaults(const ContextVk *contextVk) |
| { |
| // Set all vertex input attributes to default, the default format is Float |
| angle::FormatID defaultFormat = GetCurrentValueFormatID(gl::VertexAttribType::Float); |
| for (PackedAttribDesc &packedAttrib : mVertexInputAttribs.attribs) |
| { |
| SetBitField(packedAttrib.divisor, 0); |
| SetBitField(packedAttrib.format, defaultFormat); |
| SetBitField(packedAttrib.compressed, 0); |
| SetBitField(packedAttrib.offset, 0); |
| } |
| |
| mInputAssemblyAndRasterizationStateInfo.bits.subpass = 0; |
| mInputAssemblyAndRasterizationStateInfo.bits.depthClampEnable = |
| contextVk->getFeatures().depthClamping.enabled ? VK_TRUE : VK_FALSE; |
| mInputAssemblyAndRasterizationStateInfo.bits.rasterizationSamples = 1; |
| mInputAssemblyAndRasterizationStateInfo.bits.sampleShadingEnable = 0; |
| mInputAssemblyAndRasterizationStateInfo.bits.alphaToCoverageEnable = 0; |
| mInputAssemblyAndRasterizationStateInfo.bits.alphaToOneEnable = 0; |
| mInputAssemblyAndRasterizationStateInfo.bits.logicOpEnable = 0; |
| SetBitField(mInputAssemblyAndRasterizationStateInfo.bits.logicOp, VK_LOGIC_OP_CLEAR); |
| |
| mInputAssemblyAndRasterizationStateInfo.sampleMask = std::numeric_limits<uint16_t>::max(); |
| |
| SetBitField(mInputAssemblyAndRasterizationStateInfo.misc.topology, |
| VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); |
| SetBitField(mInputAssemblyAndRasterizationStateInfo.misc.patchVertices, 3); |
| mInputAssemblyAndRasterizationStateInfo.misc.surfaceRotation = 0; |
| mInputAssemblyAndRasterizationStateInfo.misc.viewportNegativeOneToOne = |
| contextVk->getFeatures().supportsDepthClipControl.enabled; |
| mInputAssemblyAndRasterizationStateInfo.misc.depthBoundsTest = 0; |
| mInputAssemblyAndRasterizationStateInfo.misc.minSampleShading = kMinSampleShadingScale; |
| mInputAssemblyAndRasterizationStateInfo.misc.blendEnableMask = 0; |
| |
| VkFlags allColorBits = (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | |
| VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT); |
| |
| for (uint32_t colorIndexGL = 0; colorIndexGL < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; |
| ++colorIndexGL) |
| { |
| Int4Array_Set(mColorBlendStateInfo.colorWriteMaskBits, colorIndexGL, allColorBits); |
| } |
| |
| PackedColorBlendAttachmentState blendAttachmentState; |
| SetBitField(blendAttachmentState.srcColorBlendFactor, VK_BLEND_FACTOR_ONE); |
| SetBitField(blendAttachmentState.dstColorBlendFactor, VK_BLEND_FACTOR_ZERO); |
| SetBitField(blendAttachmentState.colorBlendOp, VK_BLEND_OP_ADD); |
| SetBitField(blendAttachmentState.srcAlphaBlendFactor, VK_BLEND_FACTOR_ONE); |
| SetBitField(blendAttachmentState.dstAlphaBlendFactor, VK_BLEND_FACTOR_ZERO); |
| SetBitField(blendAttachmentState.alphaBlendOp, VK_BLEND_OP_ADD); |
| |
| std::fill(&mColorBlendStateInfo.attachments[0], |
| &mColorBlendStateInfo.attachments[gl::IMPLEMENTATION_MAX_DRAW_BUFFERS], |
| blendAttachmentState); |
| |
| mDither.emulatedDitherControl = 0; |
| mDither.unused = 0; |
| |
| SetBitField(mDynamicState.ds1And2.cullMode, VK_CULL_MODE_NONE); |
| SetBitField(mDynamicState.ds1And2.frontFace, VK_FRONT_FACE_COUNTER_CLOCKWISE); |
| SetBitField(mDynamicState.ds1And2.depthCompareOp, VK_COMPARE_OP_LESS); |
| mDynamicState.ds1And2.depthTest = 0; |
| mDynamicState.ds1And2.depthWrite = 0; |
| mDynamicState.ds1And2.stencilTest = 0; |
| |
| mDynamicState.ds1And2.rasterizerDiscardEnable = 0; |
| mDynamicState.ds1And2.depthBiasEnable = 0; |
| mDynamicState.ds1And2.primitiveRestartEnable = 0; |
| |
| mDynamicState.ds1And2.supportsDynamicState1 = |
| contextVk->getFeatures().supportsExtendedDynamicState.enabled; |
| mDynamicState.ds1And2.supportsDynamicState2 = |
| contextVk->getFeatures().supportsExtendedDynamicState2.enabled; |
| mDynamicState.ds1And2.padding = 0; |
| |
| SetBitField(mDynamicState.ds1.front.ops.fail, VK_STENCIL_OP_KEEP); |
| SetBitField(mDynamicState.ds1.front.ops.pass, VK_STENCIL_OP_KEEP); |
| SetBitField(mDynamicState.ds1.front.ops.depthFail, VK_STENCIL_OP_KEEP); |
| SetBitField(mDynamicState.ds1.front.ops.compare, VK_COMPARE_OP_ALWAYS); |
| SetBitField(mDynamicState.ds1.back.ops.fail, VK_STENCIL_OP_KEEP); |
| SetBitField(mDynamicState.ds1.back.ops.pass, VK_STENCIL_OP_KEEP); |
| SetBitField(mDynamicState.ds1.back.ops.depthFail, VK_STENCIL_OP_KEEP); |
| SetBitField(mDynamicState.ds1.back.ops.compare, VK_COMPARE_OP_ALWAYS); |
| memset(mDynamicState.ds1.vertexStrides, 0, sizeof(mDynamicState.ds1.vertexStrides)); |
| } |
| |
| angle::Result GraphicsPipelineDesc::initializePipeline( |
| ContextVk *contextVk, |
| PipelineCacheAccess *pipelineCache, |
| const RenderPass &compatibleRenderPass, |
| const PipelineLayout &pipelineLayout, |
| const gl::AttributesMask &activeAttribLocationsMask, |
| const gl::ComponentTypeMask &programAttribsTypeMask, |
| const gl::DrawBufferMask &missingOutputsMask, |
| const ShaderAndSerialMap &shaders, |
| const SpecializationConstants &specConsts, |
| Pipeline *pipelineOut, |
| CacheLookUpFeedback *feedbackOut) const |
| { |
| angle::FixedVector<VkPipelineShaderStageCreateInfo, 5> shaderStages; |
| VkPipelineVertexInputStateCreateInfo vertexInputState = {}; |
| VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = {}; |
| VkPipelineViewportStateCreateInfo viewportState = {}; |
| VkPipelineRasterizationStateCreateInfo rasterState = {}; |
| VkPipelineMultisampleStateCreateInfo multisampleState = {}; |
| VkPipelineDepthStencilStateCreateInfo depthStencilState = {}; |
| gl::DrawBuffersArray<VkPipelineColorBlendAttachmentState> blendAttachmentState; |
| VkPipelineTessellationStateCreateInfo tessellationState = {}; |
| VkPipelineTessellationDomainOriginStateCreateInfo domainOriginState = {}; |
| VkPipelineColorBlendStateCreateInfo blendState = {}; |
| VkSpecializationInfo specializationInfo = {}; |
| VkGraphicsPipelineCreateInfo createInfo = {}; |
| |
| SpecializationConstantMap<VkSpecializationMapEntry> specializationEntries; |
| InitializeSpecializationInfo(specConsts, &specializationEntries, &specializationInfo); |
| |
| // Vertex shader is always expected to be present. |
| const ShaderModule &vertexModule = shaders[gl::ShaderType::Vertex].get().get(); |
| ASSERT(vertexModule.valid()); |
| VkPipelineShaderStageCreateInfo vertexStage = {}; |
| SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| VK_SHADER_STAGE_VERTEX_BIT, vertexModule.getHandle(), |
| specializationInfo, &vertexStage); |
| shaderStages.push_back(vertexStage); |
| |
| const ShaderAndSerialPointer &tessControlPointer = shaders[gl::ShaderType::TessControl]; |
| if (tessControlPointer.valid()) |
| { |
| const ShaderModule &tessControlModule = tessControlPointer.get().get(); |
| VkPipelineShaderStageCreateInfo tessControlStage = {}; |
| SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, |
| tessControlModule.getHandle(), specializationInfo, |
| &tessControlStage); |
| shaderStages.push_back(tessControlStage); |
| } |
| |
| const ShaderAndSerialPointer &tessEvaluationPointer = shaders[gl::ShaderType::TessEvaluation]; |
| if (tessEvaluationPointer.valid()) |
| { |
| const ShaderModule &tessEvaluationModule = tessEvaluationPointer.get().get(); |
| VkPipelineShaderStageCreateInfo tessEvaluationStage = {}; |
| SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, |
| tessEvaluationModule.getHandle(), specializationInfo, |
| &tessEvaluationStage); |
| shaderStages.push_back(tessEvaluationStage); |
| } |
| |
| const ShaderAndSerialPointer &geometryPointer = shaders[gl::ShaderType::Geometry]; |
| if (geometryPointer.valid()) |
| { |
| const ShaderModule &geometryModule = geometryPointer.get().get(); |
| VkPipelineShaderStageCreateInfo geometryStage = {}; |
| SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| VK_SHADER_STAGE_GEOMETRY_BIT, geometryModule.getHandle(), |
| specializationInfo, &geometryStage); |
| shaderStages.push_back(geometryStage); |
| } |
| |
| // Fragment shader is optional. |
| // anglebug.com/3509 - Don't compile the fragment shader if rasterizerDiscardEnable = true |
| const ShaderAndSerialPointer &fragmentPointer = shaders[gl::ShaderType::Fragment]; |
| if (fragmentPointer.valid() && !mDynamicState.ds1And2.rasterizerDiscardEnable) |
| { |
| const ShaderModule &fragmentModule = fragmentPointer.get().get(); |
| VkPipelineShaderStageCreateInfo fragmentStage = {}; |
| SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| VK_SHADER_STAGE_FRAGMENT_BIT, fragmentModule.getHandle(), |
| specializationInfo, &fragmentStage); |
| shaderStages.push_back(fragmentStage); |
| } |
| |
| // TODO(jmadill): Possibly use different path for ES 3.1 split bindings/attribs. |
| gl::AttribArray<VkVertexInputBindingDescription> bindingDescs; |
| gl::AttribArray<VkVertexInputAttributeDescription> attributeDescs; |
| |
| uint32_t vertexAttribCount = 0; |
| |
| size_t unpackedSize = sizeof(shaderStages) + sizeof(vertexInputState) + |
| sizeof(inputAssemblyState) + sizeof(viewportState) + sizeof(rasterState) + |
| sizeof(multisampleState) + sizeof(depthStencilState) + |
| sizeof(tessellationState) + sizeof(blendAttachmentState) + |
| sizeof(blendState) + sizeof(bindingDescs) + sizeof(attributeDescs); |
| ANGLE_UNUSED_VARIABLE(unpackedSize); |
| |
| gl::AttribArray<VkVertexInputBindingDivisorDescriptionEXT> divisorDesc; |
| VkPipelineVertexInputDivisorStateCreateInfoEXT divisorState = {}; |
| divisorState.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT; |
| divisorState.pVertexBindingDivisors = divisorDesc.data(); |
| for (size_t attribIndexSizeT : activeAttribLocationsMask) |
| { |
| const uint32_t attribIndex = static_cast<uint32_t>(attribIndexSizeT); |
| |
| VkVertexInputBindingDescription &bindingDesc = bindingDescs[vertexAttribCount]; |
| VkVertexInputAttributeDescription &attribDesc = attributeDescs[vertexAttribCount]; |
| const PackedAttribDesc &packedAttrib = mVertexInputAttribs.attribs[attribIndex]; |
| |
| bindingDesc.binding = attribIndex; |
| bindingDesc.stride = static_cast<uint32_t>(mDynamicState.ds1.vertexStrides[attribIndex]); |
| if (packedAttrib.divisor != 0) |
| { |
| bindingDesc.inputRate = static_cast<VkVertexInputRate>(VK_VERTEX_INPUT_RATE_INSTANCE); |
| divisorDesc[divisorState.vertexBindingDivisorCount].binding = bindingDesc.binding; |
| divisorDesc[divisorState.vertexBindingDivisorCount].divisor = packedAttrib.divisor; |
| ++divisorState.vertexBindingDivisorCount; |
| } |
| else |
| { |
| bindingDesc.inputRate = static_cast<VkVertexInputRate>(VK_VERTEX_INPUT_RATE_VERTEX); |
| } |
| |
| // Get the corresponding VkFormat for the attrib's format. |
| angle::FormatID formatID = static_cast<angle::FormatID>(packedAttrib.format); |
| const Format &format = contextVk->getRenderer()->getFormat(formatID); |
| const angle::Format &intendedFormat = format.getIntendedFormat(); |
| VkFormat vkFormat = format.getActualBufferVkFormat(packedAttrib.compressed); |
| |
| const gl::ComponentType attribType = GetVertexAttributeComponentType( |
| intendedFormat.isPureInt(), intendedFormat.vertexAttribType); |
| const gl::ComponentType programAttribType = |
| gl::GetComponentTypeMask(programAttribsTypeMask, attribIndex); |
| |
| // This forces stride to 0 when glVertexAttribPointer specifies a different type from the |
| // program's attribute type except when the type mismatch is a mismatched integer sign. |
| if (bindingDesc.stride > 0 && attribType != programAttribType) |
| { |
| if (attribType == gl::ComponentType::Float || |
| programAttribType == gl::ComponentType::Float) |
| { |
| // When dealing with float to int or unsigned int or vice versa, just override the |
| // format with a compatible one. |
| vkFormat = kMismatchedComponentTypeMap[programAttribType]; |
| } |
| else |
| { |
| // When converting from an unsigned to a signed format or vice versa, attempt to |
| // match the bit width. |
| angle::FormatID convertedFormatID = gl::ConvertFormatSignedness(intendedFormat); |
| const Format &convertedFormat = |
| contextVk->getRenderer()->getFormat(convertedFormatID); |
| ASSERT(intendedFormat.channelCount == |
| convertedFormat.getIntendedFormat().channelCount); |
| ASSERT(intendedFormat.redBits == convertedFormat.getIntendedFormat().redBits); |
| ASSERT(intendedFormat.greenBits == convertedFormat.getIntendedFormat().greenBits); |
| ASSERT(intendedFormat.blueBits == convertedFormat.getIntendedFormat().blueBits); |
| ASSERT(intendedFormat.alphaBits == convertedFormat.getIntendedFormat().alphaBits); |
| |
| vkFormat = convertedFormat.getActualBufferVkFormat(packedAttrib.compressed); |
| } |
| |
| ASSERT(contextVk->getNativeExtensions().relaxedVertexAttributeTypeANGLE); |
| // If using dynamic state for stride, the value for stride is unconditionally 0 here. |
| // |ContextVk::handleDirtyGraphicsVertexBuffers| implements the same fix when setting |
| // stride dynamically. |
| ASSERT(!contextVk->getFeatures().supportsExtendedDynamicState.enabled || |
| bindingDesc.stride == 0); |
| |
| if (programAttribType == gl::ComponentType::Float || |
| attribType == gl::ComponentType::Float) |
| { |
| bindingDesc.stride = 0; // Prevent out-of-bounds accesses. |
| } |
| } |
| |
| attribDesc.binding = attribIndex; |
| attribDesc.format = vkFormat; |
| attribDesc.location = static_cast<uint32_t>(attribIndex); |
| attribDesc.offset = packedAttrib.offset; |
| |
| vertexAttribCount++; |
| } |
| |
| // The binding descriptions are filled in at draw time. |
| vertexInputState.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; |
| vertexInputState.flags = 0; |
| vertexInputState.vertexBindingDescriptionCount = vertexAttribCount; |
| vertexInputState.pVertexBindingDescriptions = bindingDescs.data(); |
| vertexInputState.vertexAttributeDescriptionCount = vertexAttribCount; |
| vertexInputState.pVertexAttributeDescriptions = attributeDescs.data(); |
| if (divisorState.vertexBindingDivisorCount) |
| { |
| vertexInputState.pNext = &divisorState; |
| } |
| |
| const PackedInputAssemblyAndRasterizationStateInfo &inputAndRaster = |
| mInputAssemblyAndRasterizationStateInfo; |
| |
| // Primitive topology is filled in at draw time. |
| inputAssemblyState.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; |
| inputAssemblyState.flags = 0; |
| inputAssemblyState.topology = static_cast<VkPrimitiveTopology>(inputAndRaster.misc.topology); |
| // http://anglebug.com/3832 |
| // We currently hit a VK Validation here where VUID |
| // VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00428 is flagged because we allow |
| // primitiveRestartEnable to be true for topologies VK_PRIMITIVE_TOPOLOGY_POINT_LIST, |
| // VK_PRIMITIVE_TOPOLOGY_LINE_LIST, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST |
| // VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY, |
| // VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY and VK_PRIMITIVE_TOPOLOGY_PATCH_LIST |
| // However if we force primiteRestartEnable to FALSE we fail tests. |
| // Need to identify alternate fix. |
| inputAssemblyState.primitiveRestartEnable = |
| static_cast<VkBool32>(mDynamicState.ds1And2.primitiveRestartEnable); |
| |
| // Set initial viewport and scissor state. |
| viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; |
| viewportState.flags = 0; |
| viewportState.viewportCount = 1; |
| viewportState.pViewports = nullptr; |
| viewportState.scissorCount = 1; |
| viewportState.pScissors = nullptr; |
| |
| VkPipelineViewportDepthClipControlCreateInfoEXT depthClipControl = {}; |
| if (contextVk->getFeatures().supportsDepthClipControl.enabled) |
| { |
| depthClipControl.sType = |
| VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT; |
| depthClipControl.negativeOneToOne = |
| static_cast<VkBool32>(inputAndRaster.misc.viewportNegativeOneToOne); |
| |
| viewportState.pNext = &depthClipControl; |
| } |
| |
| // Rasterizer state. |
| rasterState.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; |
| rasterState.flags = 0; |
| rasterState.depthClampEnable = static_cast<VkBool32>(inputAndRaster.bits.depthClampEnable); |
| rasterState.rasterizerDiscardEnable = |
| static_cast<VkBool32>(mDynamicState.ds1And2.rasterizerDiscardEnable); |
| rasterState.polygonMode = VK_POLYGON_MODE_FILL; |
| rasterState.cullMode = static_cast<VkCullModeFlags>(mDynamicState.ds1And2.cullMode); |
| rasterState.frontFace = static_cast<VkFrontFace>(mDynamicState.ds1And2.frontFace); |
| rasterState.depthBiasEnable = static_cast<VkBool32>(mDynamicState.ds1And2.depthBiasEnable); |
| rasterState.lineWidth = 0; |
| const void **pNextPtr = &rasterState.pNext; |
| |
| VkPipelineRasterizationLineStateCreateInfoEXT rasterLineState = {}; |
| rasterLineState.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT; |
| // Enable Bresenham line rasterization if available and the following conditions are met: |
| // 1.) not multisampling |
| // 2.) VUID-VkGraphicsPipelineCreateInfo-lineRasterizationMode-02766: |
| // The Vulkan spec states: If the lineRasterizationMode member of a |
| // VkPipelineRasterizationLineStateCreateInfoEXT structure included in the pNext chain of |
| // pRasterizationState is VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT or |
| // VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT and if rasterization is enabled, then the |
| // alphaToCoverageEnable, alphaToOneEnable, and sampleShadingEnable members of pMultisampleState |
| // must all be VK_FALSE. |
| if (inputAndRaster.bits.rasterizationSamples <= 1 && |
| !mDynamicState.ds1And2.rasterizerDiscardEnable && |
| !inputAndRaster.bits.alphaToCoverageEnable && !inputAndRaster.bits.alphaToOneEnable && |
| !inputAndRaster.bits.sampleShadingEnable && |
| contextVk->getFeatures().bresenhamLineRasterization.enabled) |
| { |
| rasterLineState.lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT; |
| *pNextPtr = &rasterLineState; |
| pNextPtr = &rasterLineState.pNext; |
| } |
| |
| VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexState = {}; |
| provokingVertexState.sType = |
| VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT; |
| // Always set provoking vertex mode to last if available. |
| if (contextVk->getFeatures().provokingVertex.enabled) |
| { |
| provokingVertexState.provokingVertexMode = VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT; |
| *pNextPtr = &provokingVertexState; |
| pNextPtr = &provokingVertexState.pNext; |
| } |
| |
| // When depth clamping is used, depth clipping is automatically disabled. |
| // When the 'depthClamping' feature is enabled, we'll be using depth clamping |
| // to work around a driver issue, not as an alternative to depth clipping. Therefore we need to |
| // explicitly re-enable depth clipping. |
| VkPipelineRasterizationDepthClipStateCreateInfoEXT depthClipState = {}; |
| depthClipState.sType = |
| VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT; |
| if (contextVk->getFeatures().depthClamping.enabled) |
| { |
| depthClipState.depthClipEnable = VK_TRUE; |
| *pNextPtr = &depthClipState; |
| pNextPtr = &depthClipState.pNext; |
| } |
| |
| VkPipelineRasterizationStateStreamCreateInfoEXT rasterStreamState = {}; |
| rasterStreamState.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT; |
| if (contextVk->getFeatures().supportsGeometryStreamsCapability.enabled) |
| { |
| rasterStreamState.rasterizationStream = 0; |
| *pNextPtr = &rasterStreamState; |
| pNextPtr = &rasterStreamState.pNext; |
| } |
| |
| uint32_t sampleMask = inputAndRaster.sampleMask; |
| |
| // Multisample state. |
| multisampleState.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; |
| multisampleState.flags = 0; |
| multisampleState.rasterizationSamples = |
| gl_vk::GetSamples(inputAndRaster.bits.rasterizationSamples); |
| multisampleState.sampleShadingEnable = |
| static_cast<VkBool32>(inputAndRaster.bits.sampleShadingEnable); |
| multisampleState.minSampleShading = |
| static_cast<float>(inputAndRaster.misc.minSampleShading) / kMinSampleShadingScale; |
| multisampleState.pSampleMask = &sampleMask; |
| multisampleState.alphaToCoverageEnable = |
| static_cast<VkBool32>(inputAndRaster.bits.alphaToCoverageEnable); |
| multisampleState.alphaToOneEnable = static_cast<VkBool32>(inputAndRaster.bits.alphaToOneEnable); |
| |
| // Depth/stencil state. |
| depthStencilState.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; |
| depthStencilState.flags = 0; |
| depthStencilState.depthTestEnable = static_cast<VkBool32>(mDynamicState.ds1And2.depthTest); |
| depthStencilState.depthWriteEnable = static_cast<VkBool32>(mDynamicState.ds1And2.depthWrite); |
| depthStencilState.depthCompareOp = |
| static_cast<VkCompareOp>(mDynamicState.ds1And2.depthCompareOp); |
| depthStencilState.depthBoundsTestEnable = |
| static_cast<VkBool32>(inputAndRaster.misc.depthBoundsTest); |
| depthStencilState.stencilTestEnable = static_cast<VkBool32>(mDynamicState.ds1And2.stencilTest); |
| UnpackStencilState(mDynamicState.ds1.front, &depthStencilState.front); |
| UnpackStencilState(mDynamicState.ds1.back, &depthStencilState.back); |
| depthStencilState.minDepthBounds = 0; |
| depthStencilState.maxDepthBounds = 0; |
| |
| blendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; |
| blendState.flags = 0; |
| blendState.logicOpEnable = static_cast<VkBool32>(inputAndRaster.bits.logicOpEnable); |
| blendState.logicOp = static_cast<VkLogicOp>(inputAndRaster.bits.logicOp); |
| blendState.attachmentCount = static_cast<uint32_t>(mRenderPassDesc.colorAttachmentRange()); |
| blendState.pAttachments = blendAttachmentState.data(); |
| |
| // If this graphics pipeline is for the unresolve operation, correct the color attachment count |
| // for that subpass. |
| if ((mRenderPassDesc.getColorUnresolveAttachmentMask().any() || |
| mRenderPassDesc.hasDepthStencilUnresolveAttachment()) && |
| mInputAssemblyAndRasterizationStateInfo.bits.subpass == 0) |
| { |
| blendState.attachmentCount = |
| static_cast<uint32_t>(mRenderPassDesc.getColorUnresolveAttachmentMask().count()); |
| } |
| |
| const gl::DrawBufferMask blendEnableMask(inputAndRaster.misc.blendEnableMask); |
| |
| // Zero-init all states. |
| blendAttachmentState = {}; |
| |
| const PackedColorBlendStateInfo &colorBlend = mColorBlendStateInfo; |
| |
| for (uint32_t colorIndexGL = 0; colorIndexGL < blendState.attachmentCount; ++colorIndexGL) |
| { |
| VkPipelineColorBlendAttachmentState &state = blendAttachmentState[colorIndexGL]; |
| |
| if (blendEnableMask[colorIndexGL]) |
| { |
| // To avoid triggering valid usage error, blending must be disabled for formats that do |
| // not have VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT feature bit set. |
| // From OpenGL ES clients, this means disabling blending for integer formats. |
| if (!angle::Format::Get(mRenderPassDesc[colorIndexGL]).isInt()) |
| { |
| ASSERT(!contextVk->getRenderer() |
| ->getFormat(mRenderPassDesc[colorIndexGL]) |
| .getActualRenderableImageFormat() |
| .isInt()); |
| |
| // The blend fixed-function is enabled with normal blend as well as advanced blend |
| // when the Vulkan extension is present. When emulating advanced blend in the |
| // shader, the blend fixed-function must be disabled. |
| const PackedColorBlendAttachmentState &packedBlendState = |
| colorBlend.attachments[colorIndexGL]; |
| if (packedBlendState.colorBlendOp <= static_cast<uint8_t>(VK_BLEND_OP_MAX) || |
| contextVk->getFeatures().supportsBlendOperationAdvanced.enabled) |
| { |
| state.blendEnable = VK_TRUE; |
| UnpackBlendAttachmentState(packedBlendState, &state); |
| } |
| } |
| } |
| |
| if (contextVk->getExtensions().robustFragmentShaderOutputANGLE && |
| missingOutputsMask[colorIndexGL]) |
| { |
| state.colorWriteMask = 0; |
| } |
| else |
| { |
| state.colorWriteMask = |
| Int4Array_Get<VkColorComponentFlags>(colorBlend.colorWriteMaskBits, colorIndexGL); |
| } |
| } |
| |
| // Dynamic state |
| angle::FixedVector<VkDynamicState, 21> dynamicStateList; |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_VIEWPORT); |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_SCISSOR); |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_LINE_WIDTH); |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS); |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS); |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS); |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK); |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE); |
| if (contextVk->getFeatures().supportsExtendedDynamicState.enabled) |
| { |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_CULL_MODE_EXT); |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_FRONT_FACE_EXT); |
| if (vertexAttribCount > 0) |
| { |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE); |
| } |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE); |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE); |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_DEPTH_COMPARE_OP); |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE); |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_STENCIL_OP); |
| } |
| if (contextVk->getFeatures().supportsExtendedDynamicState2.enabled) |
| { |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE); |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE); |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE); |
| } |
| if (contextVk->getFeatures().supportsFragmentShadingRate.enabled) |
| { |
| dynamicStateList.push_back(VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR); |
| } |
| |
| VkPipelineDynamicStateCreateInfo dynamicState = {}; |
| dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; |
| dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStateList.size()); |
| dynamicState.pDynamicStates = dynamicStateList.data(); |
| |
| // tessellation State |
| if (tessControlPointer.valid() && tessEvaluationPointer.valid()) |
| { |
| domainOriginState.sType = |
| VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO; |
| domainOriginState.pNext = NULL; |
| domainOriginState.domainOrigin = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT; |
| |
| tessellationState.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; |
| tessellationState.flags = 0; |
| tessellationState.pNext = &domainOriginState; |
| tessellationState.patchControlPoints = |
| static_cast<uint32_t>(inputAndRaster.misc.patchVertices); |
| } |
| |
| createInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; |
| createInfo.flags = 0; |
| createInfo.stageCount = static_cast<uint32_t>(shaderStages.size()); |
| createInfo.pStages = shaderStages.data(); |
| createInfo.pVertexInputState = &vertexInputState; |
| createInfo.pInputAssemblyState = &inputAssemblyState; |
| createInfo.pTessellationState = &tessellationState; |
| createInfo.pViewportState = &viewportState; |
| createInfo.pRasterizationState = &rasterState; |
| createInfo.pMultisampleState = &multisampleState; |
| createInfo.pDepthStencilState = &depthStencilState; |
| createInfo.pColorBlendState = &blendState; |
| createInfo.pDynamicState = dynamicStateList.empty() ? nullptr : &dynamicState; |
| createInfo.layout = pipelineLayout.getHandle(); |
| createInfo.renderPass = compatibleRenderPass.getHandle(); |
| createInfo.subpass = mInputAssemblyAndRasterizationStateInfo.bits.subpass; |
| createInfo.basePipelineHandle = VK_NULL_HANDLE; |
| createInfo.basePipelineIndex = 0; |
| |
| VkPipelineCreationFeedback feedback = {}; |
| gl::ShaderMap<VkPipelineCreationFeedback> perStageFeedback; |
| |
| VkPipelineCreationFeedbackCreateInfo feedbackInfo = {}; |
| feedbackInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO; |
| feedbackInfo.pPipelineCreationFeedback = &feedback; |
| // Provide some storage for per-stage data, even though it's not used. This first works around |
| // a VVL bug that doesn't allow `pipelineStageCreationFeedbackCount=0` despite the spec (See |
| // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/4161). Even with fixed VVL, |
| // several drivers crash when this storage is missing too. |
| feedbackInfo.pipelineStageCreationFeedbackCount = createInfo.stageCount; |
| feedbackInfo.pPipelineStageCreationFeedbacks = perStageFeedback.data(); |
| |
| const bool supportsFeedback = contextVk->getFeatures().supportsPipelineCreationFeedback.enabled; |
| if (supportsFeedback) |
| { |
| createInfo.pNext = &feedbackInfo; |
| } |
| |
| ANGLE_TRY(pipelineCache->createGraphicsPipeline(contextVk, createInfo, pipelineOut)); |
| |
| if (supportsFeedback) |
| { |
| const bool cacheHit = |
| (feedback.flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT) != |
| 0; |
| |
| *feedbackOut = cacheHit ? CacheLookUpFeedback::Hit : CacheLookUpFeedback::Miss; |
| ApplyPipelineCreationFeedback(contextVk, feedback); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| void GraphicsPipelineDesc::updateVertexInput(ContextVk *contextVk, |
| GraphicsPipelineTransitionBits *transition, |
| uint32_t attribIndex, |
| GLuint stride, |
| GLuint divisor, |
| angle::FormatID format, |
| bool compressed, |
| GLuint relativeOffset) |
| { |
| PackedAttribDesc &packedAttrib = mVertexInputAttribs.attribs[attribIndex]; |
| |
| SetBitField(packedAttrib.divisor, divisor); |
| |
| if (format == angle::FormatID::NONE) |
| { |
| UNIMPLEMENTED(); |
| } |
| |
| SetBitField(packedAttrib.format, format); |
| SetBitField(packedAttrib.compressed, compressed); |
| SetBitField(packedAttrib.offset, relativeOffset); |
| |
| constexpr size_t kAttribBits = kPackedAttribDescSize * kBitsPerByte; |
| const size_t kBit = |
| ANGLE_GET_INDEXED_TRANSITION_BIT(mVertexInputAttribs, attribs, attribIndex, kAttribBits); |
| |
| // Each attribute is 4 bytes, so only one transition bit needs to be set. |
| static_assert(kPackedAttribDescSize == kGraphicsPipelineDirtyBitBytes, |
| "Adjust transition bits"); |
| transition->set(kBit); |
| |
| if (!contextVk->getFeatures().supportsExtendedDynamicState.enabled) |
| { |
| SetBitField(mDynamicState.ds1.vertexStrides[attribIndex], stride); |
| transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT( |
| mDynamicState.ds1, vertexStrides, attribIndex, |
| sizeof(mDynamicState.ds1.vertexStrides[0]) * kBitsPerByte)); |
| } |
| } |
| |
| void GraphicsPipelineDesc::setTopology(gl::PrimitiveMode drawMode) |
| { |
| VkPrimitiveTopology vkTopology = gl_vk::GetPrimitiveTopology(drawMode); |
| SetBitField(mInputAssemblyAndRasterizationStateInfo.misc.topology, vkTopology); |
| } |
| |
| void GraphicsPipelineDesc::updateTopology(GraphicsPipelineTransitionBits *transition, |
| gl::PrimitiveMode drawMode) |
| { |
| setTopology(drawMode); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndRasterizationStateInfo, misc)); |
| } |
| |
| void GraphicsPipelineDesc::updateDepthClipControl(GraphicsPipelineTransitionBits *transition, |
| bool negativeOneToOne) |
| { |
| SetBitField(mInputAssemblyAndRasterizationStateInfo.misc.viewportNegativeOneToOne, |
| negativeOneToOne); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndRasterizationStateInfo, misc)); |
| } |
| |
| void GraphicsPipelineDesc::updatePrimitiveRestartEnabled(GraphicsPipelineTransitionBits *transition, |
| bool primitiveRestartEnabled) |
| { |
| mDynamicState.ds1And2.primitiveRestartEnable = static_cast<uint16_t>(primitiveRestartEnabled); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDynamicState, ds1And2)); |
| } |
| |
| void GraphicsPipelineDesc::updateCullMode(GraphicsPipelineTransitionBits *transition, |
| const gl::RasterizerState &rasterState) |
| { |
| SetBitField(mDynamicState.ds1And2.cullMode, gl_vk::GetCullMode(rasterState)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDynamicState, ds1And2)); |
| } |
| |
| void GraphicsPipelineDesc::updateFrontFace(GraphicsPipelineTransitionBits *transition, |
| const gl::RasterizerState &rasterState, |
| bool invertFrontFace) |
| { |
| SetBitField(mDynamicState.ds1And2.frontFace, |
| gl_vk::GetFrontFace(rasterState.frontFace, invertFrontFace)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDynamicState, ds1And2)); |
| } |
| |
| void GraphicsPipelineDesc::updateRasterizerDiscardEnabled( |
| GraphicsPipelineTransitionBits *transition, |
| bool rasterizerDiscardEnabled) |
| { |
| mDynamicState.ds1And2.rasterizerDiscardEnable = static_cast<uint32_t>(rasterizerDiscardEnabled); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDynamicState, ds1And2)); |
| } |
| |
| uint32_t GraphicsPipelineDesc::getRasterizationSamples() const |
| { |
| return mInputAssemblyAndRasterizationStateInfo.bits.rasterizationSamples; |
| } |
| |
| void GraphicsPipelineDesc::setRasterizationSamples(uint32_t rasterizationSamples) |
| { |
| mInputAssemblyAndRasterizationStateInfo.bits.rasterizationSamples = rasterizationSamples; |
| } |
| |
| void GraphicsPipelineDesc::updateRasterizationSamples(GraphicsPipelineTransitionBits *transition, |
| uint32_t rasterizationSamples) |
| { |
| setRasterizationSamples(rasterizationSamples); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndRasterizationStateInfo, bits)); |
| } |
| |
| void GraphicsPipelineDesc::updateAlphaToCoverageEnable(GraphicsPipelineTransitionBits *transition, |
| bool enable) |
| { |
| mInputAssemblyAndRasterizationStateInfo.bits.alphaToCoverageEnable = enable; |
| transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndRasterizationStateInfo, bits)); |
| } |
| |
| void GraphicsPipelineDesc::updateAlphaToOneEnable(GraphicsPipelineTransitionBits *transition, |
| bool enable) |
| { |
| mInputAssemblyAndRasterizationStateInfo.bits.alphaToOneEnable = enable; |
| transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndRasterizationStateInfo, bits)); |
| } |
| |
| void GraphicsPipelineDesc::updateSampleMask(GraphicsPipelineTransitionBits *transition, |
| uint32_t maskNumber, |
| uint32_t mask) |
| { |
| ASSERT(maskNumber == 0); |
| SetBitField(mInputAssemblyAndRasterizationStateInfo.sampleMask, mask); |
| |
| transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndRasterizationStateInfo, sampleMask)); |
| } |
| |
| void GraphicsPipelineDesc::updateSampleShading(GraphicsPipelineTransitionBits *transition, |
| bool enable, |
| float value) |
| { |
| mInputAssemblyAndRasterizationStateInfo.bits.sampleShadingEnable = enable; |
| if (enable) |
| { |
| SetBitField(mInputAssemblyAndRasterizationStateInfo.misc.minSampleShading, |
| static_cast<uint16_t>(value * kMinSampleShadingScale)); |
| } |
| else |
| { |
| mInputAssemblyAndRasterizationStateInfo.misc.minSampleShading = kMinSampleShadingScale; |
| } |
| |
| transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndRasterizationStateInfo, bits)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndRasterizationStateInfo, misc)); |
| } |
| |
| void GraphicsPipelineDesc::setSingleBlend(uint32_t colorIndexGL, |
| bool enabled, |
| VkBlendOp op, |
| VkBlendFactor srcFactor, |
| VkBlendFactor dstFactor) |
| { |
| mInputAssemblyAndRasterizationStateInfo.misc.blendEnableMask |= |
| static_cast<uint8_t>(1 << colorIndexGL); |
| |
| PackedColorBlendAttachmentState &blendAttachmentState = |
| mColorBlendStateInfo.attachments[colorIndexGL]; |
| |
| blendAttachmentState.colorBlendOp = op; |
| blendAttachmentState.alphaBlendOp = op; |
| |
| blendAttachmentState.srcColorBlendFactor = srcFactor; |
| blendAttachmentState.dstColorBlendFactor = dstFactor; |
| blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; |
| blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE; |
| } |
| |
| void GraphicsPipelineDesc::updateBlendEnabled(GraphicsPipelineTransitionBits *transition, |
| gl::DrawBufferMask blendEnabledMask) |
| { |
| SetBitField(mInputAssemblyAndRasterizationStateInfo.misc.blendEnableMask, |
| blendEnabledMask.bits()); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndRasterizationStateInfo, misc)); |
| } |
| |
| void GraphicsPipelineDesc::updateBlendEquations(GraphicsPipelineTransitionBits *transition, |
| const gl::BlendStateExt &blendStateExt, |
| gl::DrawBufferMask attachmentMask) |
| { |
| constexpr size_t kSizeBits = sizeof(PackedColorBlendAttachmentState) * 8; |
| |
| for (size_t attachmentIndex : attachmentMask) |
| { |
| PackedColorBlendAttachmentState &blendAttachmentState = |
| mColorBlendStateInfo.attachments[attachmentIndex]; |
| blendAttachmentState.colorBlendOp = |
| PackGLBlendOp(blendStateExt.getEquationColorIndexed(attachmentIndex)); |
| blendAttachmentState.alphaBlendOp = |
| PackGLBlendOp(blendStateExt.getEquationAlphaIndexed(attachmentIndex)); |
| transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mColorBlendStateInfo, attachments, |
| attachmentIndex, kSizeBits)); |
| } |
| } |
| |
| void GraphicsPipelineDesc::updateBlendFuncs(GraphicsPipelineTransitionBits *transition, |
| const gl::BlendStateExt &blendStateExt, |
| gl::DrawBufferMask attachmentMask) |
| { |
| constexpr size_t kSizeBits = sizeof(PackedColorBlendAttachmentState) * 8; |
| for (size_t attachmentIndex : attachmentMask) |
| { |
| PackedColorBlendAttachmentState &blendAttachmentState = |
| mColorBlendStateInfo.attachments[attachmentIndex]; |
| blendAttachmentState.srcColorBlendFactor = |
| PackGLBlendFactor(blendStateExt.getSrcColorIndexed(attachmentIndex)); |
| blendAttachmentState.dstColorBlendFactor = |
| PackGLBlendFactor(blendStateExt.getDstColorIndexed(attachmentIndex)); |
| blendAttachmentState.srcAlphaBlendFactor = |
| PackGLBlendFactor(blendStateExt.getSrcAlphaIndexed(attachmentIndex)); |
| blendAttachmentState.dstAlphaBlendFactor = |
| PackGLBlendFactor(blendStateExt.getDstAlphaIndexed(attachmentIndex)); |
| transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mColorBlendStateInfo, attachments, |
| attachmentIndex, kSizeBits)); |
| } |
| } |
| |
| void GraphicsPipelineDesc::resetBlendFuncsAndEquations(GraphicsPipelineTransitionBits *transition, |
| const gl::BlendStateExt &blendStateExt, |
| gl::DrawBufferMask previousAttachmentsMask, |
| gl::DrawBufferMask newAttachmentsMask) |
| { |
| // A framebuffer with attachments in P was bound, and now one with attachments in N is bound. |
| // We need to clear blend funcs and equations for attachments in P that are not in N. That is |
| // attachments in P&~N. |
| const gl::DrawBufferMask attachmentsToClear = previousAttachmentsMask & ~newAttachmentsMask; |
| // We also need to restore blend funcs and equations for attachments in N that are not in P. |
| const gl::DrawBufferMask attachmentsToAdd = newAttachmentsMask & ~previousAttachmentsMask; |
| constexpr size_t kSizeBits = sizeof(PackedColorBlendAttachmentState) * 8; |
| |
| for (size_t attachmentIndex : attachmentsToClear) |
| { |
| PackedColorBlendAttachmentState &blendAttachmentState = |
| mColorBlendStateInfo.attachments[attachmentIndex]; |
| |
| blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD; |
| blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD; |
| blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; |
| blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; |
| blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; |
| blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; |
| |
| transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mColorBlendStateInfo, attachments, |
| attachmentIndex, kSizeBits)); |
| } |
| |
| if (attachmentsToAdd.any()) |
| { |
| updateBlendFuncs(transition, blendStateExt, attachmentsToAdd); |
| updateBlendEquations(transition, blendStateExt, attachmentsToAdd); |
| } |
| } |
| |
| void GraphicsPipelineDesc::setColorWriteMasks(gl::BlendStateExt::ColorMaskStorage::Type colorMasks, |
| const gl::DrawBufferMask &alphaMask, |
| const gl::DrawBufferMask &enabledDrawBuffers) |
| { |
| PackedColorBlendStateInfo &colorBlend = mColorBlendStateInfo; |
| |
| for (uint32_t colorIndexGL = 0; colorIndexGL < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; |
| colorIndexGL++) |
| { |
| uint8_t colorMask = |
| gl::BlendStateExt::ColorMaskStorage::GetValueIndexed(colorIndexGL, colorMasks); |
| |
| uint8_t mask = 0; |
| if (enabledDrawBuffers.test(colorIndexGL)) |
| { |
| mask = alphaMask[colorIndexGL] ? (colorMask & ~VK_COLOR_COMPONENT_A_BIT) : colorMask; |
| } |
| Int4Array_Set(colorBlend.colorWriteMaskBits, colorIndexGL, mask); |
| } |
| } |
| |
| void GraphicsPipelineDesc::setSingleColorWriteMask(uint32_t colorIndexGL, |
| VkColorComponentFlags colorComponentFlags) |
| { |
| uint8_t colorMask = static_cast<uint8_t>(colorComponentFlags); |
| Int4Array_Set(mColorBlendStateInfo.colorWriteMaskBits, colorIndexGL, colorMask); |
| } |
| |
| void GraphicsPipelineDesc::updateColorWriteMasks( |
| GraphicsPipelineTransitionBits *transition, |
| gl::BlendStateExt::ColorMaskStorage::Type colorMasks, |
| const gl::DrawBufferMask &alphaMask, |
| const gl::DrawBufferMask &enabledDrawBuffers) |
| { |
| setColorWriteMasks(colorMasks, alphaMask, enabledDrawBuffers); |
| |
| for (size_t colorIndexGL = 0; colorIndexGL < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; |
| colorIndexGL++) |
| { |
| transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mColorBlendStateInfo, colorWriteMaskBits, |
| colorIndexGL, 4)); |
| } |
| } |
| |
| void GraphicsPipelineDesc::setDepthTestEnabled(bool enabled) |
| { |
| mDynamicState.ds1And2.depthTest = enabled; |
| } |
| |
| void GraphicsPipelineDesc::setDepthWriteEnabled(bool enabled) |
| { |
| mDynamicState.ds1And2.depthWrite = enabled; |
| } |
| |
| void GraphicsPipelineDesc::setDepthFunc(VkCompareOp op) |
| { |
| SetBitField(mDynamicState.ds1And2.depthCompareOp, op); |
| } |
| |
| void GraphicsPipelineDesc::setDepthClampEnabled(bool enabled) |
| { |
| mInputAssemblyAndRasterizationStateInfo.bits.depthClampEnable = enabled; |
| } |
| |
| void GraphicsPipelineDesc::setStencilTestEnabled(bool enabled) |
| { |
| mDynamicState.ds1And2.stencilTest = enabled; |
| } |
| |
| void GraphicsPipelineDesc::setStencilFrontFuncs(VkCompareOp compareOp) |
| { |
| SetBitField(mDynamicState.ds1.front.ops.compare, compareOp); |
| } |
| |
| void GraphicsPipelineDesc::setStencilBackFuncs(VkCompareOp compareOp) |
| { |
| SetBitField(mDynamicState.ds1.back.ops.compare, compareOp); |
| } |
| |
| void GraphicsPipelineDesc::setStencilFrontOps(VkStencilOp failOp, |
| VkStencilOp passOp, |
| VkStencilOp depthFailOp) |
| { |
| SetBitField(mDynamicState.ds1.front.ops.fail, failOp); |
| SetBitField(mDynamicState.ds1.front.ops.pass, passOp); |
| SetBitField(mDynamicState.ds1.front.ops.depthFail, depthFailOp); |
| } |
| |
| void GraphicsPipelineDesc::setStencilBackOps(VkStencilOp failOp, |
| VkStencilOp passOp, |
| VkStencilOp depthFailOp) |
| { |
| SetBitField(mDynamicState.ds1.back.ops.fail, failOp); |
| SetBitField(mDynamicState.ds1.back.ops.pass, passOp); |
| SetBitField(mDynamicState.ds1.back.ops.depthFail, depthFailOp); |
| } |
| |
| void GraphicsPipelineDesc::updateDepthTestEnabled(GraphicsPipelineTransitionBits *transition, |
| const gl::DepthStencilState &depthStencilState, |
| const gl::Framebuffer *drawFramebuffer) |
| { |
| // Only enable the depth test if the draw framebuffer has a depth buffer. It's possible that |
| // we're emulating a stencil-only buffer with a depth-stencil buffer |
| setDepthTestEnabled(depthStencilState.depthTest && drawFramebuffer->hasDepth()); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDynamicState, ds1And2)); |
| } |
| |
| void GraphicsPipelineDesc::updateDepthFunc(GraphicsPipelineTransitionBits *transition, |
| const gl::DepthStencilState &depthStencilState) |
| { |
| setDepthFunc(gl_vk::GetCompareOp(depthStencilState.depthFunc)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDynamicState, ds1And2)); |
| } |
| |
| void GraphicsPipelineDesc::updateSurfaceRotation(GraphicsPipelineTransitionBits *transition, |
| const SurfaceRotation surfaceRotation) |
| { |
| SetBitField(mInputAssemblyAndRasterizationStateInfo.misc.surfaceRotation, |
| IsRotatedAspectRatio(surfaceRotation)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndRasterizationStateInfo, misc)); |
| } |
| |
| void GraphicsPipelineDesc::updateDepthWriteEnabled(GraphicsPipelineTransitionBits *transition, |
| const gl::DepthStencilState &depthStencilState, |
| const gl::Framebuffer *drawFramebuffer) |
| { |
| // Don't write to depth buffers that should not exist |
| const bool depthWriteEnabled = |
| drawFramebuffer->hasDepth() && depthStencilState.depthTest && depthStencilState.depthMask; |
| if (static_cast<bool>(mDynamicState.ds1And2.depthWrite) != depthWriteEnabled) |
| { |
| setDepthWriteEnabled(depthWriteEnabled); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDynamicState, ds1And2)); |
| } |
| } |
| |
| void GraphicsPipelineDesc::updateStencilTestEnabled(GraphicsPipelineTransitionBits *transition, |
| const gl::DepthStencilState &depthStencilState, |
| const gl::Framebuffer *drawFramebuffer) |
| { |
| // Only enable the stencil test if the draw framebuffer has a stencil buffer. It's possible |
| // that we're emulating a depth-only buffer with a depth-stencil buffer |
| setStencilTestEnabled(depthStencilState.stencilTest && drawFramebuffer->hasStencil()); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDynamicState, ds1And2)); |
| } |
| |
| void GraphicsPipelineDesc::updateStencilFrontFuncs(GraphicsPipelineTransitionBits *transition, |
| const gl::DepthStencilState &depthStencilState) |
| { |
| setStencilFrontFuncs(gl_vk::GetCompareOp(depthStencilState.stencilFunc)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDynamicState, ds1)); |
| } |
| |
| void GraphicsPipelineDesc::updateStencilBackFuncs(GraphicsPipelineTransitionBits *transition, |
| const gl::DepthStencilState &depthStencilState) |
| { |
| setStencilBackFuncs(gl_vk::GetCompareOp(depthStencilState.stencilBackFunc)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDynamicState, ds1)); |
| } |
| |
| void GraphicsPipelineDesc::updateStencilFrontOps(GraphicsPipelineTransitionBits *transition, |
| const gl::DepthStencilState &depthStencilState) |
| { |
| setStencilFrontOps(gl_vk::GetStencilOp(depthStencilState.stencilFail), |
| gl_vk::GetStencilOp(depthStencilState.stencilPassDepthPass), |
| gl_vk::GetStencilOp(depthStencilState.stencilPassDepthFail)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDynamicState, ds1)); |
| } |
| |
| void GraphicsPipelineDesc::updateStencilBackOps(GraphicsPipelineTransitionBits *transition, |
| const gl::DepthStencilState &depthStencilState) |
| { |
| setStencilBackOps(gl_vk::GetStencilOp(depthStencilState.stencilBackFail), |
| gl_vk::GetStencilOp(depthStencilState.stencilBackPassDepthPass), |
| gl_vk::GetStencilOp(depthStencilState.stencilBackPassDepthFail)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDynamicState, ds1)); |
| } |
| |
| void GraphicsPipelineDesc::updatePolygonOffsetFillEnabled( |
| GraphicsPipelineTransitionBits *transition, |
| bool enabled) |
| { |
| mDynamicState.ds1And2.depthBiasEnable = enabled; |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDynamicState, ds1And2)); |
| } |
| |
| void GraphicsPipelineDesc::setRenderPassDesc(const RenderPassDesc &renderPassDesc) |
| { |
| mRenderPassDesc = renderPassDesc; |
| } |
| |
| void GraphicsPipelineDesc::updateSubpass(GraphicsPipelineTransitionBits *transition, |
| uint32_t subpass) |
| { |
| if (mInputAssemblyAndRasterizationStateInfo.bits.subpass != subpass) |
| { |
| SetBitField(mInputAssemblyAndRasterizationStateInfo.bits.subpass, subpass); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndRasterizationStateInfo, bits)); |
| } |
| } |
| |
| void GraphicsPipelineDesc::updatePatchVertices(GraphicsPipelineTransitionBits *transition, |
| GLuint value) |
| { |
| SetBitField(mInputAssemblyAndRasterizationStateInfo.misc.patchVertices, value); |
| |
| transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndRasterizationStateInfo, misc)); |
| } |
| |
| void GraphicsPipelineDesc::resetSubpass(GraphicsPipelineTransitionBits *transition) |
| { |
| updateSubpass(transition, 0); |
| } |
| |
| void GraphicsPipelineDesc::nextSubpass(GraphicsPipelineTransitionBits *transition) |
| { |
| updateSubpass(transition, mInputAssemblyAndRasterizationStateInfo.bits.subpass + 1); |
| } |
| |
| void GraphicsPipelineDesc::setSubpass(uint32_t subpass) |
| { |
| SetBitField(mInputAssemblyAndRasterizationStateInfo.bits.subpass, subpass); |
| } |
| |
| uint32_t GraphicsPipelineDesc::getSubpass() const |
| { |
| return mInputAssemblyAndRasterizationStateInfo.bits.subpass; |
| } |
| |
| void GraphicsPipelineDesc::updateEmulatedDitherControl(GraphicsPipelineTransitionBits *transition, |
| uint16_t value) |
| { |
| // Make sure we don't waste time resetting this to zero in the common no-dither case. |
| ASSERT(value != 0 || mDither.emulatedDitherControl != 0); |
| |
| mDither.emulatedDitherControl = value; |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDither, emulatedDitherControl)); |
| } |
| |
| void GraphicsPipelineDesc::updateRenderPassDesc(GraphicsPipelineTransitionBits *transition, |
| const RenderPassDesc &renderPassDesc) |
| { |
| setRenderPassDesc(renderPassDesc); |
| |
| // The RenderPass is a special case where it spans multiple bits but has no member. |
| constexpr size_t kFirstBit = |
| offsetof(GraphicsPipelineDesc, mRenderPassDesc) >> kTransitionByteShift; |
| constexpr size_t kBitCount = kRenderPassDescSize >> kTransitionByteShift; |
| for (size_t bit = 0; bit < kBitCount; ++bit) |
| { |
| transition->set(kFirstBit + bit); |
| } |
| } |
| |
| void GraphicsPipelineDesc::setRenderPassSampleCount(GLint samples) |
| { |
| mRenderPassDesc.setSamples(samples); |
| } |
| |
| void GraphicsPipelineDesc::setRenderPassColorAttachmentFormat(size_t colorIndexGL, |
| angle::FormatID formatID) |
| { |
| mRenderPassDesc.packColorAttachment(colorIndexGL, formatID); |
| } |
| |
| // AttachmentOpsArray implementation. |
| AttachmentOpsArray::AttachmentOpsArray() |
| { |
| memset(&mOps, 0, sizeof(PackedAttachmentOpsDesc) * mOps.size()); |
| } |
| |
| AttachmentOpsArray::~AttachmentOpsArray() = default; |
| |
| AttachmentOpsArray::AttachmentOpsArray(const AttachmentOpsArray &other) |
| { |
| memcpy(&mOps, &other.mOps, sizeof(PackedAttachmentOpsDesc) * mOps.size()); |
| } |
| |
| AttachmentOpsArray &AttachmentOpsArray::operator=(const AttachmentOpsArray &other) |
| { |
| memcpy(&mOps, &other.mOps, sizeof(PackedAttachmentOpsDesc) * mOps.size()); |
| return *this; |
| } |
| |
| const PackedAttachmentOpsDesc &AttachmentOpsArray::operator[](PackedAttachmentIndex index) const |
| { |
| return mOps[index.get()]; |
| } |
| |
| PackedAttachmentOpsDesc &AttachmentOpsArray::operator[](PackedAttachmentIndex index) |
| { |
| return mOps[index.get()]; |
| } |
| |
| void AttachmentOpsArray::initWithLoadStore(PackedAttachmentIndex index, |
| ImageLayout initialLayout, |
| ImageLayout finalLayout) |
| { |
| setLayouts(index, initialLayout, finalLayout); |
| setOps(index, RenderPassLoadOp::Load, RenderPassStoreOp::Store); |
| setStencilOps(index, RenderPassLoadOp::Load, RenderPassStoreOp::Store); |
| } |
| |
| void AttachmentOpsArray::setLayouts(PackedAttachmentIndex index, |
| ImageLayout initialLayout, |
| ImageLayout finalLayout) |
| { |
| PackedAttachmentOpsDesc &ops = mOps[index.get()]; |
| SetBitField(ops.initialLayout, initialLayout); |
| SetBitField(ops.finalLayout, finalLayout); |
| } |
| |
| void AttachmentOpsArray::setOps(PackedAttachmentIndex index, |
| RenderPassLoadOp loadOp, |
| RenderPassStoreOp storeOp) |
| { |
| PackedAttachmentOpsDesc &ops = mOps[index.get()]; |
| SetBitField(ops.loadOp, loadOp); |
| SetBitField(ops.storeOp, storeOp); |
| ops.isInvalidated = false; |
| } |
| |
| void AttachmentOpsArray::setStencilOps(PackedAttachmentIndex index, |
| RenderPassLoadOp loadOp, |
| RenderPassStoreOp storeOp) |
| { |
| PackedAttachmentOpsDesc &ops = mOps[index.get()]; |
| SetBitField(ops.stencilLoadOp, loadOp); |
| SetBitField(ops.stencilStoreOp, storeOp); |
| ops.isStencilInvalidated = false; |
| } |
| |
| void AttachmentOpsArray::setClearOp(PackedAttachmentIndex index) |
| { |
| PackedAttachmentOpsDesc &ops = mOps[index.get()]; |
| SetBitField(ops.loadOp, RenderPassLoadOp::Clear); |
| } |
| |
| void AttachmentOpsArray::setClearStencilOp(PackedAttachmentIndex index) |
| { |
| PackedAttachmentOpsDesc &ops = mOps[index.get()]; |
| SetBitField(ops.stencilLoadOp, RenderPassLoadOp::Clear); |
| } |
| |
| size_t AttachmentOpsArray::hash() const |
| { |
| return angle::ComputeGenericHash(mOps); |
| } |
| |
| bool operator==(const AttachmentOpsArray &lhs, const AttachmentOpsArray &rhs) |
| { |
| return (memcmp(&lhs, &rhs, sizeof(AttachmentOpsArray)) == 0); |
| } |
| |
| // DescriptorSetLayoutDesc implementation. |
| DescriptorSetLayoutDesc::DescriptorSetLayoutDesc() : mPackedDescriptorSetLayout{} {} |
| |
| DescriptorSetLayoutDesc::~DescriptorSetLayoutDesc() = default; |
| |
| DescriptorSetLayoutDesc::DescriptorSetLayoutDesc(const DescriptorSetLayoutDesc &other) = default; |
| |
| DescriptorSetLayoutDesc &DescriptorSetLayoutDesc::operator=(const DescriptorSetLayoutDesc &other) = |
| default; |
| |
| size_t DescriptorSetLayoutDesc::hash() const |
| { |
| return angle::ComputeGenericHash(mPackedDescriptorSetLayout); |
| } |
| |
| bool DescriptorSetLayoutDesc::operator==(const DescriptorSetLayoutDesc &other) const |
| { |
| return (memcmp(&mPackedDescriptorSetLayout, &other.mPackedDescriptorSetLayout, |
| sizeof(mPackedDescriptorSetLayout)) == 0); |
| } |
| |
| void DescriptorSetLayoutDesc::update(uint32_t bindingIndex, |
| VkDescriptorType descriptorType, |
| uint32_t count, |
| VkShaderStageFlags stages, |
| const Sampler *immutableSampler) |
| { |
| ASSERT(static_cast<size_t>(descriptorType) < std::numeric_limits<uint16_t>::max()); |
| ASSERT(count < std::numeric_limits<uint16_t>::max()); |
| |
| PackedDescriptorSetBinding &packedBinding = mPackedDescriptorSetLayout[bindingIndex]; |
| |
| SetBitField(packedBinding.type, descriptorType); |
| SetBitField(packedBinding.count, count); |
| SetBitField(packedBinding.stages, stages); |
| packedBinding.immutableSampler = VK_NULL_HANDLE; |
| packedBinding.pad = 0; |
| |
| if (immutableSampler) |
| { |
| ASSERT(count == 1); |
| packedBinding.immutableSampler = immutableSampler->getHandle(); |
| } |
| } |
| |
| void DescriptorSetLayoutDesc::unpackBindings(DescriptorSetLayoutBindingVector *bindings, |
| std::vector<VkSampler> *immutableSamplers) const |
| { |
| for (uint32_t bindingIndex = 0; bindingIndex < kMaxDescriptorSetLayoutBindings; ++bindingIndex) |
| { |
| const PackedDescriptorSetBinding &packedBinding = mPackedDescriptorSetLayout[bindingIndex]; |
| if (packedBinding.count == 0) |
| continue; |
| |
| VkDescriptorSetLayoutBinding binding = {}; |
| binding.binding = bindingIndex; |
| binding.descriptorCount = packedBinding.count; |
| binding.descriptorType = static_cast<VkDescriptorType>(packedBinding.type); |
| binding.stageFlags = static_cast<VkShaderStageFlags>(packedBinding.stages); |
| if (packedBinding.immutableSampler != VK_NULL_HANDLE) |
| { |
| ASSERT(packedBinding.count == 1); |
| immutableSamplers->push_back(packedBinding.immutableSampler); |
| binding.pImmutableSamplers = reinterpret_cast<const VkSampler *>(angle::DirtyPointer); |
| } |
| |
| bindings->push_back(binding); |
| } |
| if (!immutableSamplers->empty()) |
| { |
| // Patch up pImmutableSampler addresses now that the vector is stable |
| int immutableIndex = 0; |
| for (VkDescriptorSetLayoutBinding &binding : *bindings) |
| { |
| if (binding.pImmutableSamplers) |
| { |
| binding.pImmutableSamplers = &(*immutableSamplers)[immutableIndex]; |
| immutableIndex++; |
| } |
| } |
| } |
| } |
| |
| // PipelineLayoutDesc implementation. |
| PipelineLayoutDesc::PipelineLayoutDesc() |
| : mDescriptorSetLayouts{}, mPushConstantRange{}, mPadding(0) |
| {} |
| |
| PipelineLayoutDesc::~PipelineLayoutDesc() = default; |
| |
| PipelineLayoutDesc::PipelineLayoutDesc(const PipelineLayoutDesc &other) = default; |
| |
| PipelineLayoutDesc &PipelineLayoutDesc::operator=(const PipelineLayoutDesc &rhs) |
| { |
| mDescriptorSetLayouts = rhs.mDescriptorSetLayouts; |
| mPushConstantRange = rhs.mPushConstantRange; |
| return *this; |
| } |
| |
| size_t PipelineLayoutDesc::hash() const |
| { |
| return angle::ComputeGenericHash(*this); |
| } |
| |
| bool PipelineLayoutDesc::operator==(const PipelineLayoutDesc &other) const |
| { |
| return memcmp(this, &other, sizeof(PipelineLayoutDesc)) == 0; |
| } |
| |
| void PipelineLayoutDesc::updateDescriptorSetLayout(DescriptorSetIndex setIndex, |
| const DescriptorSetLayoutDesc &desc) |
| { |
| mDescriptorSetLayouts[setIndex] = desc; |
| } |
| |
| void PipelineLayoutDesc::updatePushConstantRange(VkShaderStageFlags stageMask, |
| uint32_t offset, |
| uint32_t size) |
| { |
| SetBitField(mPushConstantRange.offset, offset); |
| SetBitField(mPushConstantRange.size, size); |
| SetBitField(mPushConstantRange.stageMask, stageMask); |
| } |
| |
| // PipelineHelper implementation. |
| PipelineHelper::PipelineHelper() = default; |
| |
| PipelineHelper::~PipelineHelper() = default; |
| |
| void PipelineHelper::destroy(VkDevice device) |
| { |
| mPipeline.destroy(device); |
| mCacheLookUpFeedback = CacheLookUpFeedback::None; |
| } |
| |
| void PipelineHelper::release(ContextVk *contextVk) |
| { |
| contextVk->addGarbage(&mPipeline); |
| mCacheLookUpFeedback = CacheLookUpFeedback::None; |
| } |
| |
| void PipelineHelper::addTransition(GraphicsPipelineTransitionBits bits, |
| const GraphicsPipelineDesc *desc, |
| PipelineHelper *pipeline) |
| { |
| mTransitions.emplace_back(bits, desc, pipeline); |
| } |
| |
| // DescriptorSetDesc implementation. |
| size_t DescriptorSetDesc::hash() const |
| { |
| if (mDescriptorInfos.empty()) |
| { |
| return 0; |
| } |
| |
| return angle::ComputeGenericHash(mDescriptorInfos.data(), |
| sizeof(mDescriptorInfos[0]) * mDescriptorInfos.size()); |
| } |
| |
| // FramebufferDesc implementation. |
| |
| FramebufferDesc::FramebufferDesc() |
| { |
| reset(); |
| } |
| |
| FramebufferDesc::~FramebufferDesc() = default; |
| FramebufferDesc::FramebufferDesc(const FramebufferDesc &other) = default; |
| FramebufferDesc &FramebufferDesc::operator=(const FramebufferDesc &other) = default; |
| |
| void FramebufferDesc::update(uint32_t index, ImageOrBufferViewSubresourceSerial serial) |
| { |
| static_assert(kMaxFramebufferAttachments + 1 < std::numeric_limits<uint8_t>::max(), |
| "mMaxIndex size is too small"); |
| ASSERT(index < kMaxFramebufferAttachments); |
| mSerials[index] = serial; |
| if (serial.viewSerial.valid()) |
| { |
| SetBitField(mMaxIndex, std::max(mMaxIndex, static_cast<uint16_t>(index + 1))); |
| } |
| } |
| |
| void FramebufferDesc::updateColor(uint32_t index, ImageOrBufferViewSubresourceSerial serial) |
| { |
| update(kFramebufferDescColorIndexOffset + index, serial); |
| } |
| |
| void FramebufferDesc::updateColorResolve(uint32_t index, ImageOrBufferViewSubresourceSerial serial) |
| { |
| update(kFramebufferDescColorResolveIndexOffset + index, serial); |
| } |
| |
| void FramebufferDesc::updateUnresolveMask(FramebufferNonResolveAttachmentMask unresolveMask) |
| { |
| SetBitField(mUnresolveAttachmentMask, unresolveMask.bits()); |
| } |
| |
| void FramebufferDesc::updateDepthStencil(ImageOrBufferViewSubresourceSerial serial) |
| { |
| update(kFramebufferDescDepthStencilIndex, serial); |
| } |
| |
| void FramebufferDesc::updateDepthStencilResolve(ImageOrBufferViewSubresourceSerial serial) |
| { |
| update(kFramebufferDescDepthStencilResolveIndexOffset, serial); |
| } |
| |
| size_t FramebufferDesc::hash() const |
| { |
| return angle::ComputeGenericHash(&mSerials, sizeof(mSerials[0]) * mMaxIndex) ^ |
| mHasFramebufferFetch << 26 ^ mIsRenderToTexture << 25 ^ mLayerCount << 16 ^ |
| mUnresolveAttachmentMask; |
| } |
| |
| void FramebufferDesc::reset() |
| { |
| mMaxIndex = 0; |
| mHasFramebufferFetch = false; |
| mLayerCount = 0; |
| mSrgbWriteControlMode = 0; |
| mUnresolveAttachmentMask = 0; |
| mIsRenderToTexture = 0; |
| memset(&mSerials, 0, sizeof(mSerials)); |
| } |
| |
| bool FramebufferDesc::operator==(const FramebufferDesc &other) const |
| { |
| if (mMaxIndex != other.mMaxIndex || mLayerCount != other.mLayerCount || |
| mUnresolveAttachmentMask != other.mUnresolveAttachmentMask || |
| mHasFramebufferFetch != other.mHasFramebufferFetch || |
| mSrgbWriteControlMode != other.mSrgbWriteControlMode || |
| mIsRenderToTexture != other.mIsRenderToTexture) |
| { |
| return false; |
| } |
| |
| size_t validRegionSize = sizeof(mSerials[0]) * mMaxIndex; |
| return memcmp(&mSerials, &other.mSerials, validRegionSize) == 0; |
| } |
| |
| uint32_t FramebufferDesc::attachmentCount() const |
| { |
| uint32_t count = 0; |
| for (const ImageOrBufferViewSubresourceSerial &serial : mSerials) |
| { |
| if (serial.viewSerial.valid()) |
| { |
| count++; |
| } |
| } |
| return count; |
| } |
| |
| FramebufferNonResolveAttachmentMask FramebufferDesc::getUnresolveAttachmentMask() const |
| { |
| return FramebufferNonResolveAttachmentMask(mUnresolveAttachmentMask); |
| } |
| |
| void FramebufferDesc::updateLayerCount(uint32_t layerCount) |
| { |
| SetBitField(mLayerCount, layerCount); |
| } |
| |
| void FramebufferDesc::updateFramebufferFetchMode(bool hasFramebufferFetch) |
| { |
| SetBitField(mHasFramebufferFetch, hasFramebufferFetch); |
| } |
| |
| void FramebufferDesc::updateRenderToTexture(bool isRenderToTexture) |
| { |
| SetBitField(mIsRenderToTexture, isRenderToTexture); |
| } |
| |
| // YcbcrConversionDesc implementation |
| YcbcrConversionDesc::YcbcrConversionDesc() |
| { |
| reset(); |
| } |
| |
| YcbcrConversionDesc::~YcbcrConversionDesc() = default; |
| |
| YcbcrConversionDesc::YcbcrConversionDesc(const YcbcrConversionDesc &other) = default; |
| |
| YcbcrConversionDesc &YcbcrConversionDesc::operator=(const YcbcrConversionDesc &rhs) = default; |
| |
| size_t YcbcrConversionDesc::hash() const |
| { |
| return angle::ComputeGenericHash(*this); |
| } |
| |
| bool YcbcrConversionDesc::operator==(const YcbcrConversionDesc &other) const |
| { |
| return (memcmp(this, &other, sizeof(YcbcrConversionDesc)) == 0); |
| } |
| |
| void YcbcrConversionDesc::reset() |
| { |
| mExternalOrVkFormat = 0; |
| mIsExternalFormat = 0; |
| mConversionModel = 0; |
| mColorRange = 0; |
| mXChromaOffset = 0; |
| mYChromaOffset = 0; |
| mChromaFilter = 0; |
| mRSwizzle = 0; |
| mGSwizzle = 0; |
| mBSwizzle = 0; |
| mASwizzle = 0; |
| mPadding = 0; |
| mReserved = 0; |
| } |
| |
| void YcbcrConversionDesc::updateChromaFilter(VkFilter filter) |
| { |
| SetBitField(mChromaFilter, filter); |
| } |
| |
| void YcbcrConversionDesc::update(RendererVk *rendererVk, |
| uint64_t externalFormat, |
| VkSamplerYcbcrModelConversion conversionModel, |
| VkSamplerYcbcrRange colorRange, |
| VkChromaLocation xChromaOffset, |
| VkChromaLocation yChromaOffset, |
| VkFilter chromaFilter, |
| VkComponentMapping components, |
| angle::FormatID intendedFormatID) |
| { |
| const vk::Format &vkFormat = rendererVk->getFormat(intendedFormatID); |
| ASSERT(externalFormat != 0 || vkFormat.getIntendedFormat().isYUV); |
| |
| SetBitField(mIsExternalFormat, (externalFormat) ? 1 : 0); |
| mExternalOrVkFormat = (externalFormat) |
| ? externalFormat |
| : vkFormat.getActualImageVkFormat(vk::ImageAccess::SampleOnly); |
| SetBitField(mConversionModel, conversionModel); |
| SetBitField(mColorRange, colorRange); |
| SetBitField(mXChromaOffset, xChromaOffset); |
| SetBitField(mYChromaOffset, yChromaOffset); |
| SetBitField(mChromaFilter, chromaFilter); |
| SetBitField(mRSwizzle, components.r); |
| SetBitField(mGSwizzle, components.g); |
| SetBitField(mBSwizzle, components.b); |
| SetBitField(mASwizzle, components.a); |
| } |
| |
| angle::Result YcbcrConversionDesc::init(Context *context, |
| SamplerYcbcrConversion *conversionOut) const |
| { |
| // Create the VkSamplerYcbcrConversion |
| VkSamplerYcbcrConversionCreateInfo samplerYcbcrConversionInfo = {}; |
| samplerYcbcrConversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO; |
| samplerYcbcrConversionInfo.format = |
| getExternalFormat() == 0 ? static_cast<VkFormat>(mExternalOrVkFormat) : VK_FORMAT_UNDEFINED; |
| samplerYcbcrConversionInfo.xChromaOffset = static_cast<VkChromaLocation>(mXChromaOffset); |
| samplerYcbcrConversionInfo.yChromaOffset = static_cast<VkChromaLocation>(mYChromaOffset); |
| samplerYcbcrConversionInfo.ycbcrModel = |
| static_cast<VkSamplerYcbcrModelConversion>(mConversionModel); |
| samplerYcbcrConversionInfo.ycbcrRange = static_cast<VkSamplerYcbcrRange>(mColorRange); |
| samplerYcbcrConversionInfo.chromaFilter = static_cast<VkFilter>(mChromaFilter); |
| samplerYcbcrConversionInfo.components = { |
| static_cast<VkComponentSwizzle>(mRSwizzle), static_cast<VkComponentSwizzle>(mGSwizzle), |
| static_cast<VkComponentSwizzle>(mBSwizzle), static_cast<VkComponentSwizzle>(mASwizzle)}; |
| |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| VkExternalFormatANDROID externalFormat = {}; |
| if (getExternalFormat() != 0) |
| { |
| externalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID; |
| externalFormat.externalFormat = mExternalOrVkFormat; |
| samplerYcbcrConversionInfo.pNext = &externalFormat; |
| } |
| #else |
| // We do not support external format for any platform other than Android. |
| ASSERT(mIsExternalFormat == 0); |
| #endif // VK_USE_PLATFORM_ANDROID_KHR |
| |
| ANGLE_VK_TRY(context, conversionOut->init(context->getDevice(), samplerYcbcrConversionInfo)); |
| return angle::Result::Continue; |
| } |
| |
| // SamplerDesc implementation. |
| SamplerDesc::SamplerDesc() |
| { |
| reset(); |
| } |
| |
| SamplerDesc::~SamplerDesc() = default; |
| |
| SamplerDesc::SamplerDesc(const SamplerDesc &other) = default; |
| |
| SamplerDesc &SamplerDesc::operator=(const SamplerDesc &rhs) = default; |
| |
| SamplerDesc::SamplerDesc(ContextVk *contextVk, |
| const gl::SamplerState &samplerState, |
| bool stencilMode, |
| const YcbcrConversionDesc *ycbcrConversionDesc, |
| angle::FormatID intendedFormatID) |
| { |
| update(contextVk, samplerState, stencilMode, ycbcrConversionDesc, intendedFormatID); |
| } |
| |
| void SamplerDesc::reset() |
| { |
| mMipLodBias = 0.0f; |
| mMaxAnisotropy = 0.0f; |
| mMinLod = 0.0f; |
| mMaxLod = 0.0f; |
| mYcbcrConversionDesc.reset(); |
| mMagFilter = 0; |
| mMinFilter = 0; |
| mMipmapMode = 0; |
| mAddressModeU = 0; |
| mAddressModeV = 0; |
| mAddressModeW = 0; |
| mCompareEnabled = 0; |
| mCompareOp = 0; |
| mPadding = 0; |
| mBorderColorType = 0; |
| mBorderColor.red = 0.0f; |
| mBorderColor.green = 0.0f; |
| mBorderColor.blue = 0.0f; |
| mBorderColor.alpha = 0.0f; |
| mReserved = 0; |
| } |
| |
| void SamplerDesc::update(ContextVk *contextVk, |
| const gl::SamplerState &samplerState, |
| bool stencilMode, |
| const YcbcrConversionDesc *ycbcrConversionDesc, |
| angle::FormatID intendedFormatID) |
| { |
| const angle::FeaturesVk &featuresVk = contextVk->getFeatures(); |
| mMipLodBias = 0.0f; |
| if (featuresVk.forceTextureLodOffset1.enabled) |
| { |
| mMipLodBias = 1.0f; |
| } |
| else if (featuresVk.forceTextureLodOffset2.enabled) |
| { |
| mMipLodBias = 2.0f; |
| } |
| else if (featuresVk.forceTextureLodOffset3.enabled) |
| { |
| mMipLodBias = 3.0f; |
| } |
| else if (featuresVk.forceTextureLodOffset4.enabled) |
| { |
| mMipLodBias = 4.0f; |
| } |
| |
| mMaxAnisotropy = samplerState.getMaxAnisotropy(); |
| mMinLod = samplerState.getMinLod(); |
| mMaxLod = samplerState.getMaxLod(); |
| |
| if (ycbcrConversionDesc && ycbcrConversionDesc->valid()) |
| { |
| // Update the SamplerYcbcrConversionCache key |
| mYcbcrConversionDesc = *ycbcrConversionDesc; |
| } |
| |
| bool compareEnable = samplerState.getCompareMode() == GL_COMPARE_REF_TO_TEXTURE; |
| VkCompareOp compareOp = gl_vk::GetCompareOp(samplerState.getCompareFunc()); |
| // When sampling from stencil, deqp tests expect texture compare to have no effect |
| // dEQP - GLES31.functional.stencil_texturing.misc.compare_mode_effect |
| // states: NOTE: Texture compare mode has no effect when reading stencil values. |
| if (stencilMode) |
| { |
| compareEnable = VK_FALSE; |
| compareOp = VK_COMPARE_OP_ALWAYS; |
| } |
| |
| GLenum magFilter = samplerState.getMagFilter(); |
| GLenum minFilter = samplerState.getMinFilter(); |
| if (featuresVk.forceNearestFiltering.enabled) |
| { |
| magFilter = gl::ConvertToNearestFilterMode(magFilter); |
| minFilter = gl::ConvertToNearestFilterMode(minFilter); |
| } |
| if (featuresVk.forceNearestMipFiltering.enabled) |
| { |
| minFilter = gl::ConvertToNearestMipFilterMode(minFilter); |
| } |
| |
| SetBitField(mMagFilter, gl_vk::GetFilter(magFilter)); |
| SetBitField(mMinFilter, gl_vk::GetFilter(minFilter)); |
| SetBitField(mMipmapMode, gl_vk::GetSamplerMipmapMode(samplerState.getMinFilter())); |
| SetBitField(mAddressModeU, gl_vk::GetSamplerAddressMode(samplerState.getWrapS())); |
| SetBitField(mAddressModeV, gl_vk::GetSamplerAddressMode(samplerState.getWrapT())); |
| SetBitField(mAddressModeW, gl_vk::GetSamplerAddressMode(samplerState.getWrapR())); |
| SetBitField(mCompareEnabled, compareEnable); |
| SetBitField(mCompareOp, compareOp); |
| |
| if (!gl::IsMipmapFiltered(minFilter)) |
| { |
| // Per the Vulkan spec, GL_NEAREST and GL_LINEAR do not map directly to Vulkan, so |
| // they must be emulated (See "Mapping of OpenGL to Vulkan filter modes") |
| SetBitField(mMipmapMode, VK_SAMPLER_MIPMAP_MODE_NEAREST); |
| mMinLod = 0.0f; |
| mMaxLod = 0.25f; |
| } |
| |
| mPadding = 0; |
| |
| mBorderColorType = |
| (samplerState.getBorderColor().type == angle::ColorGeneric::Type::Float) ? 0 : 1; |
| |
| // Adjust border color according to intended format |
| const vk::Format &vkFormat = contextVk->getRenderer()->getFormat(intendedFormatID); |
| gl::ColorGeneric adjustedBorderColor = |
| AdjustBorderColor(samplerState.getBorderColor(), vkFormat.getIntendedFormat(), stencilMode); |
| mBorderColor = adjustedBorderColor.colorF; |
| |
| mReserved = 0; |
| } |
| |
| angle::Result SamplerDesc::init(ContextVk *contextVk, Sampler *sampler) const |
| { |
| const gl::Extensions &extensions = contextVk->getExtensions(); |
| |
| bool anisotropyEnable = extensions.textureFilterAnisotropicEXT && mMaxAnisotropy > 1.0f; |
| |
| VkSamplerCreateInfo createInfo = {}; |
| createInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; |
| createInfo.flags = 0; |
| createInfo.magFilter = static_cast<VkFilter>(mMagFilter); |
| createInfo.minFilter = static_cast<VkFilter>(mMinFilter); |
| createInfo.mipmapMode = static_cast<VkSamplerMipmapMode>(mMipmapMode); |
| createInfo.addressModeU = static_cast<VkSamplerAddressMode>(mAddressModeU); |
| createInfo.addressModeV = static_cast<VkSamplerAddressMode>(mAddressModeV); |
| createInfo.addressModeW = static_cast<VkSamplerAddressMode>(mAddressModeW); |
| createInfo.mipLodBias = mMipLodBias; |
| createInfo.anisotropyEnable = anisotropyEnable; |
| createInfo.maxAnisotropy = mMaxAnisotropy; |
| createInfo.compareEnable = mCompareEnabled ? VK_TRUE : VK_FALSE; |
| createInfo.compareOp = static_cast<VkCompareOp>(mCompareOp); |
| createInfo.minLod = mMinLod; |
| createInfo.maxLod = mMaxLod; |
| createInfo.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK; |
| createInfo.unnormalizedCoordinates = VK_FALSE; |
| |
| // Note: because we don't detect changes to this hint (no dirty bit), if a sampler is created |
| // with the hint enabled, and then the hint gets disabled, the next render will do so with the |
| // hint enabled. |
| VkSamplerFilteringPrecisionGOOGLE filteringInfo = {}; |
| GLenum hint = contextVk->getState().getTextureFilteringHint(); |
| if (hint == GL_NICEST) |
| { |
| ASSERT(extensions.textureFilteringHintCHROMIUM); |
| filteringInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_FILTERING_PRECISION_GOOGLE; |
| filteringInfo.samplerFilteringPrecisionMode = |
| VK_SAMPLER_FILTERING_PRECISION_MODE_HIGH_GOOGLE; |
| AddToPNextChain(&createInfo, &filteringInfo); |
| } |
| |
| VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {}; |
| if (mYcbcrConversionDesc.valid()) |
| { |
| ASSERT((contextVk->getRenderer()->getFeatures().supportsYUVSamplerConversion.enabled)); |
| samplerYcbcrConversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO; |
| samplerYcbcrConversionInfo.pNext = nullptr; |
| ANGLE_TRY(contextVk->getRenderer()->getYuvConversionCache().getSamplerYcbcrConversion( |
| contextVk, mYcbcrConversionDesc, &samplerYcbcrConversionInfo.conversion)); |
| AddToPNextChain(&createInfo, &samplerYcbcrConversionInfo); |
| |
| const VkFilter filter = contextVk->getRenderer()->getPreferredFilterForYUV(); |
| |
| // Vulkan spec requires these settings: |
| createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; |
| createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; |
| createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; |
| createInfo.anisotropyEnable = VK_FALSE; |
| createInfo.unnormalizedCoordinates = VK_FALSE; |
| createInfo.magFilter = filter; |
| createInfo.minFilter = filter; |
| } |
| |
| VkSamplerCustomBorderColorCreateInfoEXT customBorderColorInfo = {}; |
| if (createInfo.addressModeU == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER || |
| createInfo.addressModeV == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER || |
| createInfo.addressModeW == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER) |
| { |
| ASSERT((contextVk->getRenderer()->getFeatures().supportsCustomBorderColor.enabled)); |
| customBorderColorInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT; |
| |
| customBorderColorInfo.customBorderColor.float32[0] = mBorderColor.red; |
| customBorderColorInfo.customBorderColor.float32[1] = mBorderColor.green; |
| customBorderColorInfo.customBorderColor.float32[2] = mBorderColor.blue; |
| customBorderColorInfo.customBorderColor.float32[3] = mBorderColor.alpha; |
| |
| if (mBorderColorType == static_cast<uint32_t>(angle::ColorGeneric::Type::Float)) |
| { |
| createInfo.borderColor = VK_BORDER_COLOR_FLOAT_CUSTOM_EXT; |
| } |
| else |
| { |
| createInfo.borderColor = VK_BORDER_COLOR_INT_CUSTOM_EXT; |
| } |
| |
| vk::AddToPNextChain(&createInfo, &customBorderColorInfo); |
| } |
| ANGLE_VK_TRY(contextVk, sampler->init(contextVk->getDevice(), createInfo)); |
| |
| return angle::Result::Continue; |
| } |
| |
| size_t SamplerDesc::hash() const |
| { |
| return angle::ComputeGenericHash(*this); |
| } |
| |
| bool SamplerDesc::operator==(const SamplerDesc &other) const |
| { |
| return (memcmp(this, &other, sizeof(SamplerDesc)) == 0); |
| } |
| |
| // SamplerHelper implementation. |
| SamplerHelper::SamplerHelper(ContextVk *contextVk) |
| : mSamplerSerial(contextVk->getRenderer()->getResourceSerialFactory().generateSamplerSerial()) |
| {} |
| |
| SamplerHelper::~SamplerHelper() {} |
| |
| SamplerHelper::SamplerHelper(SamplerHelper &&samplerHelper) |
| { |
| *this = std::move(samplerHelper); |
| } |
| |
| SamplerHelper &SamplerHelper::operator=(SamplerHelper &&rhs) |
| { |
| std::swap(mSampler, rhs.mSampler); |
| std::swap(mSamplerSerial, rhs.mSamplerSerial); |
| return *this; |
| } |
| |
| // RenderPassHelper implementation. |
| RenderPassHelper::RenderPassHelper() : mPerfCounters{} {} |
| |
| RenderPassHelper::~RenderPassHelper() = default; |
| |
| RenderPassHelper::RenderPassHelper(RenderPassHelper &&other) |
| { |
| *this = std::move(other); |
| } |
| |
| RenderPassHelper &RenderPassHelper::operator=(RenderPassHelper &&other) |
| { |
| mRenderPass = std::move(other.mRenderPass); |
| mPerfCounters = std::move(other.mPerfCounters); |
| return *this; |
| } |
| |
| void RenderPassHelper::destroy(VkDevice device) |
| { |
| mRenderPass.destroy(device); |
| } |
| |
| void RenderPassHelper::release(ContextVk *contextVk) |
| { |
| contextVk->addGarbage(&mRenderPass); |
| } |
| |
| const RenderPass &RenderPassHelper::getRenderPass() const |
| { |
| return mRenderPass; |
| } |
| |
| RenderPass &RenderPassHelper::getRenderPass() |
| { |
| return mRenderPass; |
| } |
| |
| const RenderPassPerfCounters &RenderPassHelper::getPerfCounters() const |
| { |
| return mPerfCounters; |
| } |
| |
| RenderPassPerfCounters &RenderPassHelper::getPerfCounters() |
| { |
| return mPerfCounters; |
| } |
| |
| // DescriptorSetDesc implementation. |
| void DescriptorSetDesc::updateWriteDesc(const WriteDescriptorDesc &writeDesc) |
| { |
| uint32_t binding = writeDesc.binding; |
| WriteDescriptorDesc &destDesc = mWriteDescriptors[binding]; |
| |
| ASSERT(writeDesc.descriptorCount > 0); |
| |
| destDesc = writeDesc; |
| } |
| |
| void DescriptorSetDesc::updateDescriptorSet(UpdateDescriptorSetsBuilder *updateBuilder, |
| const DescriptorDescHandles *handles, |
| VkDescriptorSet descriptorSet) const |
| { |
| for (uint32_t writeIndex = 0; writeIndex < static_cast<uint32_t>(mWriteDescriptors.size()); |
| ++writeIndex) |
| { |
| const WriteDescriptorDesc &writeDesc = mWriteDescriptors[writeIndex]; |
| |
| if (writeDesc.descriptorCount == 0) |
| { |
| continue; |
| } |
| |
| VkWriteDescriptorSet &writeSet = updateBuilder->allocWriteDescriptorSet(); |
| |
| writeSet.descriptorCount = writeDesc.descriptorCount; |
| writeSet.descriptorType = static_cast<VkDescriptorType>(writeDesc.descriptorType); |
| writeSet.dstArrayElement = 0; |
| writeSet.dstBinding = writeIndex; |
| writeSet.dstSet = descriptorSet; |
| writeSet.pBufferInfo = nullptr; |
| writeSet.pImageInfo = nullptr; |
| writeSet.pNext = nullptr; |
| writeSet.pTexelBufferView = nullptr; |
| writeSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| |
| uint32_t infoDescIndex = writeDesc.descriptorInfoIndex; |
| |
| switch (writeSet.descriptorType) |
| { |
| case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: |
| { |
| ASSERT(writeDesc.descriptorCount == 1); |
| VkBufferView &bufferView = updateBuilder->allocBufferView(); |
| bufferView = handles[infoDescIndex].bufferView; |
| writeSet.pTexelBufferView = &bufferView; |
| break; |
| } |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: |
| { |
| VkDescriptorBufferInfo *writeBuffers = |
| updateBuilder->allocDescriptorBufferInfos(writeSet.descriptorCount); |
| for (uint32_t arrayElement = 0; arrayElement < writeSet.descriptorCount; |
| ++arrayElement) |
| { |
| const DescriptorInfoDesc &infoDesc = |
| mDescriptorInfos[infoDescIndex + arrayElement]; |
| VkDescriptorBufferInfo &bufferInfo = writeBuffers[arrayElement]; |
| bufferInfo.buffer = handles[infoDescIndex + arrayElement].buffer; |
| bufferInfo.offset = infoDesc.imageViewSerialOrOffset; |
| bufferInfo.range = infoDesc.imageLayoutOrRange; |
| } |
| writeSet.pBufferInfo = writeBuffers; |
| break; |
| } |
| case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: |
| case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: |
| case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: |
| case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: |
| { |
| VkDescriptorImageInfo *writeImages = |
| updateBuilder->allocDescriptorImageInfos(writeSet.descriptorCount); |
| for (uint32_t arrayElement = 0; arrayElement < writeSet.descriptorCount; |
| ++arrayElement) |
| { |
| const DescriptorInfoDesc &infoDesc = |
| mDescriptorInfos[infoDescIndex + arrayElement]; |
| VkDescriptorImageInfo &imageInfo = writeImages[arrayElement]; |
| |
| ImageLayout imageLayout = static_cast<ImageLayout>(infoDesc.imageLayoutOrRange); |
| |
| imageInfo.imageLayout = ConvertImageLayoutToVkImageLayout(imageLayout); |
| imageInfo.imageView = handles[infoDescIndex + arrayElement].imageView; |
| imageInfo.sampler = handles[infoDescIndex + arrayElement].sampler; |
| } |
| writeSet.pImageInfo = writeImages; |
| break; |
| } |
| |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| } |
| } |
| |
| void DescriptorSetDesc::streamOut(std::ostream &ostr) const |
| { |
| ostr << mWriteDescriptors.size() << " write descriptor descs:\n"; |
| |
| for (uint32_t index = 0; index < static_cast<uint32_t>(mWriteDescriptors.size()); ++index) |
| { |
| const WriteDescriptorDesc &writeDesc = mWriteDescriptors[index]; |
| ostr << static_cast<int>(writeDesc.binding) << ": " |
| << static_cast<int>(writeDesc.descriptorCount) << " " |
| << kDescriptorTypeNameMap[writeDesc.descriptorType] << " descriptors: "; |
| |
| for (uint32_t offset = 0; offset < writeDesc.descriptorCount; ++offset) |
| { |
| const DescriptorInfoDesc &infoDesc = |
| mDescriptorInfos[writeDesc.descriptorInfoIndex + offset]; |
| ostr << "{" << infoDesc.imageLayoutOrRange << ", " << infoDesc.imageSubresourceRange |
| << ", " << infoDesc.imageViewSerialOrOffset << ", " |
| << infoDesc.samplerOrBufferSerial << "}"; |
| } |
| |
| ostr << "\n"; |
| } |
| } |
| |
| // DescriptorSetDescBuilder implementation. |
| DescriptorSetDescBuilder::DescriptorSetDescBuilder() = default; |
| |
| DescriptorSetDescBuilder::~DescriptorSetDescBuilder() = default; |
| |
| DescriptorSetDescBuilder::DescriptorSetDescBuilder(const DescriptorSetDescBuilder &other) |
| : mDesc(other.mDesc), |
| mHandles(other.mHandles), |
| mDynamicOffsets(other.mDynamicOffsets), |
| mCurrentInfoIndex(other.mCurrentInfoIndex) |
| {} |
| |
| DescriptorSetDescBuilder &DescriptorSetDescBuilder::operator=(const DescriptorSetDescBuilder &other) |
| { |
| mDesc = other.mDesc; |
| mHandles = other.mHandles; |
| mDynamicOffsets = other.mDynamicOffsets; |
| mCurrentInfoIndex = other.mCurrentInfoIndex; |
| return *this; |
| } |
| |
| void DescriptorSetDescBuilder::reset() |
| { |
| mDesc.reset(); |
| mHandles.clear(); |
| mDynamicOffsets.clear(); |
| mCurrentInfoIndex = 0; |
| } |
| |
| void DescriptorSetDescBuilder::updateWriteDesc(uint32_t bindingIndex, |
| VkDescriptorType descriptorType, |
| uint32_t descriptorCount) |
| { |
| if (mDesc.hasWriteDescAtIndex(bindingIndex)) |
| { |
| uint32_t infoIndex = mDesc.getInfoDescIndex(bindingIndex); |
| uint32_t oldDescriptorCount = mDesc.getDescriptorSetCount(bindingIndex); |
| if (descriptorCount != oldDescriptorCount) |
| { |
| ASSERT(infoIndex + oldDescriptorCount == mCurrentInfoIndex); |
| ASSERT(descriptorCount > oldDescriptorCount); |
| uint32_t additionalDescriptors = descriptorCount - oldDescriptorCount; |
| mDesc.incrementDescriptorCount(bindingIndex, additionalDescriptors); |
| mCurrentInfoIndex += additionalDescriptors; |
| } |
| } |
| else |
| { |
| WriteDescriptorDesc writeDesc = {}; |
| SetBitField(writeDesc.binding, bindingIndex); |
| SetBitField(writeDesc.descriptorCount, descriptorCount); |
| SetBitField(writeDesc.descriptorType, descriptorType); |
| SetBitField(writeDesc.descriptorInfoIndex, mCurrentInfoIndex); |
| mCurrentInfoIndex += descriptorCount; |
| |
| mDesc.updateWriteDesc(writeDesc); |
| } |
| } |
| |
| void DescriptorSetDescBuilder::updateUniformWrite(uint32_t shaderStageCount) |
| { |
| for (uint32_t shaderIndex = 0; shaderIndex < shaderStageCount; ++shaderIndex) |
| { |
| updateWriteDesc(shaderIndex, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1); |
| } |
| } |
| |
| void DescriptorSetDescBuilder::updateUniformBuffer(uint32_t bindingIndex, |
| const BufferHelper &bufferHelper, |
| VkDeviceSize bufferRange) |
| { |
| DescriptorInfoDesc infoDesc = {}; |
| SetBitField(infoDesc.imageLayoutOrRange, bufferRange); |
| infoDesc.samplerOrBufferSerial = bufferHelper.getBlockSerial().getValue(); |
| |
| uint32_t infoIndex = mDesc.getInfoDescIndex(bindingIndex); |
| |
| mDesc.updateInfoDesc(infoIndex, infoDesc); |
| mHandles[infoIndex].buffer = bufferHelper.getBuffer().getHandle(); |
| } |
| |
| void DescriptorSetDescBuilder::updateTransformFeedbackWrite( |
| const ShaderInterfaceVariableInfoMap &variableInfoMap, |
| uint32_t xfbBufferCount) |
| { |
| updateWriteDesc(variableInfoMap.getXfbBufferBinding(0), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, |
| xfbBufferCount); |
| } |
| |
| void DescriptorSetDescBuilder::updateTransformFeedbackBuffer( |
| const Context *context, |
| const ShaderInterfaceVariableInfoMap &variableInfoMap, |
| uint32_t xfbBufferIndex, |
| const BufferHelper &bufferHelper, |
| VkDeviceSize bufferOffset, |
| VkDeviceSize bufferRange) |
| { |
| uint32_t baseBinding = variableInfoMap.getXfbBufferBinding(0); |
| |
| RendererVk *renderer = context->getRenderer(); |
| VkDeviceSize offsetAlignment = |
| renderer->getPhysicalDeviceProperties().limits.minStorageBufferOffsetAlignment; |
| // Set the offset as close as possible to the requested offset while remaining aligned. |
| VkDeviceSize alignedOffset = (bufferOffset / offsetAlignment) * offsetAlignment; |
| VkDeviceSize adjustedRange = bufferRange + (bufferOffset - alignedOffset); |
| |
| DescriptorInfoDesc infoDesc = {}; |
| SetBitField(infoDesc.imageLayoutOrRange, adjustedRange); |
| SetBitField(infoDesc.imageViewSerialOrOffset, alignedOffset); |
| infoDesc.samplerOrBufferSerial = bufferHelper.getBlockSerial().getValue(); |
| |
| uint32_t infoIndex = mDesc.getInfoDescIndex(baseBinding) + xfbBufferIndex; |
| |
| mDesc.updateInfoDesc(infoIndex, infoDesc); |
| mHandles[infoIndex].buffer = bufferHelper.getBuffer().getHandle(); |
| } |
| |
| void DescriptorSetDescBuilder::updateUniformsAndXfb(Context *context, |
| const gl::ProgramExecutable &executable, |
| const ProgramExecutableVk &executableVk, |
| const BufferHelper *currentUniformBuffer, |
| const BufferHelper &emptyBuffer, |
| bool activeUnpaused, |
| TransformFeedbackVk *transformFeedbackVk) |
| { |
| gl::ShaderBitSet linkedStages = executable.getLinkedShaderStages(); |
| |
| const ShaderInterfaceVariableInfoMap &variableInfoMap = executableVk.getVariableInfoMap(); |
| |
| for (const gl::ShaderType shaderType : linkedStages) |
| { |
| uint32_t binding = variableInfoMap.getDefaultUniformBinding(shaderType); |
| updateWriteDesc(binding, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1); |
| |
| VkDeviceSize bufferRange = executableVk.getDefaultUniformAlignedSize(context, shaderType); |
| if (bufferRange == 0) |
| { |
| updateUniformBuffer(binding, emptyBuffer, emptyBuffer.getSize()); |
| } |
| else |
| { |
| ASSERT(currentUniformBuffer); |
| updateUniformBuffer(binding, *currentUniformBuffer, bufferRange); |
| } |
| |
| if (transformFeedbackVk && shaderType == gl::ShaderType::Vertex && |
| context->getRenderer()->getFeatures().emulateTransformFeedback.enabled) |
| { |
| transformFeedbackVk->updateTransformFeedbackDescriptorDesc( |
| context, executable, variableInfoMap, emptyBuffer, activeUnpaused, this); |
| } |
| } |
| } |
| |
| void UpdatePreCacheActiveTextures(const gl::ActiveTextureMask &activeTextures, |
| const gl::ActiveTextureArray<TextureVk *> &textures, |
| const gl::SamplerBindingVector &samplers, |
| DescriptorSetDesc *desc) |
| { |
| desc->reset(); |
| |
| for (size_t textureIndex : activeTextures) |
| { |
| TextureVk *textureVk = textures[textureIndex]; |
| |
| DescriptorInfoDesc infoDesc = {}; |
| |
| if (textureVk->getState().getType() == gl::TextureType::Buffer) |
| { |
| ImageOrBufferViewSubresourceSerial imageViewSerial = textureVk->getBufferViewSerial(); |
| infoDesc.imageViewSerialOrOffset = imageViewSerial.viewSerial.getValue(); |
| } |
| else |
| { |
| gl::Sampler *sampler = samplers[textureIndex].get(); |
| const SamplerVk *samplerVk = sampler ? vk::GetImpl(sampler) : nullptr; |
| |
| const SamplerHelper &samplerHelper = |
| samplerVk ? samplerVk->getSampler() : textureVk->getSampler(); |
| const gl::SamplerState &samplerState = |
| sampler ? sampler->getSamplerState() : textureVk->getState().getSamplerState(); |
| |
| ImageOrBufferViewSubresourceSerial imageViewSerial = |
| textureVk->getImageViewSubresourceSerial(samplerState); |
| |
| // Layout is implicit. |
| |
| infoDesc.imageViewSerialOrOffset = imageViewSerial.viewSerial.getValue(); |
| infoDesc.samplerOrBufferSerial = samplerHelper.getSamplerSerial().getValue(); |
| memcpy(&infoDesc.imageSubresourceRange, &imageViewSerial.subresource, sizeof(uint32_t)); |
| } |
| |
| desc->updateInfoDesc(static_cast<uint32_t>(textureIndex), infoDesc); |
| } |
| } |
| |
| angle::Result DescriptorSetDescBuilder::updateFullActiveTextures( |
| Context *context, |
| const ShaderInterfaceVariableInfoMap &variableInfoMap, |
| const gl::ProgramExecutable &executable, |
| const gl::ActiveTextureArray<TextureVk *> &textures, |
| const gl::SamplerBindingVector &samplers, |
| bool emulateSeamfulCubeMapSampling, |
| PipelineType pipelineType) |
| { |
| reset(); |
| for (gl::ShaderType shaderType : executable.getLinkedShaderStages()) |
| { |
| ANGLE_TRY(updateExecutableActiveTexturesForShader( |
| context, shaderType, variableInfoMap, executable, textures, samplers, |
| emulateSeamfulCubeMapSampling, pipelineType)); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result DescriptorSetDescBuilder::updateExecutableActiveTexturesForShader( |
| Context *context, |
| gl::ShaderType shaderType, |
| const ShaderInterfaceVariableInfoMap &variableInfoMap, |
| const gl::ProgramExecutable &executable, |
| const gl::ActiveTextureArray<TextureVk *> &textures, |
| const gl::SamplerBindingVector &samplers, |
| bool emulateSeamfulCubeMapSampling, |
| PipelineType pipelineType) |
| { |
| const std::vector<gl::SamplerBinding> &samplerBindings = executable.getSamplerBindings(); |
| const std::vector<gl::LinkedUniform> &uniforms = executable.getUniforms(); |
| const gl::ActiveTextureTypeArray &textureTypes = executable.getActiveSamplerTypes(); |
| |
| for (uint32_t textureIndex = 0; textureIndex < samplerBindings.size(); ++textureIndex) |
| { |
| const gl::SamplerBinding &samplerBinding = samplerBindings[textureIndex]; |
| uint32_t uniformIndex = executable.getUniformIndexFromSamplerIndex(textureIndex); |
| const gl::LinkedUniform &samplerUniform = uniforms[uniformIndex]; |
| |
| if (!samplerUniform.isActive(shaderType)) |
| { |
| continue; |
| } |
| |
| const ShaderInterfaceVariableInfo &info = variableInfoMap.getIndexedVariableInfo( |
| shaderType, ShaderVariableType::Texture, textureIndex); |
| if (info.isDuplicate) |
| { |
| continue; |
| } |
| |
| uint32_t arraySize = static_cast<uint32_t>(samplerBinding.boundTextureUnits.size()); |
| uint32_t descriptorCount = arraySize * gl::ArraySizeProduct(samplerUniform.outerArraySizes); |
| VkDescriptorType descriptorType = (samplerBinding.textureType == gl::TextureType::Buffer) |
| ? VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER |
| : VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; |
| |
| updateWriteDesc(info.binding, descriptorType, descriptorCount); |
| |
| for (uint32_t arrayElement = 0; arrayElement < arraySize; ++arrayElement) |
| { |
| GLuint textureUnit = samplerBinding.boundTextureUnits[arrayElement]; |
| TextureVk *textureVk = textures[textureUnit]; |
| |
| DescriptorInfoDesc infoDesc = {}; |
| |
| uint32_t infoIndex = mDesc.getInfoDescIndex(info.binding) + arrayElement + |
| samplerUniform.outerArrayOffset; |
| |
| if (textureTypes[textureUnit] == gl::TextureType::Buffer) |
| { |
| ImageOrBufferViewSubresourceSerial imageViewSerial = |
| textureVk->getBufferViewSerial(); |
| infoDesc.imageViewSerialOrOffset = imageViewSerial.viewSerial.getValue(); |
| |
| const BufferView *view = nullptr; |
| ANGLE_TRY(textureVk->getBufferViewAndRecordUse(context, nullptr, false, &view)); |
| mHandles[infoIndex].bufferView = view->getHandle(); |
| } |
| else |
| { |
| gl::Sampler *sampler = samplers[textureUnit].get(); |
| const SamplerVk *samplerVk = sampler ? vk::GetImpl(sampler) : nullptr; |
| |
| const SamplerHelper &samplerHelper = |
| samplerVk ? samplerVk->getSampler() : textureVk->getSampler(); |
| const gl::SamplerState &samplerState = |
| sampler ? sampler->getSamplerState() : textureVk->getState().getSamplerState(); |
| |
| ImageOrBufferViewSubresourceSerial imageViewSerial = |
| textureVk->getImageViewSubresourceSerial(samplerState); |
| |
| ImageLayout imageLayout = textureVk->getImage().getCurrentImageLayout(); |
| |
| SetBitField(infoDesc.imageLayoutOrRange, imageLayout); |
| infoDesc.imageViewSerialOrOffset = imageViewSerial.viewSerial.getValue(); |
| infoDesc.samplerOrBufferSerial = samplerHelper.getSamplerSerial().getValue(); |
| memcpy(&infoDesc.imageSubresourceRange, &imageViewSerial.subresource, |
| sizeof(uint32_t)); |
| |
| mHandles[infoIndex].sampler = samplerHelper.get().getHandle(); |
| |
| if (emulateSeamfulCubeMapSampling) |
| { |
| // If emulating seamful cube mapping, use the fetch image view. This is |
| // basically the same image view as read, except it's a 2DArray view for |
| // cube maps. |
| const ImageView &imageView = textureVk->getFetchImageView( |
| context, samplerState.getSRGBDecode(), samplerUniform.texelFetchStaticUse); |
| mHandles[infoIndex].imageView = imageView.getHandle(); |
| } |
| else |
| { |
| const ImageView &imageView = textureVk->getReadImageView( |
| context, samplerState.getSRGBDecode(), samplerUniform.texelFetchStaticUse); |
| mHandles[infoIndex].imageView = imageView.getHandle(); |
| } |
| } |
| |
| mDesc.updateInfoDesc(infoIndex, infoDesc); |
| } |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| void DescriptorSetDescBuilder::updateShaderBuffers( |
| gl::ShaderType shaderType, |
| ShaderVariableType variableType, |
| const ShaderInterfaceVariableInfoMap &variableInfoMap, |
| const gl::BufferVector &buffers, |
| const std::vector<gl::InterfaceBlock> &blocks, |
| VkDescriptorType descriptorType, |
| VkDeviceSize maxBoundBufferRange, |
| const BufferHelper &emptyBuffer) |
| { |
| // Initialize the descriptor writes in a first pass. This ensures we can pack the structures |
| // corresponding to array elements tightly. |
| for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex) |
| { |
| const gl::InterfaceBlock &block = blocks[bufferIndex]; |
| |
| if (!block.isActive(shaderType)) |
| { |
| continue; |
| } |
| |
| const ShaderInterfaceVariableInfo &info = |
| variableInfoMap.getIndexedVariableInfo(shaderType, variableType, bufferIndex); |
| if (info.isDuplicate) |
| { |
| continue; |
| } |
| |
| if (block.isArray && block.arrayElement > 0) |
| { |
| mDesc.incrementDescriptorCount(info.binding, 1); |
| mCurrentInfoIndex++; |
| } |
| else |
| { |
| updateWriteDesc(info.binding, descriptorType, 1); |
| } |
| } |
| |
| // Now that we have the proper array elements counts, initialize the info structures. |
| for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex) |
| { |
| const gl::InterfaceBlock &block = blocks[bufferIndex]; |
| const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding = buffers[block.binding]; |
| |
| if (!block.isActive(shaderType)) |
| { |
| continue; |
| } |
| |
| const ShaderInterfaceVariableInfo &info = |
| variableInfoMap.getIndexedVariableInfo(shaderType, variableType, bufferIndex); |
| if (info.isDuplicate) |
| { |
| continue; |
| } |
| |
| uint32_t binding = info.binding; |
| uint32_t arrayElement = block.isArray ? block.arrayElement : 0; |
| uint32_t infoDescIndex = mDesc.getInfoDescIndex(binding) + arrayElement; |
| |
| if (bufferBinding.get() == nullptr) |
| { |
| DescriptorInfoDesc emptyDesc = {}; |
| SetBitField(emptyDesc.imageLayoutOrRange, emptyBuffer.getSize()); |
| emptyDesc.imageViewSerialOrOffset = 0; |
| emptyDesc.samplerOrBufferSerial = emptyBuffer.getBlockSerial().getValue(); |
| |
| mDesc.updateInfoDesc(infoDescIndex, emptyDesc); |
| |
| mHandles[infoDescIndex].buffer = emptyBuffer.getBuffer().getHandle(); |
| |
| if (IsDynamicDescriptor(descriptorType)) |
| { |
| mDynamicOffsets[infoDescIndex] = 0; |
| } |
| } |
| else |
| { |
| // Limit bound buffer size to maximum resource binding size. |
| GLsizeiptr boundBufferSize = gl::GetBoundBufferAvailableSize(bufferBinding); |
| VkDeviceSize size = std::min<VkDeviceSize>(boundBufferSize, maxBoundBufferRange); |
| |
| // Make sure there's no possible under/overflow with binding size. |
| static_assert(sizeof(VkDeviceSize) >= sizeof(bufferBinding.getSize()), |
| "VkDeviceSize too small"); |
| ASSERT(bufferBinding.getSize() >= 0); |
| |
| BufferVk *bufferVk = vk::GetImpl(bufferBinding.get()); |
| BufferHelper &bufferHelper = bufferVk->getBuffer(); |
| |
| DescriptorInfoDesc infoDesc = {}; |
| SetBitField(infoDesc.imageLayoutOrRange, size); |
| infoDesc.samplerOrBufferSerial = bufferHelper.getBlockSerial().getValue(); |
| |
| VkDeviceSize offset = bufferBinding.getOffset() + bufferHelper.getOffset(); |
| |
| if (IsDynamicDescriptor(descriptorType)) |
| { |
| SetBitField(mDynamicOffsets[infoDescIndex], offset); |
| } |
| else |
| { |
| SetBitField(infoDesc.imageViewSerialOrOffset, offset); |
| } |
| |
| mDesc.updateInfoDesc(infoDescIndex, infoDesc); |
| |
| mHandles[infoDescIndex].buffer = bufferHelper.getBuffer().getHandle(); |
| } |
| } |
| } |
| |
| void DescriptorSetDescBuilder::updateAtomicCounters( |
| gl::ShaderType shaderType, |
| const ShaderInterfaceVariableInfoMap &variableInfoMap, |
| const gl::BufferVector &buffers, |
| const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers, |
| const VkDeviceSize requiredOffsetAlignment, |
| vk::BufferHelper *emptyBuffer) |
| { |
| if (atomicCounterBuffers.empty() || !variableInfoMap.hasAtomicCounterInfo(shaderType)) |
| { |
| return; |
| } |
| |
| static_assert(!IsDynamicDescriptor(kStorageBufferDescriptorType), |
| "This method needs an update to handle dynamic descriptors"); |
| |
| uint32_t binding = variableInfoMap.getAtomicCounterBufferBinding(shaderType, 0); |
| |
| updateWriteDesc(binding, kStorageBufferDescriptorType, |
| gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS); |
| |
| uint32_t baseInfoIndex = mDesc.getInfoDescIndex(binding); |
| |
| DescriptorInfoDesc emptyDesc = {}; |
| SetBitField(emptyDesc.imageLayoutOrRange, emptyBuffer->getSize()); |
| emptyDesc.imageViewSerialOrOffset = 0; |
| emptyDesc.samplerOrBufferSerial = emptyBuffer->getBlockSerial().getValue(); |
| |
| // Bind the empty buffer to every array slot that's unused. |
| for (uint32_t arrayElement = 0; |
| arrayElement < gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS; ++arrayElement) |
| { |
| uint32_t infoIndex = baseInfoIndex + arrayElement; |
| mDesc.updateInfoDesc(infoIndex, emptyDesc); |
| mHandles[infoIndex].buffer = emptyBuffer->getBuffer().getHandle(); |
| } |
| |
| for (const gl::AtomicCounterBuffer &atomicCounterBuffer : atomicCounterBuffers) |
| { |
| int arrayElement = atomicCounterBuffer.binding; |
| const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding = buffers[arrayElement]; |
| |
| uint32_t infoIndex = baseInfoIndex + arrayElement; |
| |
| if (bufferBinding.get() == nullptr) |
| { |
| mDesc.updateInfoDesc(infoIndex, emptyDesc); |
| mHandles[infoIndex].buffer = emptyBuffer->getBuffer().getHandle(); |
| continue; |
| } |
| |
| BufferVk *bufferVk = vk::GetImpl(bufferBinding.get()); |
| vk::BufferHelper &bufferHelper = bufferVk->getBuffer(); |
| |
| VkDeviceSize offset = bufferBinding.getOffset() + bufferHelper.getOffset(); |
| |
| VkDeviceSize alignedOffset = (offset / requiredOffsetAlignment) * requiredOffsetAlignment; |
| VkDeviceSize offsetDiff = offset - alignedOffset; |
| |
| offset = alignedOffset; |
| |
| VkDeviceSize range = gl::GetBoundBufferAvailableSize(bufferBinding) + offsetDiff; |
| |
| DescriptorInfoDesc infoDesc = {}; |
| SetBitField(infoDesc.imageLayoutOrRange, range); |
| SetBitField(infoDesc.imageViewSerialOrOffset, offset); |
| infoDesc.samplerOrBufferSerial = bufferHelper.getBlockSerial().getValue(); |
| |
| mDesc.updateInfoDesc(infoIndex, infoDesc); |
| mHandles[infoIndex].buffer = bufferHelper.getBuffer().getHandle(); |
| } |
| } |
| |
| angle::Result DescriptorSetDescBuilder::updateImages( |
| Context *context, |
| gl::ShaderType shaderType, |
| const gl::ProgramExecutable &executable, |
| const ShaderInterfaceVariableInfoMap &variableInfoMap, |
| const gl::ActiveTextureArray<TextureVk *> &activeImages, |
| const std::vector<gl::ImageUnit> &imageUnits) |
| { |
| RendererVk *renderer = context->getRenderer(); |
| const std::vector<gl::ImageBinding> &imageBindings = executable.getImageBindings(); |
| const std::vector<gl::LinkedUniform> &uniforms = executable.getUniforms(); |
| |
| if (imageBindings.empty()) |
| { |
| return angle::Result::Continue; |
| } |
| |
| for (uint32_t imageIndex = 0; imageIndex < imageBindings.size(); ++imageIndex) |
| { |
| const gl::ImageBinding &imageBinding = imageBindings[imageIndex]; |
| uint32_t uniformIndex = executable.getUniformIndexFromImageIndex(imageIndex); |
| const gl::LinkedUniform &imageUniform = uniforms[uniformIndex]; |
| |
| if (!imageUniform.isActive(shaderType)) |
| { |
| continue; |
| } |
| |
| const ShaderInterfaceVariableInfo &info = variableInfoMap.getIndexedVariableInfo( |
| shaderType, ShaderVariableType::Image, imageIndex); |
| if (info.isDuplicate) |
| { |
| continue; |
| } |
| |
| uint32_t arraySize = static_cast<uint32_t>(imageBinding.boundImageUnits.size()); |
| uint32_t descriptorCount = arraySize * gl::ArraySizeProduct(imageUniform.outerArraySizes); |
| VkDescriptorType descriptorType = (imageBinding.textureType == gl::TextureType::Buffer) |
| ? VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER |
| : VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; |
| |
| updateWriteDesc(info.binding, descriptorType, descriptorCount); |
| |
| // Texture buffers use buffer views, so they are especially handled. |
| if (imageBinding.textureType == gl::TextureType::Buffer) |
| { |
| // Handle format reinterpretation by looking for a view with the format specified in |
| // the shader (if any, instead of the format specified to glTexBuffer). |
| const vk::Format *format = nullptr; |
| if (imageUniform.imageUnitFormat != GL_NONE) |
| { |
| format = &renderer->getFormat(imageUniform.imageUnitFormat); |
| } |
| |
| for (uint32_t arrayElement = 0; arrayElement < arraySize; ++arrayElement) |
| { |
| GLuint imageUnit = imageBinding.boundImageUnits[arrayElement]; |
| TextureVk *textureVk = activeImages[imageUnit]; |
| |
| uint32_t infoIndex = mDesc.getInfoDescIndex(info.binding) + arrayElement + |
| imageUniform.outerArrayOffset; |
| |
| const vk::BufferView *view = nullptr; |
| ANGLE_TRY(textureVk->getBufferViewAndRecordUse(context, format, true, &view)); |
| |
| DescriptorInfoDesc infoDesc = {}; |
| infoDesc.imageViewSerialOrOffset = |
| textureVk->getBufferViewSerial().viewSerial.getValue(); |
| |
| mDesc.updateInfoDesc(infoIndex, infoDesc); |
| mHandles[infoIndex].bufferView = view->getHandle(); |
| } |
| } |
| else |
| { |
| for (uint32_t arrayElement = 0; arrayElement < arraySize; ++arrayElement) |
| { |
| GLuint imageUnit = imageBinding.boundImageUnits[arrayElement]; |
| const gl::ImageUnit &binding = imageUnits[imageUnit]; |
| TextureVk *textureVk = activeImages[imageUnit]; |
| |
| vk::ImageHelper *image = &textureVk->getImage(); |
| const vk::ImageView *imageView = nullptr; |
| |
| vk::ImageOrBufferViewSubresourceSerial serial = |
| textureVk->getStorageImageViewSerial(binding); |
| |
| ANGLE_TRY(textureVk->getStorageImageView(context, binding, &imageView)); |
| |
| uint32_t infoIndex = mDesc.getInfoDescIndex(info.binding) + arrayElement + |
| imageUniform.outerArrayOffset; |
| |
| // Note: binding.access is unused because it is implied by the shader. |
| |
| DescriptorInfoDesc infoDesc = {}; |
| SetBitField(infoDesc.imageLayoutOrRange, image->getCurrentImageLayout()); |
| memcpy(&infoDesc.imageSubresourceRange, &serial.subresource, sizeof(uint32_t)); |
| infoDesc.imageViewSerialOrOffset = serial.viewSerial.getValue(); |
| |
| mDesc.updateInfoDesc(infoIndex, infoDesc); |
| mHandles[infoIndex].imageView = imageView->getHandle(); |
| } |
| } |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result DescriptorSetDescBuilder::updateInputAttachments( |
| vk::Context *context, |
| gl::ShaderType shaderType, |
| const gl::ProgramExecutable &executable, |
| const ShaderInterfaceVariableInfoMap &variableInfoMap, |
| FramebufferVk *framebufferVk) |
| { |
| if (shaderType != gl::ShaderType::Fragment) |
| { |
| return angle::Result::Continue; |
| } |
| |
| const std::vector<gl::LinkedUniform> &uniforms = executable.getUniforms(); |
| |
| if (!executable.usesFramebufferFetch()) |
| { |
| return angle::Result::Continue; |
| } |
| |
| const uint32_t baseUniformIndex = executable.getFragmentInoutRange().low(); |
| const gl::LinkedUniform &baseInputAttachment = uniforms.at(baseUniformIndex); |
| std::string baseMappedName = baseInputAttachment.mappedName; |
| |
| const ShaderInterfaceVariableInfo &baseInfo = |
| variableInfoMap.getFramebufferFetchInfo(shaderType); |
| if (baseInfo.isDuplicate) |
| { |
| return angle::Result::Continue; |
| } |
| |
| uint32_t baseBinding = baseInfo.binding - baseInputAttachment.location; |
| |
| for (size_t colorIndex : framebufferVk->getState().getColorAttachmentsMask()) |
| { |
| uint32_t binding = baseBinding + static_cast<uint32_t>(colorIndex); |
| updateWriteDesc(binding, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1); |
| |
| RenderTargetVk *renderTargetVk = framebufferVk->getColorDrawRenderTarget(colorIndex); |
| const vk::ImageView *imageView = nullptr; |
| |
| ANGLE_TRY(renderTargetVk->getImageView(context, &imageView)); |
| |
| uint32_t infoIndex = mDesc.getInfoDescIndex(binding); |
| |
| DescriptorInfoDesc infoDesc = {}; |
| |
| // We just need any layout that represents GENERAL. The serial is also not totally precise. |
| ImageOrBufferViewSubresourceSerial serial = renderTargetVk->getDrawSubresourceSerial(); |
| SetBitField(infoDesc.imageLayoutOrRange, ImageLayout::FragmentShaderWrite); |
| infoDesc.imageViewSerialOrOffset = serial.viewSerial.getValue(); |
| memcpy(&infoDesc.imageSubresourceRange, &serial.subresource, sizeof(uint32_t)); |
| |
| mDesc.updateInfoDesc(infoIndex, infoDesc); |
| mHandles[infoIndex].imageView = imageView->getHandle(); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| void DescriptorSetDescBuilder::updateDescriptorSet(UpdateDescriptorSetsBuilder *updateBuilder, |
| VkDescriptorSet descriptorSet) const |
| { |
| mDesc.updateDescriptorSet(updateBuilder, mHandles.data(), descriptorSet); |
| } |
| } // namespace vk |
| |
| // RenderPassCache implementation. |
| RenderPassCache::RenderPassCache() = default; |
| |
| RenderPassCache::~RenderPassCache() |
| { |
| ASSERT(mPayload.empty()); |
| } |
| |
| void RenderPassCache::destroy(RendererVk *rendererVk) |
| { |
| rendererVk->accumulateCacheStats(VulkanCacheType::CompatibleRenderPass, |
| mCompatibleRenderPassCacheStats); |
| rendererVk->accumulateCacheStats(VulkanCacheType::RenderPassWithOps, |
| mRenderPassWithOpsCacheStats); |
| |
| VkDevice device = rendererVk->getDevice(); |
| |
| for (auto &outerIt : mPayload) |
| { |
| for (auto &innerIt : outerIt.second) |
| { |
| innerIt.second.destroy(device); |
| } |
| } |
| mPayload.clear(); |
| } |
| |
| void RenderPassCache::clear(ContextVk *contextVk) |
| { |
| for (auto &outerIt : mPayload) |
| { |
| for (auto &innerIt : outerIt.second) |
| { |
| innerIt.second.release(contextVk); |
| } |
| } |
| mPayload.clear(); |
| } |
| |
| angle::Result RenderPassCache::addRenderPass(ContextVk *contextVk, |
| const vk::RenderPassDesc &desc, |
| vk::RenderPass **renderPassOut) |
| { |
| // Insert some placeholder attachment ops. Note that render passes with different ops are still |
| // compatible. The load/store values are not important as they are aren't used for real RPs. |
| // |
| // It would be nice to pre-populate the cache in the Renderer so we rarely miss here. |
| vk::AttachmentOpsArray ops; |
| |
| vk::PackedAttachmentIndex colorIndexVk(0); |
| for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL) |
| { |
| if (!desc.isColorAttachmentEnabled(colorIndexGL)) |
| { |
| continue; |
| } |
| |
| ops.initWithLoadStore(colorIndexVk, vk::ImageLayout::ColorAttachment, |
| vk::ImageLayout::ColorAttachment); |
| ++colorIndexVk; |
| } |
| |
| if (desc.hasDepthStencilAttachment()) |
| { |
| // This API is only called by getCompatibleRenderPass(). What we need here is to create a |
| // compatible renderpass with the desc. Vulkan spec says image layout are not counted toward |
| // render pass compatibility: "Two render passes are compatible if their corresponding |
| // color, input, resolve, and depth/stencil attachment references are compatible and if they |
| // are otherwise identical except for: Initial and final image layout in attachment |
| // descriptions; Image layout in attachment references". We pick the most used layout here |
| // since it doesn't matter. |
| vk::ImageLayout imageLayout = vk::ImageLayout::DepthStencilAttachment; |
| ops.initWithLoadStore(colorIndexVk, imageLayout, imageLayout); |
| } |
| |
| return getRenderPassWithOpsImpl(contextVk, desc, ops, false, renderPassOut); |
| } |
| |
| angle::Result RenderPassCache::getRenderPassWithOps(ContextVk *contextVk, |
| const vk::RenderPassDesc &desc, |
| const vk::AttachmentOpsArray &attachmentOps, |
| vk::RenderPass **renderPassOut) |
| { |
| return getRenderPassWithOpsImpl(contextVk, desc, attachmentOps, true, renderPassOut); |
| } |
| |
| angle::Result RenderPassCache::getRenderPassWithOpsImpl(ContextVk *contextVk, |
| const vk::RenderPassDesc &desc, |
| const vk::AttachmentOpsArray &attachmentOps, |
| bool updatePerfCounters, |
| vk::RenderPass **renderPassOut) |
| { |
| auto outerIt = mPayload.find(desc); |
| if (outerIt != mPayload.end()) |
| { |
| InnerCache &innerCache = outerIt->second; |
| |
| auto innerIt = innerCache.find(attachmentOps); |
| if (innerIt != innerCache.end()) |
| { |
| // TODO(jmadill): Could possibly use an MRU cache here. |
| vk::GetRenderPassAndUpdateCounters(contextVk, updatePerfCounters, &innerIt->second, |
| renderPassOut); |
| mRenderPassWithOpsCacheStats.hit(); |
| return angle::Result::Continue; |
| } |
| } |
| else |
| { |
| auto emplaceResult = mPayload.emplace(desc, InnerCache()); |
| outerIt = emplaceResult.first; |
| } |
| |
| mRenderPassWithOpsCacheStats.missAndIncrementSize(); |
| vk::RenderPassHelper newRenderPass; |
| ANGLE_TRY(vk::InitializeRenderPassFromDesc(contextVk, desc, attachmentOps, &newRenderPass)); |
| |
| InnerCache &innerCache = outerIt->second; |
| auto insertPos = innerCache.emplace(attachmentOps, std::move(newRenderPass)); |
| vk::GetRenderPassAndUpdateCounters(contextVk, updatePerfCounters, &insertPos.first->second, |
| renderPassOut); |
| |
| // TODO(jmadill): Trim cache, and pre-populate with the most common RPs on startup. |
| return angle::Result::Continue; |
| } |
| |
| // PipelineCacheAccess implementation. |
| std::unique_lock<std::mutex> PipelineCacheAccess::getLock() |
| { |
| if (mMutex == nullptr) |
| { |
| return std::unique_lock<std::mutex>(); |
| } |
| |
| return std::unique_lock<std::mutex>(*mMutex); |
| } |
| |
| angle::Result PipelineCacheAccess::createGraphicsPipeline( |
| vk::Context *context, |
| const VkGraphicsPipelineCreateInfo &createInfo, |
| vk::Pipeline *pipelineOut) |
| { |
| std::unique_lock<std::mutex> lock = getLock(); |
| |
| ANGLE_VK_TRY(context, |
| pipelineOut->initGraphics(context->getDevice(), createInfo, *mPipelineCache)); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result PipelineCacheAccess::createComputePipeline( |
| vk::Context *context, |
| const VkComputePipelineCreateInfo &createInfo, |
| vk::Pipeline *pipelineOut) |
| { |
| std::unique_lock<std::mutex> lock = getLock(); |
| |
| ANGLE_VK_TRY(context, |
| pipelineOut->initCompute(context->getDevice(), createInfo, *mPipelineCache)); |
| |
| return angle::Result::Continue; |
| } |
| |
| // GraphicsPipelineCache implementation. |
| GraphicsPipelineCache::GraphicsPipelineCache() = default; |
| |
| GraphicsPipelineCache::~GraphicsPipelineCache() |
| { |
| ASSERT(mPayload.empty()); |
| } |
| |
| void GraphicsPipelineCache::destroy(RendererVk *rendererVk) |
| { |
| accumulateCacheStats(rendererVk); |
| |
| VkDevice device = rendererVk->getDevice(); |
| |
| for (auto &item : mPayload) |
| { |
| vk::PipelineHelper &pipeline = item.second; |
| pipeline.destroy(device); |
| } |
| |
| mPayload.clear(); |
| } |
| |
| void GraphicsPipelineCache::release(ContextVk *contextVk) |
| { |
| if (kDumpPipelineCacheGraph && !mPayload.empty()) |
| { |
| vk::DumpPipelineCacheGraph(contextVk, mPayload); |
| } |
| |
| for (auto &item : mPayload) |
| { |
| vk::PipelineHelper &pipeline = item.second; |
| contextVk->addGarbage(&pipeline.getPipeline()); |
| } |
| |
| mPayload.clear(); |
| } |
| |
| void GraphicsPipelineCache::reset() |
| { |
| mPayload.clear(); |
| } |
| |
| angle::Result GraphicsPipelineCache::insertPipeline( |
| ContextVk *contextVk, |
| PipelineCacheAccess *pipelineCache, |
| const vk::RenderPass &compatibleRenderPass, |
| const vk::PipelineLayout &pipelineLayout, |
| const gl::AttributesMask &activeAttribLocationsMask, |
| const gl::ComponentTypeMask &programAttribsTypeMask, |
| const gl::DrawBufferMask &missingOutputsMask, |
| const vk::ShaderAndSerialMap &shaders, |
| const vk::SpecializationConstants &specConsts, |
| PipelineSource source, |
| const vk::GraphicsPipelineDesc &desc, |
| const vk::GraphicsPipelineDesc **descPtrOut, |
| vk::PipelineHelper **pipelineOut) |
| { |
| vk::Pipeline newPipeline; |
| vk::CacheLookUpFeedback feedback = vk::CacheLookUpFeedback::None; |
| |
| // This "if" is left here for the benefit of VulkanPipelineCachePerfTest. |
| if (contextVk != nullptr) |
| { |
| ANGLE_TRY(desc.initializePipeline(contextVk, pipelineCache, compatibleRenderPass, |
| pipelineLayout, activeAttribLocationsMask, |
| programAttribsTypeMask, missingOutputsMask, shaders, |
| specConsts, &newPipeline, &feedback)); |
| } |
| |
| if (source == PipelineSource::WarmUp) |
| { |
| feedback = feedback == vk::CacheLookUpFeedback::Hit ? vk::CacheLookUpFeedback::WarmUpHit |
| : vk::CacheLookUpFeedback::WarmUpMiss; |
| } |
| |
| // The Serial will be updated outside of this query. |
| auto insertedItem = mPayload.emplace(std::piecewise_construct, std::forward_as_tuple(desc), |
| std::forward_as_tuple(std::move(newPipeline), feedback)); |
| *descPtrOut = &insertedItem.first->first; |
| *pipelineOut = &insertedItem.first->second; |
| |
| return angle::Result::Continue; |
| } |
| |
| void GraphicsPipelineCache::populate(const vk::GraphicsPipelineDesc &desc, vk::Pipeline &&pipeline) |
| { |
| auto item = mPayload.find(desc); |
| if (item != mPayload.end()) |
| { |
| return; |
| } |
| |
| // Note: this function is only used for testing. |
| mPayload.emplace(std::piecewise_construct, std::forward_as_tuple(desc), |
| std::forward_as_tuple(std::move(pipeline), vk::CacheLookUpFeedback::None)); |
| } |
| |
| // DescriptorSetLayoutCache implementation. |
| DescriptorSetLayoutCache::DescriptorSetLayoutCache() = default; |
| |
| DescriptorSetLayoutCache::~DescriptorSetLayoutCache() |
| { |
| ASSERT(mPayload.empty()); |
| } |
| |
| void DescriptorSetLayoutCache::destroy(RendererVk *rendererVk) |
| { |
| rendererVk->accumulateCacheStats(VulkanCacheType::DescriptorSetLayout, mCacheStats); |
| |
| VkDevice device = rendererVk->getDevice(); |
| |
| for (auto &item : mPayload) |
| { |
| vk::RefCountedDescriptorSetLayout &layout = item.second; |
| ASSERT(!layout.isReferenced()); |
| layout.get().destroy(device); |
| } |
| |
| mPayload.clear(); |
| } |
| |
| angle::Result DescriptorSetLayoutCache::getDescriptorSetLayout( |
| vk::Context *context, |
| const vk::DescriptorSetLayoutDesc &desc, |
| vk::BindingPointer<vk::DescriptorSetLayout> *descriptorSetLayoutOut) |
| { |
| auto iter = mPayload.find(desc); |
| if (iter != mPayload.end()) |
| { |
| vk::RefCountedDescriptorSetLayout &layout = iter->second; |
| descriptorSetLayoutOut->set(&layout); |
| mCacheStats.hit(); |
| return angle::Result::Continue; |
| } |
| |
| mCacheStats.missAndIncrementSize(); |
| // We must unpack the descriptor set layout description. |
| vk::DescriptorSetLayoutBindingVector bindingVector; |
| std::vector<VkSampler> immutableSamplers; |
| desc.unpackBindings(&bindingVector, &immutableSamplers); |
| |
| VkDescriptorSetLayoutCreateInfo createInfo = {}; |
| createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; |
| createInfo.flags = 0; |
| createInfo.bindingCount = static_cast<uint32_t>(bindingVector.size()); |
| createInfo.pBindings = bindingVector.data(); |
| |
| vk::DescriptorSetLayout newLayout; |
| ANGLE_VK_TRY(context, newLayout.init(context->getDevice(), createInfo)); |
| |
| auto insertedItem = |
| mPayload.emplace(desc, vk::RefCountedDescriptorSetLayout(std::move(newLayout))); |
| vk::RefCountedDescriptorSetLayout &insertedLayout = insertedItem.first->second; |
| descriptorSetLayoutOut->set(&insertedLayout); |
| |
| return angle::Result::Continue; |
| } |
| |
| // PipelineLayoutCache implementation. |
| PipelineLayoutCache::PipelineLayoutCache() = default; |
| |
| PipelineLayoutCache::~PipelineLayoutCache() |
| { |
| ASSERT(mPayload.empty()); |
| } |
| |
| void PipelineLayoutCache::destroy(RendererVk *rendererVk) |
| { |
| accumulateCacheStats(rendererVk); |
| |
| VkDevice device = rendererVk->getDevice(); |
| |
| for (auto &item : mPayload) |
| { |
| vk::RefCountedPipelineLayout &layout = item.second; |
| layout.get().destroy(device); |
| } |
| |
| mPayload.clear(); |
| } |
| |
| angle::Result PipelineLayoutCache::getPipelineLayout( |
| vk::Context *context, |
| const vk::PipelineLayoutDesc &desc, |
| const vk::DescriptorSetLayoutPointerArray &descriptorSetLayouts, |
| vk::BindingPointer<vk::PipelineLayout> *pipelineLayoutOut) |
| { |
| auto iter = mPayload.find(desc); |
| if (iter != mPayload.end()) |
| { |
| vk::RefCountedPipelineLayout &layout = iter->second; |
| pipelineLayoutOut->set(&layout); |
| mCacheStats.hit(); |
| return angle::Result::Continue; |
| } |
| |
| mCacheStats.missAndIncrementSize(); |
| // Note this does not handle gaps in descriptor set layouts gracefully. |
| angle::FixedVector<VkDescriptorSetLayout, vk::kMaxDescriptorSetLayouts> setLayoutHandles; |
| for (const vk::BindingPointer<vk::DescriptorSetLayout> &layoutPtr : descriptorSetLayouts) |
| { |
| if (layoutPtr.valid()) |
| { |
| VkDescriptorSetLayout setLayout = layoutPtr.get().getHandle(); |
| if (setLayout != VK_NULL_HANDLE) |
| { |
| setLayoutHandles.push_back(setLayout); |
| } |
| } |
| } |
| |
| const vk::PackedPushConstantRange &descPushConstantRange = desc.getPushConstantRange(); |
| VkPushConstantRange pushConstantRange; |
| pushConstantRange.stageFlags = descPushConstantRange.stageMask; |
| pushConstantRange.offset = descPushConstantRange.offset; |
| pushConstantRange.size = descPushConstantRange.size; |
| |
| // No pipeline layout found. We must create a new one. |
| VkPipelineLayoutCreateInfo createInfo = {}; |
| createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; |
| createInfo.flags = 0; |
| createInfo.setLayoutCount = static_cast<uint32_t>(setLayoutHandles.size()); |
| createInfo.pSetLayouts = setLayoutHandles.data(); |
| if (pushConstantRange.size > 0) |
| { |
| createInfo.pushConstantRangeCount = 1; |
| createInfo.pPushConstantRanges = &pushConstantRange; |
| } |
| |
| vk::PipelineLayout newLayout; |
| ANGLE_VK_TRY(context, newLayout.init(context->getDevice(), createInfo)); |
| |
| auto insertedItem = mPayload.emplace(desc, vk::RefCountedPipelineLayout(std::move(newLayout))); |
| vk::RefCountedPipelineLayout &insertedLayout = insertedItem.first->second; |
| pipelineLayoutOut->set(&insertedLayout); |
| |
| return angle::Result::Continue; |
| } |
| |
| // YuvConversionCache implementation |
| SamplerYcbcrConversionCache::SamplerYcbcrConversionCache() = default; |
| |
| SamplerYcbcrConversionCache::~SamplerYcbcrConversionCache() |
| { |
| ASSERT(mExternalFormatPayload.empty() && mVkFormatPayload.empty()); |
| } |
| |
| void SamplerYcbcrConversionCache::destroy(RendererVk *rendererVk) |
| { |
| rendererVk->accumulateCacheStats(VulkanCacheType::SamplerYcbcrConversion, mCacheStats); |
| |
| VkDevice device = rendererVk->getDevice(); |
| |
| for (auto &iter : mExternalFormatPayload) |
| { |
| vk::SamplerYcbcrConversion &samplerYcbcrConversion = iter.second; |
| samplerYcbcrConversion.destroy(device); |
| |
| rendererVk->onDeallocateHandle(vk::HandleType::SamplerYcbcrConversion); |
| } |
| |
| for (auto &iter : mVkFormatPayload) |
| { |
| vk::SamplerYcbcrConversion &samplerYcbcrConversion = iter.second; |
| samplerYcbcrConversion.destroy(device); |
| |
| rendererVk->onDeallocateHandle(vk::HandleType::SamplerYcbcrConversion); |
| } |
| |
| mExternalFormatPayload.clear(); |
| mVkFormatPayload.clear(); |
| } |
| |
| angle::Result SamplerYcbcrConversionCache::getSamplerYcbcrConversion( |
| vk::Context *context, |
| const vk::YcbcrConversionDesc &ycbcrConversionDesc, |
| VkSamplerYcbcrConversion *vkSamplerYcbcrConversionOut) |
| { |
| ASSERT(ycbcrConversionDesc.valid()); |
| ASSERT(vkSamplerYcbcrConversionOut); |
| |
| SamplerYcbcrConversionMap &payload = |
| (ycbcrConversionDesc.getExternalFormat() != 0) ? mExternalFormatPayload : mVkFormatPayload; |
| const auto iter = payload.find(ycbcrConversionDesc); |
| if (iter != payload.end()) |
| { |
| vk::SamplerYcbcrConversion &samplerYcbcrConversion = iter->second; |
| mCacheStats.hit(); |
| *vkSamplerYcbcrConversionOut = samplerYcbcrConversion.getHandle(); |
| return angle::Result::Continue; |
| } |
| |
| mCacheStats.missAndIncrementSize(); |
| |
| // Create the VkSamplerYcbcrConversion |
| vk::SamplerYcbcrConversion wrappedSamplerYcbcrConversion; |
| ANGLE_TRY(ycbcrConversionDesc.init(context, &wrappedSamplerYcbcrConversion)); |
| |
| auto insertedItem = payload.emplace( |
| ycbcrConversionDesc, vk::SamplerYcbcrConversion(std::move(wrappedSamplerYcbcrConversion))); |
| vk::SamplerYcbcrConversion &insertedSamplerYcbcrConversion = insertedItem.first->second; |
| *vkSamplerYcbcrConversionOut = insertedSamplerYcbcrConversion.getHandle(); |
| |
| context->getRenderer()->onAllocateHandle(vk::HandleType::SamplerYcbcrConversion); |
| |
| return angle::Result::Continue; |
| } |
| |
| // SamplerCache implementation. |
| SamplerCache::SamplerCache() = default; |
| |
| SamplerCache::~SamplerCache() |
| { |
| ASSERT(mPayload.empty()); |
| } |
| |
| void SamplerCache::destroy(RendererVk *rendererVk) |
| { |
| rendererVk->accumulateCacheStats(VulkanCacheType::Sampler, mCacheStats); |
| |
| VkDevice device = rendererVk->getDevice(); |
| |
| for (auto &iter : mPayload) |
| { |
| vk::RefCountedSampler &sampler = iter.second; |
| ASSERT(!sampler.isReferenced()); |
| sampler.get().get().destroy(device); |
| |
| rendererVk->onDeallocateHandle(vk::HandleType::Sampler); |
| } |
| |
| mPayload.clear(); |
| } |
| |
| angle::Result SamplerCache::getSampler(ContextVk *contextVk, |
| const vk::SamplerDesc &desc, |
| vk::SamplerBinding *samplerOut) |
| { |
| auto iter = mPayload.find(desc); |
| if (iter != mPayload.end()) |
| { |
| vk::RefCountedSampler &sampler = iter->second; |
| samplerOut->set(&sampler); |
| mCacheStats.hit(); |
| return angle::Result::Continue; |
| } |
| |
| mCacheStats.missAndIncrementSize(); |
| vk::SamplerHelper samplerHelper(contextVk); |
| ANGLE_TRY(desc.init(contextVk, &samplerHelper.get())); |
| |
| vk::RefCountedSampler newSampler(std::move(samplerHelper)); |
| auto insertedItem = mPayload.emplace(desc, std::move(newSampler)); |
| vk::RefCountedSampler &insertedSampler = insertedItem.first->second; |
| samplerOut->set(&insertedSampler); |
| |
| contextVk->getRenderer()->onAllocateHandle(vk::HandleType::Sampler); |
| |
| return angle::Result::Continue; |
| } |
| } // namespace rx |