shader: run merged geometry stages as mesh shaders

Co-authored-by: Brandon Strong <brandostrong@gmail.com>
Co-authored-by: fxpw <fxpw@outlook.com>
Co-authored-by: shadowbeat070 <70843316+shadowbeat070@users.noreply.github.com>
This commit is contained in:
nmzik
2026-09-05 08:32:23 +04:00
parent 246212789f
commit 3213934d42
39 changed files with 1403 additions and 858 deletions
@@ -87,6 +87,7 @@ vk::DescriptorImageInfo MakeImageInfo(const TextureBinding& texture, uint32_t el
static const char* ShaderStageResourceName(ShaderType stage) {
switch (stage) {
case ShaderType::Vertex: return "Vertex";
case ShaderType::Mesh: return "Mesh";
case ShaderType::Pixel: return "Pixel";
case ShaderType::Compute: return "Compute";
default: return "Unknown";
@@ -96,6 +97,7 @@ static const char* ShaderStageResourceName(ShaderType stage) {
static vk::ShaderStageFlags NativeShaderStage(ShaderType stage) {
switch (stage) {
case ShaderType::Vertex: return vk::ShaderStageFlagBits::eVertex;
case ShaderType::Mesh: return vk::ShaderStageFlagBits::eMeshEXT;
case ShaderType::Pixel: return vk::ShaderStageFlagBits::eFragment;
case ShaderType::Compute: return vk::ShaderStageFlagBits::eCompute;
default: EXIT("unknown native shader stage\n");
@@ -1034,8 +1036,12 @@ void RenderExecutor::CommitBindings(CommandBuffer& buffer,
size_t write_count = 0;
ShaderRecompiler::IR::PushData push_data;
bool has_push_data = false;
constexpr auto GraphicsStages =
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment;
constexpr auto GraphicsStages = vk::ShaderStageFlagBits::eVertex |
vk::ShaderStageFlagBits::eMeshEXT |
vk::ShaderStageFlagBits::eFragment;
vk::ShaderStageFlags push_stages = pipeline_bind_point == vk::PipelineBindPoint::eGraphics
? vk::ShaderStageFlagBits::eFragment
: vk::ShaderStageFlags {};
for (const auto* prepared: prepared_bindings) {
EXIT_IF(prepared == nullptr || prepared->program == nullptr ||
prepared->snapshot == nullptr);
@@ -1044,6 +1050,7 @@ void RenderExecutor::CommitBindings(CommandBuffer& buffer,
descriptor_count += NativeDescriptorCount(binding);
}
const auto shader_stage = NativeShaderStage(prepared->program->stage);
push_stages |= shader_stage;
EXIT_IF((pipeline_bind_point == vk::PipelineBindPoint::eGraphics &&
(shader_stage & GraphicsStages) == vk::ShaderStageFlags {}) ||
(pipeline_bind_point == vk::PipelineBindPoint::eCompute &&
@@ -1190,10 +1197,7 @@ void RenderExecutor::CommitBindings(CommandBuffer& buffer,
}
if (has_push_data) {
const auto stages = pipeline_bind_point == vk::PipelineBindPoint::eGraphics
? vk::ShaderStageFlags {GraphicsStages}
: vk::ShaderStageFlags {vk::ShaderStageFlagBits::eCompute};
vk_buffer.pushConstants(pipeline.pipeline_layout, stages, 0, sizeof(push_data),
vk_buffer.pushConstants(pipeline.pipeline_layout, push_stages, 0, sizeof(push_data),
push_data.dwords.data());
}
@@ -204,22 +204,19 @@ struct PipelineCache::ProgramCache {
static constexpr std::size_t MaxStaticKeyWords = 13 + ShaderVertexInputInfo::RES_MAX * 13;
template <ShaderType Stage>
Permutation CompilePermutation(const ShaderParams& params,
const ShaderRecompiler::CompileOptions& options,
ShaderRecompiler::TranslateResult translated,
ShaderRecompiler::IR::ResourceSpecialization specialization,
uint32_t push_data_start_dword) {
constexpr const char* stage_name = [] {
if constexpr (Stage == ShaderType::Vertex) {
return "vs";
} else if constexpr (Stage == ShaderType::Pixel) {
return "ps";
} else {
static_assert(Stage == ShaderType::Compute);
return "cs";
}
}();
const char* stage_name = nullptr;
switch (options.stage) {
case ShaderType::Vertex: stage_name = "vs"; break;
case ShaderType::Mesh: stage_name = "ms"; break;
case ShaderType::Pixel: stage_name = "ps"; break;
case ShaderType::Compute: stage_name = "cs"; break;
default: EXIT("invalid pipeline shader stage\n");
}
auto result = ShaderRecompiler::CompileProgram(std::move(translated), options,
specialization, push_data_start_dword);
DumpShaderOriginal(stage_name, options.shader_hash, params.code, result.decoded_dump);
@@ -256,16 +253,15 @@ struct PipelineCache::ProgramCache {
template <typename InputInfo>
ShaderProgram Get(const ShaderParams& params, InputInfo& input_info,
uint32_t& push_data_cursor) {
constexpr ShaderType stage = [] {
if constexpr (std::is_same_v<InputInfo, ShaderVertexInputInfo>) {
return ShaderType::Vertex;
} else if constexpr (std::is_same_v<InputInfo, ShaderPixelInputInfo>) {
return ShaderType::Pixel;
} else {
static_assert(std::is_same_v<InputInfo, ShaderComputeInputInfo>);
return ShaderType::Compute;
}
}();
ShaderType stage;
if constexpr (std::is_same_v<InputInfo, ShaderVertexInputInfo>) {
stage = input_info.mesh.threads_num[0] != 0 ? ShaderType::Mesh : ShaderType::Vertex;
} else if constexpr (std::is_same_v<InputInfo, ShaderPixelInputInfo>) {
stage = ShaderType::Pixel;
} else {
static_assert(std::is_same_v<InputInfo, ShaderComputeInputInfo>);
stage = ShaderType::Compute;
}
lookup_key.stage = stage;
lookup_key.hash = params.hash;
@@ -307,31 +303,32 @@ struct PipelineCache::ProgramCache {
} else {
stage_input.compute = &input_info;
}
constexpr const char* label = [] {
if constexpr (stage == ShaderType::Vertex) {
return "ShaderRecompiler VS";
} else if constexpr (stage == ShaderType::Pixel) {
return "ShaderRecompiler PS";
} else {
return "ShaderRecompiler CS";
}
}();
const char* label = nullptr;
switch (stage) {
case ShaderType::Vertex: label = "ShaderRecompiler VS"; break;
case ShaderType::Mesh: label = "ShaderRecompiler MS"; break;
case ShaderType::Pixel: label = "ShaderRecompiler PS"; break;
case ShaderType::Compute: label = "ShaderRecompiler CS"; break;
default: EXIT("invalid pipeline shader stage\n");
}
ShaderRecompiler::CompileOptions options;
options.stage = stage;
options.shader_hash = params.hash;
options.user_data = params.user_data;
options.back_code = params.back_code;
options.dump_ir = Config::GetShaderLogDirection() != Config::ShaderLogDirection::Silent;
options.early_dump = options.dump_ir;
options.dump_label = label;
options.input_info = stage_input;
if constexpr (stage == ShaderType::Vertex) {
options.scratch_dwords = input_info.scratch_size_dwords;
if constexpr (std::is_same_v<InputInfo, ShaderVertexInputInfo>) {
options.user_data_base = 8;
options.scratch_dwords = input_info.scratch_size_dwords;
} else if constexpr (stage == ShaderType::Pixel) {
options.scratch_dwords = input_info.scratch_size_dwords;
} else {
options.scratch_dwords = input_info.scratch_size_dwords;
options.wave_size = input_info.wave_size;
if (stage == ShaderType::Mesh) {
options.wave_size = input_info.mesh.wave_size;
options.scratch_dwords = input_info.mesh.scratch_size_dwords;
}
} else if constexpr (std::is_same_v<InputInfo, ShaderComputeInputInfo>) {
options.wave_size = input_info.wave_size;
}
auto translated = ShaderRecompiler::TranslateProgram(params.code, options);
if (entry == programs.end()) {
@@ -340,7 +337,7 @@ struct PipelineCache::ProgramCache {
specialization));
entry = programs.try_emplace(lookup_key, std::move(resource_plan)).first;
}
entry->second.permutations.push_back(CompilePermutation<stage>(
entry->second.permutations.push_back(CompilePermutation(
params, options, std::move(translated), std::move(specialization), push_data_cursor));
const auto& permutation = entry->second.permutations.back();
input_info.stage = {.program = &permutation.program, .resources = std::move(resources)};
@@ -540,10 +537,29 @@ void PipelineCache::Save() {
PipelineCache::GraphicsPrograms PipelineCache::GetGraphicsPrograms(
const HW::VertexShaderInfo& vertex_regs, const HW::PixelShaderInfo& pixel_regs,
const HW::ShaderRegisters& sh, const HW::Context& context,
std::span<const Prospero::ColorComponentMapping, 8> target_export_mapping,
bool pixel_active, ShaderVertexInputInfo& vertex_info, ShaderPixelInputInfo& pixel_info) {
const auto vertex_params = PrepareProgram(vertex_regs, sh, vertex_info);
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,
ShaderVertexInputInfo& vertex_info, ShaderPixelInputInfo& pixel_info) {
const auto vertex_params = PrepareProgram(vertex_regs, context, user_config, vertex_info);
const bool mesh_active = vertex_info.mesh.threads_num[0] != 0;
if (mesh_active) {
EXIT_NOT_IMPLEMENTED(!m_graphics.mesh_shader_enabled);
auto& mesh = vertex_info.mesh;
mesh.host_subgroup_size = m_graphics.subgroup_size;
const auto& limits = m_graphics.mesh_shader_properties;
const auto logical_threads =
mesh.threads_num[0] * mesh.threads_num[1] * mesh.threads_num[2];
const auto host_threads = ((logical_threads + mesh.wave_size - 1u) / mesh.wave_size) *
std::min(mesh.host_subgroup_size, mesh.wave_size);
if (host_threads > limits.maxMeshWorkGroupInvocations ||
host_threads > limits.maxMeshWorkGroupSize[0] ||
mesh.max_vertices > limits.maxMeshOutputVertices ||
mesh.max_primitives > limits.maxMeshOutputPrimitives ||
mesh.lds_size_dwords * sizeof(uint32_t) > limits.maxMeshSharedMemorySize) {
EXIT("mesh shader exceeds host limits: threads=%u vertices=%u primitives=%u LDS=%u\n",
host_threads, mesh.max_vertices, mesh.max_primitives, mesh.lds_size_dwords);
}
}
ShaderParams pixel_params;
if (pixel_active) {
pixel_params = PrepareProgram(pixel_regs, sh, target_export_mapping, pixel_info);
@@ -563,7 +579,8 @@ PipelineCache::GraphicsPrograms PipelineCache::GetGraphicsPrograms(
clip.enabled = true;
}
Common::LockGuard lock(m_mutex);
uint32_t push_data_cursor = 0;
uint32_t push_data_cursor =
mesh_active ? ShaderRecompiler::IR::PushData::MeshDrawDwordCount : 0;
GraphicsPrograms result;
if (pixel_active) {
result.pixel = m_program_cache->Get(pixel_params, pixel_info, push_data_cursor);
@@ -575,7 +592,7 @@ PipelineCache::GraphicsPrograms PipelineCache::GetGraphicsPrograms(
ShaderProgram PipelineCache::GetComputeProgram(const HW::ComputeShaderInfo& regs,
const HW::ShaderRegisters& sh,
ShaderComputeInputInfo& input_info) {
input_info.needs_lds_barriers = !m_graphics.compute_wave64_supported;
input_info.host_subgroup_size = m_graphics.compute_wave64_supported ? 64u : 32u;
const auto params = PrepareProgram(regs, sh, input_info);
Common::LockGuard lock(m_mutex);
uint32_t push_data_cursor = 0;
@@ -709,30 +726,32 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline(
key.vs_shader_id = p.vs_shader_id;
key.ps_shader_id = p.ps_shader_id;
key.static_params = static_params;
EXIT_IF(vs_input_info.buffers_num < 0 ||
vs_input_info.buffers_num > ShaderVertexInputInfo::RES_MAX ||
vs_input_info.resources_num < 0 ||
vs_input_info.resources_num > ShaderVertexInputInfo::RES_MAX);
key.vertex_input.binding_count = static_cast<uint8_t>(vs_input_info.buffers_num);
key.vertex_input.attribute_count = static_cast<uint8_t>(vs_input_info.resources_num);
uint32_t attributes_num = 0;
for (int binding = 0; binding < vs_input_info.buffers_num; binding++) {
const auto& buffer = vs_input_info.buffers[binding];
EXIT_IF(buffer.attr_num < 0 || buffer.attr_num > ShaderVertexInputBuffer::ATTR_MAX);
attributes_num += static_cast<uint32_t>(buffer.attr_num);
EXIT_IF(attributes_num > static_cast<uint32_t>(vs_input_info.resources_num));
key.vertex_input.bindings[binding] = {.stride = buffer.stride,
.instance = buffer.fetch_index != 0};
for (int attribute = 0; attribute < buffer.attr_num; attribute++) {
const auto index = buffer.attr_indices[attribute];
EXIT_IF(index < 0 || index >= vs_input_info.resources_num);
key.vertex_input.attributes[index] = {
.offset = buffer.attr_offsets[attribute],
.binding = static_cast<uint8_t>(binding),
};
if (vs_input_info.stage.program->stage != ShaderType::Mesh) {
EXIT_IF(vs_input_info.buffers_num < 0 ||
vs_input_info.buffers_num > ShaderVertexInputInfo::RES_MAX ||
vs_input_info.resources_num < 0 ||
vs_input_info.resources_num > ShaderVertexInputInfo::RES_MAX);
key.vertex_input.binding_count = static_cast<uint8_t>(vs_input_info.buffers_num);
key.vertex_input.attribute_count = static_cast<uint8_t>(vs_input_info.resources_num);
uint32_t attributes_num = 0;
for (int binding = 0; binding < vs_input_info.buffers_num; binding++) {
const auto& buffer = vs_input_info.buffers[binding];
EXIT_IF(buffer.attr_num < 0 || buffer.attr_num > ShaderVertexInputBuffer::ATTR_MAX);
attributes_num += static_cast<uint32_t>(buffer.attr_num);
EXIT_IF(attributes_num > static_cast<uint32_t>(vs_input_info.resources_num));
key.vertex_input.bindings[binding] = {.stride = buffer.stride,
.instance = buffer.fetch_index != 0};
for (int attribute = 0; attribute < buffer.attr_num; attribute++) {
const auto index = buffer.attr_indices[attribute];
EXIT_IF(index < 0 || index >= vs_input_info.resources_num);
key.vertex_input.attributes[index] = {
.offset = buffer.attr_offsets[attribute],
.binding = static_cast<uint8_t>(binding),
};
}
}
EXIT_IF(attributes_num != static_cast<uint32_t>(vs_input_info.resources_num));
}
EXIT_IF(attributes_num != static_cast<uint32_t>(vs_input_info.resources_num));
if (auto iter = m_graphics_pipelines.find(key); iter != m_graphics_pipelines.end()) {
return *iter->second;
@@ -26,6 +26,7 @@ class CommandBuffer;
namespace HW {
class Context;
class Shader;
class UserConfig;
struct ComputeShaderInfo;
} // namespace HW
@@ -134,11 +135,13 @@ public:
ShaderProgram pixel;
};
GraphicsPrograms GetGraphicsPrograms(
const HW::VertexShaderInfo& vertex_regs, const HW::PixelShaderInfo& pixel_regs,
const HW::ShaderRegisters& sh, const HW::Context& context,
std::span<const Prospero::ColorComponentMapping, 8> target_export_mapping,
bool pixel_active, ShaderVertexInputInfo& vertex_info, ShaderPixelInputInfo& pixel_info);
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, ShaderVertexInputInfo& vertex_info,
ShaderPixelInputInfo& pixel_info);
ShaderProgram GetComputeProgram(const HW::ComputeShaderInfo& regs,
const HW::ShaderRegisters& sh,
ShaderComputeInputInfo& input_info);
@@ -13,6 +13,9 @@ vk::PipelineStageFlags ShaderPipelineStages(vk::ShaderStageFlags stages) {
if (stages & vk::ShaderStageFlagBits::eVertex) {
result |= vk::PipelineStageFlagBits::eVertexShader;
}
if (stages & vk::ShaderStageFlagBits::eMeshEXT) {
result |= vk::PipelineStageFlagBits::eMeshShaderEXT;
}
if (stages & vk::ShaderStageFlagBits::eFragment) {
result |= vk::PipelineStageFlagBits::eFragmentShader;
}
@@ -118,13 +121,11 @@ void ShaderWriteHazardBarrier(vk::CommandBuffer vk_buffer,
void ShaderWriteBarrier(vk::CommandBuffer vk_buffer, vk::PipelineStageFlags source_stages) {
EXIT_IF(vk_buffer == nullptr || !source_stages);
const auto barrier = MakeShaderWriteDependency();
vk_buffer.pipelineBarrier(
source_stages,
vk::PipelineStageFlagBits::eComputeShader | vk::PipelineStageFlagBits::eVertexInput |
vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eFragmentShader |
vk::PipelineStageFlagBits::eTransfer |
vk::PipelineStageFlagBits::eColorAttachmentOutput,
vk::DependencyFlags {}, 1, &barrier, 0, nullptr, 0, nullptr);
vk_buffer.pipelineBarrier(source_stages,
vk::PipelineStageFlagBits::eComputeShader |
vk::PipelineStageFlagBits::eAllGraphics |
vk::PipelineStageFlagBits::eTransfer,
vk::DependencyFlags {}, 1, &barrier, 0, nullptr, 0, nullptr);
}
} // namespace Libs::Graphics
@@ -427,8 +427,13 @@ void CreatePipelineInternal(
const PipelineStaticParameters& static_params, vk::PipelineCache driver_cache) {
const bool ps_active = ps_input_info != nullptr;
EXIT_IF(vertex_module == nullptr || (ps_active && pixel_module == nullptr));
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 auto vertex_stage =
mesh ? vk::ShaderStageFlagBits::eMeshEXT : vk::ShaderStageFlagBits::eVertex;
const bool rect_list = static_params.topology == vk::PrimitiveTopology::ePatchList;
const bool rect_list = !mesh && static_params.topology == vk::PrimitiveTopology::ePatchList;
vk::ShaderModule tess_control_shader_module = nullptr;
vk::ShaderModule tess_eval_shader_module = nullptr;
@@ -468,7 +473,7 @@ void CreatePipelineInternal(
vert_shader_stage_info.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
vert_shader_stage_info.pNext = nullptr;
vert_shader_stage_info.flags = {};
vert_shader_stage_info.stage = vk::ShaderStageFlagBits::eVertex;
vert_shader_stage_info.stage = vertex_stage;
vert_shader_stage_info.module = vertex_module;
vert_shader_stage_info.pName = "main";
vert_shader_stage_info.pSpecializationInfo = nullptr;
@@ -780,18 +785,15 @@ void CreatePipelineInternal(
color_blending.attachmentCount = static_params.color_count;
color_blending.pAttachments = color_blend_attachment;
EXIT_IF(!vs_input_info.stage);
std::vector<vk::DescriptorSetLayoutBinding> descriptor_bindings;
AddLayoutBindings(descriptor_bindings, *vs_input_info.stage.program,
vk::ShaderStageFlagBits::eVertex);
AddLayoutBindings(descriptor_bindings, *vs_input_info.stage.program, vertex_stage);
if (ps_active) {
EXIT_IF(!ps_input_info->stage);
AddLayoutBindings(descriptor_bindings, *ps_input_info->stage.program,
vk::ShaderStageFlagBits::eFragment);
}
CreateDescriptorLayout(graphics, pipeline, descriptor_bindings);
constexpr auto GraphicsStages =
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment;
const auto GraphicsStages = vertex_stage | vk::ShaderStageFlagBits::eFragment;
const vk::PushConstantRange push_constants {GraphicsStages, 0,
ShaderRecompiler::IR::NativePushConstantSize};
@@ -888,8 +890,8 @@ void CreatePipelineInternal(
pipeline_info.flags = {};
pipeline_info.stageCount = shader_stage_count;
pipeline_info.pStages = shader_stages;
pipeline_info.pVertexInputState = &vertex_input_info;
pipeline_info.pInputAssemblyState = &input_assembly;
pipeline_info.pVertexInputState = mesh ? nullptr : &vertex_input_info;
pipeline_info.pInputAssemblyState = mesh ? nullptr : &input_assembly;
vk::PipelineTessellationStateCreateInfo tessellation_state {};
tessellation_state.sType = vk::StructureType::ePipelineTessellationStateCreateInfo;
tessellation_state.patchControlPoints = 3;
+50 -77
View File
@@ -86,7 +86,6 @@ uint32_t ResolveInstanceOffset(const ShaderVertexInputInfo& vs_input_info) {
static std::atomic<uint32_t> g_draw_state_log_count = 0;
static std::atomic<uint32_t> g_draw_input_log_count = 0;
static std::atomic<uint32_t> g_mrt_state_log_count = 0;
static std::atomic<uint32_t> g_shader_stage_log_count = 0;
static std::atomic<uint32_t> g_framebuffer_skip_log_count = 0;
@@ -444,66 +443,6 @@ static bool PixelShaderHasDepthOrCoverageSideEffects(const HW::ShaderRegisters&
db.shader_execute_on_noop;
}
static bool ShouldSkipGeShader(const CommandBuffer& buffer) {
return false;
const auto& ctx = buffer.GetRegisters();
const auto& ucfg = buffer.GetUserConfig();
const auto& sh_ctx = buffer.GetShaders();
const auto& sh_regs = ctx.GetShaderRegisters();
const auto& ge_cntl = ucfg.GetGeControl();
const auto& vertex_info = sh_ctx.GetVs();
const auto stages = ctx.GetShaderStages();
const auto is_known_gs_out_prim_type = [](uint32_t value) {
switch (static_cast<Prospero::GsOutputPrimitiveType>(value)) {
case Prospero::GsOutputPrimitiveType::kPoints:
case Prospero::GsOutputPrimitiveType::kLines:
case Prospero::GsOutputPrimitiveType::kTriangles:
case Prospero::GsOutputPrimitiveType::k2dRectangle:
case Prospero::GsOutputPrimitiveType::kRectList: return true;
}
return false;
};
const bool ps5_ngg_vertex_path = stages == 0x02002000 && vertex_info.es_regs.data_addr != 0 &&
sh_regs.m_vgtGsMaxVertOut == 0x00000000 &&
is_known_gs_out_prim_type(sh_regs.m_vgtGsOutPrimType);
const bool unsupported_stage_mask = (stages != 0 && stages != 0x02002000);
const bool unsupported_gs_stage = (vertex_info.es_regs.data_addr != 0 &&
vertex_info.gs_regs.data_addr != 0 && !ps5_ngg_vertex_path);
// GE_CNTL group sizes control guest scheduling and do not constrain the host vertex path.
const bool ge_shader_regs =
(sh_regs.m_geNggSubgrpCntl != 0x00000000 && sh_regs.m_geNggSubgrpCntl != 0x00000001) ||
sh_regs.m_vgtGsMaxVertOut != 0x00000000 ||
!is_known_gs_out_prim_type(sh_regs.m_vgtGsOutPrimType) ||
sh_regs.m_geMaxOutputPerSubgroup > 0x00000040;
if (unsupported_stage_mask || unsupported_gs_stage || ge_shader_regs) {
static std::once_flag warning_once;
std::call_once(warning_once, [] {
std::printf("Warning: game uses unsupported graphics pipelines; some draw calls were "
"skipped.\n");
});
const auto log_id = g_shader_stage_log_count.fetch_add(1);
if (log_id < 32) {
LOGF("Skipping unsupported GE shader draw: stages=0x%08" PRIx32
" prim_group=0x%04" PRIx16 " vert_group=0x%04" PRIx16 " ngg=0x%08" PRIx32
" max_out=0x%08" PRIx32 " gs_max_vert=0x%08" PRIx32 " gs_out_prim=0x%08" PRIx32
" es=0x%016" PRIx64 " gs=0x%016" PRIx64 "\n",
stages, ge_cntl.primitive_group_size, ge_cntl.vertex_group_size,
sh_regs.m_geNggSubgrpCntl, sh_regs.m_geMaxOutputPerSubgroup,
sh_regs.m_vgtGsMaxVertOut, sh_regs.m_vgtGsOutPrimType,
vertex_info.es_regs.data_addr, vertex_info.gs_regs.data_addr);
}
return true;
}
return false;
}
struct DrawRenderState {
RenderDepthInfo depth_info;
RenderColorInfo color_info[RENDER_COLOR_ATTACHMENTS_MAX] = {};
@@ -1036,8 +975,8 @@ static void RefreshShaders(CommandBuffer& buffer, const DrawCallInfo& draw, bool
LogDrawPhase(draw.name, "GetGraphicsPrograms");
}
state.programs = pipeline_cache.GetGraphicsPrograms(
vertex_shader_info, pixel_shader_info, shader_regs, ctx, target_export_mapping,
state.ps_active, state.vs_input_info, state.ps_input_info);
vertex_shader_info, pixel_shader_info, shader_regs, ctx, buffer.GetUserConfig(),
target_export_mapping, state.ps_active, state.vs_input_info, state.ps_input_info);
}
static PreparedVertexBuffers PrepareVertexBuffers(uint64_t submit_id, CommandBuffer& buffer,
@@ -1176,12 +1115,38 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
bool set_bind_debug, bool set_auto_debug) {
EXIT_IF(draw.name == nullptr);
auto& ucfg = buffer.GetUserConfig();
const bool mesh_active = state.vs_input_info.stage.program->stage == ShaderType::Mesh;
uint32_t mesh_groups = 0;
if (mesh_active) {
const auto& mesh = state.vs_input_info.mesh;
if (emit.indexed || ucfg.GetPrimType() != Prospero::PrimitiveType::kTriStrip ||
primitive_restart_enable || mesh.primitives_per_group == 0) {
EXIT("unsupported mesh draw: primitive=%u indexed=%u restart=%u\n",
static_cast<uint32_t>(ucfg.GetPrimType()), emit.indexed, primitive_restart_enable);
}
if (draw.index_count < 3 || draw.instance_count == 0) {
return;
}
mesh_groups = (draw.index_count - 3u) / mesh.primitives_per_group + 1u;
const auto& limits = m_context.GetGraphics().mesh_shader_properties;
if (mesh_groups > limits.maxMeshWorkGroupCount[0] ||
draw.instance_count > limits.maxMeshWorkGroupCount[1] ||
static_cast<uint64_t>(mesh_groups) * draw.instance_count >
limits.maxMeshWorkGroupTotalCount) {
EXIT("mesh draw exceeds host workgroup limits: %ux%u\n", mesh_groups,
draw.instance_count);
}
}
LogDrawPhase(draw.name, "PrepareBindings");
auto bindings = PrepareGraphicsBindings(state.vs_input_info.stage, state.ps_input_info.stage,
state.ps_active);
auto vertex_bindings = PrepareVertexBuffers(submit_id, buffer, draw, state.vs_input_info);
auto index_binding = PrepareIndexBuffer(buffer, index_source);
PreparedVertexBuffers vertex_bindings;
PreparedIndexBuffer index_binding;
if (!mesh_active) {
vertex_bindings = PrepareVertexBuffers(submit_id, buffer, draw, state.vs_input_info);
index_binding = PrepareIndexBuffer(buffer, index_source);
}
state.rendering =
AcquireRenderTargets(buffer, state.color_info, state.color_count, state.depth_info);
@@ -1203,7 +1168,9 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
if (set_auto_debug) {
SetDrawDebugPhase(buffer, submit_id, draw, 0x200u);
}
CommitVertexBuffers(vk_buffer, vertex_bindings);
if (!mesh_active) {
CommitVertexBuffers(vk_buffer, vertex_bindings);
}
if (bindings.pixel.has_value()) {
if (set_auto_debug) {
SetDrawDebugPhase(buffer, submit_id, draw, 0x300u);
@@ -1216,7 +1183,16 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
}
CommitBindings(buffer, vk::PipelineBindPoint::eGraphics, pipeline,
std::span {descriptor_stages.data(), descriptor_stage_count});
CommitIndexBuffer(vk_buffer, index_binding);
if (mesh_active) {
const uint32_t draw_data[] {draw.index_count, emit.first_vertex, emit.first_instance};
static_assert(std::size(draw_data) == ShaderRecompiler::IR::PushData::MeshDrawDwordCount);
vk_buffer.pushConstants(pipeline.pipeline_layout,
vk::ShaderStageFlagBits::eMeshEXT |
vk::ShaderStageFlagBits::eFragment,
0, sizeof(draw_data), draw_data);
} else {
CommitIndexBuffer(vk_buffer, index_binding);
}
SetGraphicsDynamicParams(buffer, vk_buffer, state.color_info, state.color_count,
state.depth_info);
@@ -1230,14 +1206,19 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
if (set_auto_debug) {
SetDrawDebugPhase(buffer, submit_id, draw, 0x500u);
}
EmitDrawPrimitives(ucfg, vk_buffer, state.vs_input_info, draw, emit);
if (mesh_active) {
vk_buffer.drawMeshTasksEXT(mesh_groups, draw.instance_count, 1);
} else {
EmitDrawPrimitives(ucfg, vk_buffer, state.vs_input_info, draw, emit);
}
if (set_auto_debug) {
SetDrawDebugPhase(buffer, submit_id, draw, 0x600u);
}
vk::PipelineStageFlags shader_write_stages = {};
if (HasShaderBufferWrites(state.vs_input_info.stage)) {
shader_write_stages |= vk::PipelineStageFlagBits::eVertexShader;
shader_write_stages |= mesh_active ? vk::PipelineStageFlagBits::eMeshShaderEXT
: vk::PipelineStageFlagBits::eVertexShader;
}
if (state.ps_active && HasShaderBufferWrites(state.ps_input_info.stage)) {
shader_write_stages |= vk::PipelineStageFlagBits::eFragmentShader;
@@ -1280,10 +1261,6 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
return;
}
if (ShouldSkipGeShader(buffer)) {
return;
}
if (graphics_debug_dump_enabled()) {
sh_print("GraphicsRenderDrawIndex():Shader:", sh_ctx);
uc_print("GraphicsRenderDrawIndex():UserConfig:", ucfg);
@@ -1412,10 +1389,6 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D
return;
}
if (ShouldSkipGeShader(buffer)) {
return;
}
if (graphics_debug_dump_enabled()) {
sh_print("GraphicsRenderDrawIndexAuto():Shader:", sh_ctx);
uc_print("GraphicsRenderDrawIndexAuto():UserConfig:", ucfg);