diff --git a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp index 65de72cd0..bff05ce79 100644 --- a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp @@ -9,10 +9,9 @@ #include "common/threads.h" #include "graphics/guest_gpu/gpu_format.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/image/textureCommon.h" #include "graphics/host_gpu/renderer/pipeline/blendMapping.h" #include "graphics/host_gpu/renderer/render.h" #include "graphics/host_gpu/renderer/renderContext.h" @@ -332,33 +331,47 @@ struct PipelineCache::ProgramCache { template ShaderProgram Get(const ShaderParams& params, InputInfo& input_info, uint32_t& push_data_cursor) { - ShaderType stage; - if constexpr (std::is_same_v) { - stage = input_info.logical_stage; - } else if constexpr (std::is_same_v) { - stage = ShaderType::Pixel; - } else { - static_assert(std::is_same_v); - stage = ShaderType::Compute; - } - const auto user_data = std::span(params.user_data).first(params.user_data_count); - lookup_key.stage = stage; - lookup_key.hash = params.hash; - lookup_key.user_data_count = params.user_data_count; - lookup_key.code_size = static_cast(params.code.size()); - BuildStageStaticKey(input_info, lookup_key.static_state); - auto entry = programs.find(lookup_key); - ShaderRecompiler::IR::SrtRuntime runtime { + ShaderRecompiler::IR::SrtRuntime runtime { .user_data = user_data, .shader_base = params.Base(), .read_specialization_memory = ReadShaderGuestMemory, .image_heap = image_heap, }; - if constexpr (std::is_same_v) { - runtime.workgroup_counts = input_info.workgroup_counts; + ShaderRecompiler::CompileOptions options {.wave_size = input_info.wave_size}; + if constexpr (std::is_same_v) { + options.stage = input_info.logical_stage; + options.input_info.vertex = &input_info; + options.user_data_base = 8; + if (options.stage == ShaderType::Mesh || + options.stage == ShaderType::TessellationControl) { + options.user_data_base = 0; + options.wave_size = + options.stage == ShaderType::Mesh ? input_info.mesh.wave_size : 64u; + } + } else if constexpr (std::is_same_v) { + options.stage = ShaderType::Pixel; + options.input_info.pixel = &input_info; + } else { + static_assert(std::is_same_v); + options.stage = ShaderType::Compute; + options.input_info.compute = &input_info; + runtime.workgroup_counts = input_info.workgroup_counts; runtime.indirect_args_address = input_info.indirect_args_address; } + lookup_key.stage = options.stage; + lookup_key.hash = params.hash; + lookup_key.user_data_count = params.user_data_count; + lookup_key.code_size = static_cast(params.code.size()); + BuildStageStaticKey(input_info, lookup_key.static_state); + auto entry = programs.find(lookup_key); + const auto bind = [&](const Permutation& permutation) { + input_info.stage = {.program = &permutation.program, + .resources = &entry->second.resources, + .handle = &permutation.handle}; + permutation.program.bindings.AdvancePushData(push_data_cursor); + return permutation.handle; + }; bool source_changed = false; if (entry != programs.end()) { auto& cached = entry->second; @@ -370,24 +383,12 @@ struct PipelineCache::ProgramCache { } else { permutation = cached.FindPermutation(runtime, push_data_cursor); } - if (permutation != nullptr) { - input_info.stage = {.program = &permutation->program, .resources = &entry->second.resources}; - permutation->program.bindings.AdvancePushData(push_data_cursor); - return permutation->handle; - } + if (permutation != nullptr) return bind(*permutation); } - ShaderStageInputInfo stage_input {}; - if constexpr (std::is_same_v) { - stage_input.vertex = &input_info; - } else if constexpr (std::is_same_v) { - stage_input.pixel = &input_info; - } else { - stage_input.compute = &input_info; - } const char* label = nullptr; const char* stage_name = nullptr; - switch (stage) { + switch (options.stage) { case ShaderType::Vertex: label = "ShaderRecompiler VS"; stage_name = "vs"; break; case ShaderType::Mesh: label = "ShaderRecompiler MS"; stage_name = "ms"; break; case ShaderType::Local: label = "ShaderRecompiler LS"; stage_name = "ls"; break; @@ -397,8 +398,6 @@ struct PipelineCache::ProgramCache { case ShaderType::Compute: label = "ShaderRecompiler CS"; stage_name = "cs"; break; default: EXIT("invalid pipeline shader stage\n"); } - ShaderRecompiler::CompileOptions options; - options.stage = stage; options.profile = &profile; options.shader_hash = params.hash; options.user_data = user_data; @@ -407,18 +406,6 @@ struct PipelineCache::ProgramCache { options.early_dump = options.dump_ir; options.emit_debug_info = Config::GraphicsDebugDumpEnabled(); options.dump_label = label; - options.input_info = stage_input; - - if constexpr (std::is_same_v) { - options.user_data_base = 8; - options.wave_size = input_info.wave_size; - if (stage == ShaderType::Mesh || stage == ShaderType::TessellationControl) { - options.user_data_base = 0; - options.wave_size = stage == ShaderType::Mesh ? input_info.mesh.wave_size : 64u; - } - } else { - options.wave_size = input_info.wave_size; - } DumpShaderOriginal(stage_name, options.shader_hash, params.code); ShaderRecompiler::ShaderSource source; if (entry == programs.end()) source = ShaderRecompiler::PrepareShaderSource(params.code, options); @@ -453,17 +440,12 @@ struct PipelineCache::ProgramCache { } else if (source_changed) { entry->second.resource_plan = ShaderRecompiler::IR::ExtractResourcePlan(translated.program); if (const auto* permutation = entry->second.FindPermutation(runtime, push_data_cursor)) { - input_info.stage = {.program = &permutation->program, .resources = &entry->second.resources}; - permutation->program.bindings.AdvancePushData(push_data_cursor); - return permutation->handle; + return bind(*permutation); } } entry->second.permutations.push_back(CompilePermutation( stage_name, options, std::move(translated), entry->second.specialization, push_data_cursor, entry->second.FunctionCode())); - const auto& permutation = entry->second.permutations.back(); - input_info.stage = {.program = &permutation.program, .resources = &entry->second.resources}; - permutation.program.bindings.AdvancePushData(push_data_cursor); std::array(ShaderType::TessellationEvaluation) + 1> counts {}; for (const auto& [key, source]: programs) { @@ -478,7 +460,7 @@ struct PipelineCache::ProgramCache { counts[static_cast(ShaderType::Local)], counts[static_cast(ShaderType::TessellationControl)], counts[static_cast(ShaderType::TessellationEvaluation)]); - return permutation.handle; + return bind(entry->second.permutations.back()); } ProgramCache(GraphicContext& graphics, ShaderRecompiler::IR::ImageHeapSource* image_heap) @@ -521,9 +503,9 @@ 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); - m_graphics.device.destroyDescriptorSetLayout(pipeline->descriptor_set_layout, nullptr); + m_graphics.device.destroyPipeline(pipeline.pipeline, nullptr); + m_graphics.device.destroyPipelineLayout(pipeline.pipeline_layout, nullptr); + m_graphics.device.destroyDescriptorSetLayout(pipeline.descriptor_set_layout, nullptr); } }; destroy(m_graphics_pipelines); @@ -681,11 +663,34 @@ void PipelineCache::Save() { m_driver_cache_dirty = false; } -PipelineCache::GraphicsPrograms PipelineCache::GetGraphicsPrograms( - const HW::VertexShaderInfo& vertex_regs, const HW::PixelShaderInfo& pixel_regs, - const HW::ShaderRegisters& sh, const HW::Context& context, const HW::UserConfig& user_config, - std::span target_export_mapping, bool pixel_active, - std::array& vertex_info, ShaderPixelInputInfo& pixel_info) { +void PipelineCache::GetGraphicsPrograms(const CommandBuffer& command, DrawRenderState& state) { + const auto& context = command.GetRegisters(); + const auto& user_config = command.GetUserConfig(); + const auto& sh = context.GetShaderRegisters(); + const auto& vertex_regs = command.GetShaders().GetVs(); + const auto& pixel_regs = command.GetShaders().GetPs(); + auto& vertex_info = state.vertex_info; + auto& pixel_info = state.ps_input_info; + const bool object_primitive_id = context.HasObjectPrimitiveIdOutput(); + const auto write_mask = context.GetRenderTargetMask() & sh.m_cbShaderMask; + uint32_t color_output_mask = object_primitive_id ? 1u << 7u : 0; + for (uint32_t index = 0; index < RENDER_COLOR_ATTACHMENTS_MAX; index++) { + const auto slot = ShaderPixelExportTarget(sh.m_cbShaderMask, index); + if (slot >= RENDER_COLOR_ATTACHMENTS_MAX) { + break; + } + if (sh.target_output_mode[index] != 0 && render_target_mask_slot(write_mask, slot) != 0) { + color_output_mask |= 1u << slot; + } + } + const auto& db = sh.db_shader_control; + // Without color outputs, a pixel shader still runs for its depth or coverage side effects. + const bool pixel_active = + pixel_regs.ps_regs.data_addr != 0 && + (color_output_mask != 0 || db.shader_kill_enable || db.shader_z_export_enable || + db.shader_mask_export_enable || db.shader_dual_export_enable || + db.shader_execute_on_noop || sh.AlphaToMask()); + state.ps_active = pixel_active; const bool tess_active = user_config.GetPrimType() == Prospero::PrimitiveType::kPatch; std::array vertex_params; if (tess_active) { @@ -694,7 +699,6 @@ PipelineCache::GraphicsPrograms PipelineCache::GetGraphicsPrograms( vertex_params[0] = PrepareProgram(vertex_regs, context, user_config, vertex_info[0]); } const bool mesh_active = vertex_info[0].logical_stage == ShaderType::Mesh; - const bool object_primitive_id = context.HasObjectPrimitiveIdOutput(); if (object_primitive_id) { // CxObjectIdMode: register source plus a 32-bit pipelined primitive-ID addition. EXIT_NOT_IMPLEMENTED(sh.m_paClObjprimIdCntl != 2u || mesh_active || tess_active || @@ -725,6 +729,17 @@ PipelineCache::GraphicsPrograms PipelineCache::GetGraphicsPrograms( } ShaderParams pixel_params; if (pixel_active) { + std::array + target_export_mapping {}; + for (uint32_t slot = 0; slot < RENDER_COLOR_ATTACHMENTS_MAX; slot++) { + const auto& rt = context.GetRenderTarget(slot); + if ((color_output_mask & (1u << slot)) != 0 && rt.base.addr != 0) { + target_export_mapping[slot] = + TextureGetRenderTargetFormat(rt.info.format, rt.info.channel_type, + rt.info.channel_order) + .export_mapping; + } + } pixel_params = PrepareProgram(pixel_regs, sh, target_export_mapping, pixel_info); pixel_info.object_primitive_id = object_primitive_id; for (uint32_t export_index = 0; export_index < 8u; export_index++) { @@ -816,21 +831,25 @@ PipelineCache::GraphicsPrograms PipelineCache::GetGraphicsPrograms( } uint32_t push_data_cursor = ShaderRecompiler::IR::PushData::GraphicsDrawDwords( mesh_active, object_primitive_id, last_stage.user_clip_planes); - GraphicsPrograms result; if (pixel_active) { - result.pixel = m_program_cache->Get(pixel_params, pixel_info, push_data_cursor); + m_program_cache->Get(pixel_params, pixel_info, push_data_cursor); + for (const auto& output: pixel_info.stage.program->info.outputs) { + if (output.kind == ShaderRecompiler::IR::StageOutputKind::Mrt || + output.kind == ShaderRecompiler::IR::StageOutputKind::ObjectPrimitiveId) { + state.mrt_mask |= 1u << output.location; + } + } + state.mrt_mask &= color_output_mask; } for (uint32_t i = 0; i < (tess_active ? 3u : 1u); i++) { - result.vertex[i] = m_program_cache->Get(vertex_params[i], vertex_info[i], push_data_cursor); + m_program_cache->Get(vertex_params[i], vertex_info[i], push_data_cursor); } - return result; } ShaderProgram PipelineCache::GetComputeProgram(const HW::ComputeShaderInfo& regs, - const HW::ShaderRegisters& sh, ShaderComputeInputInfo& input_info) { input_info.host_subgroup_size = m_graphics.SupportsComputeWave64() ? 64u : 32u; - const auto params = PrepareProgram(regs, sh, input_info); + const auto params = PrepareProgram(regs, input_info); input_info.lds_storage = input_info.lds_size_dwords * 4u > m_graphics.GetPhysicalDeviceProperties().limits.maxComputeSharedMemorySize; uint32_t push_data_cursor = 0; @@ -841,32 +860,32 @@ bool PipelineStaticParameters::operator==(const PipelineStaticParameters& other) return std::memcmp(this, &other, sizeof(*this)) == 0; } -PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline( - std::span colors, const RenderDepthInfo& depth, - std::span vertex_info, CommandBuffer& command, - const ShaderPixelInputInfo* ps_input_info, vk::PrimitiveTopology topology, - bool primitive_restart_enable, const GraphicsPrograms& programs) { +PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline(const CommandBuffer& command, + const DrawRenderState& state) { + const auto* colors = state.color_info; + const auto& depth = state.depth_info; + const auto vertex_info = state.VertexStages(); + const auto& ps_input_info = state.ps_input_info; const auto& vs_input_info = vertex_info.front(); - const auto& vertex_program = programs.vertex[0]; - const auto& pixel_program = programs.pixel; + const auto* vertex_program = vs_input_info.stage.handle; + const auto* pixel_program = state.ps_input_info.stage.handle; KYTY_PROFILER_BLOCK("PipelineCache::CreatePipeline(Gfx)", profiler::colors::DeepOrangeA200); - EXIT_IF(colors.size() > RENDER_COLOR_ATTACHMENTS_MAX); - EXIT_IF(!vertex_program); - const bool ps_active = ps_input_info != nullptr; - EXIT_IF(ps_active && !pixel_program); - const auto color_count = static_cast(colors.size()); + EXIT_IF(vertex_program == nullptr); + const bool ps_active = state.ps_active; + EXIT_IF(ps_active && pixel_program == nullptr); + const auto color_count = state.color_count; auto& ctx = command.GetRegisters(); const HW::ModeControl& mc = ctx.GetModeControl(); - const auto vs_id = vertex_program.id; - const auto ps_id = ps_active ? pixel_program.id : 0; + const auto vs_id = vertex_program->id; + const auto ps_id = ps_active ? pixel_program->id : 0; GraphicsPipelineKey key {}; - for (uint32_t i = 0; i < programs.vertex.size(); i++) { - key.vertex_shader_ids[i] = programs.vertex[i].id; + for (uint32_t i = 0; i < vertex_info.size(); i++) { + key.vertex_shader_ids[i] = vertex_info[i].stage.handle->id; } key.ps_shader_id = ps_id; auto& static_params = key.static_params; @@ -893,8 +912,8 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline( const auto& rt = ctx.GetRenderTarget(colors[i].target_slot); const auto& bc = ctx.GetBlendControl(colors[i].target_slot); auto alpha_source = ShaderAlphaBlendSource::None; - if (slot == 0 && ps_input_info != nullptr) { - alpha_source = ps_input_info->alpha_blend_source; + if (slot == 0 && ps_active) { + alpha_source = ps_input_info.alpha_blend_source; } static_params.blend_enable[slot] = bc.enable && !rt.info.blend_bypass; if (static_params.blend_enable[slot] && alpha_source == ShaderAlphaBlendSource::None && @@ -964,7 +983,7 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline( 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) { + 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"); @@ -974,11 +993,11 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline( 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.topology = state.topology; + static_params.primitive_restart_enable = state.primitive_restart_enable; static_params.samples = attachment_samples; static_params.sample_shading_enable = - ps_active && attachment_samples > 1 && ps_input_info->ps_sample_shading; + 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"); } @@ -1014,73 +1033,67 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline( } } - if (auto iter = m_graphics_pipelines.find(key); iter != m_graphics_pipelines.end()) { - return *iter->second; + auto [iter, inserted] = m_graphics_pipelines.try_emplace(key); + auto& pipeline = iter->second; + if (!inserted) { + return pipeline; } if (graphics_debug_dump_enabled()) { ShaderDbgDumpInputInfo(vs_input_info); if (ps_active) { - ShaderDbgDumpInputInfo(*ps_input_info); + ShaderDbgDumpInputInfo(ps_input_info); } LOGF("PipelineTrace: shader modules VS=%" PRIu64 " module=%p PS=%" PRIu64 " module=%p\n", - vs_id, static_cast(vertex_program.module), ps_id, - static_cast(pixel_program.module)); + vs_id, static_cast(vertex_program->module), ps_id, + ps_active ? static_cast(pixel_program->module) : nullptr); } - auto cached = std::make_unique(); LogPipelineTrace("CreatePipelineInternal begin", vs_id, ps_id); { Common::DebugCounters::ScopedTimer timer(Common::DebugCounters::Counter::PipelinesCreated, Common::DebugCounters::Counter::PipelineCreateNs, Common::DebugCounters::Peak::PipelineCreateNs); - CreatePipelineInternal(m_graphics, *cached, rendering, key.vertex_input, vertex_info, - ps_input_info, programs, static_params, m_driver_cache); + CreatePipelineInternal(m_graphics, pipeline, rendering, key.vertex_input, state, + static_params, m_driver_cache); } LogPipelineTrace("CreatePipelineInternal done", vs_id, ps_id); - 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); + EXIT_NOT_IMPLEMENTED(pipeline.pipeline == nullptr); + EXIT_NOT_IMPLEMENTED(pipeline.pipeline_layout == nullptr); CheckpointDriverCache(); - return *iter->second; + return pipeline; } -PipelineCache::Pipeline& -PipelineCache::GetComputePipeline(const ShaderComputeInputInfo& input_info, - const ShaderProgram& compute_program) { +PipelineCache::Pipeline& PipelineCache::GetComputePipeline(const ShaderComputeInputInfo& input_info) { KYTY_PROFILER_BLOCK("PipelineCache::CreatePipeline(Compute)", profiler::colors::RedA100); - EXIT_IF(!compute_program); + EXIT_IF(input_info.stage.handle == nullptr); + const auto& compute_program = *input_info.stage.handle; - if (auto iter = m_compute_pipelines.find(compute_program.id); - iter != m_compute_pipelines.end()) { - return *iter->second; + auto [iter, inserted] = m_compute_pipelines.try_emplace(compute_program.id); + auto& pipeline = iter->second; + if (!inserted) { + return pipeline; } if (graphics_debug_dump_enabled()) { ShaderDbgDumpInputInfo(input_info); } - auto cached = std::make_unique(); { Common::DebugCounters::ScopedTimer timer(Common::DebugCounters::Counter::PipelinesCreated, Common::DebugCounters::Counter::PipelineCreateNs, Common::DebugCounters::Peak::PipelineCreateNs); - CreatePipelineInternal(m_graphics, *cached, input_info, compute_program.module, + CreatePipelineInternal(m_graphics, pipeline, input_info, compute_program.module, m_driver_cache); } - EXIT_NOT_IMPLEMENTED(cached->pipeline == nullptr); - EXIT_NOT_IMPLEMENTED(cached->pipeline_layout == nullptr); - - auto [iter, inserted] = m_compute_pipelines.emplace(compute_program.id, std::move(cached)); - EXIT_IF(!inserted); + EXIT_NOT_IMPLEMENTED(pipeline.pipeline == nullptr); + EXIT_NOT_IMPLEMENTED(pipeline.pipeline_layout == nullptr); CheckpointDriverCache(); - return *iter->second; + return pipeline; } } // namespace Libs::Graphics diff --git a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h index f49f88200..da9353394 100644 --- a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h +++ b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h @@ -4,6 +4,8 @@ #include "common/abi.h" #include "common/assert.h" #include "common/common.h" +#include "graphics/host_gpu/renderer/colorRenderTarget.h" +#include "graphics/host_gpu/renderer/depthRenderTarget.h" #include "graphics/host_gpu/renderer/renderTarget.h" #include "graphics/host_gpu/vulkanCommon.h" #include "graphics/shader/shader.h" @@ -19,17 +21,8 @@ namespace Libs::Graphics { struct GraphicContext; -struct RenderColorInfo; -struct RenderDepthInfo; class CommandBuffer; -namespace HW { -class Context; -class Shader; -class UserConfig; -struct ComputeShaderInfo; -} // namespace HW - namespace ShaderRecompiler::IR { class ImageHeapSource; } // namespace ShaderRecompiler::IR @@ -105,6 +98,24 @@ struct ShaderProgram { explicit operator bool() const { return id != 0 && module != nullptr; } }; +// A draw's topology, shader stages (from PipelineCache::GetGraphicsPrograms) and targets. +struct DrawRenderState { + RenderDepthInfo depth_info; + RenderColorInfo color_info[RENDER_COLOR_ATTACHMENTS_MAX] = {}; + uint32_t color_count = 0; + // Color targets the pixel program writes. + uint32_t mrt_mask = 0; + vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList; + bool primitive_restart_enable = false; + bool ps_active = false; + std::array vertex_info; + ShaderPixelInputInfo ps_input_info; + + [[nodiscard]] std::span VertexStages() const { + return {vertex_info.data(), vertex_info[1].stage ? 3u : 1u}; + } +}; + // The owning renderer serializes access, including saves while the GPU is running. class PipelineCache { public: @@ -121,32 +132,14 @@ public: bool uses_image_heap = false; }; - struct GraphicsPrograms { - std::array vertex; - ShaderProgram pixel; - - [[nodiscard]] uint32_t VertexStageCount() const { return vertex[1] ? 3u : 1u; } - }; - - GraphicsPrograms - GetGraphicsPrograms(const HW::VertexShaderInfo& vertex_regs, - const HW::PixelShaderInfo& pixel_regs, const HW::ShaderRegisters& sh, - const HW::Context& context, const HW::UserConfig& user_config, - std::span target_export_mapping, - bool pixel_active, std::array& vertex_info, - ShaderPixelInputInfo& pixel_info); + // Reads the shaders and registers bound in `command` into the state's shader stages. + void GetGraphicsPrograms(const CommandBuffer& command, DrawRenderState& state); + // An empty program skips the dispatch. ShaderProgram GetComputeProgram(const HW::ComputeShaderInfo& regs, - const HW::ShaderRegisters& sh, ShaderComputeInputInfo& input_info); - Pipeline& GetGraphicsPipeline(std::span colors, - const RenderDepthInfo& depth, - std::span vertex_info, - CommandBuffer& command, const ShaderPixelInputInfo* ps_input_info, - vk::PrimitiveTopology topology, bool primitive_restart_enable, - const GraphicsPrograms& programs); - Pipeline& GetComputePipeline(const ShaderComputeInputInfo& input_info, - const ShaderProgram& compute_program); + Pipeline& GetGraphicsPipeline(const CommandBuffer& command, const DrawRenderState& state); + Pipeline& GetComputePipeline(const ShaderComputeInputInfo& input_info); private: struct ProgramCache; @@ -182,9 +175,8 @@ private: std::filesystem::path m_driver_cache_path; std::chrono::steady_clock::time_point m_next_driver_cache_save; bool m_driver_cache_dirty = false; - std::unordered_map, GraphicsPipelineKeyHash> - m_graphics_pipelines; - std::unordered_map> m_compute_pipelines; + std::unordered_map m_graphics_pipelines; + std::unordered_map m_compute_pipelines; void InitializeDriverCache(); void CheckpointDriverCache(); @@ -192,13 +184,11 @@ private: void LogPipelineTrace(const char* phase, uint64_t vertex_program_id, uint64_t pixel_program_id); 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 PipelineRenderingState& rendering, + const PipelineVertexInputState& vertex_input, + const DrawRenderState& state, + const PipelineStaticParameters& static_params, + vk::PipelineCache driver_cache); void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& pipeline, const ShaderComputeInputInfo& input_info, vk::ShaderModule compute_module, vk::PipelineCache driver_cache); diff --git a/src/graphics/host_gpu/renderer/pipeline/shaders.cpp b/src/graphics/host_gpu/renderer/pipeline/shaders.cpp index 9a2863bc4..083770921 100644 --- a/src/graphics/host_gpu/renderer/pipeline/shaders.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/shaders.cpp @@ -242,22 +242,20 @@ static void CreateDescriptorLayout(GraphicContext& graphics, PipelineCache::Pipe // 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 PipelineRenderingState& rendering, + const PipelineVertexInputState& vertex_input, + const DrawRenderState& state, + const PipelineStaticParameters& static_params, + vk::PipelineCache driver_cache) { + const auto vertex_info = state.VertexStages(); const auto& vs_input_info = vertex_info.front(); - const auto& vertex_program = programs.vertex[0]; - const auto& pixel_program = programs.pixel; + const auto* ps_input_info = state.ps_active ? &state.ps_input_info : nullptr; + const auto& vertex_program = *vs_input_info.stage.handle; + const auto* pixel_program = state.ps_input_info.stage.handle; 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 ps_active = state.ps_active; 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 = @@ -267,8 +265,7 @@ void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& p vk::ShaderModule tess_eval_shader_module = nullptr; if (rect_list) { - const auto shaders = - BuildRectListShaders(vs_input_info, ps_active ? ps_input_info : nullptr); + const auto shaders = BuildRectListShaders(vs_input_info, ps_input_info); tess_control_shader_module = CompileSPV(shaders.control, graphics.device); if (graphics_debug_dump_enabled()) { LOGF("PipelineTrace: vkCreateShaderModule RectList TCS done module=%p\n", @@ -290,7 +287,7 @@ void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& p 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, + .module = vertex_info[i].stage.handle->module, .pName = "main"}; } if (rect_list) { @@ -305,7 +302,7 @@ void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& p } if (ps_active) { shader_stages[shader_stage_count++] = {.stage = vk::ShaderStageFlagBits::eFragment, - .module = pixel_program.module, + .module = pixel_program->module, .pName = "main"}; } @@ -477,7 +474,6 @@ void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& p graphics_stages |= native_stage; } if (ps_active) { - EXIT_IF(!ps_input_info->stage); AddLayoutBindings(descriptor_bindings, *ps_input_info->stage.program, vk::ShaderStageFlagBits::eFragment); pipeline.uses_image_heap |= ps_input_info->stage.program->info.uses_image_heap; @@ -498,7 +494,7 @@ void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& p 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); + vertex_program.id, ps_active ? pixel_program->id : 0, 1u); } auto result = graphics.device.createPipelineLayout(&pipeline_layout_info, nullptr, &pipeline.pipeline_layout); @@ -573,7 +569,7 @@ void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& p 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, + 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); diff --git a/src/graphics/host_gpu/renderer/render.h b/src/graphics/host_gpu/renderer/render.h index 9a43337d4..08b556474 100644 --- a/src/graphics/host_gpu/renderer/render.h +++ b/src/graphics/host_gpu/renderer/render.h @@ -23,14 +23,9 @@ class Shader; } // namespace HW struct GraphicContext; -struct ShaderBufferResource; -struct ShaderComputeInputInfo; -struct RenderDepthInfo; -struct RenderColorInfo; struct DrawCallInfo; struct DrawEmitInfo; struct DrawIndexBufferSource; -struct DrawRenderState; class RenderContext; class CommandScheduler; struct RenderExecutorTestAccess; @@ -198,9 +193,8 @@ private: uint32_t render_target_slice_offset, DrawRenderState& state); void ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buffer, const DrawCallInfo& draw, - DrawRenderState& state, vk::PrimitiveTopology topology, - const DrawEmitInfo& emit, const DrawIndexBufferSource& index_source, - bool primitive_restart_enable); + DrawRenderState& state, const DrawEmitInfo& emit, + const DrawIndexBufferSource& index_source); [[nodiscard]] RenderState AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors, uint32_t color_count, RenderDepthInfo& depth, vk::ImageAspectFlags& feedback_aspects, diff --git a/src/graphics/host_gpu/renderer/renderCompute.cpp b/src/graphics/host_gpu/renderer/renderCompute.cpp index 93e225149..c9a518bb1 100644 --- a/src/graphics/host_gpu/renderer/renderCompute.cpp +++ b/src/graphics/host_gpu/renderer/renderCompute.cpp @@ -248,7 +248,6 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, uint32_t thread_group_z, uint32_t mode, bool async_compute) { EXIT_IF(buffer.IsInvalid()); m_context.GetCommandScheduler().PopPendingOperations(); - auto& ctx = buffer.GetRegisters(); auto& sh_ctx = buffer.GetShaders(); buffer.SetDebugInfo(static_cast(CommandBufferDebugOp::DispatchDirect), submit_id, @@ -293,7 +292,6 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, } const auto& cs_regs = sh_ctx.GetCs(); - const auto& sh_regs = ctx.GetShaderRegisters(); ShaderComputeInputInfo input_info {}; input_info.async_compute = async_compute; @@ -312,9 +310,7 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, } } m_context.GetImageHeap().BeginDraw(); - const auto compute_program = - m_context.GetPipelineCache().GetComputeProgram(cs_regs, sh_regs, input_info); - if (!compute_program) return; + if (!m_context.GetPipelineCache().GetComputeProgram(cs_regs, input_info)) return; if (use_thread_dimensions) { input_info.dispatch_threads_num[0] = thread_group_x; input_info.dispatch_threads_num[1] = thread_group_y; @@ -351,8 +347,7 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, } buffer.EndRendering(); - auto& pipeline = - m_context.GetPipelineCache().GetComputePipeline(input_info, compute_program); + auto& pipeline = m_context.GetPipelineCache().GetComputePipeline(input_info); auto& bindings = m_compute_bindings; PrepareBindings(input_info.stage, bindings); if (program.bindings.dispatch_thread_dword != ShaderRecompiler::IR::PushData::NoStart) { @@ -502,11 +497,9 @@ void RenderExecutor::DispatchIndirect(uint64_t submit_id, CommandBuffer& buffer, input_info.async_compute = async_compute; input_info.indirect_args_address = args_addr; m_context.GetImageHeap().BeginDraw(); - const auto compute_program = m_context.GetPipelineCache().GetComputeProgram( - cs_regs, buffer.GetRegisters().GetShaderRegisters(), input_info); - if (!compute_program) return; + if (!m_context.GetPipelineCache().GetComputeProgram(cs_regs, input_info)) return; buffer.EndRendering(); - auto& pipeline = m_context.GetPipelineCache().GetComputePipeline(input_info, compute_program); + auto& pipeline = m_context.GetPipelineCache().GetComputePipeline(input_info); auto& bindings = m_compute_bindings; PrepareBindings(input_info.stage, bindings); PreparedBindings* descriptor_stage = &bindings; diff --git a/src/graphics/host_gpu/renderer/renderDraw.cpp b/src/graphics/host_gpu/renderer/renderDraw.cpp index f03c1da7c..f43a63bd4 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.cpp +++ b/src/graphics/host_gpu/renderer/renderDraw.cpp @@ -17,7 +17,6 @@ #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/textureCommon.h" #include "graphics/host_gpu/renderer/pipeline/pipelineCache.h" #include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h" #include "graphics/host_gpu/renderer/render.h" @@ -433,22 +432,6 @@ static bool DrawHasValidVertexShader(const HW::Shader& sh_ctx) { return vs.es_regs.data_addr != 0; } -static bool PixelShaderHasDepthOrCoverageSideEffects(const HW::ShaderRegisters& sh_regs) { - const auto& db = sh_regs.db_shader_control; - return db.shader_kill_enable || db.shader_z_export_enable || db.shader_mask_export_enable || - db.shader_dual_export_enable || db.shader_execute_on_noop || sh_regs.AlphaToMask(); -} - -struct DrawRenderState { - RenderDepthInfo depth_info; - RenderColorInfo color_info[RENDER_COLOR_ATTACHMENTS_MAX] = {}; - uint32_t color_count = 0; - bool ps_active = true; - std::array vertex_info; - ShaderPixelInputInfo ps_input_info; - PipelineCache::GraphicsPrograms programs; -}; - struct DrawCallInfo { CommandBufferDebugOp debug_op = CommandBufferDebugOp::DrawIndex; uint32_t index_count = 0; @@ -588,23 +571,6 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo return state; } -static uint32_t DrawColorOutputMask(const HW::Context& ctx) { - const auto& sh_regs = ctx.GetShaderRegisters(); - const auto write_mask = ctx.GetRenderTargetMask() & sh_regs.m_cbShaderMask; - uint32_t output_mask = ctx.HasObjectPrimitiveIdOutput() ? 1u << 7u : 0; - for (uint32_t index = 0; index < RENDER_COLOR_ATTACHMENTS_MAX; index++) { - const auto slot = ShaderPixelExportTarget(sh_regs.m_cbShaderMask, index); - if (slot >= RENDER_COLOR_ATTACHMENTS_MAX) { - break; - } - if (sh_regs.target_output_mode[index] != 0 && - render_target_mask_slot(write_mask, slot) != 0) { - output_mask |= 1u << slot; - } - } - return output_mask; -} - enum class CbColorMode : uint8_t { Disable = 0, Normal = 1, @@ -869,62 +835,19 @@ static bool ResolvePrimitiveRestart(const CommandBuffer& buffer, return false; } -static void RefreshShaders(CommandBuffer& buffer, const DrawCallInfo& draw, - uint32_t color_output_mask, DrawRenderState& state) { - auto& ctx = buffer.GetRegisters(); - auto& sh_ctx = buffer.GetShaders(); - - const auto& vertex_shader_info = sh_ctx.GetVs(); - const auto& pixel_shader_info = sh_ctx.GetPs(); - const auto& shader_regs = ctx.GetShaderRegisters(); - - state.programs = {}; - state.ps_input_info = {}; - std::array - target_export_mapping {}; - for (uint32_t slot = 0; slot < RENDER_COLOR_ATTACHMENTS_MAX; slot++) { - const auto& rt = ctx.GetRenderTarget(slot); - if ((color_output_mask & (1u << slot)) != 0 && rt.base.addr != 0) { - target_export_mapping[slot] = - TextureGetRenderTargetFormat(rt.info.format, rt.info.channel_type, - rt.info.channel_order) - .export_mapping; - } - } - auto& pipeline_cache = buffer.GetContext().GetPipelineCache(); - buffer.GetContext().GetImageHeap().BeginDraw(); - if (draw.IsIndexed()) { - LogDrawPhase(draw.Name(), "GetGraphicsPrograms"); - } - state.programs = pipeline_cache.GetGraphicsPrograms( - vertex_shader_info, pixel_shader_info, shader_regs, ctx, buffer.GetUserConfig(), - target_export_mapping, state.ps_active, state.vertex_info, state.ps_input_info); -} - bool RenderExecutor::PrepareDrawRenderState(CommandBuffer& buffer, const DrawCallInfo& draw, uint32_t render_target_slice_offset, DrawRenderState& state) { - const auto& shader_regs = buffer.GetRegisters().GetShaderRegisters(); - const auto color_output_mask = DrawColorOutputMask(buffer.GetRegisters()); - state.ps_active = buffer.GetShaders().GetPs().ps_regs.data_addr != 0 && - (color_output_mask != 0 || - PixelShaderHasDepthOrCoverageSideEffects(shader_regs)); - RefreshShaders(buffer, draw, color_output_mask, state); - uint32_t mrt_mask = 0; - if (state.ps_active) { - for (const auto& output: state.ps_input_info.stage.program->info.outputs) { - if (output.kind == ShaderRecompiler::IR::StageOutputKind::Mrt || - output.kind == ShaderRecompiler::IR::StageOutputKind::ObjectPrimitiveId) { - mrt_mask |= 1u << output.location; - } - } + m_context.GetImageHeap().BeginDraw(); + if (draw.IsIndexed()) { + LogDrawPhase(draw.Name(), "GetGraphicsPrograms"); } - mrt_mask &= color_output_mask; + m_context.GetPipelineCache().GetGraphicsPrograms(buffer, state); if (draw.IsIndexed()) { LogDrawPhase(draw.Name(), "ResolveRenderColorTarget"); } for (uint32_t slot = 0; slot < RENDER_COLOR_ATTACHMENTS_MAX; slot++) { - if ((mrt_mask & (1u << slot)) != 0) { + if ((state.mrt_mask & (1u << slot)) != 0) { ResolveRenderColorTarget(buffer, state.color_info[state.color_count], render_target_slice_offset, slot); if (state.color_info[state.color_count].image_id) { @@ -1123,15 +1046,14 @@ bool RenderExecutor::CopyDepthToColor(CommandBuffer& buffer, const DrawCallInfo& void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buffer, const DrawCallInfo& draw, DrawRenderState& state, - vk::PrimitiveTopology topology, const DrawEmitInfo& emit, - const DrawIndexBufferSource& index_source, - bool primitive_restart_enable) { - auto& ucfg = buffer.GetUserConfig(); + const DrawEmitInfo& emit, + const DrawIndexBufferSource& index_source) { + auto& ucfg = buffer.GetUserConfig(); + const bool primitive_restart_enable = state.primitive_restart_enable; if (CopyDepthToColor(buffer, draw, emit, state)) { return; } - const auto vertex_stages = - std::span {state.vertex_info.data(), state.programs.VertexStageCount()}; + const auto vertex_stages = state.VertexStages(); const bool mesh_active = state.vertex_info[0].stage.program->stage == ShaderType::Mesh; uint32_t mesh_groups = 0; if (mesh_active) { @@ -1202,10 +1124,7 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff if (draw.IsIndexed()) { LogDrawPhase(draw.Name(), "CreatePipeline"); } - auto& pipeline = m_context.GetPipelineCache().GetGraphicsPipeline( - std::span {state.color_info, state.color_count}, state.depth_info, vertex_stages, buffer, - state.ps_active ? &state.ps_input_info : nullptr, topology, primitive_restart_enable, - state.programs); + auto& pipeline = m_context.GetPipelineCache().GetGraphicsPipeline(buffer, state); vk::ImageAspectFlags feedback_aspects; const auto rendering = AcquireRenderTargets(buffer, state.color_info, state.color_count, state.depth_info, @@ -1387,8 +1306,8 @@ bool RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer, hw_check(buffer); - vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList; - if (!GetDrawTopology(ucfg, topology)) { + DrawRenderState state {}; + if (!GetDrawTopology(ucfg, state.topology)) { return true; } @@ -1409,7 +1328,7 @@ bool RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer, default: EXIT("unknown index_type_and_size: %u\n", args.index_type_and_size); } index_source.size = static_cast(args.index_count) * index_source.guest_element_size; - const bool primitive_restart = ResolvePrimitiveRestart(buffer, index_source); + state.primitive_restart_enable = ResolvePrimitiveRestart(buffer, index_source); std::vector expanded_indices; if (index_source.guest_element_size == 1) { @@ -1417,7 +1336,8 @@ bool RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer, const auto* src = static_cast(args.index_addr); expanded_indices.resize(args.index_count); for (uint32_t i = 0; i < args.index_count; i++) { - expanded_indices[i] = primitive_restart && src[i] == 0xffu ? 0xffffu : src[i]; + expanded_indices[i] = + state.primitive_restart_enable && src[i] == 0xffu ? 0xffffu : src[i]; } index_source.host_data = expanded_indices.data(); index_source.size = expanded_indices.size() * sizeof(uint16_t); @@ -1425,7 +1345,6 @@ bool RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer, const DrawCallInfo draw {CommandBufferDebugOp::DrawIndex, args.index_count, args.instance_count, args.first_instance}; - DrawRenderState state {}; if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, state)) { ResetBindings(); return true; @@ -1436,8 +1355,7 @@ bool RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer, const auto emit = ResolveDrawEmit(ucfg, state.vertex_info[0], args.offset_source, args.base_vertex, args.first_instance, args.indirect); if (emit) { - ExecutePreparedDraw(submit_id, buffer, draw, state, topology, *emit, index_source, - primitive_restart); + ExecutePreparedDraw(submit_id, buffer, draw, state, *emit, index_source); } ResetBindings(); return emit.has_value(); @@ -1493,12 +1411,11 @@ bool RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D const DrawCallInfo draw {CommandBufferDebugOp::DrawIndexAuto, args.vertex_count, args.instance_count, args.first_instance}; - vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList; - if (!GetDrawTopology(ucfg, topology)) { + DrawRenderState state {}; + if (!GetDrawTopology(ucfg, state.topology)) { ResetBindings(); return true; } - DrawRenderState state {}; if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, state)) { ResetBindings(); return true; @@ -1523,7 +1440,7 @@ bool RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D static_cast(args.first_vertex), args.first_instance, args.indirect); if (emit) { - ExecutePreparedDraw(submit_id, buffer, draw, state, topology, *emit, {}, false); + ExecutePreparedDraw(submit_id, buffer, draw, state, *emit, {}); } ResetBindings(); return emit.has_value(); diff --git a/src/graphics/shader/shader.cpp b/src/graphics/shader/shader.cpp index 76f52cca7..ef961b5c5 100644 --- a/src/graphics/shader/shader.cpp +++ b/src/graphics/shader/shader.cpp @@ -377,7 +377,6 @@ static void ShaderGetStaticInputInfoPS( } static void ShaderGetStaticInputInfoCS(const HW::ComputeShaderInfo& regs, - const HW::ShaderRegisters& /*sh*/, const ShaderMappedData& data, ShaderComputeInputInfo& info) { const bool async_compute = info.async_compute; const bool dispatch_thread_dimensions = info.dispatch_thread_dimensions; @@ -684,10 +683,9 @@ ShaderParams PrepareProgram( std::span(regs.ps_user_sgpr.value, regs.ps_regs.rsrc2.user_sgpr), data); } -ShaderParams PrepareProgram(const HW::ComputeShaderInfo& regs, const HW::ShaderRegisters& sh, - ShaderComputeInputInfo& info) { +ShaderParams PrepareProgram(const HW::ComputeShaderInfo& regs, ShaderComputeInputInfo& info) { const auto [data, hash] = ShaderGetMappedData(regs.cs_regs.data_addr, "ShaderGetInputInfoCS():"); - ShaderGetStaticInputInfoCS(regs, sh, data, info); + ShaderGetStaticInputInfoCS(regs, data, info); return GetShaderParams( regs.cs_regs.data_addr, hash, std::span(regs.cs_user_sgpr.value, regs.cs_regs.user_sgpr), data); diff --git a/src/graphics/shader/shader.h b/src/graphics/shader/shader.h index 6fdca0562..b80c31350 100644 --- a/src/graphics/shader/shader.h +++ b/src/graphics/shader/shader.h @@ -49,9 +49,12 @@ namespace ShaderRecompiler::IR { struct CompiledShaderInfo; } // namespace ShaderRecompiler::IR +struct ShaderProgram; + struct ShaderStageRuntime { const ShaderRecompiler::IR::CompiledShaderInfo* program = nullptr; const ShaderRecompiler::IR::ResourceSnapshot* resources = nullptr; + const ShaderProgram* handle = nullptr; [[nodiscard]] explicit operator bool() const { return program != nullptr && resources != nullptr; diff --git a/src/graphics/shader/shaderCompiler.h b/src/graphics/shader/shaderCompiler.h index d4b05f5af..a348b6e93 100644 --- a/src/graphics/shader/shaderCompiler.h +++ b/src/graphics/shader/shaderCompiler.h @@ -39,8 +39,7 @@ ShaderParams PrepareProgram( const HW::PixelShaderInfo& regs, const HW::ShaderRegisters& sh, std::span target_export_mapping, ShaderPixelInputInfo& input_info); -ShaderParams PrepareProgram(const HW::ComputeShaderInfo& regs, const HW::ShaderRegisters& sh, - ShaderComputeInputInfo& input_info); +ShaderParams PrepareProgram(const HW::ComputeShaderInfo& regs, ShaderComputeInputInfo& input_info); } // namespace Libs::Graphics diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index d6f59f21a..57613f983 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -10983,7 +10983,7 @@ public: shaders.SetCsUserSgpr(12 + i, output.fields[i]); ShaderComputeInputInfo input{}; (void)context.GetPipelineCache().GetComputeProgram( - shaders.GetCs(), processor.GetCtx().GetShaderRegisters(), input); + shaders.GetCs(), input); PreparedBindings bindings; executor.PrepareBindings(input.stage, bindings); executor.RebindImages(bindings); @@ -11098,7 +11098,7 @@ public: } ShaderComputeInputInfo input{}; const auto program = context.GetPipelineCache().GetComputeProgram( - shaders.GetCs(), processor.GetCtx().GetShaderRegisters(), input); + shaders.GetCs(), input); if (original_id == 0) original_id = program.id; Require(name, "exact function identity", (program.id == original_id) == (test.value == 41), @@ -11190,7 +11190,7 @@ public: LibKernel::Memory::WriteBacking(output, actual.data(), wave_size * 4); ShaderComputeInputInfo input{}; const auto program = context.GetPipelineCache().GetComputeProgram( - shaders.GetCs(), processor.GetCtx().GetShaderRegisters(), input); + shaders.GetCs(), input); if (phase == 1) original_vector_id = program.id; if (phase >= 2) Require(name, "ordered helper identity", (program.id == original_vector_id) == (phase == 3), @@ -11733,7 +11733,7 @@ public: const auto clean_tick = scheduler.CurrentTick(); ShaderComputeInputInfo input{}; const auto program = context.GetPipelineCache().GetComputeProgram( - shaders.GetCs(), processor.GetCtx().GetShaderRegisters(), input); + shaders.GetCs(), input); if (previous_program.id != 0) { Require(name, "count permutation reuse", program.id == previous_program.id, "a changing GPU count compiled a new shader permutation"); @@ -11815,7 +11815,7 @@ public: input.workgroup_counts[0] = input.workgroup_counts[1] = 1; input.workgroup_counts[2] = test.groups; const auto program = context.GetPipelineCache().GetComputeProgram( - shaders.GetCs(), processor.GetCtx().GetShaderRegisters(), input); + shaders.GetCs(), input); Require(name, "workgroup table refresh and coalescing", input.workgroup_counts[2] == test.groups && input.stage.program->info.images.size() == 1 && @@ -11833,7 +11833,7 @@ public: const auto *image_storage = snapshot->images.data(); const auto flattened_capacity = snapshot->flattened_srt.capacity(); const auto repeated = context.GetPipelineCache().GetComputeProgram( - shaders.GetCs(), processor.GetCtx().GetShaderRegisters(), input); + shaders.GetCs(), input); Require(name, "workgroup table cache-hit storage reuse", repeated.id == program.id && input.stage.resources == snapshot && snapshot->images.data() == image_storage && @@ -12072,7 +12072,7 @@ public: input.workgroup_counts[0] = material_groups; input.workgroup_counts[1] = input.workgroup_counts[2] = 1u; (void)context.GetPipelineCache().GetComputeProgram( - shaders.GetCs(), processor.GetCtx().GetShaderRegisters(), input); + shaders.GetCs(), input); const auto &info = input.stage.program->info; const auto root = std::ranges::find_if(info.buffers, [](const auto &buffer) { return buffer.indirect_resources.size() == material_rows; @@ -18365,10 +18365,12 @@ public: mode.polymode_back_ptype = back; mode.provoking_vtx_last = provoking_last; registers.SetModeControl(mode); - return context.GetPipelineCache().GetGraphicsPipeline( - std::span{&color, 1u}, depth, std::span{&vertex, 1u}, scheduler.Current(), &pixel, - topology, false, - PipelineCache::GraphicsPrograms{{vertex_shader}, pixel_shader}); + DrawRenderState state{.depth_info = depth, .color_info = {color}, .color_count = 1, + .topology = topology, .ps_active = true, + .vertex_info = {vertex}, .ps_input_info = pixel}; + state.vertex_info[0].stage.handle = &vertex_shader; + state.ps_input_info.stage.handle = &pixel_shader; + return context.GetPipelineCache().GetGraphicsPipeline(scheduler.Current(), state); }; auto &filled = pipeline(true, 2, 2); const auto draw = [&](const PipelineCache::Pipeline &selected, uint32_t vertex_count = 3, @@ -18719,17 +18721,50 @@ public: ShaderMapUserData(pixel_address, {.type = Prospero::ShaderBinaryType::kPs, .code_size_bytes = static_cast(native_pixel.size() * sizeof(u32))}); - HW::VertexShaderInfo native_vertex_regs{}; - native_vertex_regs.es_regs.data_addr = vertex_address; - HW::PixelShaderInfo native_pixel_regs{}; - native_pixel_regs.ps_regs.data_addr = pixel_address; + shaders.SetEsShaderBase(vertex_address); + shaders.SetPsShaderBase(pixel_address); const auto owned_vertex_shader = vertex_shader; const auto owned_pixel_shader = pixel_shader; depth.stencil_test_enable = false; registers.SetViewportTransformControl(0x300); user_config.SetPrimitiveType(Prospero::PrimitiveType::kTriList); - std::array export_mapping{}; - std::array native_vertex_info{}; + // The cache reads the bound shaders, the color outputs and the export mapping itself. + const auto refresh = [&] { + DrawRenderState state{}; + context.GetPipelineCache().GetGraphicsPrograms(scheduler.Current(), state); + return state; + }; + const auto native_programs = [&] { + const auto native = refresh(); + vertex = native.vertex_info[0]; + pixel = native.ps_input_info; + vertex_shader = *vertex.stage.handle; + pixel_shader = *pixel.stage.handle; + }; + const auto silent = refresh(); + registers.SetDepthShaderControl({.shader_kill_enable = true}); + const auto killing = refresh(); + registers.SetDepthShaderControl({.shader_z_export_enable = true}); + const auto depth_writing = refresh(); + registers.SetDepthShaderControl({}); + registers.SetTargetOutputMode(0, 4); + const auto writing = refresh(); + // Slot 1 takes color output, but the pixel program exports only MRT0. + registers.SetRenderTargetMask(0xff); + registers.SetShaderMask(0xff); + registers.SetTargetOutputMode(1, 4); + const auto partial = refresh(); + registers.SetTargetOutputMode(1, 0); + registers.SetShaderMask(0xf); + registers.SetRenderTargetMask(0xf); + Require(name, "pixel stage activity", + !silent.ps_active && !silent.ps_input_info.stage && silent.mrt_mask == 0 && + killing.ps_active && killing.ps_input_info.stage && killing.mrt_mask == 0 && + depth_writing.ps_active && depth_writing.mrt_mask == 0 && + writing.ps_active && writing.mrt_mask == 1u && partial.mrt_mask == 1u && + writing.VertexStages().size() == 1 && + writing.vertex_info[0].stage.handle != nullptr, + "pixel activity or its MRT mask ignored the color outputs or depth side effects"); std::array wave_ids{}; std::array, 2> wave_keys; struct PixelWaveCase { uint32_t control, width, color; }; @@ -18737,9 +18772,7 @@ public: PixelWaveCase{0x8008, 32, 0x3e800000}, PixelWaveCase{0x0008, 64, 0}}) { registers.SetPsInControl(wave.control); - const auto programs = context.GetPipelineCache().GetGraphicsPrograms( - native_vertex_regs, native_pixel_regs, registers.GetShaderRegisters(), - registers, user_config, export_mapping, true, native_vertex_info, pixel); + native_programs(); Require(name, "pixel wave metadata propagation", pixel.input_num == 8 && pixel.stage.program->wave_size == wave.width, "SPI_PS_IN_CONTROL width did not reach the compiled pixel program"); @@ -18748,14 +18781,11 @@ public: BuildStageStaticKey(pixel, key); if (wave_ids[index] != 0) { Require(name, "pixel wave cache reuse", - programs.pixel.id == wave_ids[index] && key == wave_keys[index], + pixel_shader.id == wave_ids[index] && key == wave_keys[index], "restoring wave64 did not reuse its original program"); } - wave_ids[index] = programs.pixel.id; + wave_ids[index] = pixel_shader.id; wave_keys[index] = std::move(key); - vertex_shader = programs.vertex[0]; - pixel_shader = programs.pixel; - vertex = native_vertex_info[0]; draw(pipeline(true, 2, 2)); const auto wave_pixels = read_color(); for (size_t component = 0; component < wave_pixels.size(); component++) { @@ -18791,8 +18821,8 @@ public: *(position + 1) = EncodeExp1(3, 4, 0, 9); return code; }(); - native_vertex_regs.es_regs.data_addr = reinterpret_cast(waterfall_vertex.data()); - ShaderMapUserData(native_vertex_regs.es_regs.data_addr, + shaders.SetEsShaderBase(reinterpret_cast(waterfall_vertex.data())); + ShaderMapUserData(shaders.GetVs().es_regs.data_addr, {.type = Prospero::ShaderBinaryType::kGs, .user_data = &native_user_data, .code_size_bytes = static_cast(waterfall_vertex.size() * sizeof(u32))}); registers.SetPsInControl(0x8008); @@ -18800,12 +18830,7 @@ public: for (const u32 wave : {32u, 64u}) { registers.SetShaderStages((saved_stages & ~0x00400000u) | (wave == 32u ? 0x00400000u : 0u)); - const auto programs = context.GetPipelineCache().GetGraphicsPrograms( - native_vertex_regs, native_pixel_regs, registers.GetShaderRegisters(), - registers, user_config, export_mapping, true, native_vertex_info, pixel); - vertex_shader = programs.vertex[0]; - pixel_shader = programs.pixel; - vertex = native_vertex_info[0]; + native_programs(); Require(name, "native NGG waterfall wave", vertex.stage.program->stage == ShaderType::Vertex && vertex.stage.program->wave_size == wave, @@ -18820,7 +18845,7 @@ public: } } registers.SetShaderStages(saved_stages); - native_vertex_regs.es_regs.data_addr = vertex_address; + shaders.SetEsShaderBase(vertex_address); // Reversed RGBA targets must use logical Sa for RGB, with a separate // zero or unit source factor for separate alpha attenuation or accumulation. @@ -18835,8 +18860,8 @@ public: AppendEnd(&code); return code; }(); - native_pixel_regs.ps_regs.data_addr = reinterpret_cast(blend_pixel.data()); - ShaderMapUserData(native_pixel_regs.ps_regs.data_addr, + shaders.SetPsShaderBase(reinterpret_cast(blend_pixel.data())); + ShaderMapUserData(shaders.GetPs().ps_regs.data_addr, {.type = Prospero::ShaderBinaryType::kPs, .code_size_bytes = static_cast(blend_pixel.size() * sizeof(u32))}); const auto saved_blend = registers.GetBlendControl(0); @@ -18845,8 +18870,13 @@ public: registers.SetTargetOutputMode(0, 4); auto blend_target_info = saved_target_info; blend_target_info.blend_bypass = false; + // A reversed RGBA32F target exports through the ABGR mapping. + blend_target_info.format = Prospero::ChannelLayout::k32_32_32_32; + blend_target_info.channel_type = Prospero::ChannelType::kFloat; + blend_target_info.channel_order = Prospero::ChannelOrder::kReversed; registers.SetColorInfo(0, blend_target_info); - export_mapping[0] = color.export_mapping = Prospero::ColorMappingAbgr; + registers.SetColorBase(0, {.addr = depth_address + 0x10000}); + color.export_mapping = Prospero::ColorMappingAbgr; HW::BlendControl alpha_blend{}; alpha_blend.enable = true; alpha_blend.color_srcblend = static_cast(Prospero::BlendFactor::kSrcAlpha); @@ -18869,22 +18899,18 @@ public: alpha_blend.separate_alpha_blend = test.separate; alpha_blend.alpha_srcblend = static_cast(test.alpha_source); registers.SetBlendControl(0, alpha_blend); - const auto programs = context.GetPipelineCache().GetGraphicsPrograms( - native_vertex_regs, native_pixel_regs, registers.GetShaderRegisters(), - registers, user_config, export_mapping, true, native_vertex_info, pixel); + native_programs(); Require(name, "logical alpha blend program", pixel.alpha_blend_source == test.mode && - pixel.dual_source_blending, + pixel.dual_source_blending && + pixel.target_export_mapping[0] == Prospero::ColorMappingAbgr, "the separate alpha equation did not reach the compiled pixel program"); - vertex_shader = programs.vertex[0]; - pixel_shader = programs.pixel; - vertex = native_vertex_info[0]; auto &selected = pipeline(true, 2, 2); if (test.mode == ShaderAlphaBlendSource::SourceAlpha) { Require(name, "equal blend equation cache reuse", - broadcast_program == 0 || (programs.pixel.id == broadcast_program && + broadcast_program == 0 || (pixel_shader.id == broadcast_program && selected.pipeline == broadcast_pipeline), "equivalent separate alpha state created another shader or pipeline"); - broadcast_program = programs.pixel.id; + broadcast_program = pixel_shader.id; broadcast_pipeline = selected.pipeline; } constexpr std::array destination{0.3f, 0.3f, 0.1f, 0.2f}; @@ -18898,9 +18924,10 @@ public: "reversed blending changed logical RGB or the separate alpha equation"); } } - native_pixel_regs.ps_regs.data_addr = pixel_address; - export_mapping[0] = color.export_mapping = {}; + shaders.SetPsShaderBase(pixel_address); + color.export_mapping = {}; registers.SetBlendControl(0, saved_blend); + registers.SetColorBase(0, {}); registers.SetColorInfo(0, saved_target_info); registers.SetTargetOutputMode(0, saved_output_mode); @@ -18939,17 +18966,12 @@ public: user_config.SetPrimitiveType(Prospero::PrimitiveType::kTriStrip); for (size_t i = 0; i < position_shaders.size(); i++) { const auto &code = position_shaders[i]; - native_vertex_regs.es_regs.data_addr = reinterpret_cast(code.data()); - ShaderMapUserData(native_vertex_regs.es_regs.data_addr, + shaders.SetEsShaderBase(reinterpret_cast(code.data())); + ShaderMapUserData(shaders.GetVs().es_regs.data_addr, {.type = Prospero::ShaderBinaryType::kGs, .user_data = &native_user_data, .code_size_bytes = static_cast(code.size() * sizeof(u32))}); - const auto programs = context.GetPipelineCache().GetGraphicsPrograms( - native_vertex_regs, native_pixel_regs, registers.GetShaderRegisters(), - registers, user_config, export_mapping, true, native_vertex_info, pixel); - vertex_shader = programs.vertex[0]; - pixel_shader = programs.pixel; - vertex = native_vertex_info[0]; + native_programs(); draw(pipeline(true, 2, 2, false, false, vk::PrimitiveTopology::eTriangleStrip), 4); const auto pixels = read_color(); bool second_triangle = false; @@ -19486,11 +19508,7 @@ public: shaders.SetGsShaderResource2({}); shaders.SetPsShaderBase(ps_address); user_config.SetPrimitiveType(Prospero::PrimitiveType::kTriList); - native_vertex_regs.es_regs.data_addr = vertex_address; - native_pixel_regs.ps_regs.data_addr = ps_address; - const auto programs = context.GetPipelineCache().GetGraphicsPrograms( - native_vertex_regs, native_pixel_regs, registers.GetShaderRegisters(), - registers, user_config, export_mapping, true, native_vertex_info, pixel); + native_programs(); Require(name, "packed format follows export ordinal", pixel.target_output_mode[2] == 9 && pixel.wave_size == test.wave && pixel.target_packed_unorm_mask == 4u && @@ -19498,9 +19516,9 @@ public: "packed conversion or rounding contaminated another MRT or followed a physical slot"); if (test.slot == 2) { auto &id = test.truncate ? truncate_program : half_up_program; - Require(name, "packed rounding program reuse", id == 0 || id == programs.pixel.id, + Require(name, "packed rounding program reuse", id == 0 || id == pixel_shader.id, "restoring half-up rounding failed to reuse its program"); - id = programs.pixel.id; + id = pixel_shader.id; } RenderColorInfo packed_target{}, float_target{}; RenderExecutorTestAccess::ResolveRenderColorTarget( @@ -44518,13 +44536,13 @@ void CheckComputeLdsLimit(VulkanHarness &vulkan) { uint16_t{112}, uint16_t{128}, uint16_t{128}}) { regs.cs_regs.lds_size = units; ShaderComputeInputInfo input{}; - const auto program = cache.GetComputeProgram(regs, {}, input); + const auto program = cache.GetComputeProgram(regs, input); Require(name, "guest allocation and device backing", input.lds_size_dwords == units * 128u && input.lds_storage == (units > limit_units), "compute LDS was truncated or chose backing that exceeds the device limit"); Require(name, "pipeline creation", - cache.GetComputePipeline(input, program).pipeline != nullptr, + cache.GetComputePipeline(input).pipeline != nullptr, "full-size LDS shader did not create a Vulkan compute pipeline"); if (previous.id != 0) { Require(name, "guest-size cache identity", diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 8b912d184..96ef6787c 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -10810,11 +10810,10 @@ void TestGraphicsComputeGetreg() { ShaderMappedData mapped{}; mapped.code_size_bytes = sizeof(shader); ShaderMapUserData(regs.cs_regs.data_addr, mapped); - HW::ShaderRegisters sh{}; ShaderComputeInputInfo graphics{}, async{}; async.async_compute = true; - (void)PrepareProgram(regs, sh, graphics); - (void)PrepareProgram(regs, sh, async); + (void)PrepareProgram(regs, graphics); + (void)PrepareProgram(regs, async); Check(!graphics.async_compute && async.async_compute && MakeStageStaticKey(graphics) != MakeStageStaticKey(async) && MakeStageStaticKey(graphics).size() == MakeStageStaticKey(async).size(), @@ -15782,13 +15781,12 @@ void TestComputeLdsAllocationIdentity() { ShaderMappedData mapped{}; mapped.code_size_bytes = sizeof(shader); ShaderMapUserData(regs.cs_regs.data_addr, mapped); - HW::ShaderRegisters sh{}; const auto compile = [&](uint16_t encoded_lds_size, uint32_t expected_dwords) { regs.cs_regs.lds_size = encoded_lds_size; ShaderComputeInputInfo input_info{}; - const auto params = PrepareProgram(regs, sh, input_info); + const auto params = PrepareProgram(regs, input_info); Check(input_info.lds_size_dwords == expected_dwords, "COMPUTE_PGM_RSRC2 LDS allocation units were not decoded"); auto options = MakeCompileOptions(ShaderType::Compute); @@ -15854,7 +15852,7 @@ void TestComputeLdsAllocationIdentity() { ShaderMapUserData(regs.cs_regs.data_addr, mapped); regs.cs_regs.lds_size = decode_lds_field(lds_896_rsrc2); ShaderComputeInputInfo scratch_info{}; - const auto scratch_params = PrepareProgram(regs, sh, scratch_info); + const auto scratch_params = PrepareProgram(regs, scratch_info); Check(scratch_info.scratch_size_dwords == 7, "AGC per-thread scratch size was not propagated"); auto scratch_options = MakeCompileOptions(ShaderType::Compute);