mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
851 lines
34 KiB
C++
851 lines
34 KiB
C++
#include "common/assert.h"
|
|
#include "common/common.h"
|
|
#include "common/emulatorConfig.h"
|
|
#include "common/logging/log.h"
|
|
#include "common/profiler.h"
|
|
#include "graphics/guest_gpu/gpu_defs.h"
|
|
#include "graphics/host_gpu/graphicContext.h"
|
|
#include "graphics/host_gpu/renderer/debug.h"
|
|
#include "graphics/host_gpu/renderer/pipeline/descriptors.h"
|
|
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
|
#include "graphics/host_gpu/renderer/render.h"
|
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
|
#include "graphics/host_gpu/renderer/renderTarget.h"
|
|
#include "graphics/host_gpu/vulkanCommon.h"
|
|
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
|
#include "graphics/shader/rectListShader.h"
|
|
#include "graphics/shader/shader.h"
|
|
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <span>
|
|
#include <vector>
|
|
|
|
namespace Libs::Graphics {
|
|
|
|
// IDK: maybe we can remove it?
|
|
constexpr uint8_t kTemporaryVertexAttribFormat113 =
|
|
static_cast<uint8_t>(Prospero::VertexAttribFormat::k16_16SInt);
|
|
constexpr uint32_t kTemporaryPs5BufferFormat121 = 121u;
|
|
|
|
static bool NarrowInputFormat(vk::Format& format, uint32_t& size, uint32_t used_components) {
|
|
if (used_components == 0 || used_components >= size) {
|
|
return false;
|
|
}
|
|
|
|
switch (format) {
|
|
case vk::Format::eR32G32B32A32Sfloat:
|
|
switch (used_components) {
|
|
case 1: format = vk::Format::eR32Sfloat; break;
|
|
case 2: format = vk::Format::eR32G32Sfloat; break;
|
|
case 3: format = vk::Format::eR32G32B32Sfloat; break;
|
|
default: return false;
|
|
}
|
|
size = used_components;
|
|
return true;
|
|
case vk::Format::eR32G32B32Sfloat:
|
|
switch (used_components) {
|
|
case 1: format = vk::Format::eR32Sfloat; break;
|
|
case 2: format = vk::Format::eR32G32Sfloat; break;
|
|
default: return false;
|
|
}
|
|
size = used_components;
|
|
return true;
|
|
case vk::Format::eR16G16B16A16Sfloat:
|
|
switch (used_components) {
|
|
case 1: format = vk::Format::eR16Sfloat; break;
|
|
case 2: format = vk::Format::eR16G16Sfloat; break;
|
|
default: return false;
|
|
}
|
|
size = used_components;
|
|
return true;
|
|
case vk::Format::eR8G8B8A8Unorm:
|
|
switch (used_components) {
|
|
case 1: format = vk::Format::eR8Unorm; break;
|
|
case 2: format = vk::Format::eR8G8Unorm; break;
|
|
default: return false;
|
|
}
|
|
size = used_components;
|
|
return true;
|
|
case vk::Format::eR8G8B8A8Snorm:
|
|
if (used_components != 2) {
|
|
return false;
|
|
}
|
|
format = vk::Format::eR8G8Snorm;
|
|
size = 2;
|
|
return true;
|
|
case vk::Format::eR8G8B8A8Uint:
|
|
switch (used_components) {
|
|
case 1: format = vk::Format::eR8Uint; break;
|
|
case 2: format = vk::Format::eR8G8Uint; break;
|
|
default: return false;
|
|
}
|
|
size = used_components;
|
|
return true;
|
|
default: break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void GetInputFormat(const ShaderBufferResource& res, vk::Format& format, uint32_t& size,
|
|
uint32_t used_components) {
|
|
const auto fmt = res.Format();
|
|
const auto raw_format = res.RawFormat();
|
|
if (raw_format == kTemporaryVertexAttribFormat113) {
|
|
static bool logged_113 = false;
|
|
if (!logged_113) {
|
|
LOGF("InputFormat: temporary: accepting invalid PS5 buffer format 113 as "
|
|
"vk::Format::eR32G32B32A32Sfloat\n");
|
|
logged_113 = true;
|
|
}
|
|
format = vk::Format::eR32G32B32A32Sfloat;
|
|
size = 4;
|
|
if (NarrowInputFormat(format, size, used_components)) {
|
|
LOGF("InputFormat: narrowing fmt=%u to %s for used_components=%u\n", raw_format,
|
|
vk::to_string(format).c_str(), used_components);
|
|
}
|
|
return;
|
|
}
|
|
if (raw_format == kTemporaryPs5BufferFormat121) {
|
|
static bool logged_121 = false;
|
|
if (!logged_121) {
|
|
LOGF("InputFormat: accepting PS5 buffer format 121 as vk::Format::eR16G16Sfloat\n");
|
|
logged_121 = true;
|
|
}
|
|
format = vk::Format::eR16G16Sfloat;
|
|
size = 2;
|
|
if (NarrowInputFormat(format, size, used_components)) {
|
|
LOGF("InputFormat: narrowing fmt=%u to %s for used_components=%u\n", raw_format,
|
|
vk::to_string(format).c_str(), used_components);
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (fmt) {
|
|
case Prospero::BufferFormat::k32_32_32_32Float:
|
|
format = vk::Format::eR32G32B32A32Sfloat;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k32_32_32_32SInt:
|
|
format = vk::Format::eR32G32B32A32Sint;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k32_32_32_32UInt:
|
|
format = vk::Format::eR32G32B32A32Uint;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k32_32_32Float:
|
|
format = vk::Format::eR32G32B32Sfloat;
|
|
size = 3;
|
|
break;
|
|
case Prospero::BufferFormat::k32_32_32SInt:
|
|
format = vk::Format::eR32G32B32Sint;
|
|
size = 3;
|
|
break;
|
|
case Prospero::BufferFormat::k32_32_32UInt:
|
|
format = vk::Format::eR32G32B32Uint;
|
|
size = 3;
|
|
break;
|
|
case Prospero::BufferFormat::k16_16_16_16Float:
|
|
format = vk::Format::eR16G16B16A16Sfloat;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k16_16_16_16SInt:
|
|
format = vk::Format::eR16G16B16A16Sint;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k16_16_16_16UInt:
|
|
format = vk::Format::eR16G16B16A16Uint;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k16_16_16_16SScaled:
|
|
format = vk::Format::eR16G16B16A16Sscaled;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k16_16_16_16UScaled:
|
|
format = vk::Format::eR16G16B16A16Uscaled;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k16_16_16_16SNorm:
|
|
format = vk::Format::eR16G16B16A16Snorm;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k16_16_16_16UNorm:
|
|
format = vk::Format::eR16G16B16A16Unorm;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k32_32Float:
|
|
format = vk::Format::eR32G32Sfloat;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k32_32SInt:
|
|
format = vk::Format::eR32G32Sint;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k32_32UInt:
|
|
format = vk::Format::eR32G32Uint;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k8_8_8_8UInt:
|
|
format = vk::Format::eR8G8B8A8Uint;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k8_8_8_8SScaled:
|
|
format = vk::Format::eR8G8B8A8Sscaled;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k8_8_8_8UScaled:
|
|
format = vk::Format::eR8G8B8A8Uscaled;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k8_8_8_8SNorm:
|
|
format = vk::Format::eR8G8B8A8Snorm;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k8_8_8_8UNorm:
|
|
format = vk::Format::eR8G8B8A8Unorm;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k10_10_10_2UNorm:
|
|
format = vk::Format::eA2B10G10R10UnormPack32;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k10_10_10_2SNorm:
|
|
format = vk::Format::eA2B10G10R10SnormPack32;
|
|
size = 4;
|
|
break;
|
|
case Prospero::BufferFormat::k16_16Float:
|
|
format = vk::Format::eR16G16Sfloat;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k16_16SInt:
|
|
format = vk::Format::eR16G16Sint;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k16_16UInt:
|
|
format = vk::Format::eR16G16Uint;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k16_16SScaled:
|
|
format = vk::Format::eR16G16Sscaled;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k16_16UScaled:
|
|
format = vk::Format::eR16G16Uscaled;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k16_16SNorm:
|
|
format = vk::Format::eR16G16Snorm;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k16_16UNorm:
|
|
format = vk::Format::eR16G16Unorm;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k32Float:
|
|
format = vk::Format::eR32Sfloat;
|
|
size = 1;
|
|
break;
|
|
case Prospero::BufferFormat::k32SInt:
|
|
format = vk::Format::eR32Sint;
|
|
size = 1;
|
|
break;
|
|
case Prospero::BufferFormat::k32UInt:
|
|
format = vk::Format::eR32Uint;
|
|
size = 1;
|
|
break;
|
|
case Prospero::BufferFormat::k8_8SInt:
|
|
format = vk::Format::eR8G8Sint;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k8_8UInt:
|
|
format = vk::Format::eR8G8Uint;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k8_8SScaled:
|
|
format = vk::Format::eR8G8Sscaled;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k8_8UScaled:
|
|
format = vk::Format::eR8G8Uscaled;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k8_8SNorm:
|
|
format = vk::Format::eR8G8Snorm;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k8_8UNorm:
|
|
format = vk::Format::eR8G8Unorm;
|
|
size = 2;
|
|
break;
|
|
case Prospero::BufferFormat::k16Float:
|
|
format = vk::Format::eR16Sfloat;
|
|
size = 1;
|
|
break;
|
|
case Prospero::BufferFormat::k16SInt:
|
|
format = vk::Format::eR16Sint;
|
|
size = 1;
|
|
break;
|
|
case Prospero::BufferFormat::k16UInt:
|
|
format = vk::Format::eR16Uint;
|
|
size = 1;
|
|
break;
|
|
case Prospero::BufferFormat::k16SScaled:
|
|
format = vk::Format::eR16Sscaled;
|
|
size = 1;
|
|
break;
|
|
case Prospero::BufferFormat::k16UScaled:
|
|
format = vk::Format::eR16Uscaled;
|
|
size = 1;
|
|
break;
|
|
case Prospero::BufferFormat::k16SNorm:
|
|
format = vk::Format::eR16Snorm;
|
|
size = 1;
|
|
break;
|
|
case Prospero::BufferFormat::k16UNorm:
|
|
format = vk::Format::eR16Unorm;
|
|
size = 1;
|
|
break;
|
|
case Prospero::BufferFormat::k8SInt:
|
|
format = vk::Format::eR8Sint;
|
|
size = 1;
|
|
break;
|
|
case Prospero::BufferFormat::k8UInt:
|
|
format = vk::Format::eR8Uint;
|
|
size = 1;
|
|
break;
|
|
case Prospero::BufferFormat::k8SScaled:
|
|
format = vk::Format::eR8Sscaled;
|
|
size = 1;
|
|
break;
|
|
case Prospero::BufferFormat::k8UScaled:
|
|
format = vk::Format::eR8Uscaled;
|
|
size = 1;
|
|
break;
|
|
case Prospero::BufferFormat::k8SNorm:
|
|
format = vk::Format::eR8Snorm;
|
|
size = 1;
|
|
break;
|
|
case Prospero::BufferFormat::k8UNorm:
|
|
format = vk::Format::eR8Unorm;
|
|
size = 1;
|
|
break;
|
|
default:
|
|
EXIT("unknown format: fmt = %u\n", raw_format);
|
|
format = vk::Format::eUndefined;
|
|
size = 4;
|
|
break;
|
|
}
|
|
|
|
if (NarrowInputFormat(format, size, used_components)) {
|
|
static std::atomic<uint64_t> log_count = 0;
|
|
auto log_id = log_count.fetch_add(1);
|
|
if (log_id < 32) {
|
|
LOGF("VertexInput: narrowed vertex format to %" PRIu32
|
|
" component(s) for shader fetch\n",
|
|
used_components);
|
|
}
|
|
}
|
|
}
|
|
|
|
static vk::BlendFactor GetBlendFactor(uint32_t factor) {
|
|
switch (static_cast<Prospero::BlendFactor>(factor)) {
|
|
case Prospero::BlendFactor::kZero: return vk::BlendFactor::eZero;
|
|
case Prospero::BlendFactor::kOne: return vk::BlendFactor::eOne;
|
|
case Prospero::BlendFactor::kSrcColor: return vk::BlendFactor::eSrcColor;
|
|
case Prospero::BlendFactor::kOneMinusSrcColor: return vk::BlendFactor::eOneMinusSrcColor;
|
|
case Prospero::BlendFactor::kSrcAlpha: return vk::BlendFactor::eSrcAlpha;
|
|
case Prospero::BlendFactor::kOneMinusSrcAlpha: return vk::BlendFactor::eOneMinusSrcAlpha;
|
|
case Prospero::BlendFactor::kDstAlpha: return vk::BlendFactor::eDstAlpha;
|
|
case Prospero::BlendFactor::kOneMinusDstAlpha: return vk::BlendFactor::eOneMinusDstAlpha;
|
|
case Prospero::BlendFactor::kDstColor: return vk::BlendFactor::eDstColor;
|
|
case Prospero::BlendFactor::kOneMinusDstColor: return vk::BlendFactor::eOneMinusDstColor;
|
|
case Prospero::BlendFactor::kSrcAlphaSaturate: return vk::BlendFactor::eSrcAlphaSaturate;
|
|
case Prospero::BlendFactor::kConstantColor: return vk::BlendFactor::eConstantColor;
|
|
case Prospero::BlendFactor::kOneMinusConstantColor:
|
|
return vk::BlendFactor::eOneMinusConstantColor;
|
|
case Prospero::BlendFactor::kSrc1Color: return vk::BlendFactor::eSrc1Color;
|
|
case Prospero::BlendFactor::kOneMinusSrc1Color: return vk::BlendFactor::eOneMinusSrc1Color;
|
|
case Prospero::BlendFactor::kSrc1Alpha: return vk::BlendFactor::eSrc1Alpha;
|
|
case Prospero::BlendFactor::kOneMinusSrc1Alpha: return vk::BlendFactor::eOneMinusSrc1Alpha;
|
|
case Prospero::BlendFactor::kConstantAlpha: return vk::BlendFactor::eConstantAlpha;
|
|
case Prospero::BlendFactor::kOneMinusConstantAlpha:
|
|
return vk::BlendFactor::eOneMinusConstantAlpha;
|
|
default: EXIT("unknown factor: %u\n", factor);
|
|
}
|
|
return vk::BlendFactor::eZero;
|
|
}
|
|
|
|
static vk::BlendOp GetBlendOp(uint32_t op) {
|
|
switch (static_cast<Prospero::BlendOp>(op)) {
|
|
case Prospero::BlendOp::kAdd: return vk::BlendOp::eAdd;
|
|
case Prospero::BlendOp::kSubtract: return vk::BlendOp::eSubtract;
|
|
case Prospero::BlendOp::kMin: return vk::BlendOp::eMin;
|
|
case Prospero::BlendOp::kMax: return vk::BlendOp::eMax;
|
|
case Prospero::BlendOp::kReverseSubtract: return vk::BlendOp::eReverseSubtract;
|
|
default: EXIT("unknown op: %u\n", op);
|
|
}
|
|
return vk::BlendOp::eAdd;
|
|
}
|
|
|
|
static void AddLayoutBindings(std::vector<vk::DescriptorSetLayoutBinding>& descriptor_bindings,
|
|
const ShaderRecompiler::IR::CompiledShaderInfo& program,
|
|
vk::ShaderStageFlagBits stage) {
|
|
for (const auto& binding: program.bindings.descriptors) {
|
|
descriptor_bindings.push_back(
|
|
{ShaderRecompiler::IR::NativeBinding(program.stage, binding.kind),
|
|
NativeDescriptorType(binding.kind), NativeDescriptorCount(binding), stage, nullptr});
|
|
}
|
|
}
|
|
|
|
static void CreateDescriptorLayout(GraphicContext& graphics, PipelineCache::Pipeline& pipeline,
|
|
std::span<const vk::DescriptorSetLayoutBinding> bindings) {
|
|
uint32_t descriptor_count = 0;
|
|
for (const auto& binding: bindings) {
|
|
descriptor_count += binding.descriptorCount;
|
|
}
|
|
pipeline.uses_push_descriptors = descriptor_count <= graphics.max_push_descriptors;
|
|
|
|
vk::DescriptorSetLayoutCreateInfo create {};
|
|
create.flags = pipeline.uses_push_descriptors
|
|
? vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR
|
|
: vk::DescriptorSetLayoutCreateFlags {};
|
|
create.bindingCount = static_cast<uint32_t>(bindings.size());
|
|
create.pBindings = bindings.data();
|
|
EXIT_IF(graphics.device.createDescriptorSetLayout(
|
|
&create, nullptr, &pipeline.descriptor_set_layout) != vk::Result::eSuccess);
|
|
}
|
|
|
|
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
|
void CreatePipelineInternal(
|
|
GraphicContext& graphics, PipelineCache::Pipeline& pipeline,
|
|
const PipelineRenderingState& rendering, const PipelineVertexInputState& vertex_input,
|
|
const ShaderVertexInputInfo& vs_input_info, const ShaderProgram& vertex_program,
|
|
const ShaderPixelInputInfo* ps_input_info, const ShaderProgram& pixel_program,
|
|
const PipelineStaticParameters& static_params, vk::PipelineCache driver_cache) {
|
|
const bool ps_active = ps_input_info != nullptr;
|
|
EXIT_IF(!vertex_program || (ps_active && !pixel_program));
|
|
const bool with_depth = rendering.depth_format != vk::Format::eUndefined ||
|
|
rendering.stencil_format != vk::Format::eUndefined;
|
|
EXIT_IF(!vs_input_info.stage);
|
|
const bool mesh = vs_input_info.stage.program->stage == ShaderType::Mesh;
|
|
EXIT_NOT_IMPLEMENTED(mesh && !graphics.mesh_shader_enabled);
|
|
const auto vertex_stage =
|
|
mesh ? vk::ShaderStageFlagBits::eMeshEXT : vk::ShaderStageFlagBits::eVertex;
|
|
|
|
const bool rect_list = !mesh && static_params.topology == vk::PrimitiveTopology::ePatchList;
|
|
|
|
vk::ShaderModule tess_control_shader_module = nullptr;
|
|
vk::ShaderModule tess_eval_shader_module = nullptr;
|
|
|
|
vk::ShaderModuleCreateInfo create_info {};
|
|
vk::Result result {};
|
|
if (rect_list) {
|
|
const auto shaders =
|
|
BuildRectListShaders(vs_input_info, ps_active ? ps_input_info : nullptr);
|
|
create_info.codeSize = shaders.control.size() * 4;
|
|
create_info.pCode = shaders.control.data();
|
|
result =
|
|
graphics.device.createShaderModule(&create_info, nullptr, &tess_control_shader_module);
|
|
if (graphics_debug_dump_enabled()) {
|
|
LOGF("PipelineTrace: vkCreateShaderModule RectList TCS done result=%s module=%p\n",
|
|
vk::to_string(result).c_str(), static_cast<void*>(tess_control_shader_module));
|
|
}
|
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
|
|
|
create_info.codeSize = shaders.evaluation.size() * 4;
|
|
create_info.pCode = shaders.evaluation.data();
|
|
result =
|
|
graphics.device.createShaderModule(&create_info, nullptr, &tess_eval_shader_module);
|
|
if (graphics_debug_dump_enabled()) {
|
|
LOGF("PipelineTrace: vkCreateShaderModule RectList TES done result=%s module=%p\n",
|
|
vk::to_string(result).c_str(), static_cast<void*>(tess_eval_shader_module));
|
|
}
|
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
|
}
|
|
|
|
EXIT_NOT_IMPLEMENTED(
|
|
rect_list && (tess_control_shader_module == nullptr || tess_eval_shader_module == nullptr));
|
|
|
|
vk::PipelineShaderStageCreateInfo vert_shader_stage_info {};
|
|
vert_shader_stage_info.stage = vertex_stage;
|
|
vert_shader_stage_info.module = vertex_program.module;
|
|
vert_shader_stage_info.pName = "main";
|
|
|
|
vk::PipelineShaderStageCreateInfo frag_shader_stage_info {};
|
|
frag_shader_stage_info.stage = vk::ShaderStageFlagBits::eFragment;
|
|
frag_shader_stage_info.module = pixel_program.module;
|
|
frag_shader_stage_info.pName = "main";
|
|
|
|
vk::PipelineShaderStageCreateInfo tess_control_shader_stage_info {};
|
|
tess_control_shader_stage_info.stage = vk::ShaderStageFlagBits::eTessellationControl;
|
|
tess_control_shader_stage_info.module = tess_control_shader_module;
|
|
tess_control_shader_stage_info.pName = "main";
|
|
|
|
vk::PipelineShaderStageCreateInfo tess_eval_shader_stage_info {};
|
|
tess_eval_shader_stage_info.stage = vk::ShaderStageFlagBits::eTessellationEvaluation;
|
|
tess_eval_shader_stage_info.module = tess_eval_shader_module;
|
|
tess_eval_shader_stage_info.pName = "main";
|
|
|
|
vk::PipelineShaderStageCreateInfo shader_stages[4] = {};
|
|
uint32_t shader_stage_count = 0;
|
|
shader_stages[shader_stage_count++] = vert_shader_stage_info;
|
|
if (rect_list) {
|
|
shader_stages[shader_stage_count++] = tess_control_shader_stage_info;
|
|
shader_stages[shader_stage_count++] = tess_eval_shader_stage_info;
|
|
}
|
|
if (ps_active) {
|
|
shader_stages[shader_stage_count++] = frag_shader_stage_info;
|
|
}
|
|
|
|
vk::VertexInputAttributeDescription input_attr[ShaderVertexInputInfo::RES_MAX] {};
|
|
vk::VertexInputBindingDescription input_desc[ShaderVertexInputInfo::RES_MAX] {};
|
|
|
|
for (uint32_t binding = 0; binding < vertex_input.binding_count; binding++) {
|
|
input_desc[binding].binding = binding;
|
|
input_desc[binding].stride = vertex_input.bindings[binding].stride;
|
|
input_desc[binding].inputRate = vertex_input.bindings[binding].instance
|
|
? vk::VertexInputRate::eInstance
|
|
: vk::VertexInputRate::eVertex;
|
|
}
|
|
for (uint32_t index = 0; index < vertex_input.attribute_count; index++) {
|
|
input_attr[index].binding = vertex_input.attributes[index].binding;
|
|
input_attr[index].location = index;
|
|
input_attr[index].offset = vertex_input.attributes[index].offset;
|
|
|
|
uint32_t attr_size = 4;
|
|
const auto registers_num = vs_input_info.resources_dst[index].registers_num;
|
|
const auto compiled_components =
|
|
vs_input_info.stage.program->info.vertex_fetch_components[index];
|
|
const auto used_components =
|
|
compiled_components > 0 ? static_cast<int>(compiled_components) : registers_num;
|
|
GetInputFormat(vs_input_info.resources[index], input_attr[index].format, attr_size,
|
|
static_cast<uint32_t>(used_components));
|
|
|
|
if (graphics_debug_dump_enabled()) {
|
|
static std::atomic_uint log_count = 0;
|
|
const auto log_id = log_count.fetch_add(1, std::memory_order_relaxed);
|
|
if (log_id < 128) {
|
|
LOGF("VertexInputState[%u]: attr=%u binding=%u offset=%u stride=%u fmt=%d "
|
|
"src_fmt=%u dst=v%u regs=%u"
|
|
" fetched_components=%u attr_size=%u swizzle=%u,%u,%u,%u\n",
|
|
log_id, index, input_attr[index].binding, input_attr[index].offset,
|
|
input_desc[input_attr[index].binding].stride,
|
|
static_cast<int>(input_attr[index].format),
|
|
static_cast<uint32_t>(vs_input_info.resources[index].Format()),
|
|
static_cast<uint32_t>(vs_input_info.resources_dst[index].register_start),
|
|
static_cast<uint32_t>(registers_num), static_cast<uint32_t>(used_components),
|
|
attr_size, static_cast<uint32_t>(vs_input_info.resources[index].DstSelX()),
|
|
static_cast<uint32_t>(vs_input_info.resources[index].DstSelY()),
|
|
static_cast<uint32_t>(vs_input_info.resources[index].DstSelZ()),
|
|
static_cast<uint32_t>(vs_input_info.resources[index].DstSelW()));
|
|
}
|
|
}
|
|
|
|
if (vs_input_info.resources[index].OutOfBounds() != 0) {
|
|
static bool logged = false;
|
|
if (!logged) {
|
|
LOGF("VertexInput: temporary: accepting PS5 out-of-bounds behavior %" PRIu8 "\n",
|
|
vs_input_info.resources[index].OutOfBounds());
|
|
logged = true;
|
|
}
|
|
}
|
|
|
|
EXIT_NOT_IMPLEMENTED(vs_input_info.resources[index].AddTid());
|
|
EXIT_NOT_IMPLEMENTED(vs_input_info.resources[index].SwizzleEnabled());
|
|
|
|
EXIT_IF(registers_num < 1 || registers_num > 4);
|
|
}
|
|
|
|
vk::PipelineVertexInputStateCreateInfo vertex_input_info {};
|
|
vertex_input_info.vertexBindingDescriptionCount = vertex_input.binding_count;
|
|
vertex_input_info.pVertexBindingDescriptions = input_desc;
|
|
vertex_input_info.vertexAttributeDescriptionCount = vertex_input.attribute_count;
|
|
vertex_input_info.pVertexAttributeDescriptions = input_attr;
|
|
|
|
vk::PipelineInputAssemblyStateCreateInfo input_assembly {};
|
|
input_assembly.topology = static_params.topology;
|
|
input_assembly.primitiveRestartEnable =
|
|
static_params.primitive_restart_enable ? VK_TRUE : VK_FALSE;
|
|
|
|
vk::PipelineViewportDepthClipControlCreateInfoEXT depth_clip_control {};
|
|
depth_clip_control.negativeOneToOne = (static_params.negative_one_to_one ? VK_TRUE : VK_FALSE);
|
|
|
|
vk::PipelineViewportStateCreateInfo viewport_state {};
|
|
viewport_state.pNext = &depth_clip_control;
|
|
viewport_state.viewportCount = 1;
|
|
viewport_state.scissorCount = 1;
|
|
|
|
vk::CullModeFlags cull_mode = vk::CullModeFlagBits::eNone;
|
|
if (static_params.cull_back) {
|
|
cull_mode |= vk::CullModeFlagBits::eBack;
|
|
}
|
|
if (static_params.cull_front) {
|
|
cull_mode |= vk::CullModeFlagBits::eFront;
|
|
}
|
|
|
|
vk::FrontFace front_face =
|
|
(static_params.face ? vk::FrontFace::eClockwise : vk::FrontFace::eCounterClockwise);
|
|
|
|
vk::PipelineRasterizationDepthClipStateCreateInfoEXT clip_ext {};
|
|
clip_ext.depthClipEnable = static_params.depth_clip_enable ? VK_TRUE : VK_FALSE;
|
|
|
|
vk::PipelineRasterizationStateCreateInfo rasterizer {};
|
|
// MoltenVK lacks VK_EXT_depth_clip_enable; omit the depth-clip struct on macOS and accept
|
|
// Vulkan's default depth clipping (enabled) instead of the PS5's clamp behavior.
|
|
#if !defined(__APPLE__)
|
|
rasterizer.pNext = &clip_ext;
|
|
#endif
|
|
rasterizer.cullMode = cull_mode;
|
|
rasterizer.frontFace = front_face;
|
|
rasterizer.lineWidth = 1.0f;
|
|
|
|
vk::PipelineMultisampleStateCreateInfo multisampling {};
|
|
multisampling.sampleShadingEnable = static_params.sample_shading_enable ? VK_TRUE : VK_FALSE;
|
|
multisampling.rasterizationSamples = vulkan_sample_count(static_params.samples);
|
|
multisampling.minSampleShading = 1.0f;
|
|
|
|
vk::PipelineColorBlendAttachmentState color_blend_attachment[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
|
for (uint32_t i = 0; i < rendering.color_count; i++) {
|
|
EXIT_NOT_IMPLEMENTED((static_params.color_mask[i] & ~0x0fu) != 0);
|
|
color_blend_attachment[i].colorWriteMask =
|
|
vk::ColorComponentFlags {static_params.color_mask[i]};
|
|
color_blend_attachment[i].blendEnable =
|
|
(static_params.blend_enable[i] && !static_params.blend_bypass[i]) ? VK_TRUE : VK_FALSE;
|
|
color_blend_attachment[i].srcColorBlendFactor =
|
|
GetBlendFactor(static_params.color_srcblend[i]);
|
|
color_blend_attachment[i].dstColorBlendFactor =
|
|
GetBlendFactor(static_params.color_destblend[i]);
|
|
color_blend_attachment[i].colorBlendOp = GetBlendOp(static_params.color_comb_fcn[i]);
|
|
color_blend_attachment[i].srcAlphaBlendFactor =
|
|
(static_params.separate_alpha_blend[i] ? GetBlendFactor(static_params.alpha_srcblend[i])
|
|
: color_blend_attachment[i].srcColorBlendFactor);
|
|
color_blend_attachment[i].dstAlphaBlendFactor =
|
|
(static_params.separate_alpha_blend[i]
|
|
? GetBlendFactor(static_params.alpha_destblend[i])
|
|
: color_blend_attachment[i].dstColorBlendFactor);
|
|
color_blend_attachment[i].alphaBlendOp =
|
|
(static_params.separate_alpha_blend[i] ? GetBlendOp(static_params.alpha_comb_fcn[i])
|
|
: color_blend_attachment[i].colorBlendOp);
|
|
}
|
|
|
|
vk::Bool32 color_write_enable[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
|
for (uint32_t i = 0; i < rendering.color_count; i++) {
|
|
color_write_enable[i] = VK_TRUE;
|
|
}
|
|
|
|
vk::PipelineColorWriteCreateInfoEXT color_write {};
|
|
color_write.attachmentCount = rendering.color_count;
|
|
color_write.pColorWriteEnables = color_write_enable;
|
|
|
|
vk::PipelineColorBlendStateCreateInfo color_blending {};
|
|
// MoltenVK lacks VK_EXT_color_write_enable; drop the dynamic color-write struct on macOS
|
|
// and rely on each attachment's static colorWriteMask (all channels enabled by default).
|
|
#if !defined(__APPLE__)
|
|
color_blending.pNext = &color_write;
|
|
#endif
|
|
color_blending.logicOp = vk::LogicOp::eCopy;
|
|
color_blending.attachmentCount = rendering.color_count;
|
|
color_blending.pAttachments = color_blend_attachment;
|
|
|
|
std::vector<vk::DescriptorSetLayoutBinding> descriptor_bindings;
|
|
AddLayoutBindings(descriptor_bindings, *vs_input_info.stage.program, vertex_stage);
|
|
if (ps_active) {
|
|
EXIT_IF(!ps_input_info->stage);
|
|
AddLayoutBindings(descriptor_bindings, *ps_input_info->stage.program,
|
|
vk::ShaderStageFlagBits::eFragment);
|
|
}
|
|
CreateDescriptorLayout(graphics, pipeline, descriptor_bindings);
|
|
const auto GraphicsStages = vertex_stage | vk::ShaderStageFlagBits::eFragment;
|
|
const vk::PushConstantRange push_constants {GraphicsStages, 0,
|
|
ShaderRecompiler::IR::NativePushConstantSize};
|
|
|
|
vk::PipelineLayoutCreateInfo pipeline_layout_info {};
|
|
pipeline_layout_info.setLayoutCount = 1;
|
|
pipeline_layout_info.pSetLayouts = &pipeline.descriptor_set_layout;
|
|
pipeline_layout_info.pushConstantRangeCount = 1;
|
|
pipeline_layout_info.pPushConstantRanges = &push_constants;
|
|
|
|
EXIT_IF(pipeline.pipeline_layout != nullptr);
|
|
|
|
if (graphics_debug_dump_enabled()) {
|
|
LOGF("PipelineTrace: vkCreatePipelineLayout begin VS=%" PRIu64 " PS=%" PRIu64
|
|
" set_layouts=1 push_constants=%" PRIu32 "\n",
|
|
vertex_program.id, ps_active ? pixel_program.id : 0, 1u);
|
|
}
|
|
result = graphics.device.createPipelineLayout(&pipeline_layout_info, nullptr,
|
|
&pipeline.pipeline_layout);
|
|
if (graphics_debug_dump_enabled()) {
|
|
LOGF("PipelineTrace: vkCreatePipelineLayout done result=%s layout=%p\n",
|
|
vk::to_string(result).c_str(), static_cast<void*>(pipeline.pipeline_layout));
|
|
}
|
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
|
|
|
EXIT_NOT_IMPLEMENTED(pipeline.pipeline_layout == nullptr);
|
|
|
|
vk::PipelineDepthStencilStateCreateInfo depth_stencil_info {};
|
|
depth_stencil_info.depthBoundsTestEnable =
|
|
#if defined(__APPLE__)
|
|
VK_FALSE; // MoltenVK lacks the depthBounds feature; depth-bounds testing is disabled
|
|
#else
|
|
(static_params.depth_bounds_test_enable ? VK_TRUE : VK_FALSE);
|
|
#endif
|
|
depth_stencil_info.stencilTestEnable = (static_params.stencil_test_enable ? VK_TRUE : VK_FALSE);
|
|
depth_stencil_info.front.failOp = static_params.stencil_front.failOp;
|
|
depth_stencil_info.front.passOp = static_params.stencil_front.passOp;
|
|
depth_stencil_info.front.depthFailOp = static_params.stencil_front.depthFailOp;
|
|
depth_stencil_info.front.compareOp = static_params.stencil_front.compareOp;
|
|
depth_stencil_info.back.failOp = static_params.stencil_back.failOp;
|
|
depth_stencil_info.back.passOp = static_params.stencil_back.passOp;
|
|
depth_stencil_info.back.depthFailOp = static_params.stencil_back.depthFailOp;
|
|
depth_stencil_info.back.compareOp = static_params.stencil_back.compareOp;
|
|
depth_stencil_info.minDepthBounds = static_params.depth_min_bounds;
|
|
depth_stencil_info.maxDepthBounds = static_params.depth_max_bounds;
|
|
|
|
const vk::DynamicState dynamic_states[] = {
|
|
vk::DynamicState::eViewport,
|
|
vk::DynamicState::eScissor,
|
|
vk::DynamicState::eLineWidth,
|
|
vk::DynamicState::eDepthTestEnable,
|
|
vk::DynamicState::eDepthWriteEnable,
|
|
vk::DynamicState::eDepthCompareOp,
|
|
vk::DynamicState::eDepthBiasEnable,
|
|
vk::DynamicState::eDepthBias,
|
|
vk::DynamicState::eStencilCompareMask,
|
|
vk::DynamicState::eStencilReference,
|
|
vk::DynamicState::eStencilWriteMask,
|
|
vk::DynamicState::eBlendConstants,
|
|
#if !defined(__APPLE__)
|
|
// Keep last so depth-only pipelines can omit this dynamic state.
|
|
vk::DynamicState::eColorWriteEnableEXT, // unsupported by MoltenVK; static mask instead
|
|
#endif
|
|
};
|
|
auto dynamic_states_count =
|
|
static_cast<uint32_t>(sizeof(dynamic_states) / sizeof(dynamic_states[0]));
|
|
#if !defined(__APPLE__)
|
|
if (rendering.color_count == 0) {
|
|
dynamic_states_count--;
|
|
}
|
|
#endif
|
|
|
|
vk::PipelineDynamicStateCreateInfo dynamic_state {};
|
|
dynamic_state.dynamicStateCount = dynamic_states_count;
|
|
dynamic_state.pDynamicStates = dynamic_states;
|
|
|
|
vk::GraphicsPipelineCreateInfo pipeline_info {};
|
|
vk::PipelineRenderingCreateInfo rendering_info {};
|
|
rendering_info.colorAttachmentCount = rendering.color_count;
|
|
rendering_info.pColorAttachmentFormats = rendering.color_formats.data();
|
|
rendering_info.depthAttachmentFormat = rendering.depth_format;
|
|
rendering_info.stencilAttachmentFormat = rendering.stencil_format;
|
|
pipeline_info.pNext = &rendering_info;
|
|
pipeline_info.stageCount = shader_stage_count;
|
|
pipeline_info.pStages = shader_stages;
|
|
pipeline_info.pVertexInputState = mesh ? nullptr : &vertex_input_info;
|
|
pipeline_info.pInputAssemblyState = mesh ? nullptr : &input_assembly;
|
|
vk::PipelineTessellationStateCreateInfo tessellation_state {};
|
|
tessellation_state.patchControlPoints = 3;
|
|
pipeline_info.pTessellationState = (rect_list ? &tessellation_state : nullptr);
|
|
pipeline_info.pViewportState = &viewport_state;
|
|
pipeline_info.pRasterizationState = &rasterizer;
|
|
pipeline_info.pMultisampleState = &multisampling;
|
|
pipeline_info.pDepthStencilState = (with_depth ? &depth_stencil_info : nullptr);
|
|
pipeline_info.pColorBlendState = &color_blending;
|
|
pipeline_info.pDynamicState = &dynamic_state;
|
|
pipeline_info.layout = pipeline.pipeline_layout;
|
|
pipeline_info.basePipelineIndex = -1;
|
|
|
|
EXIT_IF(pipeline.pipeline != nullptr);
|
|
|
|
if (graphics_debug_dump_enabled()) {
|
|
LOGF("PipelineTrace: vkCreateGraphicsPipelines begin VS=%" PRIu64 " PS=%" PRIu64
|
|
" topology=%" PRIu32 " color_mask=0x%08" PRIx32
|
|
" depth=%s blend=%s dyn_states=%" PRIu32 "\n",
|
|
vertex_program.id, ps_active ? pixel_program.id : 0,
|
|
static_cast<uint32_t>(static_params.topology), static_params.color_mask[0],
|
|
(with_depth ? "true" : "false"), (static_params.blend_enable[0] ? "true" : "false"),
|
|
dynamic_states_count);
|
|
}
|
|
result = graphics.device.createGraphicsPipelines(driver_cache, 1, &pipeline_info, nullptr,
|
|
&pipeline.pipeline);
|
|
if (graphics_debug_dump_enabled()) {
|
|
LOGF("PipelineTrace: vkCreateGraphicsPipelines done result=%s pipeline=%p\n",
|
|
vk::to_string(result).c_str(), static_cast<void*>(pipeline.pipeline));
|
|
}
|
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
|
|
|
EXIT_NOT_IMPLEMENTED(pipeline.pipeline == nullptr);
|
|
|
|
if (tess_control_shader_module != nullptr) {
|
|
graphics.device.destroyShaderModule(tess_control_shader_module, nullptr);
|
|
}
|
|
if (tess_eval_shader_module != nullptr) {
|
|
graphics.device.destroyShaderModule(tess_eval_shader_module, nullptr);
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
|
void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& pipeline,
|
|
const ShaderComputeInputInfo& input_info,
|
|
vk::ShaderModule compute_module, vk::PipelineCache driver_cache) {
|
|
EXIT_IF(compute_module == nullptr);
|
|
|
|
vk::PipelineShaderStageCreateInfo comp_shader_stage_info {};
|
|
vk::PipelineShaderStageRequiredSubgroupSizeCreateInfo comp_subgroup_size {};
|
|
comp_shader_stage_info.stage = vk::ShaderStageFlagBits::eCompute;
|
|
comp_shader_stage_info.module = compute_module;
|
|
comp_shader_stage_info.pName = "main";
|
|
EXIT_IF(!input_info.stage);
|
|
const auto wave_size = input_info.stage.program->wave_size;
|
|
if (graphics.compute_subgroup_size_control_enabled &&
|
|
wave_size >= graphics.min_subgroup_size && wave_size <= graphics.max_subgroup_size) {
|
|
comp_subgroup_size.requiredSubgroupSize = wave_size;
|
|
comp_shader_stage_info.pNext = &comp_subgroup_size;
|
|
}
|
|
|
|
std::vector<vk::DescriptorSetLayoutBinding> descriptor_bindings;
|
|
AddLayoutBindings(descriptor_bindings, *input_info.stage.program,
|
|
vk::ShaderStageFlagBits::eCompute);
|
|
CreateDescriptorLayout(graphics, pipeline, descriptor_bindings);
|
|
const vk::PushConstantRange push_constants {vk::ShaderStageFlagBits::eCompute, 0,
|
|
ShaderRecompiler::IR::NativePushConstantSize};
|
|
|
|
vk::PipelineLayoutCreateInfo pipeline_layout_info {};
|
|
pipeline_layout_info.setLayoutCount = 1;
|
|
pipeline_layout_info.pSetLayouts = &pipeline.descriptor_set_layout;
|
|
pipeline_layout_info.pushConstantRangeCount = 1;
|
|
pipeline_layout_info.pPushConstantRanges = &push_constants;
|
|
|
|
EXIT_IF(pipeline.pipeline_layout != nullptr);
|
|
|
|
LOGF("PipelineTrace: vkCreatePipelineLayout CS begin set_layouts=1 push_constants=%u\n",
|
|
1u);
|
|
auto result = graphics.device.createPipelineLayout(&pipeline_layout_info, nullptr,
|
|
&pipeline.pipeline_layout);
|
|
LOGF("PipelineTrace: vkCreatePipelineLayout CS done result=%s layout=%p\n",
|
|
vk::to_string(result).c_str(), static_cast<void*>(pipeline.pipeline_layout));
|
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
|
|
|
EXIT_NOT_IMPLEMENTED(pipeline.pipeline_layout == nullptr);
|
|
|
|
vk::ComputePipelineCreateInfo info {};
|
|
info.stage = comp_shader_stage_info;
|
|
info.layout = pipeline.pipeline_layout;
|
|
info.basePipelineIndex = -1;
|
|
|
|
EXIT_IF(pipeline.pipeline != nullptr);
|
|
|
|
LOGF("PipelineTrace: vkCreateComputePipelines begin layout=%p\n",
|
|
static_cast<void*>(pipeline.pipeline_layout));
|
|
result = graphics.device.createComputePipelines(driver_cache, 1, &info, nullptr,
|
|
&pipeline.pipeline);
|
|
LOGF("PipelineTrace: vkCreateComputePipelines done result=%s pipeline=%p\n",
|
|
vk::to_string(result).c_str(), static_cast<void*>(pipeline.pipeline));
|
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
|
|
|
EXIT_NOT_IMPLEMENTED(pipeline.pipeline == nullptr);
|
|
}
|
|
|
|
} // namespace Libs::Graphics
|