| /* | |
| * Copyright 2025 Rive | |
| */ | |
| #include "rive/math/bitwise.hpp" | |
| #include "rive/renderer/vulkan/render_context_vulkan_impl.hpp" | |
| #include "rive/renderer/vulkan/vulkan_context.hpp" | |
| #include "rive/renderer/stack_vector.hpp" | |
| #include "shaders/constants.glsl" | |
| #include "common_layouts.hpp" | |
| #include "draw_pipeline_layout_vulkan.hpp" | |
| #include "pipeline_manager_vulkan.hpp" | |
| #include "render_pass_vulkan.hpp" | |
| namespace rive::gpu | |
| { | |
| static VkStencilOp vk_stencil_op(StencilOp op) | |
| { | |
| switch (op) | |
| { | |
| case StencilOp::keep: | |
| return VK_STENCIL_OP_KEEP; | |
| case StencilOp::replace: | |
| return VK_STENCIL_OP_REPLACE; | |
| case StencilOp::zero: | |
| return VK_STENCIL_OP_ZERO; | |
| case StencilOp::decrClamp: | |
| return VK_STENCIL_OP_DECREMENT_AND_CLAMP; | |
| case StencilOp::incrWrap: | |
| return VK_STENCIL_OP_INCREMENT_AND_WRAP; | |
| case StencilOp::decrWrap: | |
| return VK_STENCIL_OP_DECREMENT_AND_WRAP; | |
| } | |
| RIVE_UNREACHABLE(); | |
| } | |
| static VkCompareOp vk_compare_op(gpu::StencilCompareOp op) | |
| { | |
| switch (op) | |
| { | |
| case gpu::StencilCompareOp::less: | |
| return VK_COMPARE_OP_LESS; | |
| case gpu::StencilCompareOp::equal: | |
| return VK_COMPARE_OP_EQUAL; | |
| case gpu::StencilCompareOp::lessOrEqual: | |
| return VK_COMPARE_OP_LESS_OR_EQUAL; | |
| case gpu::StencilCompareOp::notEqual: | |
| return VK_COMPARE_OP_NOT_EQUAL; | |
| case gpu::StencilCompareOp::always: | |
| return VK_COMPARE_OP_ALWAYS; | |
| } | |
| RIVE_UNREACHABLE(); | |
| } | |
| static VkCullModeFlags vk_cull_mode(CullFace cullFace) | |
| { | |
| switch (cullFace) | |
| { | |
| case CullFace::none: | |
| return VK_CULL_MODE_NONE; | |
| case CullFace::clockwise: | |
| return VK_CULL_MODE_FRONT_BIT; | |
| case CullFace::counterclockwise: | |
| return VK_CULL_MODE_BACK_BIT; | |
| } | |
| RIVE_UNREACHABLE(); | |
| } | |
| constexpr static VkBlendOp vk_blend_op(gpu::BlendEquation equation) | |
| { | |
| switch (equation) | |
| { | |
| case gpu::BlendEquation::none: | |
| case gpu::BlendEquation::srcOver: | |
| case gpu::BlendEquation::plus: | |
| return VK_BLEND_OP_ADD; | |
| case gpu::BlendEquation::min: | |
| return VK_BLEND_OP_MIN; | |
| case gpu::BlendEquation::max: | |
| return VK_BLEND_OP_MAX; | |
| case gpu::BlendEquation::screen: | |
| case gpu::BlendEquation::overlay: | |
| case gpu::BlendEquation::darken: | |
| case gpu::BlendEquation::lighten: | |
| case gpu::BlendEquation::colorDodge: | |
| case gpu::BlendEquation::colorBurn: | |
| case gpu::BlendEquation::hardLight: | |
| case gpu::BlendEquation::softLight: | |
| case gpu::BlendEquation::difference: | |
| case gpu::BlendEquation::exclusion: | |
| case gpu::BlendEquation::multiply: | |
| case gpu::BlendEquation::hue: | |
| case gpu::BlendEquation::saturation: | |
| case gpu::BlendEquation::color: | |
| case gpu::BlendEquation::luminosity: | |
| break; | |
| } | |
| RIVE_UNREACHABLE(); | |
| } | |
| constexpr static VkBlendFactor vk_dst_blend_factor(gpu::BlendEquation equation) | |
| { | |
| switch (equation) | |
| { | |
| case gpu::BlendEquation::none: | |
| return VK_BLEND_FACTOR_ZERO; | |
| case gpu::BlendEquation::srcOver: | |
| return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; | |
| case gpu::BlendEquation::plus: | |
| case gpu::BlendEquation::min: | |
| case gpu::BlendEquation::max: | |
| return VK_BLEND_FACTOR_ONE; | |
| case gpu::BlendEquation::screen: | |
| case gpu::BlendEquation::overlay: | |
| case gpu::BlendEquation::darken: | |
| case gpu::BlendEquation::lighten: | |
| case gpu::BlendEquation::colorDodge: | |
| case gpu::BlendEquation::colorBurn: | |
| case gpu::BlendEquation::hardLight: | |
| case gpu::BlendEquation::softLight: | |
| case gpu::BlendEquation::difference: | |
| case gpu::BlendEquation::exclusion: | |
| case gpu::BlendEquation::multiply: | |
| case gpu::BlendEquation::hue: | |
| case gpu::BlendEquation::saturation: | |
| case gpu::BlendEquation::color: | |
| case gpu::BlendEquation::luminosity: | |
| break; | |
| } | |
| RIVE_UNREACHABLE(); | |
| } | |
| uint64_t DrawPipelineVulkan::PipelineProps::createKey( | |
| const PlatformFeatures& platformFeatures) const | |
| { | |
| uint64_t key = gpu::pipeline_unique_key( | |
| drawType, | |
| shaderFeatures, | |
| interlockMode, | |
| shaderMiscFlags, | |
| drawContents, | |
| enums::is_flag_set(renderPassOptions, | |
| RenderPassOptionsVulkan::fixedFunctionColorOutput), | |
| blendMode, | |
| platformFeatures); | |
| const uint64_t renderPassKeyNoInterlockMode = | |
| RenderPassVulkan::KeyNoInterlockMode(renderPassOptions, | |
| renderTargetFormat, | |
| colorLoadAction); | |
| key = math::add_bits_to_key( | |
| key, | |
| renderPassKeyNoInterlockMode, | |
| RenderPassVulkan::KEY_NO_INTERLOCK_MODE_BIT_COUNT); | |
| key = | |
| math::add_bits_to_key(key, uint64_t(drawPipelineOptions), OPTION_COUNT); | |
| return key; | |
| } | |
| uint32_t subpass_index(gpu::DrawType drawType, | |
| gpu::LoadAction colorLoadAction, | |
| gpu::InterlockMode interlockMode, | |
| gpu::ShaderMiscFlags shaderMiscFlags) | |
| { | |
| if (interlockMode == gpu::InterlockMode::clockwiseAtomic) | |
| { | |
| // In clockwiseAtomic mode, borrowed coverage is rendered in a separate | |
| // subpass prior to the main one. | |
| return enums::is_flag_set(shaderMiscFlags, | |
| gpu::ShaderMiscFlags::borrowedCoveragePass) | |
| ? 0 | |
| : 1; | |
| } | |
| const uint32_t mainSubpassIdx = | |
| (interlockMode == gpu::InterlockMode::msaa && | |
| colorLoadAction == gpu::LoadAction::preserveRenderTarget) | |
| ? 1 | |
| : 0; | |
| switch (drawType) | |
| { | |
| case gpu::DrawType::renderPassInitialize: | |
| assert(mainSubpassIdx == 1); | |
| return 0; | |
| case gpu::DrawType::midpointFanPatches: | |
| case gpu::DrawType::midpointFanCenterAAPatches: | |
| case gpu::DrawType::outerCurvePatches: | |
| case gpu::DrawType::interiorTriangulation: | |
| case gpu::DrawType::atlasBlit: | |
| case gpu::DrawType::imageRect: | |
| case gpu::DrawType::imageMesh: | |
| case gpu::DrawType::msaaStrokes: | |
| case gpu::DrawType::msaaMidpointFanBorrowedCoverage: | |
| case gpu::DrawType::msaaMidpointFans: | |
| case gpu::DrawType::msaaMidpointFanStencilReset: | |
| case gpu::DrawType::msaaMidpointFanPathsStencil: | |
| case gpu::DrawType::msaaMidpointFanPathsCover: | |
| case gpu::DrawType::msaaOuterCubics: | |
| case gpu::DrawType::clipReset: | |
| return mainSubpassIdx; | |
| case gpu::DrawType::renderPassResolve: | |
| return mainSubpassIdx + 1; | |
| } | |
| RIVE_UNREACHABLE(); | |
| } | |
| DrawPipelineVulkan::DrawPipelineVulkan( | |
| PipelineManagerVulkan* pipelineManager, | |
| const DrawPipelineLayoutVulkan& pipelineLayout, | |
| const PipelineProps& props, | |
| VkRenderPass vkRenderPass, | |
| const PlatformFeatures& platformFeatures | |
| #ifdef WITH_RIVE_TOOLS | |
| , | |
| SynthesizedFailureType synthesizedFailureType | |
| #endif | |
| ) : | |
| m_vk(ref_rcp(pipelineManager->vulkanContext())) | |
| { | |
| #ifdef WITH_RIVE_TOOLS | |
| if (synthesizedFailureType == SynthesizedFailureType::pipelineCreation) | |
| { | |
| return; | |
| } | |
| #endif | |
| const bool pipelineWriteOnlyRenderTarget = | |
| enums::is_flag_set(props.renderPassOptions, | |
| RenderPassOptionsVulkan::fixedFunctionColorOutput); | |
| const gpu::PipelineState pipelineState = | |
| get_pipeline_state(props.drawType, | |
| props.interlockMode, | |
| props.shaderMiscFlags, | |
| props.drawContents, | |
| pipelineWriteOnlyRenderTarget, | |
| props.blendMode, | |
| platformFeatures); | |
| const gpu::InterlockMode interlockMode = pipelineLayout.interlockMode(); | |
| uint32_t subpassIndex = subpass_index(props.drawType, | |
| props.colorLoadAction, | |
| interlockMode, | |
| props.shaderMiscFlags); | |
| auto& vertShader = | |
| pipelineManager->getVertexShaderSynchronous(props.drawType, | |
| props.shaderFeatures, | |
| interlockMode); | |
| if (vertShader.module() == VK_NULL_HANDLE) | |
| { | |
| return; | |
| } | |
| auto& fragShader = | |
| pipelineManager->getFragmentShaderSynchronous(props.drawType, | |
| props.shaderFeatures, | |
| interlockMode, | |
| props.shaderMiscFlags); | |
| if (fragShader.module() == VK_NULL_HANDLE) | |
| { | |
| return; | |
| } | |
| uint32_t shaderPermutationFlags[] = { | |
| enums::is_flag_set(props.shaderFeatures, | |
| gpu::ShaderFeatures::ENABLE_CLIPPING), | |
| enums::is_flag_set(props.shaderFeatures, | |
| gpu::ShaderFeatures::ENABLE_CLIP_RECT), | |
| enums::is_flag_set(props.shaderFeatures, | |
| gpu::ShaderFeatures::ENABLE_ADVANCED_BLEND), | |
| enums::is_flag_set(props.shaderFeatures, | |
| gpu::ShaderFeatures::ENABLE_FEATHER), | |
| enums::is_flag_set(props.shaderFeatures, | |
| gpu::ShaderFeatures::ENABLE_EVEN_ODD), | |
| enums::is_flag_set(props.shaderFeatures, | |
| gpu::ShaderFeatures::ENABLE_NESTED_CLIPPING), | |
| enums::is_flag_set(props.shaderFeatures, | |
| gpu::ShaderFeatures::ENABLE_HSL_BLEND_MODES), | |
| enums::is_flag_set(props.shaderFeatures, | |
| gpu::ShaderFeatures::ENABLE_DITHER), | |
| enums::is_flag_set(props.shaderMiscFlags, | |
| gpu::ShaderMiscFlags::clockwiseFill), | |
| enums::is_flag_set(props.shaderMiscFlags, | |
| gpu::ShaderMiscFlags::nestedClipUpdateOnly), | |
| enums::is_flag_set(props.shaderMiscFlags, | |
| gpu::ShaderMiscFlags::borrowedCoveragePass), | |
| enums::is_flag_set(props.shaderMiscFlags, | |
| gpu::ShaderMiscFlags::storeColorClear), | |
| enums::is_flag_set(props.shaderMiscFlags, | |
| gpu::ShaderMiscFlags::loadColorFromDstTexture), | |
| pipelineManager->vendorID() == vkutil::vendors::ARM, | |
| }; | |
| static_assert(std::size(shaderPermutationFlags) == SPECIALIZATION_COUNT); | |
| static_assert(CLIPPING_SPECIALIZATION_IDX == 0); | |
| static_assert(CLIP_RECT_SPECIALIZATION_IDX == 1); | |
| static_assert(ADVANCED_BLEND_SPECIALIZATION_IDX == 2); | |
| static_assert(FEATHER_SPECIALIZATION_IDX == 3); | |
| static_assert(EVEN_ODD_SPECIALIZATION_IDX == 4); | |
| static_assert(NESTED_CLIPPING_SPECIALIZATION_IDX == 5); | |
| static_assert(HSL_BLEND_MODES_SPECIALIZATION_IDX == 6); | |
| static_assert(DITHER_SPECIALIZATION_IDX == 7); | |
| static_assert(CLOCKWISE_FILL_SPECIALIZATION_IDX == 8); | |
| static_assert(NESTED_CLIP_UPDATE_ONLY_IDX == 9); | |
| static_assert(BORROWED_COVERAGE_PASS_SPECIALIZATION_IDX == 10); | |
| static_assert(STORE_COLOR_CLEAR_SPECIALIZATION_IDX == 11); | |
| static_assert(LOAD_COLOR_FROM_DST_TEXTURE_SPECIALIZATION_IDX == 12); | |
| static_assert(VULKAN_VENDOR_ARM_SPECIALIZATION_IDX == 13); | |
| static_assert(SPECIALIZATION_COUNT == 14); | |
| VkSpecializationMapEntry permutationMapEntries[SPECIALIZATION_COUNT]; | |
| for (uint32_t i = 0; i < SPECIALIZATION_COUNT; ++i) | |
| { | |
| permutationMapEntries[i] = { | |
| .constantID = i, | |
| .offset = i * static_cast<uint32_t>(sizeof(uint32_t)), | |
| .size = sizeof(uint32_t), | |
| }; | |
| } | |
| VkSpecializationInfo specializationInfo = { | |
| .mapEntryCount = SPECIALIZATION_COUNT, | |
| .pMapEntries = permutationMapEntries, | |
| .dataSize = sizeof(shaderPermutationFlags), | |
| .pData = &shaderPermutationFlags, | |
| }; | |
| VkPipelineShaderStageCreateInfo stages[] = { | |
| { | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | |
| .stage = VK_SHADER_STAGE_VERTEX_BIT, | |
| .module = vertShader.module(), | |
| .pName = "main", | |
| .pSpecializationInfo = &specializationInfo, | |
| }, | |
| { | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | |
| .stage = VK_SHADER_STAGE_FRAGMENT_BIT, | |
| .module = fragShader.module(), | |
| .pName = "main", | |
| .pSpecializationInfo = &specializationInfo, | |
| }, | |
| }; | |
| VkPipelineRasterizationStateCreateInfo | |
| pipelineRasterizationStateCreateInfo = { | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, | |
| .polygonMode = enums::is_flag_set(props.drawPipelineOptions, | |
| Options::wireframe) | |
| ? VK_POLYGON_MODE_LINE | |
| : VK_POLYGON_MODE_FILL, | |
| .cullMode = vk_cull_mode(pipelineState.cullFace), | |
| .frontFace = VK_FRONT_FACE_CLOCKWISE, | |
| .lineWidth = 1.0, | |
| }; | |
| gpu::BlendEquation blendEquation = pipelineState.blendEquation; | |
| bool colorWriteEnabled = pipelineState.colorWriteEnabled; | |
| if (interlockMode == gpu::InterlockMode::rasterOrdering || | |
| interlockMode == gpu::InterlockMode::atomics || | |
| (interlockMode == gpu::InterlockMode::clockwiseAtomic && | |
| !enums::is_flag_set(props.shaderMiscFlags, | |
| gpu::ShaderMiscFlags::borrowedCoveragePass))) | |
| { | |
| // The pipeline state gets generated under the assumption that pixel | |
| // local storage can still be written when colorWriteEnabled is false. | |
| // So when Vulkan implements PLS via color attachments, we need to | |
| // override the colorWriteEnabled state. | |
| colorWriteEnabled = true; | |
| } | |
| if (interlockMode == gpu::InterlockMode::atomics && | |
| !enums::is_flag_set(props.shaderMiscFlags, | |
| gpu::ShaderMiscFlags::coalescedResolveAndTransfer)) | |
| { | |
| // Vulkan deviates from the other renderers by enabling src-over | |
| // blending for PLS planes in atomic mode. | |
| // | |
| // Advanced blend modes are handled by rearranging the math such that | |
| // the correct color isn't reached until *AFTER* this blend state is | |
| // applied. This primarily benefits us by hinting to the hardware that | |
| // it doesn't need to read or write anything when a == 0, but it also | |
| // saves flops by offloading the blend work onto the ROP blending unit. | |
| // | |
| // Clip also has blend enabled, which allows us to preserve both clip | |
| // and color contents by just emitting a=0 (instead of loading and | |
| // re-emitting the current value) when a PLS plane needs to remain | |
| // unchanged during a fragment invocation. | |
| blendEquation = gpu::BlendEquation::srcOver; | |
| } | |
| #ifndef NDEBUG | |
| else if (enums::is_flag_set( | |
| props.shaderMiscFlags, | |
| gpu::ShaderMiscFlags::coalescedResolveAndTransfer)) | |
| { | |
| assert(interlockMode == gpu::InterlockMode::atomics); | |
| assert(blendEquation == gpu::BlendEquation::none); | |
| } | |
| #endif | |
| StackVector<VkPipelineColorBlendAttachmentState, PLS_PLANE_COUNT> | |
| blendStates; | |
| blendStates.push_back_n( | |
| pipelineLayout.colorAttachmentCount(subpassIndex, | |
| pipelineLayout.renderPassOptions()), | |
| { | |
| .blendEnable = blendEquation != gpu::BlendEquation::none, | |
| .srcColorBlendFactor = VK_BLEND_FACTOR_ONE, | |
| .dstColorBlendFactor = vk_dst_blend_factor(blendEquation), | |
| .colorBlendOp = vk_blend_op(blendEquation), | |
| .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, | |
| .dstAlphaBlendFactor = vk_dst_blend_factor(blendEquation), | |
| .alphaBlendOp = vk_blend_op(blendEquation), | |
| .colorWriteMask = colorWriteEnabled ? vkutil::kColorWriteMaskRGBA | |
| : vkutil::kColorWriteMaskNone, | |
| }); | |
| if (m_vk->features.independentBlend && | |
| interlockMode == gpu::InterlockMode::clockwiseAtomic) | |
| { | |
| // Since we support independentBlend, it will hopefully give us a perf | |
| // boost to disable color writes to clip/color when not being updated. | |
| // Otherwise, we will rely on the shader emitting no-op values for the | |
| // non-updated planes. | |
| if (enums::is_flag_set(props.shaderMiscFlags, | |
| gpu::ShaderMiscFlags::clipUpdateOnly)) | |
| { | |
| blendStates[COLOR_PLANE_IDX].colorWriteMask = | |
| vkutil::kColorWriteMaskNone; | |
| } | |
| else if (props.drawType != gpu::DrawType::renderPassInitialize) | |
| { | |
| assert(props.drawType != gpu::DrawType::clipReset); | |
| assert(!enums::is_flag_set( | |
| props.shaderMiscFlags, | |
| gpu::ShaderMiscFlags::nestedClipUpdateOnly)); | |
| blendStates[CLIP_PLANE_IDX].colorWriteMask = | |
| vkutil::kColorWriteMaskNone; | |
| } | |
| } | |
| VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo = { | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | |
| .attachmentCount = blendStates.size(), | |
| .pAttachments = blendStates.data(), | |
| }; | |
| if (interlockMode == gpu::InterlockMode::rasterOrdering && | |
| m_vk->features.rasterizationOrderColorAttachmentAccess) | |
| { | |
| pipelineColorBlendStateCreateInfo.flags |= | |
| VK_PIPELINE_COLOR_BLEND_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_BIT_EXT; | |
| } | |
| VkPipelineDepthStencilStateCreateInfo depthStencilState = { | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, | |
| .depthTestEnable = pipelineState.depthTestEnabled, | |
| .depthWriteEnable = pipelineState.depthWriteEnabled, | |
| .depthCompareOp = VK_COMPARE_OP_LESS, | |
| .depthBoundsTestEnable = VK_FALSE, | |
| .stencilTestEnable = pipelineState.stencilTestEnabled, | |
| .minDepthBounds = gpu::DEPTH_MIN, | |
| .maxDepthBounds = gpu::DEPTH_MAX, | |
| }; | |
| if (pipelineState.stencilTestEnabled) | |
| { | |
| depthStencilState.front = { | |
| .failOp = vk_stencil_op(pipelineState.stencilFrontOps.failOp), | |
| .passOp = vk_stencil_op(pipelineState.stencilFrontOps.passOp), | |
| .depthFailOp = | |
| vk_stencil_op(pipelineState.stencilFrontOps.depthFailOp), | |
| .compareOp = vk_compare_op(pipelineState.stencilFrontOps.compareOp), | |
| .compareMask = pipelineState.stencilCompareMask, | |
| .writeMask = pipelineState.stencilWriteMask, | |
| .reference = pipelineState.stencilReference, | |
| }; | |
| depthStencilState.back = | |
| !pipelineState.stencilDoubleSided | |
| ? depthStencilState.front | |
| : VkStencilOpState{ | |
| .failOp = | |
| vk_stencil_op(pipelineState.stencilBackOps.failOp), | |
| .passOp = | |
| vk_stencil_op(pipelineState.stencilBackOps.passOp), | |
| .depthFailOp = vk_stencil_op( | |
| pipelineState.stencilBackOps.depthFailOp), | |
| .compareOp = | |
| vk_compare_op(pipelineState.stencilBackOps.compareOp), | |
| .compareMask = pipelineState.stencilCompareMask, | |
| .writeMask = pipelineState.stencilWriteMask, | |
| .reference = pipelineState.stencilReference, | |
| }; | |
| } | |
| VkPipelineMultisampleStateCreateInfo msaaState = { | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, | |
| .rasterizationSamples = | |
| (interlockMode == gpu::InterlockMode::msaa && | |
| props.drawType != gpu::DrawType::renderPassResolve) | |
| ? VK_SAMPLE_COUNT_4_BIT | |
| : VK_SAMPLE_COUNT_1_BIT, | |
| }; | |
| VkGraphicsPipelineCreateInfo pipelineCreateInfo = { | |
| .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | |
| .stageCount = 2, | |
| .pStages = stages, | |
| .pViewportState = &layout::SINGLE_VIEWPORT, | |
| .pRasterizationState = &pipelineRasterizationStateCreateInfo, | |
| .pMultisampleState = &msaaState, | |
| .pDepthStencilState = interlockMode == gpu::InterlockMode::msaa | |
| ? &depthStencilState | |
| : nullptr, | |
| .pColorBlendState = &pipelineColorBlendStateCreateInfo, | |
| .pDynamicState = &layout::DYNAMIC_VIEWPORT_SCISSOR, | |
| .layout = *pipelineLayout, | |
| .renderPass = vkRenderPass, | |
| .subpass = subpassIndex, | |
| }; | |
| switch (props.drawType) | |
| { | |
| case DrawType::midpointFanPatches: | |
| case DrawType::midpointFanCenterAAPatches: | |
| case DrawType::outerCurvePatches: | |
| case DrawType::msaaOuterCubics: | |
| case DrawType::msaaStrokes: | |
| case DrawType::msaaMidpointFanBorrowedCoverage: | |
| case DrawType::msaaMidpointFans: | |
| case DrawType::msaaMidpointFanStencilReset: | |
| case DrawType::msaaMidpointFanPathsStencil: | |
| case DrawType::msaaMidpointFanPathsCover: | |
| pipelineCreateInfo.pVertexInputState = | |
| &layout::PATH_VERTEX_INPUT_STATE; | |
| pipelineCreateInfo.pInputAssemblyState = | |
| &layout::INPUT_ASSEMBLY_TRIANGLE_LIST; | |
| break; | |
| case DrawType::clipReset: | |
| case DrawType::interiorTriangulation: | |
| case DrawType::atlasBlit: | |
| pipelineCreateInfo.pVertexInputState = | |
| &layout::INTERIOR_TRI_VERTEX_INPUT_STATE; | |
| pipelineCreateInfo.pInputAssemblyState = | |
| &layout::INPUT_ASSEMBLY_TRIANGLE_LIST; | |
| break; | |
| case DrawType::imageRect: | |
| pipelineCreateInfo.pVertexInputState = | |
| &layout::IMAGE_RECT_VERTEX_INPUT_STATE; | |
| pipelineCreateInfo.pInputAssemblyState = | |
| &layout::INPUT_ASSEMBLY_TRIANGLE_LIST; | |
| break; | |
| case DrawType::imageMesh: | |
| pipelineCreateInfo.pVertexInputState = | |
| &layout::IMAGE_MESH_VERTEX_INPUT_STATE; | |
| pipelineCreateInfo.pInputAssemblyState = | |
| &layout::INPUT_ASSEMBLY_TRIANGLE_LIST; | |
| break; | |
| case DrawType::renderPassResolve: | |
| case DrawType::renderPassInitialize: | |
| pipelineCreateInfo.pVertexInputState = | |
| &layout::EMPTY_VERTEX_INPUT_STATE; | |
| pipelineCreateInfo.pInputAssemblyState = | |
| &layout::INPUT_ASSEMBLY_TRIANGLE_STRIP; | |
| break; | |
| } | |
| if (m_vk->CreateGraphicsPipelines(m_vk->device, | |
| VK_NULL_HANDLE, | |
| 1, | |
| &pipelineCreateInfo, | |
| nullptr, | |
| &m_vkPipeline) != VK_SUCCESS) | |
| { | |
| m_vkPipeline = VK_NULL_HANDLE; | |
| } | |
| } | |
| DrawPipelineVulkan::~DrawPipelineVulkan() | |
| { | |
| m_vk->DestroyPipeline(m_vk->device, m_vkPipeline, nullptr); | |
| } | |
| } // namespace rive::gpu |