| // Copyright 2018 The Dawn Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "dawn_native/vulkan/RenderPipelineVk.h" |
| |
| #include "dawn_native/CreatePipelineAsyncTask.h" |
| #include "dawn_native/vulkan/DeviceVk.h" |
| #include "dawn_native/vulkan/FencedDeleter.h" |
| #include "dawn_native/vulkan/PipelineLayoutVk.h" |
| #include "dawn_native/vulkan/RenderPassCache.h" |
| #include "dawn_native/vulkan/ShaderModuleVk.h" |
| #include "dawn_native/vulkan/TextureVk.h" |
| #include "dawn_native/vulkan/UtilsVulkan.h" |
| #include "dawn_native/vulkan/VulkanError.h" |
| |
| namespace dawn_native::vulkan { |
| |
| namespace { |
| |
| VkVertexInputRate VulkanInputRate(wgpu::VertexStepMode stepMode) { |
| switch (stepMode) { |
| case wgpu::VertexStepMode::Vertex: |
| return VK_VERTEX_INPUT_RATE_VERTEX; |
| case wgpu::VertexStepMode::Instance: |
| return VK_VERTEX_INPUT_RATE_INSTANCE; |
| } |
| UNREACHABLE(); |
| } |
| |
| VkFormat VulkanVertexFormat(wgpu::VertexFormat format) { |
| switch (format) { |
| case wgpu::VertexFormat::Uint8x2: |
| return VK_FORMAT_R8G8_UINT; |
| case wgpu::VertexFormat::Uint8x4: |
| return VK_FORMAT_R8G8B8A8_UINT; |
| case wgpu::VertexFormat::Sint8x2: |
| return VK_FORMAT_R8G8_SINT; |
| case wgpu::VertexFormat::Sint8x4: |
| return VK_FORMAT_R8G8B8A8_SINT; |
| case wgpu::VertexFormat::Unorm8x2: |
| return VK_FORMAT_R8G8_UNORM; |
| case wgpu::VertexFormat::Unorm8x4: |
| return VK_FORMAT_R8G8B8A8_UNORM; |
| case wgpu::VertexFormat::Snorm8x2: |
| return VK_FORMAT_R8G8_SNORM; |
| case wgpu::VertexFormat::Snorm8x4: |
| return VK_FORMAT_R8G8B8A8_SNORM; |
| case wgpu::VertexFormat::Uint16x2: |
| return VK_FORMAT_R16G16_UINT; |
| case wgpu::VertexFormat::Uint16x4: |
| return VK_FORMAT_R16G16B16A16_UINT; |
| case wgpu::VertexFormat::Sint16x2: |
| return VK_FORMAT_R16G16_SINT; |
| case wgpu::VertexFormat::Sint16x4: |
| return VK_FORMAT_R16G16B16A16_SINT; |
| case wgpu::VertexFormat::Unorm16x2: |
| return VK_FORMAT_R16G16_UNORM; |
| case wgpu::VertexFormat::Unorm16x4: |
| return VK_FORMAT_R16G16B16A16_UNORM; |
| case wgpu::VertexFormat::Snorm16x2: |
| return VK_FORMAT_R16G16_SNORM; |
| case wgpu::VertexFormat::Snorm16x4: |
| return VK_FORMAT_R16G16B16A16_SNORM; |
| case wgpu::VertexFormat::Float16x2: |
| return VK_FORMAT_R16G16_SFLOAT; |
| case wgpu::VertexFormat::Float16x4: |
| return VK_FORMAT_R16G16B16A16_SFLOAT; |
| case wgpu::VertexFormat::Float32: |
| return VK_FORMAT_R32_SFLOAT; |
| case wgpu::VertexFormat::Float32x2: |
| return VK_FORMAT_R32G32_SFLOAT; |
| case wgpu::VertexFormat::Float32x3: |
| return VK_FORMAT_R32G32B32_SFLOAT; |
| case wgpu::VertexFormat::Float32x4: |
| return VK_FORMAT_R32G32B32A32_SFLOAT; |
| case wgpu::VertexFormat::Uint32: |
| return VK_FORMAT_R32_UINT; |
| case wgpu::VertexFormat::Uint32x2: |
| return VK_FORMAT_R32G32_UINT; |
| case wgpu::VertexFormat::Uint32x3: |
| return VK_FORMAT_R32G32B32_UINT; |
| case wgpu::VertexFormat::Uint32x4: |
| return VK_FORMAT_R32G32B32A32_UINT; |
| case wgpu::VertexFormat::Sint32: |
| return VK_FORMAT_R32_SINT; |
| case wgpu::VertexFormat::Sint32x2: |
| return VK_FORMAT_R32G32_SINT; |
| case wgpu::VertexFormat::Sint32x3: |
| return VK_FORMAT_R32G32B32_SINT; |
| case wgpu::VertexFormat::Sint32x4: |
| return VK_FORMAT_R32G32B32A32_SINT; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| VkPrimitiveTopology VulkanPrimitiveTopology(wgpu::PrimitiveTopology topology) { |
| switch (topology) { |
| case wgpu::PrimitiveTopology::PointList: |
| return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; |
| case wgpu::PrimitiveTopology::LineList: |
| return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; |
| case wgpu::PrimitiveTopology::LineStrip: |
| return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; |
| case wgpu::PrimitiveTopology::TriangleList: |
| return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; |
| case wgpu::PrimitiveTopology::TriangleStrip: |
| return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; |
| } |
| UNREACHABLE(); |
| } |
| |
| bool ShouldEnablePrimitiveRestart(wgpu::PrimitiveTopology topology) { |
| // Primitive restart is always enabled in WebGPU but Vulkan validation rules ask that |
| // primitive restart be only enabled on primitive topologies that support restarting. |
| switch (topology) { |
| case wgpu::PrimitiveTopology::PointList: |
| case wgpu::PrimitiveTopology::LineList: |
| case wgpu::PrimitiveTopology::TriangleList: |
| return false; |
| case wgpu::PrimitiveTopology::LineStrip: |
| case wgpu::PrimitiveTopology::TriangleStrip: |
| return true; |
| } |
| UNREACHABLE(); |
| } |
| |
| VkFrontFace VulkanFrontFace(wgpu::FrontFace face) { |
| switch (face) { |
| case wgpu::FrontFace::CCW: |
| return VK_FRONT_FACE_COUNTER_CLOCKWISE; |
| case wgpu::FrontFace::CW: |
| return VK_FRONT_FACE_CLOCKWISE; |
| } |
| UNREACHABLE(); |
| } |
| |
| VkCullModeFlagBits VulkanCullMode(wgpu::CullMode mode) { |
| switch (mode) { |
| case wgpu::CullMode::None: |
| return VK_CULL_MODE_NONE; |
| case wgpu::CullMode::Front: |
| return VK_CULL_MODE_FRONT_BIT; |
| case wgpu::CullMode::Back: |
| return VK_CULL_MODE_BACK_BIT; |
| } |
| UNREACHABLE(); |
| } |
| |
| VkBlendFactor VulkanBlendFactor(wgpu::BlendFactor factor) { |
| switch (factor) { |
| case wgpu::BlendFactor::Zero: |
| return VK_BLEND_FACTOR_ZERO; |
| case wgpu::BlendFactor::One: |
| return VK_BLEND_FACTOR_ONE; |
| case wgpu::BlendFactor::Src: |
| return VK_BLEND_FACTOR_SRC_COLOR; |
| case wgpu::BlendFactor::OneMinusSrc: |
| return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; |
| case wgpu::BlendFactor::SrcAlpha: |
| return VK_BLEND_FACTOR_SRC_ALPHA; |
| case wgpu::BlendFactor::OneMinusSrcAlpha: |
| return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; |
| case wgpu::BlendFactor::Dst: |
| return VK_BLEND_FACTOR_DST_COLOR; |
| case wgpu::BlendFactor::OneMinusDst: |
| return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; |
| case wgpu::BlendFactor::DstAlpha: |
| return VK_BLEND_FACTOR_DST_ALPHA; |
| case wgpu::BlendFactor::OneMinusDstAlpha: |
| return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; |
| case wgpu::BlendFactor::SrcAlphaSaturated: |
| return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; |
| case wgpu::BlendFactor::Constant: |
| return VK_BLEND_FACTOR_CONSTANT_COLOR; |
| case wgpu::BlendFactor::OneMinusConstant: |
| return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR; |
| } |
| UNREACHABLE(); |
| } |
| |
| VkBlendOp VulkanBlendOperation(wgpu::BlendOperation operation) { |
| switch (operation) { |
| case wgpu::BlendOperation::Add: |
| return VK_BLEND_OP_ADD; |
| case wgpu::BlendOperation::Subtract: |
| return VK_BLEND_OP_SUBTRACT; |
| case wgpu::BlendOperation::ReverseSubtract: |
| return VK_BLEND_OP_REVERSE_SUBTRACT; |
| case wgpu::BlendOperation::Min: |
| return VK_BLEND_OP_MIN; |
| case wgpu::BlendOperation::Max: |
| return VK_BLEND_OP_MAX; |
| } |
| UNREACHABLE(); |
| } |
| |
| VkColorComponentFlags VulkanColorWriteMask(wgpu::ColorWriteMask mask, |
| bool isDeclaredInFragmentShader) { |
| // Vulkan and Dawn color write masks match, static assert it and return the mask |
| static_assert(static_cast<VkColorComponentFlagBits>(wgpu::ColorWriteMask::Red) == |
| VK_COLOR_COMPONENT_R_BIT, |
| ""); |
| static_assert(static_cast<VkColorComponentFlagBits>(wgpu::ColorWriteMask::Green) == |
| VK_COLOR_COMPONENT_G_BIT, |
| ""); |
| static_assert(static_cast<VkColorComponentFlagBits>(wgpu::ColorWriteMask::Blue) == |
| VK_COLOR_COMPONENT_B_BIT, |
| ""); |
| static_assert(static_cast<VkColorComponentFlagBits>(wgpu::ColorWriteMask::Alpha) == |
| VK_COLOR_COMPONENT_A_BIT, |
| ""); |
| |
| // According to Vulkan SPEC (Chapter 14.3): "The input values to blending or color |
| // attachment writes are undefined for components which do not correspond to a fragment |
| // shader outputs", we set the color write mask to 0 to prevent such undefined values |
| // being written into the color attachments. |
| return isDeclaredInFragmentShader ? static_cast<VkColorComponentFlags>(mask) |
| : static_cast<VkColorComponentFlags>(0); |
| } |
| |
| VkPipelineColorBlendAttachmentState ComputeColorDesc(const ColorTargetState* state, |
| bool isDeclaredInFragmentShader) { |
| VkPipelineColorBlendAttachmentState attachment; |
| attachment.blendEnable = state->blend != nullptr ? VK_TRUE : VK_FALSE; |
| if (attachment.blendEnable) { |
| attachment.srcColorBlendFactor = VulkanBlendFactor(state->blend->color.srcFactor); |
| attachment.dstColorBlendFactor = VulkanBlendFactor(state->blend->color.dstFactor); |
| attachment.colorBlendOp = VulkanBlendOperation(state->blend->color.operation); |
| attachment.srcAlphaBlendFactor = VulkanBlendFactor(state->blend->alpha.srcFactor); |
| attachment.dstAlphaBlendFactor = VulkanBlendFactor(state->blend->alpha.dstFactor); |
| attachment.alphaBlendOp = VulkanBlendOperation(state->blend->alpha.operation); |
| } else { |
| // Swiftshader's Vulkan implementation appears to expect these values to be valid |
| // even when blending is not enabled. |
| attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; |
| attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; |
| attachment.colorBlendOp = VK_BLEND_OP_ADD; |
| attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; |
| attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; |
| attachment.alphaBlendOp = VK_BLEND_OP_ADD; |
| } |
| attachment.colorWriteMask = |
| VulkanColorWriteMask(state->writeMask, isDeclaredInFragmentShader); |
| return attachment; |
| } |
| |
| VkStencilOp VulkanStencilOp(wgpu::StencilOperation op) { |
| switch (op) { |
| case wgpu::StencilOperation::Keep: |
| return VK_STENCIL_OP_KEEP; |
| case wgpu::StencilOperation::Zero: |
| return VK_STENCIL_OP_ZERO; |
| case wgpu::StencilOperation::Replace: |
| return VK_STENCIL_OP_REPLACE; |
| case wgpu::StencilOperation::IncrementClamp: |
| return VK_STENCIL_OP_INCREMENT_AND_CLAMP; |
| case wgpu::StencilOperation::DecrementClamp: |
| return VK_STENCIL_OP_DECREMENT_AND_CLAMP; |
| case wgpu::StencilOperation::Invert: |
| return VK_STENCIL_OP_INVERT; |
| case wgpu::StencilOperation::IncrementWrap: |
| return VK_STENCIL_OP_INCREMENT_AND_WRAP; |
| case wgpu::StencilOperation::DecrementWrap: |
| return VK_STENCIL_OP_DECREMENT_AND_WRAP; |
| } |
| UNREACHABLE(); |
| } |
| |
| VkPipelineDepthStencilStateCreateInfo ComputeDepthStencilDesc( |
| const DepthStencilState* descriptor) { |
| VkPipelineDepthStencilStateCreateInfo depthStencilState; |
| depthStencilState.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; |
| depthStencilState.pNext = nullptr; |
| depthStencilState.flags = 0; |
| |
| // Depth writes only occur if depth is enabled |
| depthStencilState.depthTestEnable = |
| (descriptor->depthCompare == wgpu::CompareFunction::Always && |
| !descriptor->depthWriteEnabled) |
| ? VK_FALSE |
| : VK_TRUE; |
| depthStencilState.depthWriteEnable = descriptor->depthWriteEnabled ? VK_TRUE : VK_FALSE; |
| depthStencilState.depthCompareOp = ToVulkanCompareOp(descriptor->depthCompare); |
| depthStencilState.depthBoundsTestEnable = false; |
| depthStencilState.minDepthBounds = 0.0f; |
| depthStencilState.maxDepthBounds = 1.0f; |
| |
| depthStencilState.stencilTestEnable = |
| StencilTestEnabled(descriptor) ? VK_TRUE : VK_FALSE; |
| |
| depthStencilState.front.failOp = VulkanStencilOp(descriptor->stencilFront.failOp); |
| depthStencilState.front.passOp = VulkanStencilOp(descriptor->stencilFront.passOp); |
| depthStencilState.front.depthFailOp = |
| VulkanStencilOp(descriptor->stencilFront.depthFailOp); |
| depthStencilState.front.compareOp = ToVulkanCompareOp(descriptor->stencilFront.compare); |
| |
| depthStencilState.back.failOp = VulkanStencilOp(descriptor->stencilBack.failOp); |
| depthStencilState.back.passOp = VulkanStencilOp(descriptor->stencilBack.passOp); |
| depthStencilState.back.depthFailOp = |
| VulkanStencilOp(descriptor->stencilBack.depthFailOp); |
| depthStencilState.back.compareOp = ToVulkanCompareOp(descriptor->stencilBack.compare); |
| |
| // Dawn doesn't have separate front and back stencil masks. |
| depthStencilState.front.compareMask = descriptor->stencilReadMask; |
| depthStencilState.back.compareMask = descriptor->stencilReadMask; |
| depthStencilState.front.writeMask = descriptor->stencilWriteMask; |
| depthStencilState.back.writeMask = descriptor->stencilWriteMask; |
| |
| // The stencil reference is always dynamic |
| depthStencilState.front.reference = 0; |
| depthStencilState.back.reference = 0; |
| |
| return depthStencilState; |
| } |
| |
| } // anonymous namespace |
| |
| // static |
| Ref<RenderPipeline> RenderPipeline::CreateUninitialized( |
| Device* device, |
| const RenderPipelineDescriptor* descriptor) { |
| return AcquireRef(new RenderPipeline(device, descriptor)); |
| } |
| |
| MaybeError RenderPipeline::Initialize() { |
| Device* device = ToBackend(GetDevice()); |
| |
| // There are at most 2 shader stages in render pipeline, i.e. vertex and fragment |
| std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages; |
| std::array<std::vector<OverridableConstantScalar>, 2> specializationDataEntriesPerStages; |
| std::array<std::vector<VkSpecializationMapEntry>, 2> specializationMapEntriesPerStages; |
| std::array<VkSpecializationInfo, 2> specializationInfoPerStages; |
| uint32_t stageCount = 0; |
| |
| for (auto stage : IterateStages(this->GetStageMask())) { |
| VkPipelineShaderStageCreateInfo shaderStage; |
| |
| const ProgrammableStage& programmableStage = GetStage(stage); |
| DAWN_TRY_ASSIGN(shaderStage.module, |
| ToBackend(programmableStage.module) |
| ->GetTransformedModuleHandle(programmableStage.entryPoint.c_str(), |
| ToBackend(GetLayout()))); |
| |
| shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
| shaderStage.pNext = nullptr; |
| shaderStage.flags = 0; |
| shaderStage.pSpecializationInfo = nullptr; |
| shaderStage.pName = programmableStage.entryPoint.c_str(); |
| |
| switch (stage) { |
| case dawn_native::SingleShaderStage::Vertex: { |
| shaderStage.stage = VK_SHADER_STAGE_VERTEX_BIT; |
| break; |
| } |
| case dawn_native::SingleShaderStage::Fragment: { |
| shaderStage.stage = VK_SHADER_STAGE_FRAGMENT_BIT; |
| break; |
| } |
| default: { |
| // For render pipeline only Vertex and Fragment stage is possible |
| DAWN_UNREACHABLE(); |
| break; |
| } |
| } |
| |
| shaderStage.pSpecializationInfo = |
| GetVkSpecializationInfo(programmableStage, &specializationInfoPerStages[stageCount], |
| &specializationDataEntriesPerStages[stageCount], |
| &specializationMapEntriesPerStages[stageCount]); |
| |
| DAWN_ASSERT(stageCount < 2); |
| shaderStages[stageCount] = shaderStage; |
| stageCount++; |
| } |
| |
| PipelineVertexInputStateCreateInfoTemporaryAllocations tempAllocations; |
| VkPipelineVertexInputStateCreateInfo vertexInputCreateInfo = |
| ComputeVertexInputDesc(&tempAllocations); |
| |
| VkPipelineInputAssemblyStateCreateInfo inputAssembly; |
| inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; |
| inputAssembly.pNext = nullptr; |
| inputAssembly.flags = 0; |
| inputAssembly.topology = VulkanPrimitiveTopology(GetPrimitiveTopology()); |
| inputAssembly.primitiveRestartEnable = ShouldEnablePrimitiveRestart(GetPrimitiveTopology()); |
| |
| // A dummy viewport/scissor info. The validation layers force use to provide at least one |
| // scissor and one viewport here, even if we choose to make them dynamic. |
| VkViewport viewportDesc; |
| viewportDesc.x = 0.0f; |
| viewportDesc.y = 0.0f; |
| viewportDesc.width = 1.0f; |
| viewportDesc.height = 1.0f; |
| viewportDesc.minDepth = 0.0f; |
| viewportDesc.maxDepth = 1.0f; |
| VkRect2D scissorRect; |
| scissorRect.offset.x = 0; |
| scissorRect.offset.y = 0; |
| scissorRect.extent.width = 1; |
| scissorRect.extent.height = 1; |
| VkPipelineViewportStateCreateInfo viewport; |
| viewport.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; |
| viewport.pNext = nullptr; |
| viewport.flags = 0; |
| viewport.viewportCount = 1; |
| viewport.pViewports = &viewportDesc; |
| viewport.scissorCount = 1; |
| viewport.pScissors = &scissorRect; |
| |
| VkPipelineRasterizationStateCreateInfo rasterization; |
| rasterization.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; |
| rasterization.pNext = nullptr; |
| rasterization.flags = 0; |
| rasterization.depthClampEnable = ShouldClampDepth() ? VK_TRUE : VK_FALSE; |
| rasterization.rasterizerDiscardEnable = VK_FALSE; |
| rasterization.polygonMode = VK_POLYGON_MODE_FILL; |
| rasterization.cullMode = VulkanCullMode(GetCullMode()); |
| rasterization.frontFace = VulkanFrontFace(GetFrontFace()); |
| rasterization.depthBiasEnable = IsDepthBiasEnabled(); |
| rasterization.depthBiasConstantFactor = GetDepthBias(); |
| rasterization.depthBiasClamp = GetDepthBiasClamp(); |
| rasterization.depthBiasSlopeFactor = GetDepthBiasSlopeScale(); |
| rasterization.lineWidth = 1.0f; |
| |
| VkPipelineMultisampleStateCreateInfo multisample; |
| multisample.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; |
| multisample.pNext = nullptr; |
| multisample.flags = 0; |
| multisample.rasterizationSamples = VulkanSampleCount(GetSampleCount()); |
| multisample.sampleShadingEnable = VK_FALSE; |
| multisample.minSampleShading = 0.0f; |
| // VkPipelineMultisampleStateCreateInfo.pSampleMask is an array of length |
| // ceil(rasterizationSamples / 32) and since we're passing a single uint32_t |
| // we have to assert that this length is indeed 1. |
| ASSERT(multisample.rasterizationSamples <= 32); |
| VkSampleMask sampleMask = GetSampleMask(); |
| multisample.pSampleMask = &sampleMask; |
| multisample.alphaToCoverageEnable = IsAlphaToCoverageEnabled(); |
| multisample.alphaToOneEnable = VK_FALSE; |
| |
| VkPipelineDepthStencilStateCreateInfo depthStencilState = |
| ComputeDepthStencilDesc(GetDepthStencilState()); |
| |
| VkPipelineColorBlendStateCreateInfo colorBlend; |
| // colorBlend may hold pointers to elements in colorBlendAttachments, so it must have a |
| // definition scope as same as colorBlend |
| ityp::array<ColorAttachmentIndex, VkPipelineColorBlendAttachmentState, kMaxColorAttachments> |
| colorBlendAttachments; |
| if (GetStageMask() & wgpu::ShaderStage::Fragment) { |
| // Initialize the "blend state info" that will be chained in the "create info" from the |
| // data pre-computed in the ColorState |
| const auto& fragmentOutputsWritten = |
| GetStage(SingleShaderStage::Fragment).metadata->fragmentOutputsWritten; |
| for (ColorAttachmentIndex i : IterateBitSet(GetColorAttachmentsMask())) { |
| const ColorTargetState* target = GetColorTargetState(i); |
| colorBlendAttachments[i] = ComputeColorDesc(target, fragmentOutputsWritten[i]); |
| } |
| |
| colorBlend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; |
| colorBlend.pNext = nullptr; |
| colorBlend.flags = 0; |
| // LogicOp isn't supported so we disable it. |
| colorBlend.logicOpEnable = VK_FALSE; |
| colorBlend.logicOp = VK_LOGIC_OP_CLEAR; |
| colorBlend.attachmentCount = static_cast<uint32_t>(GetColorAttachmentsMask().count()); |
| colorBlend.pAttachments = colorBlendAttachments.data(); |
| // The blend constant is always dynamic so we fill in a dummy value |
| colorBlend.blendConstants[0] = 0.0f; |
| colorBlend.blendConstants[1] = 0.0f; |
| colorBlend.blendConstants[2] = 0.0f; |
| colorBlend.blendConstants[3] = 0.0f; |
| } |
| |
| // Tag all state as dynamic but stencil masks and depth bias. |
| VkDynamicState dynamicStates[] = { |
| VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, |
| VK_DYNAMIC_STATE_LINE_WIDTH, VK_DYNAMIC_STATE_BLEND_CONSTANTS, |
| VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_REFERENCE, |
| }; |
| VkPipelineDynamicStateCreateInfo dynamic; |
| dynamic.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; |
| dynamic.pNext = nullptr; |
| dynamic.flags = 0; |
| dynamic.dynamicStateCount = sizeof(dynamicStates) / sizeof(dynamicStates[0]); |
| dynamic.pDynamicStates = dynamicStates; |
| |
| // Get a VkRenderPass that matches the attachment formats for this pipeline, load/store ops |
| // don't matter so set them all to LoadOp::Load / StoreOp::Store. Whether the render pass |
| // has resolve target and whether depth/stencil attachment is read-only also don't matter, |
| // so set them both to false. |
| VkRenderPass renderPass = VK_NULL_HANDLE; |
| { |
| RenderPassCacheQuery query; |
| |
| for (ColorAttachmentIndex i : IterateBitSet(GetColorAttachmentsMask())) { |
| query.SetColor(i, GetColorAttachmentFormat(i), wgpu::LoadOp::Load, |
| wgpu::StoreOp::Store, false); |
| } |
| |
| if (HasDepthStencilAttachment()) { |
| query.SetDepthStencil(GetDepthStencilFormat(), wgpu::LoadOp::Load, |
| wgpu::StoreOp::Store, wgpu::LoadOp::Load, |
| wgpu::StoreOp::Store, false); |
| } |
| |
| query.SetSampleCount(GetSampleCount()); |
| |
| DAWN_TRY_ASSIGN(renderPass, device->GetRenderPassCache()->GetRenderPass(query)); |
| } |
| |
| // The create info chains in a bunch of things created on the stack here or inside state |
| // objects. |
| VkGraphicsPipelineCreateInfo createInfo; |
| createInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; |
| createInfo.pNext = nullptr; |
| createInfo.flags = 0; |
| createInfo.stageCount = stageCount; |
| createInfo.pStages = shaderStages.data(); |
| createInfo.pVertexInputState = &vertexInputCreateInfo; |
| createInfo.pInputAssemblyState = &inputAssembly; |
| createInfo.pTessellationState = nullptr; |
| createInfo.pViewportState = &viewport; |
| createInfo.pRasterizationState = &rasterization; |
| createInfo.pMultisampleState = &multisample; |
| createInfo.pDepthStencilState = &depthStencilState; |
| createInfo.pColorBlendState = |
| (GetStageMask() & wgpu::ShaderStage::Fragment) ? &colorBlend : nullptr; |
| createInfo.pDynamicState = &dynamic; |
| createInfo.layout = ToBackend(GetLayout())->GetHandle(); |
| createInfo.renderPass = renderPass; |
| createInfo.subpass = 0; |
| createInfo.basePipelineHandle = VkPipeline{}; |
| createInfo.basePipelineIndex = -1; |
| |
| DAWN_TRY(CheckVkSuccess( |
| device->fn.CreateGraphicsPipelines(device->GetVkDevice(), VkPipelineCache{}, 1, |
| &createInfo, nullptr, &*mHandle), |
| "CreateGraphicsPipeline")); |
| |
| SetLabelImpl(); |
| |
| return {}; |
| } |
| |
| void RenderPipeline::SetLabelImpl() { |
| SetDebugName(ToBackend(GetDevice()), VK_OBJECT_TYPE_PIPELINE, |
| reinterpret_cast<uint64_t&>(mHandle), "Dawn_RenderPipeline", GetLabel()); |
| } |
| |
| VkPipelineVertexInputStateCreateInfo RenderPipeline::ComputeVertexInputDesc( |
| PipelineVertexInputStateCreateInfoTemporaryAllocations* tempAllocations) { |
| // Fill in the "binding info" that will be chained in the create info |
| uint32_t bindingCount = 0; |
| for (VertexBufferSlot slot : IterateBitSet(GetVertexBufferSlotsUsed())) { |
| const VertexBufferInfo& bindingInfo = GetVertexBuffer(slot); |
| |
| VkVertexInputBindingDescription* bindingDesc = &tempAllocations->bindings[bindingCount]; |
| bindingDesc->binding = static_cast<uint8_t>(slot); |
| bindingDesc->stride = bindingInfo.arrayStride; |
| bindingDesc->inputRate = VulkanInputRate(bindingInfo.stepMode); |
| |
| bindingCount++; |
| } |
| |
| // Fill in the "attribute info" that will be chained in the create info |
| uint32_t attributeCount = 0; |
| for (VertexAttributeLocation loc : IterateBitSet(GetAttributeLocationsUsed())) { |
| const VertexAttributeInfo& attributeInfo = GetAttribute(loc); |
| |
| VkVertexInputAttributeDescription* attributeDesc = |
| &tempAllocations->attributes[attributeCount]; |
| attributeDesc->location = static_cast<uint8_t>(loc); |
| attributeDesc->binding = static_cast<uint8_t>(attributeInfo.vertexBufferSlot); |
| attributeDesc->format = VulkanVertexFormat(attributeInfo.format); |
| attributeDesc->offset = attributeInfo.offset; |
| |
| attributeCount++; |
| } |
| |
| // Build the create info |
| VkPipelineVertexInputStateCreateInfo createInfo; |
| createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; |
| createInfo.pNext = nullptr; |
| createInfo.flags = 0; |
| createInfo.vertexBindingDescriptionCount = bindingCount; |
| createInfo.pVertexBindingDescriptions = tempAllocations->bindings.data(); |
| createInfo.vertexAttributeDescriptionCount = attributeCount; |
| createInfo.pVertexAttributeDescriptions = tempAllocations->attributes.data(); |
| return createInfo; |
| } |
| |
| RenderPipeline::~RenderPipeline() = default; |
| |
| void RenderPipeline::DestroyImpl() { |
| RenderPipelineBase::DestroyImpl(); |
| if (mHandle != VK_NULL_HANDLE) { |
| ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle); |
| mHandle = VK_NULL_HANDLE; |
| } |
| } |
| |
| VkPipeline RenderPipeline::GetHandle() const { |
| return mHandle; |
| } |
| |
| void RenderPipeline::InitializeAsync(Ref<RenderPipelineBase> renderPipeline, |
| WGPUCreateRenderPipelineAsyncCallback callback, |
| void* userdata) { |
| std::unique_ptr<CreateRenderPipelineAsyncTask> asyncTask = |
| std::make_unique<CreateRenderPipelineAsyncTask>(std::move(renderPipeline), callback, |
| userdata); |
| CreateRenderPipelineAsyncTask::RunAsync(std::move(asyncTask)); |
| } |
| |
| } // namespace dawn_native::vulkan |