#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h" #include "common/assert.h" #include "common/logging/log.h" #include "common/profiler.h" #include "graphics/guest_gpu/hardwareContext.h" #include "graphics/host_gpu/renderer/colorRenderTarget.h" #include "graphics/host_gpu/renderer/debug.h" #include "graphics/host_gpu/renderer/depthRenderTarget.h" #include "graphics/host_gpu/renderer/image/imageView.h" #include "graphics/host_gpu/renderer/render.h" #include "graphics/host_gpu/renderer/renderContext.h" #include #include #include #include namespace Libs::Graphics { namespace { void NormalizeStaticParamsForDynamicState(PipelineStaticParameters& static_params) { static_params.viewport_scale[0] = 0.5f; static_params.viewport_scale[1] = 0.5f; static_params.viewport_scale[2] = 1.0f; static_params.viewport_offset[0] = 0.5f; static_params.viewport_offset[1] = 0.5f; static_params.viewport_offset[2] = 0.0f; static_params.scissor_ltrb[0] = 0; static_params.scissor_ltrb[1] = 0; static_params.scissor_ltrb[2] = 1; static_params.scissor_ltrb[3] = 1; } } // namespace PipelineCache::~PipelineCache() { auto destroy = [this](const auto& pipelines) { for (const auto& [key, pipeline]: pipelines) { (void)key; m_graphics.device.destroyPipeline(pipeline->pipeline, nullptr); m_graphics.device.destroyPipelineLayout(pipeline->pipeline_layout, nullptr); } }; destroy(m_graphics_pipelines); destroy(m_compute_pipelines); } bool PipelineStaticParameters::operator==(const PipelineStaticParameters& other) const noexcept { return std::memcmp(this, &other, sizeof(*this)) == 0; } PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline( RenderColorInfo* colors, uint32_t color_count, RenderDepthInfo& depth, ShaderVertexInputInfo& vs_input_info, RenderCommandBuffer& command, ShaderPixelInputInfo* ps_input_info, vk::PrimitiveTopology topology, bool primitive_restart_enable, bool ps_active, std::span vs_spirv, std::span ps_spirv) { KYTY_PROFILER_BLOCK("PipelineCache::CreatePipeline(Gfx)", profiler::colors::DeepOrangeA200); EXIT_IF(colors == nullptr); EXIT_IF(color_count > RENDER_COLOR_ATTACHMENTS_MAX); EXIT_IF(vs_spirv.empty()); EXIT_IF(ps_active && ps_spirv.empty()); Common::LockGuard lock(m_mutex); auto& ctx = command.GetRegisters(); auto& sh_ctx = command.GetShaders(); const auto& vertex_info = sh_ctx.GetVs(); const auto& ps_regs = sh_ctx.GetPs(); const HW::BlendColor& bclr = ctx.GetBlendColor(); uint32_t color_mask[RENDER_COLOR_ATTACHMENTS_MAX] = {}; for (uint32_t i = 0; i < color_count; i++) { color_mask[i] = (colors[i].image_id ? colors[i].export_mapping.ApplyMask(render_target_mask_slot( ctx.GetRenderTargetMask(), colors[i].target_slot)) : 0); } const HW::ModeControl& mc = ctx.GetModeControl(); auto vs_id = ShaderGetIdVS(vertex_info, vs_input_info, true); ShaderId ps_id {}; if (ps_active) { ps_id = ShaderGetIdPS(ps_regs, *ps_input_info, true); } PipelineStaticParameters static_params {}; GraphicsPipeline p {}; p.ps_shader_id = ps_id; p.vs_shader_id = vs_id; static_params.color_count = color_count; PipelineRenderingState rendering {}; rendering.color_count = color_count; uint32_t attachment_samples = 0; for (uint32_t i = 0; i < color_count; i++) { EXIT_IF(!colors[i].image_id || colors[i].format == vk::Format::eUndefined); rendering.color_formats[i] = colors[i].format; if (attachment_samples == 0) { attachment_samples = colors[i].samples; } else if (attachment_samples != colors[i].samples) { EXIT("mixed color attachment sample counts are unsupported: %u and %u\n", attachment_samples, colors[i].samples); } } const bool with_depth = depth.format != vk::Format::eUndefined && static_cast(depth.image_id); if (with_depth) { const auto aspects = ImageViewOps::DepthAspectMask(depth.format); rendering.depth_format = aspects & vk::ImageAspectFlagBits::eDepth ? depth.format : vk::Format::eUndefined; rendering.stencil_format = aspects & vk::ImageAspectFlagBits::eStencil ? depth.format : vk::Format::eUndefined; if (attachment_samples == 0) { attachment_samples = depth.samples; } else if (attachment_samples != depth.samples) { EXIT("mixed color/depth sample counts are unsupported: %u and %u\n", attachment_samples, depth.samples); } } if (color_count == 0 && !with_depth) { attachment_samples = render_sample_count(ctx.GetAaConfig().msaa_num_samples); EXIT_IF(!static_cast( m_graphics.GetPhysicalDeviceProperties().limits.framebufferNoAttachmentsSampleCounts & vulkan_sample_count(attachment_samples))); } EXIT_IF(attachment_samples == 0 || vulkan_sample_count(attachment_samples) == vk::SampleCountFlagBits {}); if (ps_active && depth.depth_test_enable && ps_input_info->ps_execute_on_noop) { static std::atomic log_count {0}; if (log_count.fetch_add(1, std::memory_order_relaxed) < 16) { LOGF("Pipeline: temporary: accepting EXEC_ON_NOOP with depth test enabled\n"); } } const auto& clip_control = ctx.GetClipControl(); static_params.negative_one_to_one = !clip_control.dx_clip_space; static_params.depth_clip_enable = clip_control.IsZClipEnabled(); static_params.topology = topology; static_params.primitive_restart_enable = primitive_restart_enable; static_params.samples = attachment_samples; static_params.sample_shading_enable = ps_active && attachment_samples > 1 && ps_input_info->ps_sample_shading; if (static_params.sample_shading_enable && !m_graphics.sample_rate_shading_enabled) { EXIT("Pipeline: sample-rate shading is required but unsupported by the host\n"); } static_params.with_depth = with_depth; static_params.depth_test_enable = depth.depth_test_enable; static_params.depth_write_enable = (depth.depth_write_enable && !depth.depth_clear_enable); static_params.depth_compare_op = depth.depth_compare_op; static_params.depth_bounds_test_enable = depth.depth_bounds_test_enable; static_params.depth_min_bounds = depth.depth_min_bounds; static_params.depth_max_bounds = depth.depth_max_bounds; static_params.stencil_test_enable = depth.stencil_test_enable; static_params.stencil_front = depth.stencil_static_front; static_params.stencil_back = depth.stencil_static_back; for (uint32_t i = 0; i < RENDER_COLOR_ATTACHMENTS_MAX; i++) { static_params.color_mask[i] = color_mask[i]; } const bool rect_list = topology == vk::PrimitiveTopology::ePatchList; static_params.cull_back = !rect_list && mc.cull_back; static_params.cull_front = !rect_list && mc.cull_front; static_params.face = mc.face; for (uint32_t i = 0; i < color_count; i++) { const auto& rt = ctx.GetRenderTarget(colors[i].target_slot); const auto& bc = ctx.GetBlendControl(colors[i].target_slot); static_params.color_srcblend[i] = bc.color_srcblend; static_params.color_comb_fcn[i] = bc.color_comb_fcn; static_params.color_destblend[i] = bc.color_destblend; static_params.alpha_srcblend[i] = bc.alpha_srcblend; static_params.alpha_comb_fcn[i] = bc.alpha_comb_fcn; static_params.alpha_destblend[i] = bc.alpha_destblend; static_params.separate_alpha_blend[i] = bc.separate_alpha_blend; static_params.blend_enable[i] = bc.enable; static_params.blend_bypass[i] = rt.info.blend_bypass; } static_params.blend_color_red = bclr.red; static_params.blend_color_green = bclr.green; static_params.blend_color_blue = bclr.blue; static_params.blend_color_alpha = bclr.alpha; NormalizeStaticParamsForDynamicState(static_params); GraphicsPipelineKey key {}; key.rendering = rendering; key.vs_shader_id = p.vs_shader_id; key.ps_shader_id = p.ps_shader_id; key.static_params = static_params; if (auto iter = m_graphics_pipelines.find(key); iter != m_graphics_pipelines.end()) { return *iter->second; } if (graphics_debug_dump_enabled()) { ShaderDbgDumpInputInfo(vs_input_info); if (ps_active) { ShaderDbgDumpInputInfo(*ps_input_info); } LOGF("PipelineTrace: shader binaries VS=0x%08" PRIx32 "/0x%08" PRIx32 " words=%" PRIu64 " PS=0x%08" PRIx32 "/0x%08" PRIx32 " words=%" PRIu64 "\n", vs_id.hash0, vs_id.crc32, static_cast(vs_spirv.size()), ps_id.hash0, ps_id.crc32, static_cast(ps_spirv.size())); } auto cached = std::make_unique(p); LogPipelineTrace("CreatePipelineInternal begin", vs_id.hash0, vs_id.crc32, ps_id.hash0, ps_id.crc32); CreatePipelineInternal(m_graphics, m_descriptor_cache, *cached, rendering, vs_input_info, vs_spirv, ps_input_info, ps_spirv, static_params, vs_id.hash0, vs_id.crc32, ps_id.hash0, ps_id.crc32, ps_active); LogPipelineTrace("CreatePipelineInternal done", vs_id.hash0, vs_id.crc32, ps_id.hash0, ps_id.crc32); EXIT_NOT_IMPLEMENTED(cached->pipeline == nullptr); EXIT_NOT_IMPLEMENTED(cached->pipeline_layout == nullptr); auto [iter, inserted] = m_graphics_pipelines.emplace(std::move(key), std::move(cached)); EXIT_IF(!inserted); return *iter->second; } PipelineCache::ComputePipeline& PipelineCache::CreateComputePipeline(ShaderComputeInputInfo& input_info, const HW::ComputeShaderInfo& cs_regs, std::span cs_spirv) { KYTY_PROFILER_BLOCK("PipelineCache::CreatePipeline(Compute)", profiler::colors::RedA100); EXIT_IF(cs_spirv.empty()); Common::LockGuard lock(m_mutex); auto cs_id = ShaderGetIdCS(cs_regs, input_info, true); ComputePipeline p {}; p.cs_shader_id = cs_id; ComputePipelineKey key {}; key.cs_shader_id = p.cs_shader_id; if (auto iter = m_compute_pipelines.find(key); iter != m_compute_pipelines.end()) { return *iter->second; } if (graphics_debug_dump_enabled()) { ShaderDbgDumpInputInfo(input_info); } auto cached = std::make_unique(p); CreatePipelineInternal(m_graphics, m_descriptor_cache, *cached, input_info, cs_spirv); EXIT_NOT_IMPLEMENTED(cached->pipeline == nullptr); EXIT_NOT_IMPLEMENTED(cached->pipeline_layout == nullptr); auto [iter, inserted] = m_compute_pipelines.emplace(std::move(key), std::move(cached)); EXIT_IF(!inserted); return *iter->second; } } // namespace Libs::Graphics