pipeline: look up draw programs and pipelines from the command buffer

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