mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-26 12:56:54 +01:00
pipeline: look up draw programs and pipelines from the command buffer
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user