#include "common/assert.h" #include "common/common.h" #include "common/emulatorConfig.h" #include "common/logging/log.h" #include "common/profiler.h" #include "graphics/guest_gpu/gpu_defs.h" #include "graphics/host_gpu/graphicContext.h" #include "graphics/host_gpu/renderer/debug.h" #include "graphics/host_gpu/renderer/pipeline/descriptors.h" #include "graphics/host_gpu/renderer/pipeline/pipelineCache.h" #include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h" #include "graphics/host_gpu/renderer/render.h" #include "graphics/host_gpu/renderer/renderContext.h" #include "graphics/host_gpu/renderer/renderTarget.h" #include "graphics/host_gpu/vulkanCommon.h" #include "graphics/shader/recompiler/BufferFormat.h" #include "graphics/shader/recompiler/ir/ShaderIR.h" #include "graphics/shader/rectListShader.h" #include "graphics/shader/shader.h" #include #include #include #include namespace Libs::Graphics { // IDK: maybe we can remove it? constexpr uint8_t kTemporaryVertexAttribFormat113 = static_cast(Prospero::VertexAttribFormat::k16_16SInt); constexpr uint32_t kTemporaryPs5BufferFormat121 = 121u; static bool NarrowInputFormat(vk::Format& format, uint32_t& size, uint32_t used_components) { if (used_components == 0 || used_components >= size) { return false; } switch (format) { case vk::Format::eR32G32B32A32Sfloat: switch (used_components) { case 1: format = vk::Format::eR32Sfloat; break; case 2: format = vk::Format::eR32G32Sfloat; break; case 3: format = vk::Format::eR32G32B32Sfloat; break; default: return false; } size = used_components; return true; case vk::Format::eR32G32B32Sfloat: switch (used_components) { case 1: format = vk::Format::eR32Sfloat; break; case 2: format = vk::Format::eR32G32Sfloat; break; default: return false; } size = used_components; return true; case vk::Format::eR16G16B16A16Sfloat: switch (used_components) { case 1: format = vk::Format::eR16Sfloat; break; case 2: format = vk::Format::eR16G16Sfloat; break; default: return false; } size = used_components; return true; case vk::Format::eR8G8B8A8Unorm: switch (used_components) { case 1: format = vk::Format::eR8Unorm; break; case 2: format = vk::Format::eR8G8Unorm; break; default: return false; } size = used_components; return true; case vk::Format::eR8G8B8A8Snorm: if (used_components != 2) { return false; } format = vk::Format::eR8G8Snorm; size = 2; return true; case vk::Format::eR8G8B8A8Uint: switch (used_components) { case 1: format = vk::Format::eR8Uint; break; case 2: format = vk::Format::eR8G8Uint; break; default: return false; } size = used_components; return true; default: break; } return false; } static void GetInputFormat(const ShaderBufferResource& res, vk::Format& format, uint32_t& size, uint32_t used_components) { const auto fmt = res.Format(); const auto raw_format = res.RawFormat(); if (raw_format == kTemporaryVertexAttribFormat113) { static bool logged_113 = false; if (!logged_113) { LOGF("InputFormat: temporary: accepting invalid PS5 buffer format 113 as " "vk::Format::eR32G32B32A32Sfloat\n"); logged_113 = true; } format = vk::Format::eR32G32B32A32Sfloat; size = 4; if (NarrowInputFormat(format, size, used_components)) { LOGF("InputFormat: narrowing fmt=%u to %s for used_components=%u\n", raw_format, vk::to_string(format).c_str(), used_components); } return; } if (raw_format == kTemporaryPs5BufferFormat121) { static bool logged_121 = false; if (!logged_121) { LOGF("InputFormat: accepting PS5 buffer format 121 as vk::Format::eR16G16Sfloat\n"); logged_121 = true; } format = vk::Format::eR16G16Sfloat; size = 2; return; } format = VulkanFormat(fmt); size = ShaderRecompiler::Format::GetFormatInfo(fmt).component_count; if (format == vk::Format::eUndefined || size == 0) { EXIT("unknown vertex format: fmt = %u\n", raw_format); } if (NarrowInputFormat(format, size, used_components)) { static std::atomic log_count = 0; auto log_id = log_count.fetch_add(1); if (log_id < 32) { LOGF("VertexInput: narrowed vertex format to %" PRIu32 " component(s) for shader fetch\n", used_components); } } } static vk::BlendFactor GetBlendFactor(uint32_t factor, bool remap_source_alpha) { switch (static_cast(factor)) { case Prospero::BlendFactor::kZero: return vk::BlendFactor::eZero; case Prospero::BlendFactor::kOne: return vk::BlendFactor::eOne; case Prospero::BlendFactor::kSrcColor: return vk::BlendFactor::eSrcColor; case Prospero::BlendFactor::kOneMinusSrcColor: return vk::BlendFactor::eOneMinusSrcColor; case Prospero::BlendFactor::kSrcAlpha: return remap_source_alpha ? vk::BlendFactor::eSrc1Color : vk::BlendFactor::eSrcAlpha; case Prospero::BlendFactor::kOneMinusSrcAlpha: return remap_source_alpha ? vk::BlendFactor::eOneMinusSrc1Color : vk::BlendFactor::eOneMinusSrcAlpha; case Prospero::BlendFactor::kDstAlpha: return vk::BlendFactor::eDstAlpha; case Prospero::BlendFactor::kOneMinusDstAlpha: return vk::BlendFactor::eOneMinusDstAlpha; case Prospero::BlendFactor::kDstColor: return vk::BlendFactor::eDstColor; case Prospero::BlendFactor::kOneMinusDstColor: return vk::BlendFactor::eOneMinusDstColor; case Prospero::BlendFactor::kSrcAlphaSaturate: return vk::BlendFactor::eSrcAlphaSaturate; case Prospero::BlendFactor::kConstantColor: return vk::BlendFactor::eConstantColor; case Prospero::BlendFactor::kOneMinusConstantColor: return vk::BlendFactor::eOneMinusConstantColor; case Prospero::BlendFactor::kSrc1Color: return vk::BlendFactor::eSrc1Color; case Prospero::BlendFactor::kOneMinusSrc1Color: return vk::BlendFactor::eOneMinusSrc1Color; case Prospero::BlendFactor::kSrc1Alpha: return vk::BlendFactor::eSrc1Alpha; case Prospero::BlendFactor::kOneMinusSrc1Alpha: return vk::BlendFactor::eOneMinusSrc1Alpha; case Prospero::BlendFactor::kConstantAlpha: return vk::BlendFactor::eConstantAlpha; case Prospero::BlendFactor::kOneMinusConstantAlpha: return vk::BlendFactor::eOneMinusConstantAlpha; default: EXIT("unknown factor: %u\n", factor); } return vk::BlendFactor::eZero; } static vk::BlendOp GetBlendOp(uint32_t op) { switch (static_cast(op)) { case Prospero::BlendOp::kAdd: return vk::BlendOp::eAdd; case Prospero::BlendOp::kSubtract: return vk::BlendOp::eSubtract; case Prospero::BlendOp::kMin: return vk::BlendOp::eMin; case Prospero::BlendOp::kMax: return vk::BlendOp::eMax; case Prospero::BlendOp::kReverseSubtract: return vk::BlendOp::eReverseSubtract; default: EXIT("unknown op: %u\n", op); } return vk::BlendOp::eAdd; } static void AddLayoutBindings(std::vector& descriptor_bindings, const ShaderRecompiler::IR::CompiledShaderInfo& program, vk::ShaderStageFlagBits stage) { for (const auto& binding: program.bindings.descriptors) { descriptor_bindings.push_back( {ShaderRecompiler::IR::NativeBinding(program.stage, binding.kind), NativeDescriptorType(binding.kind), NativeDescriptorCount(binding), stage, nullptr}); } } static void CreateDescriptorLayout(GraphicContext& graphics, PipelineCache::Pipeline& pipeline, std::span bindings) { uint32_t descriptor_count = 0; for (const auto& binding: bindings) { descriptor_count += binding.descriptorCount; } pipeline.uses_push_descriptors = descriptor_count <= graphics.max_push_descriptors; vk::DescriptorSetLayoutCreateInfo create {}; create.flags = pipeline.uses_push_descriptors ? vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR : vk::DescriptorSetLayoutCreateFlags {}; create.bindingCount = static_cast(bindings.size()); create.pBindings = bindings.data(); EXIT_IF(graphics.device.createDescriptorSetLayout( &create, nullptr, &pipeline.descriptor_set_layout) != vk::Result::eSuccess); } // NOLINTNEXTLINE(readability-function-cognitive-complexity) void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& pipeline, const PipelineRenderingState& rendering, const PipelineVertexInputState& vertex_input, std::span vertex_info, const ShaderPixelInputInfo* ps_input_info, const PipelineCache::GraphicsPrograms& programs, const PipelineStaticParameters& static_params, vk::PipelineCache driver_cache) { const auto& vs_input_info = vertex_info.front(); const auto& vertex_program = programs.vertex[0]; const auto& pixel_program = programs.pixel; const bool tessellation = vertex_info.size() == 3; const bool ps_active = ps_input_info != nullptr; EXIT_IF(!vertex_program || (ps_active && !pixel_program)); const bool with_depth = rendering.depth_format != vk::Format::eUndefined || rendering.stencil_format != vk::Format::eUndefined; EXIT_IF(!vs_input_info.stage); const bool mesh = vs_input_info.stage.program->stage == ShaderType::Mesh; EXIT_NOT_IMPLEMENTED(mesh && !graphics.mesh_shader_enabled); const bool rect_list = !mesh && !tessellation && static_params.topology == vk::PrimitiveTopology::ePatchList; vk::ShaderModule tess_control_shader_module = nullptr; vk::ShaderModule tess_eval_shader_module = nullptr; if (rect_list) { const auto shaders = BuildRectListShaders(vs_input_info, ps_active ? ps_input_info : nullptr); tess_control_shader_module = CompileSPV(shaders.control, graphics.device); if (graphics_debug_dump_enabled()) { LOGF("PipelineTrace: vkCreateShaderModule RectList TCS done module=%p\n", static_cast(tess_control_shader_module)); } tess_eval_shader_module = CompileSPV(shaders.evaluation, graphics.device); if (graphics_debug_dump_enabled()) { LOGF("PipelineTrace: vkCreateShaderModule RectList TES done module=%p\n", static_cast(tess_eval_shader_module)); } } EXIT_NOT_IMPLEMENTED( rect_list && (tess_control_shader_module == nullptr || tess_eval_shader_module == nullptr)); vk::PipelineShaderStageCreateInfo shader_stages[4] {}; uint32_t shader_stage_count = 0; for (uint32_t i = 0; i < vertex_info.size(); i++) { shader_stages[shader_stage_count++] = {.stage = NativeShaderStage(vertex_info[i].logical_stage), .module = programs.vertex[i].module, .pName = "main"}; } if (rect_list) { shader_stages[shader_stage_count++] = {.stage = vk::ShaderStageFlagBits::eTessellationControl, .module = tess_control_shader_module, .pName = "main"}; shader_stages[shader_stage_count++] = {.stage = vk::ShaderStageFlagBits::eTessellationEvaluation, .module = tess_eval_shader_module, .pName = "main"}; } if (ps_active) { shader_stages[shader_stage_count++] = {.stage = vk::ShaderStageFlagBits::eFragment, .module = pixel_program.module, .pName = "main"}; } vk::VertexInputAttributeDescription input_attr[ShaderVertexInputInfo::RES_MAX] {}; vk::VertexInputBindingDescription input_desc[ShaderVertexInputInfo::RES_MAX] {}; for (uint32_t binding = 0; binding < vertex_input.binding_count; binding++) { input_desc[binding].binding = binding; input_desc[binding].stride = vertex_input.bindings[binding].stride; input_desc[binding].inputRate = vertex_input.bindings[binding].instance ? vk::VertexInputRate::eInstance : vk::VertexInputRate::eVertex; } for (uint32_t index = 0; index < vertex_input.attribute_count; index++) { input_attr[index].binding = vertex_input.attributes[index].binding; input_attr[index].location = index; input_attr[index].offset = vertex_input.attributes[index].offset; uint32_t attr_size = 4; const auto registers_num = vs_input_info.resources_dst[index].registers_num; const auto compiled_components = vs_input_info.stage.program->info.vertex_fetch_components[index]; const auto used_components = compiled_components > 0 ? static_cast(compiled_components) : registers_num; GetInputFormat(vs_input_info.resources[index], input_attr[index].format, attr_size, static_cast(used_components)); if (graphics_debug_dump_enabled()) { static std::atomic_uint log_count = 0; const auto log_id = log_count.fetch_add(1, std::memory_order_relaxed); if (log_id < 128) { LOGF("VertexInputState[%u]: attr=%u binding=%u offset=%u stride=%u fmt=%d " "src_fmt=%u dst=v%u regs=%u" " fetched_components=%u attr_size=%u swizzle=%u,%u,%u,%u\n", log_id, index, input_attr[index].binding, input_attr[index].offset, input_desc[input_attr[index].binding].stride, static_cast(input_attr[index].format), static_cast(vs_input_info.resources[index].Format()), static_cast(vs_input_info.resources_dst[index].register_start), static_cast(registers_num), static_cast(used_components), attr_size, static_cast(vs_input_info.resources[index].DstSelX()), static_cast(vs_input_info.resources[index].DstSelY()), static_cast(vs_input_info.resources[index].DstSelZ()), static_cast(vs_input_info.resources[index].DstSelW())); } } if (vs_input_info.resources[index].OutOfBounds() != 0) { static bool logged = false; if (!logged) { LOGF("VertexInput: temporary: accepting PS5 out-of-bounds behavior %" PRIu8 "\n", vs_input_info.resources[index].OutOfBounds()); logged = true; } } EXIT_NOT_IMPLEMENTED(vs_input_info.resources[index].AddTid()); EXIT_NOT_IMPLEMENTED(vs_input_info.resources[index].SwizzleEnabled()); EXIT_IF(registers_num < 1 || registers_num > 4); } vk::PipelineVertexInputStateCreateInfo vertex_input_info {}; vertex_input_info.vertexBindingDescriptionCount = vertex_input.binding_count; vertex_input_info.pVertexBindingDescriptions = input_desc; vertex_input_info.vertexAttributeDescriptionCount = vertex_input.attribute_count; vertex_input_info.pVertexAttributeDescriptions = input_attr; vk::PipelineInputAssemblyStateCreateInfo input_assembly {}; input_assembly.topology = static_params.topology; input_assembly.primitiveRestartEnable = static_params.primitive_restart_enable ? VK_TRUE : VK_FALSE; vk::PipelineViewportDepthClipControlCreateInfoEXT depth_clip_control {}; depth_clip_control.negativeOneToOne = (static_params.negative_one_to_one ? VK_TRUE : VK_FALSE); vk::PipelineViewportStateCreateInfo viewport_state {}; viewport_state.pNext = &depth_clip_control; vk::CullModeFlags cull_mode = vk::CullModeFlagBits::eNone; if (static_params.cull_back) { cull_mode |= vk::CullModeFlagBits::eBack; } if (static_params.cull_front) { cull_mode |= vk::CullModeFlagBits::eFront; } vk::FrontFace front_face = (static_params.face ? vk::FrontFace::eClockwise : vk::FrontFace::eCounterClockwise); vk::PipelineRasterizationDepthClipStateCreateInfoEXT clip_ext {}; clip_ext.depthClipEnable = static_params.depth_clip_enable ? VK_TRUE : VK_FALSE; vk::PipelineRasterizationStateCreateInfo rasterizer {}; // MoltenVK lacks VK_EXT_depth_clip_enable; omit the depth-clip struct on macOS and accept // Vulkan's default depth clipping (enabled) instead of the PS5's clamp behavior. #if !defined(__APPLE__) // The DB clamps depth to the viewport range after polygon offset is applied. rasterizer.depthClampEnable = VK_TRUE; rasterizer.pNext = &clip_ext; #endif vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex {}; EXIT_NOT_IMPLEMENTED(static_params.provoking_vtx_last && !graphics.provoking_vertex_last_enabled); if (graphics.provoking_vertex_last_enabled) { provoking_vertex.provokingVertexMode = static_params.provoking_vtx_last ? vk::ProvokingVertexModeEXT::eLastVertex : vk::ProvokingVertexModeEXT::eFirstVertex; provoking_vertex.pNext = rasterizer.pNext; rasterizer.pNext = &provoking_vertex; } rasterizer.cullMode = cull_mode; rasterizer.frontFace = front_face; rasterizer.polygonMode = static_params.polygon_mode; rasterizer.lineWidth = 1.0f; vk::PipelineMultisampleStateCreateInfo multisampling {}; multisampling.sampleShadingEnable = static_params.sample_shading_enable ? VK_TRUE : VK_FALSE; multisampling.rasterizationSamples = vulkan_sample_count(static_params.samples); multisampling.minSampleShading = 1.0f; vk::PipelineColorBlendAttachmentState color_blend_attachment[RENDER_COLOR_ATTACHMENTS_MAX] = {}; for (uint32_t i = 0; i < rendering.color_count; i++) { EXIT_NOT_IMPLEMENTED((static_params.color_mask[i] & ~0x0fu) != 0); color_blend_attachment[i].colorWriteMask = vk::ColorComponentFlags {static_params.color_mask[i]}; color_blend_attachment[i].blendEnable = static_params.blend_enable[i] ? VK_TRUE : VK_FALSE; // Only target 0 can use the second blend source carrying logical alpha. const bool remap_source_alpha = i == 0 && static_params.blend_alpha_source_remap; color_blend_attachment[i].srcColorBlendFactor = GetBlendFactor(static_params.color_srcblend[i], remap_source_alpha); color_blend_attachment[i].dstColorBlendFactor = GetBlendFactor(static_params.color_destblend[i], remap_source_alpha); color_blend_attachment[i].colorBlendOp = GetBlendOp(static_params.color_comb_fcn[i]); color_blend_attachment[i].srcAlphaBlendFactor = (static_params.separate_alpha_blend[i] ? GetBlendFactor(static_params.alpha_srcblend[i], remap_source_alpha) : color_blend_attachment[i].srcColorBlendFactor); color_blend_attachment[i].dstAlphaBlendFactor = (static_params.separate_alpha_blend[i] ? GetBlendFactor(static_params.alpha_destblend[i], remap_source_alpha) : color_blend_attachment[i].dstColorBlendFactor); color_blend_attachment[i].alphaBlendOp = (static_params.separate_alpha_blend[i] ? GetBlendOp(static_params.alpha_comb_fcn[i]) : color_blend_attachment[i].colorBlendOp); } vk::Bool32 color_write_enable[RENDER_COLOR_ATTACHMENTS_MAX] = {}; for (uint32_t i = 0; i < rendering.color_count; i++) { color_write_enable[i] = VK_TRUE; } vk::PipelineColorWriteCreateInfoEXT color_write {}; color_write.attachmentCount = rendering.color_count; color_write.pColorWriteEnables = color_write_enable; vk::PipelineColorBlendStateCreateInfo color_blending {}; // MoltenVK lacks VK_EXT_color_write_enable; drop the dynamic color-write struct on macOS // and rely on each attachment's static colorWriteMask (all channels enabled by default). #if !defined(__APPLE__) color_blending.pNext = &color_write; #endif color_blending.logicOp = vk::LogicOp::eCopy; color_blending.attachmentCount = rendering.color_count; color_blending.pAttachments = color_blend_attachment; std::vector descriptor_bindings; vk::ShaderStageFlags graphics_stages = vk::ShaderStageFlagBits::eFragment; for (const auto& stage: vertex_info) { const auto native_stage = NativeShaderStage(stage.logical_stage); AddLayoutBindings(descriptor_bindings, *stage.stage.program, native_stage); graphics_stages |= native_stage; } if (ps_active) { EXIT_IF(!ps_input_info->stage); AddLayoutBindings(descriptor_bindings, *ps_input_info->stage.program, vk::ShaderStageFlagBits::eFragment); } CreateDescriptorLayout(graphics, pipeline, descriptor_bindings); const vk::PushConstantRange push_constants {graphics_stages, 0, ShaderRecompiler::IR::NativePushConstantSize}; vk::PipelineLayoutCreateInfo pipeline_layout_info {}; pipeline_layout_info.setLayoutCount = 1; pipeline_layout_info.pSetLayouts = &pipeline.descriptor_set_layout; pipeline_layout_info.pushConstantRangeCount = 1; pipeline_layout_info.pPushConstantRanges = &push_constants; EXIT_IF(pipeline.pipeline_layout != nullptr); if (graphics_debug_dump_enabled()) { LOGF("PipelineTrace: vkCreatePipelineLayout begin VS=%" PRIu64 " PS=%" PRIu64 " set_layouts=1 push_constants=%" PRIu32 "\n", vertex_program.id, ps_active ? pixel_program.id : 0, 1u); } auto result = graphics.device.createPipelineLayout(&pipeline_layout_info, nullptr, &pipeline.pipeline_layout); if (graphics_debug_dump_enabled()) { LOGF("PipelineTrace: vkCreatePipelineLayout done result=%s layout=%p\n", vk::to_string(result).c_str(), static_cast(pipeline.pipeline_layout)); } EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess); EXIT_NOT_IMPLEMENTED(pipeline.pipeline_layout == nullptr); vk::PipelineDepthStencilStateCreateInfo depth_stencil_info {}; depth_stencil_info.depthBoundsTestEnable = #if defined(__APPLE__) VK_FALSE; // MoltenVK lacks the depthBounds feature; depth-bounds testing is disabled #else (static_params.depth_bounds_test_enable ? VK_TRUE : VK_FALSE); #endif depth_stencil_info.minDepthBounds = static_params.depth_min_bounds; depth_stencil_info.maxDepthBounds = static_params.depth_max_bounds; std::vector dynamic_states { vk::DynamicState::eViewportWithCount, vk::DynamicState::eScissorWithCount, vk::DynamicState::eLineWidth, vk::DynamicState::eDepthTestEnable, vk::DynamicState::eDepthWriteEnable, vk::DynamicState::eDepthCompareOp, vk::DynamicState::eDepthBiasEnable, vk::DynamicState::eDepthBias, vk::DynamicState::eStencilTestEnable, vk::DynamicState::eStencilOp, vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilReference, vk::DynamicState::eStencilWriteMask, vk::DynamicState::eBlendConstants, }; #if !defined(__APPLE__) if (rendering.color_count != 0) { dynamic_states.push_back(vk::DynamicState::eColorWriteEnableEXT); } #endif if (graphics.attachment_feedback_loop_enabled) { dynamic_states.push_back(vk::DynamicState::eAttachmentFeedbackLoopEnableEXT); } vk::PipelineDynamicStateCreateInfo dynamic_state {}; dynamic_state.dynamicStateCount = static_cast(dynamic_states.size()); dynamic_state.pDynamicStates = dynamic_states.data(); vk::GraphicsPipelineCreateInfo pipeline_info {}; vk::PipelineRenderingCreateInfo rendering_info {}; rendering_info.colorAttachmentCount = rendering.color_count; rendering_info.pColorAttachmentFormats = rendering.color_formats.data(); rendering_info.depthAttachmentFormat = rendering.depth_format; rendering_info.stencilAttachmentFormat = rendering.stencil_format; pipeline_info.pNext = &rendering_info; pipeline_info.stageCount = shader_stage_count; pipeline_info.pStages = shader_stages; pipeline_info.pVertexInputState = mesh ? nullptr : &vertex_input_info; pipeline_info.pInputAssemblyState = mesh ? nullptr : &input_assembly; vk::PipelineTessellationStateCreateInfo tessellation_state {}; tessellation_state.patchControlPoints = tessellation ? vs_input_info.tess.input_control_points : 3u; pipeline_info.pTessellationState = (rect_list || tessellation) ? &tessellation_state : nullptr; pipeline_info.pViewportState = &viewport_state; pipeline_info.pRasterizationState = &rasterizer; pipeline_info.pMultisampleState = &multisampling; pipeline_info.pDepthStencilState = (with_depth ? &depth_stencil_info : nullptr); pipeline_info.pColorBlendState = &color_blending; pipeline_info.pDynamicState = &dynamic_state; pipeline_info.layout = pipeline.pipeline_layout; pipeline_info.basePipelineIndex = -1; EXIT_IF(pipeline.pipeline != nullptr); if (graphics_debug_dump_enabled()) { LOGF("PipelineTrace: vkCreateGraphicsPipelines begin VS=%" PRIu64 " PS=%" PRIu64 " topology=%" PRIu32 " color_mask=0x%08" PRIx32 " depth=%s blend=%s dyn_states=%" PRIu32 "\n", vertex_program.id, ps_active ? pixel_program.id : 0, static_cast(static_params.topology), static_params.color_mask[0], (with_depth ? "true" : "false"), (static_params.blend_enable[0] ? "true" : "false"), dynamic_state.dynamicStateCount); } result = graphics.device.createGraphicsPipelines(driver_cache, 1, &pipeline_info, nullptr, &pipeline.pipeline); if (graphics_debug_dump_enabled()) { LOGF("PipelineTrace: vkCreateGraphicsPipelines done result=%s pipeline=%p\n", vk::to_string(result).c_str(), static_cast(pipeline.pipeline)); } EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess); EXIT_NOT_IMPLEMENTED(pipeline.pipeline == nullptr); if (tess_control_shader_module != nullptr) { graphics.device.destroyShaderModule(tess_control_shader_module, nullptr); } if (tess_eval_shader_module != nullptr) { graphics.device.destroyShaderModule(tess_eval_shader_module, nullptr); } } // NOLINTNEXTLINE(readability-function-cognitive-complexity) void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& pipeline, const ShaderComputeInputInfo& input_info, vk::ShaderModule compute_module, vk::PipelineCache driver_cache) { EXIT_IF(compute_module == nullptr); vk::PipelineShaderStageCreateInfo comp_shader_stage_info {}; vk::PipelineShaderStageRequiredSubgroupSizeCreateInfo comp_subgroup_size {}; comp_shader_stage_info.stage = vk::ShaderStageFlagBits::eCompute; comp_shader_stage_info.module = compute_module; comp_shader_stage_info.pName = "main"; EXIT_IF(!input_info.stage); const auto wave_size = input_info.stage.program->wave_size; if (graphics.compute_subgroup_size_control_enabled && wave_size >= graphics.min_subgroup_size && wave_size <= graphics.max_subgroup_size) { comp_subgroup_size.requiredSubgroupSize = wave_size; comp_shader_stage_info.pNext = &comp_subgroup_size; } std::vector descriptor_bindings; AddLayoutBindings(descriptor_bindings, *input_info.stage.program, vk::ShaderStageFlagBits::eCompute); CreateDescriptorLayout(graphics, pipeline, descriptor_bindings); const vk::PushConstantRange push_constants {vk::ShaderStageFlagBits::eCompute, 0, ShaderRecompiler::IR::NativePushConstantSize}; vk::PipelineLayoutCreateInfo pipeline_layout_info {}; pipeline_layout_info.setLayoutCount = 1; pipeline_layout_info.pSetLayouts = &pipeline.descriptor_set_layout; pipeline_layout_info.pushConstantRangeCount = 1; pipeline_layout_info.pPushConstantRanges = &push_constants; EXIT_IF(pipeline.pipeline_layout != nullptr); LOGF("PipelineTrace: vkCreatePipelineLayout CS begin set_layouts=1 push_constants=%u\n", 1u); auto result = graphics.device.createPipelineLayout(&pipeline_layout_info, nullptr, &pipeline.pipeline_layout); LOGF("PipelineTrace: vkCreatePipelineLayout CS done result=%s layout=%p\n", vk::to_string(result).c_str(), static_cast(pipeline.pipeline_layout)); EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess); EXIT_NOT_IMPLEMENTED(pipeline.pipeline_layout == nullptr); vk::ComputePipelineCreateInfo info {}; info.stage = comp_shader_stage_info; info.layout = pipeline.pipeline_layout; info.basePipelineIndex = -1; EXIT_IF(pipeline.pipeline != nullptr); LOGF("PipelineTrace: vkCreateComputePipelines begin layout=%p\n", static_cast(pipeline.pipeline_layout)); result = graphics.device.createComputePipelines(driver_cache, 1, &info, nullptr, &pipeline.pipeline); LOGF("PipelineTrace: vkCreateComputePipelines done result=%s pipeline=%p\n", vk::to_string(result).c_str(), static_cast(pipeline.pipeline)); EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess); EXIT_NOT_IMPLEMENTED(pipeline.pipeline == nullptr); } } // namespace Libs::Graphics