| // |
| // 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 "libANGLE/BlobCache.h" |
| #include "libANGLE/VertexAttribute.h" |
| #include "libANGLE/renderer/vulkan/FramebufferVk.h" |
| #include "libANGLE/renderer/vulkan/ProgramVk.h" |
| #include "libANGLE/renderer/vulkan/RendererVk.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 |
| { |
| namespace vk |
| { |
| |
| namespace |
| { |
| |
| 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); |
| default: |
| UNREACHABLE(); |
| return 0; |
| } |
| } |
| |
| 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); |
| default: |
| UNREACHABLE(); |
| return 0; |
| } |
| } |
| |
| VkStencilOp PackGLStencilOp(GLenum compareOp) |
| { |
| switch (compareOp) |
| { |
| case GL_KEEP: |
| return VK_STENCIL_OP_KEEP; |
| case GL_ZERO: |
| return VK_STENCIL_OP_ZERO; |
| case GL_REPLACE: |
| return VK_STENCIL_OP_REPLACE; |
| case GL_INCR: |
| return VK_STENCIL_OP_INCREMENT_AND_CLAMP; |
| case GL_DECR: |
| return VK_STENCIL_OP_DECREMENT_AND_CLAMP; |
| case GL_INCR_WRAP: |
| return VK_STENCIL_OP_INCREMENT_AND_WRAP; |
| case GL_DECR_WRAP: |
| return VK_STENCIL_OP_DECREMENT_AND_WRAP; |
| case GL_INVERT: |
| return VK_STENCIL_OP_INVERT; |
| default: |
| UNREACHABLE(); |
| return VK_STENCIL_OP_KEEP; |
| } |
| } |
| |
| VkCompareOp PackGLCompareFunc(GLenum compareFunc) |
| { |
| switch (compareFunc) |
| { |
| case GL_NEVER: |
| return VK_COMPARE_OP_NEVER; |
| case GL_ALWAYS: |
| return VK_COMPARE_OP_ALWAYS; |
| case GL_LESS: |
| return VK_COMPARE_OP_LESS; |
| case GL_LEQUAL: |
| return VK_COMPARE_OP_LESS_OR_EQUAL; |
| case GL_EQUAL: |
| return VK_COMPARE_OP_EQUAL; |
| case GL_GREATER: |
| return VK_COMPARE_OP_GREATER; |
| case GL_GEQUAL: |
| return VK_COMPARE_OP_GREATER_OR_EQUAL; |
| case GL_NOTEQUAL: |
| return VK_COMPARE_OP_NOT_EQUAL; |
| default: |
| UNREACHABLE(); |
| return VK_COMPARE_OP_NEVER; |
| } |
| } |
| |
| void UnpackAttachmentDesc(VkAttachmentDescription *desc, |
| const vk::Format &format, |
| uint8_t samples, |
| const vk::PackedAttachmentOpsDesc &ops) |
| { |
| // We would only need this flag for duplicated attachments. Apply it conservatively. |
| desc->flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT; |
| desc->format = format.vkImageFormat; |
| desc->samples = gl_vk::GetSamples(samples); |
| desc->loadOp = static_cast<VkAttachmentLoadOp>(ops.loadOp); |
| desc->storeOp = static_cast<VkAttachmentStoreOp>(ops.storeOp); |
| desc->stencilLoadOp = static_cast<VkAttachmentLoadOp>(ops.stencilLoadOp); |
| desc->stencilStoreOp = static_cast<VkAttachmentStoreOp>(ops.stencilStoreOp); |
| desc->initialLayout = static_cast<VkImageLayout>(ops.initialLayout); |
| desc->finalLayout = static_cast<VkImageLayout>(ops.finalLayout); |
| } |
| |
| void UnpackStencilState(const vk::PackedStencilOpState &packedState, |
| uint8_t stencilReference, |
| 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 = packedState.compareMask; |
| stateOut->writeMask = packedState.writeMask; |
| stateOut->reference = stencilReference; |
| } |
| |
| void UnpackBlendAttachmentState(const vk::PackedColorBlendAttachmentState &packedState, |
| VkPipelineColorBlendAttachmentState *stateOut) |
| { |
| stateOut->srcColorBlendFactor = static_cast<VkBlendFactor>(packedState.srcColorBlendFactor); |
| stateOut->dstColorBlendFactor = static_cast<VkBlendFactor>(packedState.dstColorBlendFactor); |
| stateOut->colorBlendOp = static_cast<VkBlendOp>(packedState.colorBlendOp); |
| stateOut->srcAlphaBlendFactor = static_cast<VkBlendFactor>(packedState.srcAlphaBlendFactor); |
| stateOut->dstAlphaBlendFactor = static_cast<VkBlendFactor>(packedState.dstAlphaBlendFactor); |
| stateOut->alphaBlendOp = static_cast<VkBlendOp>(packedState.alphaBlendOp); |
| } |
| |
| void SetPipelineShaderStageInfo(const VkStructureType type, |
| const VkShaderStageFlagBits stage, |
| const VkShaderModule module, |
| VkPipelineShaderStageCreateInfo *shaderStage) |
| { |
| shaderStage->sType = type; |
| shaderStage->flags = 0; |
| shaderStage->stage = stage; |
| shaderStage->module = module; |
| shaderStage->pName = "main"; |
| shaderStage->pSpecializationInfo = nullptr; |
| } |
| |
| angle::Result InitializeRenderPassFromDesc(vk::Context *context, |
| const RenderPassDesc &desc, |
| const AttachmentOpsArray &ops, |
| RenderPass *renderPass) |
| { |
| // Unpack the packed and split representation into the format required by Vulkan. |
| gl::DrawBuffersVector<VkAttachmentReference> colorAttachmentRefs; |
| VkAttachmentReference depthStencilAttachmentRef = {VK_ATTACHMENT_UNUSED}; |
| gl::AttachmentArray<VkAttachmentDescription> attachmentDescs; |
| |
| uint32_t colorAttachmentCount = 0; |
| uint32_t 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)) |
| { |
| VkAttachmentReference colorRef; |
| colorRef.attachment = VK_ATTACHMENT_UNUSED; |
| colorRef.layout = VK_IMAGE_LAYOUT_UNDEFINED; |
| colorAttachmentRefs.push_back(colorRef); |
| |
| continue; |
| } |
| |
| uint32_t colorIndexVk = colorAttachmentCount++; |
| angle::FormatID formatID = desc[colorIndexGL]; |
| ASSERT(formatID != angle::FormatID::NONE); |
| const vk::Format &format = context->getRenderer()->getFormat(formatID); |
| |
| VkAttachmentReference colorRef; |
| colorRef.attachment = colorIndexVk; |
| colorRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| |
| colorAttachmentRefs.push_back(colorRef); |
| |
| UnpackAttachmentDesc(&attachmentDescs[colorIndexVk], format, desc.samples(), |
| ops[colorIndexVk]); |
| |
| ++attachmentCount; |
| } |
| |
| if (desc.hasDepthStencilAttachment()) |
| { |
| uint32_t depthStencilIndex = static_cast<uint32_t>(desc.depthStencilAttachmentIndex()); |
| uint32_t depthStencilIndexVk = colorAttachmentCount; |
| |
| angle::FormatID formatID = desc[depthStencilIndex]; |
| ASSERT(formatID != angle::FormatID::NONE); |
| const vk::Format &format = context->getRenderer()->getFormat(formatID); |
| |
| depthStencilAttachmentRef.attachment = depthStencilIndexVk; |
| depthStencilAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
| |
| UnpackAttachmentDesc(&attachmentDescs[depthStencilIndexVk], format, desc.samples(), |
| ops[depthStencilIndexVk]); |
| |
| ++attachmentCount; |
| } |
| |
| VkSubpassDescription subpassDesc = {}; |
| |
| subpassDesc.flags = 0; |
| subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
| subpassDesc.inputAttachmentCount = 0; |
| subpassDesc.pInputAttachments = nullptr; |
| subpassDesc.colorAttachmentCount = static_cast<uint32_t>(colorAttachmentRefs.size()); |
| subpassDesc.pColorAttachments = colorAttachmentRefs.data(); |
| subpassDesc.pResolveAttachments = nullptr; |
| subpassDesc.pDepthStencilAttachment = |
| (depthStencilAttachmentRef.attachment != VK_ATTACHMENT_UNUSED ? &depthStencilAttachmentRef |
| : nullptr); |
| subpassDesc.preserveAttachmentCount = 0; |
| subpassDesc.pPreserveAttachments = nullptr; |
| |
| VkRenderPassCreateInfo createInfo = {}; |
| createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; |
| createInfo.flags = 0; |
| createInfo.attachmentCount = attachmentCount; |
| createInfo.pAttachments = attachmentDescs.data(); |
| createInfo.subpassCount = 1; |
| createInfo.pSubpasses = &subpassDesc; |
| createInfo.dependencyCount = 0; |
| createInfo.pDependencies = nullptr; |
| |
| ANGLE_VK_TRY(context, renderPass->init(context->getDevice(), createInfo)); |
| return angle::Result::Continue; |
| } |
| |
| // 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); |
| } |
| } |
| |
| // Helper macro that casts to a bitfield type then verifies no bits were dropped. |
| #define SetBitField(lhs, rhs) \ |
| lhs = static_cast<typename std::decay<decltype(lhs)>::type>(rhs); \ |
| ASSERT(static_cast<decltype(rhs)>(lhs) == (rhs)) |
| |
| // 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}, |
| }}; |
| } // 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::setSamples(GLint samples) |
| { |
| ASSERT(samples < std::numeric_limits<uint8_t>::max()); |
| mSamples = static_cast<uint8_t>(samples); |
| } |
| |
| 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(mHasDepthStencilAttachment == false); |
| // 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 reason why we need depth/stencil to be packed last. |
| mColorAttachmentRange = |
| std::max<uint8_t>(mColorAttachmentRange, static_cast<uint8_t>(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(mHasDepthStencilAttachment == false); |
| |
| // 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) |
| { |
| // Though written as Count, there is only ever a single depth/stencil attachment. |
| ASSERT(mHasDepthStencilAttachment == false); |
| |
| size_t index = depthStencilAttachmentIndex(); |
| ASSERT(index < mAttachmentFormats.size()); |
| |
| uint8_t &packedFormat = mAttachmentFormats[index]; |
| SetBitField(packedFormat, formatID); |
| |
| mHasDepthStencilAttachment = true; |
| } |
| |
| RenderPassDesc &RenderPassDesc::operator=(const RenderPassDesc &other) |
| { |
| memcpy(this, &other, sizeof(RenderPassDesc)); |
| return *this; |
| } |
| |
| 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; |
| } |
| |
| 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. |
| return colorAttachmentCount + mHasDepthStencilAttachment; |
| } |
| |
| 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 |
| { |
| return angle::ComputeGenericHash(*this); |
| } |
| |
| 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() |
| { |
| // 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.stride, 0); |
| SetBitField(packedAttrib.divisor, 0); |
| SetBitField(packedAttrib.format, defaultFormat); |
| SetBitField(packedAttrib.offset, 0); |
| } |
| |
| mRasterizationAndMultisampleStateInfo.bits.depthClampEnable = 0; |
| mRasterizationAndMultisampleStateInfo.bits.rasterizationDiscardEnable = 0; |
| SetBitField(mRasterizationAndMultisampleStateInfo.bits.polygonMode, VK_POLYGON_MODE_FILL); |
| SetBitField(mRasterizationAndMultisampleStateInfo.bits.cullMode, VK_CULL_MODE_BACK_BIT); |
| SetBitField(mRasterizationAndMultisampleStateInfo.bits.frontFace, |
| VK_FRONT_FACE_COUNTER_CLOCKWISE); |
| mRasterizationAndMultisampleStateInfo.bits.depthBiasEnable = 0; |
| mRasterizationAndMultisampleStateInfo.depthBiasConstantFactor = 0.0f; |
| mRasterizationAndMultisampleStateInfo.depthBiasClamp = 0.0f; |
| mRasterizationAndMultisampleStateInfo.depthBiasSlopeFactor = 0.0f; |
| mRasterizationAndMultisampleStateInfo.lineWidth = 1.0f; |
| |
| mRasterizationAndMultisampleStateInfo.bits.rasterizationSamples = 1; |
| mRasterizationAndMultisampleStateInfo.bits.sampleShadingEnable = 0; |
| mRasterizationAndMultisampleStateInfo.minSampleShading = 0.0f; |
| for (uint32_t &sampleMask : mRasterizationAndMultisampleStateInfo.sampleMask) |
| { |
| sampleMask = 0xFFFFFFFF; |
| } |
| mRasterizationAndMultisampleStateInfo.bits.alphaToCoverageEnable = 0; |
| mRasterizationAndMultisampleStateInfo.bits.alphaToOneEnable = 0; |
| |
| mDepthStencilStateInfo.enable.depthTest = 0; |
| mDepthStencilStateInfo.enable.depthWrite = 1; |
| SetBitField(mDepthStencilStateInfo.depthCompareOp, VK_COMPARE_OP_LESS); |
| mDepthStencilStateInfo.enable.depthBoundsTest = 0; |
| mDepthStencilStateInfo.enable.stencilTest = 0; |
| mDepthStencilStateInfo.minDepthBounds = 0.0f; |
| mDepthStencilStateInfo.maxDepthBounds = 0.0f; |
| SetBitField(mDepthStencilStateInfo.front.ops.fail, VK_STENCIL_OP_KEEP); |
| SetBitField(mDepthStencilStateInfo.front.ops.pass, VK_STENCIL_OP_KEEP); |
| SetBitField(mDepthStencilStateInfo.front.ops.depthFail, VK_STENCIL_OP_KEEP); |
| SetBitField(mDepthStencilStateInfo.front.ops.compare, VK_COMPARE_OP_ALWAYS); |
| SetBitField(mDepthStencilStateInfo.front.compareMask, 0xFF); |
| SetBitField(mDepthStencilStateInfo.front.writeMask, 0xFF); |
| mDepthStencilStateInfo.frontStencilReference = 0; |
| SetBitField(mDepthStencilStateInfo.back.ops.fail, VK_STENCIL_OP_KEEP); |
| SetBitField(mDepthStencilStateInfo.back.ops.pass, VK_STENCIL_OP_KEEP); |
| SetBitField(mDepthStencilStateInfo.back.ops.depthFail, VK_STENCIL_OP_KEEP); |
| SetBitField(mDepthStencilStateInfo.back.ops.compare, VK_COMPARE_OP_ALWAYS); |
| SetBitField(mDepthStencilStateInfo.back.compareMask, 0xFF); |
| SetBitField(mDepthStencilStateInfo.back.writeMask, 0xFF); |
| mDepthStencilStateInfo.backStencilReference = 0; |
| |
| PackedInputAssemblyAndColorBlendStateInfo &inputAndBlend = mInputAssemblyAndColorBlendStateInfo; |
| inputAndBlend.logic.opEnable = 0; |
| inputAndBlend.logic.op = static_cast<uint32_t>(VK_LOGIC_OP_CLEAR); |
| inputAndBlend.blendEnableMask = 0; |
| inputAndBlend.blendConstants[0] = 0.0f; |
| inputAndBlend.blendConstants[1] = 0.0f; |
| inputAndBlend.blendConstants[2] = 0.0f; |
| inputAndBlend.blendConstants[3] = 0.0f; |
| |
| 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(inputAndBlend.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(&inputAndBlend.attachments[0], |
| &inputAndBlend.attachments[gl::IMPLEMENTATION_MAX_DRAW_BUFFERS], |
| blendAttachmentState); |
| |
| inputAndBlend.primitive.topology = static_cast<uint16_t>(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); |
| inputAndBlend.primitive.restartEnable = 0; |
| |
| // Viewport and scissor will be set to valid values when framebuffer being binded |
| mViewport.x = 0.0f; |
| mViewport.y = 0.0f; |
| mViewport.width = 0.0f; |
| mViewport.height = 0.0f; |
| mViewport.minDepth = 0.0f; |
| mViewport.maxDepth = 1.0f; |
| |
| mScissor.offset.x = 0; |
| mScissor.offset.y = 0; |
| mScissor.extent.width = 0; |
| mScissor.extent.height = 0; |
| } |
| |
| angle::Result GraphicsPipelineDesc::initializePipeline( |
| ContextVk *contextVk, |
| const vk::PipelineCache &pipelineCacheVk, |
| const RenderPass &compatibleRenderPass, |
| const PipelineLayout &pipelineLayout, |
| const gl::AttributesMask &activeAttribLocationsMask, |
| const gl::ComponentTypeMask &programAttribsTypeMask, |
| const ShaderModule *vertexModule, |
| const ShaderModule *fragmentModule, |
| const ShaderModule *geometryModule, |
| Pipeline *pipelineOut) const |
| { |
| angle::FixedVector<VkPipelineShaderStageCreateInfo, 3> shaderStages; |
| VkPipelineVertexInputStateCreateInfo vertexInputState = {}; |
| VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = {}; |
| VkPipelineViewportStateCreateInfo viewportState = {}; |
| VkPipelineRasterizationStateCreateInfo rasterState = {}; |
| VkPipelineMultisampleStateCreateInfo multisampleState = {}; |
| VkPipelineDepthStencilStateCreateInfo depthStencilState = {}; |
| std::array<VkPipelineColorBlendAttachmentState, gl::IMPLEMENTATION_MAX_DRAW_BUFFERS> |
| blendAttachmentState; |
| VkPipelineColorBlendStateCreateInfo blendState = {}; |
| VkGraphicsPipelineCreateInfo createInfo = {}; |
| |
| // Vertex shader is always expected to be present. |
| ASSERT(vertexModule != nullptr); |
| VkPipelineShaderStageCreateInfo vertexStage = {}; |
| SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| VK_SHADER_STAGE_VERTEX_BIT, vertexModule->getHandle(), &vertexStage); |
| shaderStages.push_back(vertexStage); |
| |
| if (geometryModule) |
| { |
| VkPipelineShaderStageCreateInfo geometryStage = {}; |
| SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| VK_SHADER_STAGE_GEOMETRY_BIT, geometryModule->getHandle(), |
| &geometryStage); |
| shaderStages.push_back(geometryStage); |
| } |
| |
| // Fragment shader is optional. |
| // anglebug.com/3509 - Don't compile the fragment shader if rasterizationDiscardEnable = true |
| if (fragmentModule && !mRasterizationAndMultisampleStateInfo.bits.rasterizationDiscardEnable) |
| { |
| VkPipelineShaderStageCreateInfo fragmentStage = {}; |
| SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| VK_SHADER_STAGE_FRAGMENT_BIT, fragmentModule->getHandle(), |
| &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(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>(packedAttrib.stride); |
| 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 vk::Format &format = contextVk->getRenderer()->getFormat(formatID); |
| const angle::Format &angleFormat = format.angleFormat(); |
| VkFormat vkFormat = format.vkBufferFormat; |
| |
| gl::ComponentType attribType = |
| GetVertexAttributeComponentType(angleFormat.isPureInt(), angleFormat.vertexAttribType); |
| gl::ComponentType programAttribType = |
| gl::GetComponentTypeMask(programAttribsTypeMask, attribIndex); |
| |
| if (attribType != programAttribType) |
| { |
| // Override the format with a compatible one. |
| vkFormat = kMismatchedComponentTypeMap[programAttribType]; |
| |
| bindingDesc.stride = 0; // Prevent out-of-bounds accesses. |
| } |
| |
| // The binding index could become more dynamic in ES 3.1. |
| 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; |
| |
| // 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>(mInputAssemblyAndColorBlendStateInfo.primitive.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>(mInputAssemblyAndColorBlendStateInfo.primitive.restartEnable); |
| |
| // Set initial viewport and scissor state. |
| |
| // 0-sized viewports are invalid in Vulkan. We always use a scissor that at least matches the |
| // requested viewport, so it's safe to adjust the viewport size here. |
| VkViewport viewport = mViewport; |
| if (viewport.width == 0) |
| { |
| viewport.width = 1; |
| } |
| if (viewport.height == 0) |
| { |
| viewport.height = 1; |
| } |
| |
| viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; |
| viewportState.flags = 0; |
| viewportState.viewportCount = 1; |
| viewportState.pViewports = &viewport; |
| viewportState.scissorCount = 1; |
| viewportState.pScissors = &mScissor; |
| |
| const PackedRasterizationAndMultisampleStateInfo &rasterAndMS = |
| mRasterizationAndMultisampleStateInfo; |
| |
| // Rasterizer state. |
| rasterState.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; |
| rasterState.flags = 0; |
| rasterState.depthClampEnable = static_cast<VkBool32>(rasterAndMS.bits.depthClampEnable); |
| rasterState.rasterizerDiscardEnable = |
| static_cast<VkBool32>(rasterAndMS.bits.rasterizationDiscardEnable); |
| rasterState.polygonMode = static_cast<VkPolygonMode>(rasterAndMS.bits.polygonMode); |
| rasterState.cullMode = static_cast<VkCullModeFlags>(rasterAndMS.bits.cullMode); |
| rasterState.frontFace = static_cast<VkFrontFace>(rasterAndMS.bits.frontFace); |
| rasterState.depthBiasEnable = static_cast<VkBool32>(rasterAndMS.bits.depthBiasEnable); |
| rasterState.depthBiasConstantFactor = rasterAndMS.depthBiasConstantFactor; |
| rasterState.depthBiasClamp = rasterAndMS.depthBiasClamp; |
| rasterState.depthBiasSlopeFactor = rasterAndMS.depthBiasSlopeFactor; |
| rasterState.lineWidth = rasterAndMS.lineWidth; |
| |
| // Multisample state. |
| multisampleState.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; |
| multisampleState.flags = 0; |
| multisampleState.rasterizationSamples = |
| gl_vk::GetSamples(rasterAndMS.bits.rasterizationSamples); |
| multisampleState.sampleShadingEnable = |
| static_cast<VkBool32>(rasterAndMS.bits.sampleShadingEnable); |
| multisampleState.minSampleShading = rasterAndMS.minSampleShading; |
| multisampleState.pSampleMask = rasterAndMS.sampleMask; |
| multisampleState.alphaToCoverageEnable = |
| static_cast<VkBool32>(rasterAndMS.bits.alphaToCoverageEnable); |
| multisampleState.alphaToOneEnable = static_cast<VkBool32>(rasterAndMS.bits.alphaToOneEnable); |
| |
| // Depth/stencil state. |
| depthStencilState.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; |
| depthStencilState.flags = 0; |
| depthStencilState.depthTestEnable = |
| static_cast<VkBool32>(mDepthStencilStateInfo.enable.depthTest); |
| depthStencilState.depthWriteEnable = |
| static_cast<VkBool32>(mDepthStencilStateInfo.enable.depthWrite); |
| depthStencilState.depthCompareOp = |
| static_cast<VkCompareOp>(mDepthStencilStateInfo.depthCompareOp); |
| depthStencilState.depthBoundsTestEnable = |
| static_cast<VkBool32>(mDepthStencilStateInfo.enable.depthBoundsTest); |
| depthStencilState.stencilTestEnable = |
| static_cast<VkBool32>(mDepthStencilStateInfo.enable.stencilTest); |
| UnpackStencilState(mDepthStencilStateInfo.front, mDepthStencilStateInfo.frontStencilReference, |
| &depthStencilState.front); |
| UnpackStencilState(mDepthStencilStateInfo.back, mDepthStencilStateInfo.backStencilReference, |
| &depthStencilState.back); |
| depthStencilState.minDepthBounds = mDepthStencilStateInfo.minDepthBounds; |
| depthStencilState.maxDepthBounds = mDepthStencilStateInfo.maxDepthBounds; |
| |
| const PackedInputAssemblyAndColorBlendStateInfo &inputAndBlend = |
| mInputAssemblyAndColorBlendStateInfo; |
| |
| blendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; |
| blendState.flags = 0; |
| blendState.logicOpEnable = static_cast<VkBool32>(inputAndBlend.logic.opEnable); |
| blendState.logicOp = static_cast<VkLogicOp>(inputAndBlend.logic.op); |
| blendState.attachmentCount = static_cast<uint32_t>(mRenderPassDesc.colorAttachmentRange()); |
| blendState.pAttachments = blendAttachmentState.data(); |
| |
| for (int i = 0; i < 4; i++) |
| { |
| blendState.blendConstants[i] = inputAndBlend.blendConstants[i]; |
| } |
| |
| const gl::DrawBufferMask blendEnableMask(inputAndBlend.blendEnableMask); |
| |
| for (uint32_t colorIndexGL = 0; colorIndexGL < blendState.attachmentCount; ++colorIndexGL) |
| { |
| VkPipelineColorBlendAttachmentState &state = blendAttachmentState[colorIndexGL]; |
| |
| state.blendEnable = blendEnableMask[colorIndexGL] ? VK_TRUE : VK_FALSE; |
| state.colorWriteMask = |
| Int4Array_Get<VkColorComponentFlags>(inputAndBlend.colorWriteMaskBits, colorIndexGL); |
| UnpackBlendAttachmentState(inputAndBlend.attachments[colorIndexGL], &state); |
| } |
| |
| // We would define dynamic state here if it were to be used. |
| |
| 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 = nullptr; |
| createInfo.pViewportState = &viewportState; |
| createInfo.pRasterizationState = &rasterState; |
| createInfo.pMultisampleState = &multisampleState; |
| createInfo.pDepthStencilState = &depthStencilState; |
| createInfo.pColorBlendState = &blendState; |
| createInfo.pDynamicState = nullptr; |
| createInfo.layout = pipelineLayout.getHandle(); |
| createInfo.renderPass = compatibleRenderPass.getHandle(); |
| createInfo.subpass = 0; |
| createInfo.basePipelineHandle = VK_NULL_HANDLE; |
| createInfo.basePipelineIndex = 0; |
| |
| ANGLE_VK_TRY(contextVk, |
| pipelineOut->initGraphics(contextVk->getDevice(), createInfo, pipelineCacheVk)); |
| return angle::Result::Continue; |
| } |
| |
| void GraphicsPipelineDesc::updateVertexInput(GraphicsPipelineTransitionBits *transition, |
| uint32_t attribIndex, |
| GLuint stride, |
| GLuint divisor, |
| angle::FormatID format, |
| GLuint relativeOffset) |
| { |
| vk::PackedAttribDesc &packedAttrib = mVertexInputAttribs.attribs[attribIndex]; |
| |
| SetBitField(packedAttrib.stride, stride); |
| SetBitField(packedAttrib.divisor, divisor); |
| |
| if (format == angle::FormatID::NONE) |
| { |
| UNIMPLEMENTED(); |
| } |
| |
| SetBitField(packedAttrib.format, format); |
| SetBitField(packedAttrib.offset, relativeOffset); |
| |
| constexpr size_t kAttribBits = kPackedAttribDescSize * kBitsPerByte; |
| const size_t kBit = |
| ANGLE_GET_INDEXED_TRANSITION_BIT(mVertexInputAttribs, attribs, attribIndex, kAttribBits); |
| |
| // Cover the next dirty bit conservatively. Because each attribute is 6 bytes. |
| transition->set(kBit); |
| transition->set(kBit + 1); |
| } |
| |
| void GraphicsPipelineDesc::updateTopology(GraphicsPipelineTransitionBits *transition, |
| gl::PrimitiveMode drawMode) |
| { |
| VkPrimitiveTopology vkTopology = gl_vk::GetPrimitiveTopology(drawMode); |
| SetBitField(mInputAssemblyAndColorBlendStateInfo.primitive.topology, vkTopology); |
| |
| transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo, primitive)); |
| } |
| |
| void GraphicsPipelineDesc::updatePrimitiveRestartEnabled(GraphicsPipelineTransitionBits *transition, |
| bool primitiveRestartEnabled) |
| { |
| mInputAssemblyAndColorBlendStateInfo.primitive.restartEnable = |
| static_cast<uint16_t>(primitiveRestartEnabled); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo, primitive)); |
| } |
| |
| void GraphicsPipelineDesc::setCullMode(VkCullModeFlagBits cullMode) |
| { |
| SetBitField(mRasterizationAndMultisampleStateInfo.bits.cullMode, cullMode); |
| } |
| |
| void GraphicsPipelineDesc::updateCullMode(GraphicsPipelineTransitionBits *transition, |
| const gl::RasterizerState &rasterState) |
| { |
| setCullMode(gl_vk::GetCullMode(rasterState)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits)); |
| } |
| |
| void GraphicsPipelineDesc::updateFrontFace(GraphicsPipelineTransitionBits *transition, |
| const gl::RasterizerState &rasterState, |
| bool invertFrontFace) |
| { |
| mRasterizationAndMultisampleStateInfo.bits.frontFace = |
| static_cast<uint16_t>(gl_vk::GetFrontFace(rasterState.frontFace, invertFrontFace)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits)); |
| } |
| |
| void GraphicsPipelineDesc::updateLineWidth(GraphicsPipelineTransitionBits *transition, |
| float lineWidth) |
| { |
| mRasterizationAndMultisampleStateInfo.lineWidth = lineWidth; |
| transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, lineWidth)); |
| } |
| |
| void GraphicsPipelineDesc::updateRasterizerDiscardEnabled( |
| GraphicsPipelineTransitionBits *transition, |
| bool rasterizerDiscardEnabled) |
| { |
| mRasterizationAndMultisampleStateInfo.bits.rasterizationDiscardEnable = |
| static_cast<uint32_t>(rasterizerDiscardEnabled); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits)); |
| } |
| |
| void GraphicsPipelineDesc::setRasterizationSamples(uint32_t rasterizationSamples) |
| { |
| mRasterizationAndMultisampleStateInfo.bits.rasterizationSamples = rasterizationSamples; |
| } |
| |
| void GraphicsPipelineDesc::updateRasterizationSamples(GraphicsPipelineTransitionBits *transition, |
| uint32_t rasterizationSamples) |
| { |
| setRasterizationSamples(rasterizationSamples); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits)); |
| } |
| |
| void GraphicsPipelineDesc::updateAlphaToCoverageEnable(GraphicsPipelineTransitionBits *transition, |
| bool enable) |
| { |
| mRasterizationAndMultisampleStateInfo.bits.alphaToCoverageEnable = enable; |
| transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits)); |
| } |
| |
| void GraphicsPipelineDesc::updateAlphaToOneEnable(GraphicsPipelineTransitionBits *transition, |
| bool enable) |
| { |
| mRasterizationAndMultisampleStateInfo.bits.alphaToOneEnable = enable; |
| transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits)); |
| } |
| |
| void GraphicsPipelineDesc::updateSampleMask(GraphicsPipelineTransitionBits *transition, |
| uint32_t maskNumber, |
| uint32_t mask) |
| { |
| ASSERT(maskNumber < gl::MAX_SAMPLE_MASK_WORDS); |
| mRasterizationAndMultisampleStateInfo.sampleMask[maskNumber] = mask; |
| |
| constexpr size_t kMaskBits = |
| sizeof(mRasterizationAndMultisampleStateInfo.sampleMask[0]) * kBitsPerByte; |
| transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, |
| sampleMask, maskNumber, kMaskBits)); |
| } |
| |
| void GraphicsPipelineDesc::updateBlendColor(GraphicsPipelineTransitionBits *transition, |
| const gl::ColorF &color) |
| { |
| mInputAssemblyAndColorBlendStateInfo.blendConstants[0] = color.red; |
| mInputAssemblyAndColorBlendStateInfo.blendConstants[1] = color.green; |
| mInputAssemblyAndColorBlendStateInfo.blendConstants[2] = color.blue; |
| mInputAssemblyAndColorBlendStateInfo.blendConstants[3] = color.alpha; |
| constexpr size_t kSize = sizeof(mInputAssemblyAndColorBlendStateInfo.blendConstants[0]) * 8; |
| |
| for (int index = 0; index < 4; ++index) |
| { |
| const size_t kBit = ANGLE_GET_INDEXED_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo, |
| blendConstants, index, kSize); |
| transition->set(kBit); |
| } |
| } |
| |
| void GraphicsPipelineDesc::updateBlendEnabled(GraphicsPipelineTransitionBits *transition, |
| bool isBlendEnabled) |
| { |
| gl::DrawBufferMask blendEnabled; |
| if (isBlendEnabled) |
| blendEnabled.set(); |
| mInputAssemblyAndColorBlendStateInfo.blendEnableMask = |
| static_cast<uint8_t>(blendEnabled.bits()); |
| transition->set( |
| ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo, blendEnableMask)); |
| } |
| |
| void GraphicsPipelineDesc::updateBlendEquations(GraphicsPipelineTransitionBits *transition, |
| const gl::BlendState &blendState) |
| { |
| constexpr size_t kSize = sizeof(PackedColorBlendAttachmentState) * 8; |
| |
| for (size_t attachmentIndex = 0; attachmentIndex < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; |
| ++attachmentIndex) |
| { |
| PackedColorBlendAttachmentState &blendAttachmentState = |
| mInputAssemblyAndColorBlendStateInfo.attachments[attachmentIndex]; |
| blendAttachmentState.colorBlendOp = PackGLBlendOp(blendState.blendEquationRGB); |
| blendAttachmentState.alphaBlendOp = PackGLBlendOp(blendState.blendEquationAlpha); |
| transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo, |
| attachments, attachmentIndex, kSize)); |
| } |
| } |
| |
| void GraphicsPipelineDesc::updateBlendFuncs(GraphicsPipelineTransitionBits *transition, |
| const gl::BlendState &blendState) |
| { |
| constexpr size_t kSize = sizeof(PackedColorBlendAttachmentState) * 8; |
| for (size_t attachmentIndex = 0; attachmentIndex < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; |
| ++attachmentIndex) |
| { |
| PackedColorBlendAttachmentState &blendAttachmentState = |
| mInputAssemblyAndColorBlendStateInfo.attachments[attachmentIndex]; |
| blendAttachmentState.srcColorBlendFactor = PackGLBlendFactor(blendState.sourceBlendRGB); |
| blendAttachmentState.dstColorBlendFactor = PackGLBlendFactor(blendState.destBlendRGB); |
| blendAttachmentState.srcAlphaBlendFactor = PackGLBlendFactor(blendState.sourceBlendAlpha); |
| blendAttachmentState.dstAlphaBlendFactor = PackGLBlendFactor(blendState.destBlendAlpha); |
| transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo, |
| attachments, attachmentIndex, kSize)); |
| } |
| } |
| |
| void GraphicsPipelineDesc::setColorWriteMask(VkColorComponentFlags colorComponentFlags, |
| const gl::DrawBufferMask &alphaMask) |
| { |
| PackedInputAssemblyAndColorBlendStateInfo &inputAndBlend = mInputAssemblyAndColorBlendStateInfo; |
| uint8_t colorMask = static_cast<uint8_t>(colorComponentFlags); |
| |
| for (uint32_t colorIndexGL = 0; colorIndexGL < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; |
| colorIndexGL++) |
| { |
| uint8_t mask = |
| alphaMask[colorIndexGL] ? (colorMask & ~VK_COLOR_COMPONENT_A_BIT) : colorMask; |
| Int4Array_Set(inputAndBlend.colorWriteMaskBits, colorIndexGL, mask); |
| } |
| } |
| |
| void GraphicsPipelineDesc::setSingleColorWriteMask(uint32_t colorIndexGL, |
| VkColorComponentFlags colorComponentFlags) |
| { |
| PackedInputAssemblyAndColorBlendStateInfo &inputAndBlend = mInputAssemblyAndColorBlendStateInfo; |
| uint8_t colorMask = static_cast<uint8_t>(colorComponentFlags); |
| Int4Array_Set(inputAndBlend.colorWriteMaskBits, colorIndexGL, colorMask); |
| } |
| |
| void GraphicsPipelineDesc::updateColorWriteMask(GraphicsPipelineTransitionBits *transition, |
| VkColorComponentFlags colorComponentFlags, |
| const gl::DrawBufferMask &alphaMask) |
| { |
| setColorWriteMask(colorComponentFlags, alphaMask); |
| |
| for (size_t colorIndexGL = 0; colorIndexGL < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; |
| colorIndexGL++) |
| { |
| transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo, |
| colorWriteMaskBits, colorIndexGL, 4)); |
| } |
| } |
| |
| void GraphicsPipelineDesc::setDepthTestEnabled(bool enabled) |
| { |
| mDepthStencilStateInfo.enable.depthTest = enabled; |
| } |
| |
| void GraphicsPipelineDesc::setDepthWriteEnabled(bool enabled) |
| { |
| mDepthStencilStateInfo.enable.depthWrite = enabled; |
| } |
| |
| void GraphicsPipelineDesc::setDepthFunc(VkCompareOp op) |
| { |
| SetBitField(mDepthStencilStateInfo.depthCompareOp, op); |
| } |
| |
| void GraphicsPipelineDesc::setStencilTestEnabled(bool enabled) |
| { |
| mDepthStencilStateInfo.enable.stencilTest = enabled; |
| } |
| |
| void GraphicsPipelineDesc::setStencilFrontFuncs(uint8_t reference, |
| VkCompareOp compareOp, |
| uint8_t compareMask) |
| { |
| mDepthStencilStateInfo.frontStencilReference = reference; |
| mDepthStencilStateInfo.front.compareMask = compareMask; |
| SetBitField(mDepthStencilStateInfo.front.ops.compare, compareOp); |
| } |
| |
| void GraphicsPipelineDesc::setStencilBackFuncs(uint8_t reference, |
| VkCompareOp compareOp, |
| uint8_t compareMask) |
| { |
| mDepthStencilStateInfo.backStencilReference = reference; |
| mDepthStencilStateInfo.back.compareMask = compareMask; |
| SetBitField(mDepthStencilStateInfo.back.ops.compare, compareOp); |
| } |
| |
| void GraphicsPipelineDesc::setStencilFrontOps(VkStencilOp failOp, |
| VkStencilOp passOp, |
| VkStencilOp depthFailOp) |
| { |
| SetBitField(mDepthStencilStateInfo.front.ops.fail, failOp); |
| SetBitField(mDepthStencilStateInfo.front.ops.pass, passOp); |
| SetBitField(mDepthStencilStateInfo.front.ops.depthFail, depthFailOp); |
| } |
| |
| void GraphicsPipelineDesc::setStencilBackOps(VkStencilOp failOp, |
| VkStencilOp passOp, |
| VkStencilOp depthFailOp) |
| { |
| SetBitField(mDepthStencilStateInfo.back.ops.fail, failOp); |
| SetBitField(mDepthStencilStateInfo.back.ops.pass, passOp); |
| SetBitField(mDepthStencilStateInfo.back.ops.depthFail, depthFailOp); |
| } |
| |
| void GraphicsPipelineDesc::setStencilFrontWriteMask(uint8_t mask) |
| { |
| mDepthStencilStateInfo.front.writeMask = mask; |
| } |
| |
| void GraphicsPipelineDesc::setStencilBackWriteMask(uint8_t mask) |
| { |
| mDepthStencilStateInfo.back.writeMask = mask; |
| } |
| |
| 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(mDepthStencilStateInfo, enable)); |
| } |
| |
| void GraphicsPipelineDesc::updateDepthFunc(GraphicsPipelineTransitionBits *transition, |
| const gl::DepthStencilState &depthStencilState) |
| { |
| setDepthFunc(PackGLCompareFunc(depthStencilState.depthFunc)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, depthCompareOp)); |
| } |
| |
| void GraphicsPipelineDesc::updateDepthWriteEnabled(GraphicsPipelineTransitionBits *transition, |
| const gl::DepthStencilState &depthStencilState, |
| const gl::Framebuffer *drawFramebuffer) |
| { |
| // Don't write to depth buffers that should not exist |
| setDepthWriteEnabled(drawFramebuffer->hasDepth() ? depthStencilState.depthMask : false); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, enable)); |
| } |
| |
| 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(mDepthStencilStateInfo, enable)); |
| } |
| |
| void GraphicsPipelineDesc::updateStencilFrontFuncs(GraphicsPipelineTransitionBits *transition, |
| GLint ref, |
| const gl::DepthStencilState &depthStencilState) |
| { |
| setStencilFrontFuncs(static_cast<uint8_t>(ref), |
| PackGLCompareFunc(depthStencilState.stencilFunc), |
| static_cast<uint8_t>(depthStencilState.stencilMask)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, front)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, frontStencilReference)); |
| } |
| |
| void GraphicsPipelineDesc::updateStencilBackFuncs(GraphicsPipelineTransitionBits *transition, |
| GLint ref, |
| const gl::DepthStencilState &depthStencilState) |
| { |
| setStencilBackFuncs(static_cast<uint8_t>(ref), |
| PackGLCompareFunc(depthStencilState.stencilBackFunc), |
| static_cast<uint8_t>(depthStencilState.stencilBackMask)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, back)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, backStencilReference)); |
| } |
| |
| void GraphicsPipelineDesc::updateStencilFrontOps(GraphicsPipelineTransitionBits *transition, |
| const gl::DepthStencilState &depthStencilState) |
| { |
| setStencilFrontOps(PackGLStencilOp(depthStencilState.stencilFail), |
| PackGLStencilOp(depthStencilState.stencilPassDepthPass), |
| PackGLStencilOp(depthStencilState.stencilPassDepthFail)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, front)); |
| } |
| |
| void GraphicsPipelineDesc::updateStencilBackOps(GraphicsPipelineTransitionBits *transition, |
| const gl::DepthStencilState &depthStencilState) |
| { |
| setStencilBackOps(PackGLStencilOp(depthStencilState.stencilBackFail), |
| PackGLStencilOp(depthStencilState.stencilBackPassDepthPass), |
| PackGLStencilOp(depthStencilState.stencilBackPassDepthFail)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, back)); |
| } |
| |
| void GraphicsPipelineDesc::updateStencilFrontWriteMask( |
| GraphicsPipelineTransitionBits *transition, |
| const gl::DepthStencilState &depthStencilState, |
| const gl::Framebuffer *drawFramebuffer) |
| { |
| // Don't write to stencil buffers that should not exist |
| setStencilFrontWriteMask(static_cast<uint8_t>( |
| drawFramebuffer->hasStencil() ? depthStencilState.stencilWritemask : 0)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, front)); |
| } |
| |
| void GraphicsPipelineDesc::updateStencilBackWriteMask( |
| GraphicsPipelineTransitionBits *transition, |
| const gl::DepthStencilState &depthStencilState, |
| const gl::Framebuffer *drawFramebuffer) |
| { |
| // Don't write to stencil buffers that should not exist |
| setStencilBackWriteMask(static_cast<uint8_t>( |
| drawFramebuffer->hasStencil() ? depthStencilState.stencilBackWritemask : 0)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, back)); |
| } |
| |
| void GraphicsPipelineDesc::updatePolygonOffsetFillEnabled( |
| GraphicsPipelineTransitionBits *transition, |
| bool enabled) |
| { |
| mRasterizationAndMultisampleStateInfo.bits.depthBiasEnable = enabled; |
| transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits)); |
| } |
| |
| void GraphicsPipelineDesc::updatePolygonOffset(GraphicsPipelineTransitionBits *transition, |
| const gl::RasterizerState &rasterState) |
| { |
| mRasterizationAndMultisampleStateInfo.depthBiasSlopeFactor = rasterState.polygonOffsetFactor; |
| mRasterizationAndMultisampleStateInfo.depthBiasConstantFactor = rasterState.polygonOffsetUnits; |
| transition->set( |
| ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, depthBiasSlopeFactor)); |
| transition->set( |
| ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, depthBiasConstantFactor)); |
| } |
| |
| void GraphicsPipelineDesc::setRenderPassDesc(const RenderPassDesc &renderPassDesc) |
| { |
| mRenderPassDesc = renderPassDesc; |
| } |
| |
| void GraphicsPipelineDesc::setViewport(const VkViewport &viewport) |
| { |
| mViewport = viewport; |
| } |
| |
| void GraphicsPipelineDesc::updateViewport(GraphicsPipelineTransitionBits *transition, |
| const VkViewport &viewport) |
| { |
| mViewport = viewport; |
| transition->set(ANGLE_GET_TRANSITION_BIT(mViewport, x)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mViewport, y)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mViewport, width)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mViewport, height)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mViewport, minDepth)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mViewport, maxDepth)); |
| } |
| |
| void GraphicsPipelineDesc::updateDepthRange(GraphicsPipelineTransitionBits *transition, |
| float nearPlane, |
| float farPlane) |
| { |
| // GLES2.0 Section 2.12.1: Each of n and f are clamped to lie within [0, 1], as are all |
| // arguments of type clampf. |
| ASSERT(nearPlane >= 0.0f && nearPlane <= 1.0f); |
| ASSERT(farPlane >= 0.0f && farPlane <= 1.0f); |
| mViewport.minDepth = nearPlane; |
| mViewport.maxDepth = farPlane; |
| transition->set(ANGLE_GET_TRANSITION_BIT(mViewport, minDepth)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mViewport, maxDepth)); |
| } |
| |
| void GraphicsPipelineDesc::setScissor(const VkRect2D &scissor) |
| { |
| mScissor = scissor; |
| } |
| |
| void GraphicsPipelineDesc::updateScissor(GraphicsPipelineTransitionBits *transition, |
| const VkRect2D &scissor) |
| { |
| mScissor = scissor; |
| transition->set(ANGLE_GET_TRANSITION_BIT(mScissor, offset.x)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mScissor, offset.y)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mScissor, extent.width)); |
| transition->set(ANGLE_GET_TRANSITION_BIT(mScissor, extent.height)); |
| } |
| |
| 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); |
| } |
| } |
| |
| // 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[](size_t index) const |
| { |
| return mOps[index]; |
| } |
| |
| PackedAttachmentOpsDesc &AttachmentOpsArray::operator[](size_t index) |
| { |
| return mOps[index]; |
| } |
| |
| void AttachmentOpsArray::initDummyOp(size_t index, |
| VkImageLayout initialLayout, |
| VkImageLayout finalLayout) |
| { |
| PackedAttachmentOpsDesc &ops = mOps[index]; |
| |
| SetBitField(ops.initialLayout, initialLayout); |
| SetBitField(ops.finalLayout, finalLayout); |
| SetBitField(ops.loadOp, VK_ATTACHMENT_LOAD_OP_LOAD); |
| SetBitField(ops.stencilLoadOp, VK_ATTACHMENT_LOAD_OP_DONT_CARE); |
| SetBitField(ops.storeOp, VK_ATTACHMENT_STORE_OP_STORE); |
| SetBitField(ops.stencilStoreOp, VK_ATTACHMENT_STORE_OP_DONT_CARE); |
| } |
| |
| void AttachmentOpsArray::initWithLoadStore(size_t index, |
| VkImageLayout initialLayout, |
| VkImageLayout finalLayout) |
| { |
| PackedAttachmentOpsDesc &ops = mOps[index]; |
| |
| SetBitField(ops.initialLayout, initialLayout); |
| SetBitField(ops.finalLayout, finalLayout); |
| SetBitField(ops.loadOp, VK_ATTACHMENT_LOAD_OP_LOAD); |
| SetBitField(ops.stencilLoadOp, VK_ATTACHMENT_LOAD_OP_LOAD); |
| SetBitField(ops.storeOp, VK_ATTACHMENT_STORE_OP_STORE); |
| SetBitField(ops.stencilStoreOp, VK_ATTACHMENT_STORE_OP_STORE); |
| } |
| |
| 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 type, |
| uint32_t count, |
| VkShaderStageFlags stages) |
| { |
| ASSERT(static_cast<size_t>(type) < std::numeric_limits<uint16_t>::max()); |
| ASSERT(count < std::numeric_limits<uint16_t>::max()); |
| |
| PackedDescriptorSetBinding &packedBinding = mPackedDescriptorSetLayout[bindingIndex]; |
| |
| SetBitField(packedBinding.type, type); |
| SetBitField(packedBinding.count, count); |
| SetBitField(packedBinding.stages, stages); |
| } |
| |
| void DescriptorSetLayoutDesc::unpackBindings(DescriptorSetLayoutBindingVector *bindings) 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); |
| binding.pImmutableSamplers = nullptr; |
| |
| bindings->push_back(binding); |
| } |
| } |
| |
| // PipelineLayoutDesc implementation. |
| PipelineLayoutDesc::PipelineLayoutDesc() : mDescriptorSetLayouts{}, mPushConstantRanges{} {} |
| |
| PipelineLayoutDesc::~PipelineLayoutDesc() = default; |
| |
| PipelineLayoutDesc::PipelineLayoutDesc(const PipelineLayoutDesc &other) = default; |
| |
| PipelineLayoutDesc &PipelineLayoutDesc::operator=(const PipelineLayoutDesc &rhs) |
| { |
| mDescriptorSetLayouts = rhs.mDescriptorSetLayouts; |
| mPushConstantRanges = rhs.mPushConstantRanges; |
| 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(uint32_t setIndex, |
| const DescriptorSetLayoutDesc &desc) |
| { |
| ASSERT(setIndex < mDescriptorSetLayouts.size()); |
| mDescriptorSetLayouts[setIndex] = desc; |
| } |
| |
| void PipelineLayoutDesc::updatePushConstantRange(gl::ShaderType shaderType, |
| uint32_t offset, |
| uint32_t size) |
| { |
| ASSERT(shaderType == gl::ShaderType::Vertex || shaderType == gl::ShaderType::Fragment || |
| shaderType == gl::ShaderType::Geometry || shaderType == gl::ShaderType::Compute); |
| PackedPushConstantRange &packed = mPushConstantRanges[shaderType]; |
| packed.offset = offset; |
| packed.size = size; |
| } |
| |
| const PushConstantRangeArray<PackedPushConstantRange> &PipelineLayoutDesc::getPushConstantRanges() |
| const |
| { |
| return mPushConstantRanges; |
| } |
| |
| // PipelineHelper implementation. |
| PipelineHelper::PipelineHelper() = default; |
| |
| PipelineHelper::~PipelineHelper() = default; |
| |
| void PipelineHelper::destroy(VkDevice device) |
| { |
| mPipeline.destroy(device); |
| } |
| |
| void PipelineHelper::addTransition(GraphicsPipelineTransitionBits bits, |
| const GraphicsPipelineDesc *desc, |
| PipelineHelper *pipeline) |
| { |
| mTransitions.emplace_back(bits, desc, pipeline); |
| } |
| |
| TextureDescriptorDesc::TextureDescriptorDesc() : mMaxIndex(0) |
| { |
| mSerials.fill({0, 0}); |
| } |
| |
| TextureDescriptorDesc::~TextureDescriptorDesc() = default; |
| TextureDescriptorDesc::TextureDescriptorDesc(const TextureDescriptorDesc &other) = default; |
| TextureDescriptorDesc &TextureDescriptorDesc::operator=(const TextureDescriptorDesc &other) = |
| default; |
| |
| void TextureDescriptorDesc::update(size_t index, Serial textureSerial, Serial samplerSerial) |
| { |
| if (index >= mMaxIndex) |
| { |
| mMaxIndex = static_cast<uint32_t>(index + 1); |
| } |
| |
| // If the serial number overflows we should defragment and regenerate all serials. |
| // There should never be more than UINT_MAX textures alive at a time. |
| ASSERT(textureSerial.getValue() < std::numeric_limits<uint32_t>::max()); |
| ASSERT(samplerSerial.getValue() < std::numeric_limits<uint32_t>::max()); |
| mSerials[index].texture = static_cast<uint32_t>(textureSerial.getValue()); |
| mSerials[index].sampler = static_cast<uint32_t>(samplerSerial.getValue()); |
| } |
| |
| size_t TextureDescriptorDesc::hash() const |
| { |
| return angle::ComputeGenericHash(&mSerials, sizeof(TexUnitSerials) * mMaxIndex); |
| } |
| |
| void TextureDescriptorDesc::reset() |
| { |
| memset(mSerials.data(), 0, sizeof(mSerials[0]) * mMaxIndex); |
| mMaxIndex = 0; |
| } |
| |
| bool TextureDescriptorDesc::operator==(const TextureDescriptorDesc &other) const |
| { |
| if (mMaxIndex != other.mMaxIndex) |
| return false; |
| |
| if (mMaxIndex == 0) |
| return true; |
| |
| return memcmp(mSerials.data(), other.mSerials.data(), sizeof(TexUnitSerials) * mMaxIndex) == 0; |
| } |
| |
| } // namespace vk |
| |
| // RenderPassCache implementation. |
| RenderPassCache::RenderPassCache() = default; |
| |
| RenderPassCache::~RenderPassCache() |
| { |
| ASSERT(mPayload.empty()); |
| } |
| |
| void RenderPassCache::destroy(VkDevice device) |
| { |
| for (auto &outerIt : mPayload) |
| { |
| for (auto &innerIt : outerIt.second) |
| { |
| innerIt.second.get().destroy(device); |
| } |
| } |
| mPayload.clear(); |
| } |
| |
| angle::Result RenderPassCache::addRenderPass(ContextVk *contextVk, |
| Serial serial, |
| const vk::RenderPassDesc &desc, |
| vk::RenderPass **renderPassOut) |
| { |
| // Insert some dummy attachment ops. Note that render passes with different ops are still |
| // compatible. |
| // |
| // It would be nice to pre-populate the cache in the Renderer so we rarely miss here. |
| vk::AttachmentOpsArray ops; |
| |
| uint32_t colorAttachmentCount = 0; |
| for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL) |
| { |
| if (!desc.isColorAttachmentEnabled(colorIndexGL)) |
| { |
| continue; |
| } |
| |
| uint32_t colorIndexVk = colorAttachmentCount++; |
| ops.initDummyOp(colorIndexVk, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); |
| } |
| |
| if (desc.hasDepthStencilAttachment()) |
| { |
| uint32_t depthStencilIndexVk = colorAttachmentCount; |
| ops.initDummyOp(depthStencilIndexVk, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, |
| VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); |
| } |
| |
| return getRenderPassWithOps(contextVk, serial, desc, ops, renderPassOut); |
| } |
| |
| angle::Result RenderPassCache::getRenderPassWithOps(vk::Context *context, |
| Serial serial, |
| const vk::RenderPassDesc &desc, |
| const vk::AttachmentOpsArray &attachmentOps, |
| 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()) |
| { |
| // Update the serial before we return. |
| // TODO(jmadill): Could possibly use an MRU cache here. |
| innerIt->second.updateSerial(serial); |
| *renderPassOut = &innerIt->second.get(); |
| return angle::Result::Continue; |
| } |
| } |
| else |
| { |
| auto emplaceResult = mPayload.emplace(desc, InnerCache()); |
| outerIt = emplaceResult.first; |
| } |
| |
| vk::RenderPass newRenderPass; |
| ANGLE_TRY(vk::InitializeRenderPassFromDesc(context, desc, attachmentOps, &newRenderPass)); |
| |
| vk::RenderPassAndSerial withSerial(std::move(newRenderPass), serial); |
| |
| InnerCache &innerCache = outerIt->second; |
| auto insertPos = innerCache.emplace(attachmentOps, std::move(withSerial)); |
| *renderPassOut = &insertPos.first->second.get(); |
| |
| // TODO(jmadill): Trim cache, and pre-populate with the most common RPs on startup. |
| return angle::Result::Continue; |
| } |
| |
| // GraphicsPipelineCache implementation. |
| GraphicsPipelineCache::GraphicsPipelineCache() = default; |
| |
| GraphicsPipelineCache::~GraphicsPipelineCache() |
| { |
| ASSERT(mPayload.empty()); |
| } |
| |
| void GraphicsPipelineCache::destroy(VkDevice device) |
| { |
| for (auto &item : mPayload) |
| { |
| vk::PipelineHelper &pipeline = item.second; |
| pipeline.destroy(device); |
| } |
| |
| mPayload.clear(); |
| } |
| |
| void GraphicsPipelineCache::release(ContextVk *context) |
| { |
| for (auto &item : mPayload) |
| { |
| vk::PipelineHelper &pipeline = item.second; |
| context->addGarbage(&pipeline.getPipeline()); |
| } |
| |
| mPayload.clear(); |
| } |
| |
| angle::Result GraphicsPipelineCache::insertPipeline( |
| ContextVk *contextVk, |
| const vk::PipelineCache &pipelineCacheVk, |
| const vk::RenderPass &compatibleRenderPass, |
| const vk::PipelineLayout &pipelineLayout, |
| const gl::AttributesMask &activeAttribLocationsMask, |
| const gl::ComponentTypeMask &programAttribsTypeMask, |
| const vk::ShaderModule *vertexModule, |
| const vk::ShaderModule *fragmentModule, |
| const vk::ShaderModule *geometryModule, |
| const vk::GraphicsPipelineDesc &desc, |
| const vk::GraphicsPipelineDesc **descPtrOut, |
| vk::PipelineHelper **pipelineOut) |
| { |
| vk::Pipeline newPipeline; |
| |
| // This "if" is left here for the benefit of VulkanPipelineCachePerfTest. |
| if (contextVk != nullptr) |
| { |
| contextVk->getRenderer()->onNewGraphicsPipeline(); |
| ANGLE_TRY(desc.initializePipeline(contextVk, pipelineCacheVk, compatibleRenderPass, |
| pipelineLayout, activeAttribLocationsMask, |
| programAttribsTypeMask, vertexModule, fragmentModule, |
| geometryModule, &newPipeline)); |
| } |
| |
| // The Serial will be updated outside of this query. |
| auto insertedItem = mPayload.emplace(desc, std::move(newPipeline)); |
| *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; |
| } |
| |
| mPayload.emplace(desc, std::move(pipeline)); |
| } |
| |
| // DescriptorSetLayoutCache implementation. |
| DescriptorSetLayoutCache::DescriptorSetLayoutCache() = default; |
| |
| DescriptorSetLayoutCache::~DescriptorSetLayoutCache() |
| { |
| ASSERT(mPayload.empty()); |
| } |
| |
| void DescriptorSetLayoutCache::destroy(VkDevice device) |
| { |
| 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); |
| return angle::Result::Continue; |
| } |
| |
| // We must unpack the descriptor set layout description. |
| vk::DescriptorSetLayoutBindingVector bindings; |
| desc.unpackBindings(&bindings); |
| |
| VkDescriptorSetLayoutCreateInfo createInfo = {}; |
| createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; |
| createInfo.flags = 0; |
| createInfo.bindingCount = static_cast<uint32_t>(bindings.size()); |
| createInfo.pBindings = bindings.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(VkDevice device) |
| { |
| 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); |
| return angle::Result::Continue; |
| } |
| |
| // 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::PushConstantRangeArray<vk::PackedPushConstantRange> &descPushConstantRanges = |
| desc.getPushConstantRanges(); |
| |
| gl::ShaderVector<VkPushConstantRange> pushConstantRanges; |
| |
| for (const gl::ShaderType shaderType : gl::AllShaderTypes()) |
| { |
| const vk::PackedPushConstantRange &pushConstantDesc = descPushConstantRanges[shaderType]; |
| if (pushConstantDesc.size > 0) |
| { |
| VkPushConstantRange range; |
| range.stageFlags = gl_vk::kShaderStageMap[shaderType]; |
| range.offset = pushConstantDesc.offset; |
| range.size = pushConstantDesc.size; |
| |
| pushConstantRanges.push_back(range); |
| } |
| } |
| |
| // 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(); |
| createInfo.pushConstantRangeCount = static_cast<uint32_t>(pushConstantRanges.size()); |
| createInfo.pPushConstantRanges = pushConstantRanges.data(); |
| |
| 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; |
| } |
| } // namespace rx |