mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 11:23:49 +00:00
259 lines
10 KiB
C++
259 lines
10 KiB
C++
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
|
|
|
#include "common/assert.h"
|
|
#include "common/logging/log.h"
|
|
#include "common/profiler.h"
|
|
#include "graphics/guest_gpu/hardwareContext.h"
|
|
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
|
|
#include "graphics/host_gpu/renderer/debug.h"
|
|
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
|
|
#include "graphics/host_gpu/renderer/image/imageView.h"
|
|
#include "graphics/host_gpu/renderer/render.h"
|
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
|
|
|
#include <atomic>
|
|
#include <cstring>
|
|
#include <span>
|
|
#include <utility>
|
|
|
|
namespace Libs::Graphics {
|
|
|
|
namespace {
|
|
|
|
void NormalizeStaticParamsForDynamicState(PipelineStaticParameters& static_params) {
|
|
static_params.viewport_scale[0] = 0.5f;
|
|
static_params.viewport_scale[1] = 0.5f;
|
|
static_params.viewport_scale[2] = 1.0f;
|
|
static_params.viewport_offset[0] = 0.5f;
|
|
static_params.viewport_offset[1] = 0.5f;
|
|
static_params.viewport_offset[2] = 0.0f;
|
|
|
|
static_params.scissor_ltrb[0] = 0;
|
|
static_params.scissor_ltrb[1] = 0;
|
|
static_params.scissor_ltrb[2] = 1;
|
|
static_params.scissor_ltrb[3] = 1;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
PipelineCache::~PipelineCache() {
|
|
auto destroy = [this](const auto& pipelines) {
|
|
for (const auto& [key, pipeline]: pipelines) {
|
|
(void)key;
|
|
m_graphics.device.destroyPipeline(pipeline->pipeline, nullptr);
|
|
m_graphics.device.destroyPipelineLayout(pipeline->pipeline_layout, nullptr);
|
|
}
|
|
};
|
|
destroy(m_graphics_pipelines);
|
|
destroy(m_compute_pipelines);
|
|
}
|
|
|
|
bool PipelineStaticParameters::operator==(const PipelineStaticParameters& other) const noexcept {
|
|
return std::memcmp(this, &other, sizeof(*this)) == 0;
|
|
}
|
|
|
|
PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline(
|
|
RenderColorInfo* colors, uint32_t color_count, RenderDepthInfo& depth,
|
|
ShaderVertexInputInfo& vs_input_info, RenderCommandBuffer& command,
|
|
ShaderPixelInputInfo* ps_input_info, vk::PrimitiveTopology topology, bool ps_active,
|
|
std::span<const uint32_t> vs_spirv, std::span<const uint32_t> ps_spirv) {
|
|
KYTY_PROFILER_BLOCK("PipelineCache::CreatePipeline(Gfx)", profiler::colors::DeepOrangeA200);
|
|
|
|
EXIT_IF(colors == nullptr);
|
|
EXIT_IF(color_count > RENDER_COLOR_ATTACHMENTS_MAX);
|
|
EXIT_IF(vs_spirv.empty());
|
|
EXIT_IF(ps_active && ps_spirv.empty());
|
|
|
|
Common::LockGuard lock(m_mutex);
|
|
auto& ctx = command.GetRegisters();
|
|
auto& sh_ctx = command.GetShaders();
|
|
|
|
const auto& vertex_info = sh_ctx.GetVs();
|
|
const auto& ps_regs = sh_ctx.GetPs();
|
|
const HW::BlendColor& bclr = ctx.GetBlendColor();
|
|
uint32_t color_mask[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
|
for (uint32_t i = 0; i < color_count; i++) {
|
|
color_mask[i] =
|
|
(colors[i].image_id ? colors[i].export_mapping.ApplyMask(render_target_mask_slot(
|
|
ctx.GetRenderTargetMask(), colors[i].target_slot))
|
|
: 0);
|
|
}
|
|
const HW::ModeControl& mc = ctx.GetModeControl();
|
|
|
|
auto vs_id = ShaderGetIdVS(vertex_info, vs_input_info, true);
|
|
ShaderId ps_id {};
|
|
if (ps_active) {
|
|
ps_id = ShaderGetIdPS(ps_regs, *ps_input_info, true);
|
|
}
|
|
|
|
PipelineStaticParameters static_params {};
|
|
GraphicsPipeline p {};
|
|
p.ps_shader_id = ps_id;
|
|
p.vs_shader_id = vs_id;
|
|
|
|
static_params.color_count = color_count;
|
|
PipelineRenderingState rendering {};
|
|
rendering.color_count = color_count;
|
|
uint32_t attachment_samples = 0;
|
|
for (uint32_t i = 0; i < color_count; i++) {
|
|
EXIT_IF(!colors[i].image_id || colors[i].format == vk::Format::eUndefined);
|
|
rendering.color_formats[i] = colors[i].format;
|
|
if (attachment_samples == 0) {
|
|
attachment_samples = colors[i].samples;
|
|
} else if (attachment_samples != colors[i].samples) {
|
|
EXIT("mixed color attachment sample counts are unsupported: %u and %u\n",
|
|
attachment_samples, colors[i].samples);
|
|
}
|
|
}
|
|
const bool with_depth =
|
|
depth.format != vk::Format::eUndefined && static_cast<bool>(depth.image_id);
|
|
if (with_depth) {
|
|
const auto aspects = ImageViewOps::DepthAspectMask(depth.format);
|
|
rendering.depth_format =
|
|
aspects & vk::ImageAspectFlagBits::eDepth ? depth.format : vk::Format::eUndefined;
|
|
rendering.stencil_format =
|
|
aspects & vk::ImageAspectFlagBits::eStencil ? depth.format : vk::Format::eUndefined;
|
|
if (attachment_samples == 0) {
|
|
attachment_samples = depth.samples;
|
|
} else if (attachment_samples != depth.samples) {
|
|
EXIT("mixed color/depth sample counts are unsupported: %u and %u\n", attachment_samples,
|
|
depth.samples);
|
|
}
|
|
}
|
|
EXIT_IF(attachment_samples == 0 ||
|
|
vulkan_sample_count(attachment_samples) == vk::SampleCountFlagBits {});
|
|
|
|
if (ps_active && depth.depth_test_enable && ps_input_info->ps_execute_on_noop) {
|
|
static std::atomic<uint32_t> log_count {0};
|
|
if (log_count.fetch_add(1, std::memory_order_relaxed) < 16) {
|
|
LOGF("Pipeline: temporary: accepting EXEC_ON_NOOP with depth test enabled\n");
|
|
}
|
|
}
|
|
|
|
const auto& clip_control = ctx.GetClipControl();
|
|
EXIT_NOT_IMPLEMENTED(!clip_control.IsZClipModeRepresentable());
|
|
static_params.negative_one_to_one = !clip_control.dx_clip_space;
|
|
static_params.depth_clip_enable = clip_control.IsZClipEnabled();
|
|
static_params.topology = topology;
|
|
static_params.samples = attachment_samples;
|
|
static_params.sample_shading_enable =
|
|
ps_active && attachment_samples > 1 && ps_input_info->ps_sample_shading;
|
|
if (static_params.sample_shading_enable && !m_graphics.sample_rate_shading_enabled) {
|
|
EXIT("Pipeline: sample-rate shading is required but unsupported by the host\n");
|
|
}
|
|
static_params.with_depth = with_depth;
|
|
static_params.depth_test_enable = depth.depth_test_enable;
|
|
static_params.depth_write_enable = (depth.depth_write_enable && !depth.depth_clear_enable);
|
|
static_params.depth_compare_op = depth.depth_compare_op;
|
|
static_params.depth_bounds_test_enable = depth.depth_bounds_test_enable;
|
|
static_params.depth_min_bounds = depth.depth_min_bounds;
|
|
static_params.depth_max_bounds = depth.depth_max_bounds;
|
|
static_params.stencil_test_enable = depth.stencil_test_enable;
|
|
static_params.stencil_front = depth.stencil_static_front;
|
|
static_params.stencil_back = depth.stencil_static_back;
|
|
for (uint32_t i = 0; i < RENDER_COLOR_ATTACHMENTS_MAX; i++) {
|
|
static_params.color_mask[i] = color_mask[i];
|
|
}
|
|
const bool rect_list = topology == vk::PrimitiveTopology::ePatchList;
|
|
static_params.cull_back = !rect_list && mc.cull_back;
|
|
static_params.cull_front = !rect_list && mc.cull_front;
|
|
static_params.face = mc.face;
|
|
|
|
for (uint32_t i = 0; i < color_count; i++) {
|
|
const auto& rt = ctx.GetRenderTarget(colors[i].target_slot);
|
|
const auto& bc = ctx.GetBlendControl(colors[i].target_slot);
|
|
static_params.color_srcblend[i] = bc.color_srcblend;
|
|
static_params.color_comb_fcn[i] = bc.color_comb_fcn;
|
|
static_params.color_destblend[i] = bc.color_destblend;
|
|
static_params.alpha_srcblend[i] = bc.alpha_srcblend;
|
|
static_params.alpha_comb_fcn[i] = bc.alpha_comb_fcn;
|
|
static_params.alpha_destblend[i] = bc.alpha_destblend;
|
|
static_params.separate_alpha_blend[i] = bc.separate_alpha_blend;
|
|
static_params.blend_enable[i] = bc.enable;
|
|
static_params.blend_bypass[i] = rt.info.blend_bypass;
|
|
}
|
|
static_params.blend_color_red = bclr.red;
|
|
static_params.blend_color_green = bclr.green;
|
|
static_params.blend_color_blue = bclr.blue;
|
|
static_params.blend_color_alpha = bclr.alpha;
|
|
|
|
NormalizeStaticParamsForDynamicState(static_params);
|
|
|
|
GraphicsPipelineKey key {};
|
|
key.rendering = rendering;
|
|
key.vs_shader_id = p.vs_shader_id;
|
|
key.ps_shader_id = p.ps_shader_id;
|
|
key.static_params = static_params;
|
|
|
|
if (auto iter = m_graphics_pipelines.find(key); iter != m_graphics_pipelines.end()) {
|
|
return *iter->second;
|
|
}
|
|
|
|
if (graphics_debug_dump_enabled()) {
|
|
ShaderDbgDumpInputInfo(vs_input_info);
|
|
if (ps_active) {
|
|
ShaderDbgDumpInputInfo(*ps_input_info);
|
|
}
|
|
LOGF("PipelineTrace: shader binaries VS=0x%08" PRIx32 "/0x%08" PRIx32 " words=%" PRIu64
|
|
" PS=0x%08" PRIx32 "/0x%08" PRIx32 " words=%" PRIu64 "\n",
|
|
vs_id.hash0, vs_id.crc32, static_cast<uint64_t>(vs_spirv.size()), ps_id.hash0,
|
|
ps_id.crc32, static_cast<uint64_t>(ps_spirv.size()));
|
|
}
|
|
|
|
auto cached = std::make_unique<GraphicsPipeline>(p);
|
|
LogPipelineTrace("CreatePipelineInternal begin", vs_id.hash0, vs_id.crc32, ps_id.hash0,
|
|
ps_id.crc32);
|
|
CreatePipelineInternal(m_graphics, m_descriptor_cache, *cached, rendering, vs_input_info,
|
|
vs_spirv, ps_input_info, ps_spirv, static_params, vs_id.hash0,
|
|
vs_id.crc32, ps_id.hash0, ps_id.crc32, ps_active);
|
|
LogPipelineTrace("CreatePipelineInternal done", vs_id.hash0, vs_id.crc32, ps_id.hash0,
|
|
ps_id.crc32);
|
|
|
|
EXIT_NOT_IMPLEMENTED(cached->pipeline == nullptr);
|
|
EXIT_NOT_IMPLEMENTED(cached->pipeline_layout == nullptr);
|
|
|
|
auto [iter, inserted] = m_graphics_pipelines.emplace(std::move(key), std::move(cached));
|
|
EXIT_IF(!inserted);
|
|
|
|
return *iter->second;
|
|
}
|
|
|
|
PipelineCache::ComputePipeline&
|
|
PipelineCache::CreateComputePipeline(ShaderComputeInputInfo& input_info,
|
|
const HW::ComputeShaderInfo& cs_regs,
|
|
std::span<const uint32_t> cs_spirv) {
|
|
KYTY_PROFILER_BLOCK("PipelineCache::CreatePipeline(Compute)", profiler::colors::RedA100);
|
|
|
|
EXIT_IF(cs_spirv.empty());
|
|
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
auto cs_id = ShaderGetIdCS(cs_regs, input_info, true);
|
|
|
|
ComputePipeline p {};
|
|
p.cs_shader_id = cs_id;
|
|
|
|
ComputePipelineKey key {};
|
|
key.cs_shader_id = p.cs_shader_id;
|
|
|
|
if (auto iter = m_compute_pipelines.find(key); iter != m_compute_pipelines.end()) {
|
|
return *iter->second;
|
|
}
|
|
|
|
if (graphics_debug_dump_enabled()) {
|
|
ShaderDbgDumpInputInfo(input_info);
|
|
}
|
|
|
|
auto cached = std::make_unique<ComputePipeline>(p);
|
|
CreatePipelineInternal(m_graphics, m_descriptor_cache, *cached, input_info, cs_spirv);
|
|
|
|
EXIT_NOT_IMPLEMENTED(cached->pipeline == nullptr);
|
|
EXIT_NOT_IMPLEMENTED(cached->pipeline_layout == nullptr);
|
|
|
|
auto [iter, inserted] = m_compute_pipelines.emplace(std::move(key), std::move(cached));
|
|
EXIT_IF(!inserted);
|
|
|
|
return *iter->second;
|
|
}
|
|
} // namespace Libs::Graphics
|