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 "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 <typename InputInfo>
ShaderProgram Get(const ShaderParams& params, InputInfo& input_info,
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);
lookup_key.stage = 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);
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<InputInfo, ShaderComputeInputInfo>) {
runtime.workgroup_counts = input_info.workgroup_counts;
ShaderRecompiler::CompileOptions options {.wave_size = input_info.wave_size};
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;
}
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;
if (entry != programs.end()) {
auto& cached = entry->second;
@@ -370,24 +383,12 @@ struct PipelineCache::ProgramCache {
} else {
permutation = cached.FindPermutation<false>(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<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* 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<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);
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<true>(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<size_t, static_cast<size_t>(ShaderType::TessellationEvaluation) + 1> counts {};
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::TessellationControl)],
counts[static_cast<size_t>(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<const Prospero::ColorComponentMapping, 8> target_export_mapping, bool pixel_active,
std::array<ShaderVertexInputInfo, 3>& 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<ShaderParams, 3> 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<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_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<const RenderColorInfo> colors, const RenderDepthInfo& depth,
std::span<const ShaderVertexInputInfo> 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<uint32_t>(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<uint32_t> 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<void*>(vertex_program.module), ps_id,
static_cast<void*>(pixel_program.module));
vs_id, static_cast<void*>(vertex_program->module), ps_id,
ps_active ? static_cast<void*>(pixel_program->module) : nullptr);
}
auto cached = std::make_unique<Pipeline>();
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<Pipeline>();
{
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
@@ -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<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.
class PipelineCache {
public:
@@ -121,32 +132,14 @@ public:
bool uses_image_heap = false;
};
struct GraphicsPrograms {
std::array<ShaderProgram, 3> 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<const Prospero::ColorComponentMapping, 8> target_export_mapping,
bool pixel_active, std::array<ShaderVertexInputInfo, 3>& 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<const RenderColorInfo> colors,
const RenderDepthInfo& depth,
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);
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<GraphicsPipelineKey, std::unique_ptr<Pipeline>, GraphicsPipelineKeyHash>
m_graphics_pipelines;
std::unordered_map<uint64_t, std::unique_ptr<Pipeline>> m_compute_pipelines;
std::unordered_map<GraphicsPipelineKey, Pipeline, GraphicsPipelineKeyHash> m_graphics_pipelines;
std::unordered_map<uint64_t, Pipeline> 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<const ShaderVertexInputInfo> 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);
@@ -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<const ShaderVertexInputInfo> 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<uint32_t>(static_params.topology), static_params.color_mask[0],
(with_depth ? "true" : "false"), (static_params.blend_enable[0] ? "true" : "false"),
dynamic_state.dynamicStateCount);
+2 -8
View File
@@ -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,
@@ -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<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& 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;
+20 -103
View File
@@ -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<ShaderVertexInputInfo, 3> 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<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,
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<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;
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);
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<int32_t>(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();
+2 -4
View File
@@ -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<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,
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<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;
} // 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;
+1 -2
View File
@@ -39,8 +39,7 @@ ShaderParams PrepareProgram(
const HW::PixelShaderInfo& regs, const HW::ShaderRegisters& sh,
std::span<const Prospero::ColorComponentMapping, 8> 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
+83 -65
View File
@@ -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<uint32_t>(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<Prospero::ColorComponentMapping, 8> export_mapping{};
std::array<ShaderVertexInputInfo, 3> 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<uint64_t, 2> wave_ids{};
std::array<std::vector<u32>, 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<uint64_t>(waterfall_vertex.data());
ShaderMapUserData(native_vertex_regs.es_regs.data_addr,
shaders.SetEsShaderBase(reinterpret_cast<uint64_t>(waterfall_vertex.data()));
ShaderMapUserData(shaders.GetVs().es_regs.data_addr,
{.type = Prospero::ShaderBinaryType::kGs, .user_data = &native_user_data,
.code_size_bytes = static_cast<u32>(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<uint64_t>(blend_pixel.data());
ShaderMapUserData(native_pixel_regs.ps_regs.data_addr,
shaders.SetPsShaderBase(reinterpret_cast<uint64_t>(blend_pixel.data()));
ShaderMapUserData(shaders.GetPs().ps_regs.data_addr,
{.type = Prospero::ShaderBinaryType::kPs,
.code_size_bytes = static_cast<uint32_t>(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<uint8_t>(Prospero::BlendFactor::kSrcAlpha);
@@ -18869,22 +18899,18 @@ public:
alpha_blend.separate_alpha_blend = test.separate;
alpha_blend.alpha_srcblend = static_cast<uint8_t>(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<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");
}
}
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<uint64_t>(code.data());
ShaderMapUserData(native_vertex_regs.es_regs.data_addr,
shaders.SetEsShaderBase(reinterpret_cast<uint64_t>(code.data()));
ShaderMapUserData(shaders.GetVs().es_regs.data_addr,
{.type = Prospero::ShaderBinaryType::kGs,
.user_data = &native_user_data,
.code_size_bytes = static_cast<uint32_t>(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",
+4 -6
View File
@@ -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);