shader_recompiler: Implement attribute loads/stores

This commit is contained in:
raphaelthegreat 2024-05-24 03:02:21 +03:00
parent 08e155946e
commit 0eaa7d5859
28 changed files with 399 additions and 197 deletions

View File

@ -324,6 +324,8 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
src/shader_recompiler/frontend/control_flow_graph.h src/shader_recompiler/frontend/control_flow_graph.h
src/shader_recompiler/frontend/decode.cpp src/shader_recompiler/frontend/decode.cpp
src/shader_recompiler/frontend/decode.h src/shader_recompiler/frontend/decode.h
src/shader_recompiler/frontend/fetch_shader.cpp
src/shader_recompiler/frontend/fetch_shader.h
src/shader_recompiler/frontend/format.cpp src/shader_recompiler/frontend/format.cpp
src/shader_recompiler/frontend/instruction.cpp src/shader_recompiler/frontend/instruction.cpp
src/shader_recompiler/frontend/instruction.h src/shader_recompiler/frontend/instruction.h
@ -333,6 +335,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp
src/shader_recompiler/ir/passes/resource_tracking_pass.cpp src/shader_recompiler/ir/passes/resource_tracking_pass.cpp
src/shader_recompiler/ir/passes/constant_propogation_pass.cpp src/shader_recompiler/ir/passes/constant_propogation_pass.cpp
src/shader_recompiler/ir/passes/info_collection.cpp
src/shader_recompiler/ir/passes/passes.h src/shader_recompiler/ir/passes/passes.h
src/shader_recompiler/ir/abstract_syntax_list.h src/shader_recompiler/ir/abstract_syntax_list.h
src/shader_recompiler/ir/attribute.cpp src/shader_recompiler/ir/attribute.cpp

View File

@ -20,7 +20,6 @@
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include "core/libraries/videoout/video_out.h" #include "core/libraries/videoout/video_out.h"
#include "core/linker.h" #include "core/linker.h"
#include "core/tls.h"
#include "input/controller.h" #include "input/controller.h"
#include "sdl_window.h" #include "sdl_window.h"

View File

@ -171,7 +171,7 @@ Id DefineMain(EmitContext& ctx, IR::Program& program) {
void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size()); const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size());
spv::ExecutionModel execution_model{}; spv::ExecutionModel execution_model{};
switch (program.stage) { switch (program.info.stage) {
case Stage::Compute: { case Stage::Compute: {
// const std::array<u32, 3> workgroup_size{program.workgroup_size}; // const std::array<u32, 3> workgroup_size{program.workgroup_size};
// execution_model = spv::ExecutionModel::GLCompute; // execution_model = spv::ExecutionModel::GLCompute;
@ -194,7 +194,7 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
// } // }
break; break;
default: default:
throw NotImplementedException("Stage {}", u32(program.stage)); throw NotImplementedException("Stage {}", u32(program.info.stage));
} }
ctx.AddEntryPoint(execution_model, main, "main", interfaces); ctx.AddEntryPoint(execution_model, main, "main", interfaces);
} }
@ -222,7 +222,7 @@ std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program, Binding
EmitContext ctx{profile, program, bindings}; EmitContext ctx{profile, program, bindings};
const Id main{DefineMain(ctx, program)}; const Id main{DefineMain(ctx, program)};
DefineEntryPoint(program, ctx, main); DefineEntryPoint(program, ctx, main);
if (program.stage == Stage::Vertex) { if (program.info.stage == Stage::Vertex) {
ctx.AddExtension("SPV_KHR_shader_draw_parameters"); ctx.AddExtension("SPV_KHR_shader_draw_parameters");
ctx.AddCapability(spv::Capability::DrawParameters); ctx.AddCapability(spv::Capability::DrawParameters);
} }

View File

@ -10,12 +10,11 @@ namespace {
Id OutputAttrPointer(EmitContext& ctx, IR::Attribute attr, u32 element) { Id OutputAttrPointer(EmitContext& ctx, IR::Attribute attr, u32 element) {
if (IR::IsParam(attr)) { if (IR::IsParam(attr)) {
const u32 index{u32(attr) - u32(IR::Attribute::Param0)}; const u32 index{u32(attr) - u32(IR::Attribute::Param0)};
const auto& info{ctx.output_params.at(index).at(element)}; const auto& info{ctx.output_params.at(index)};
if (info.num_components == 1) { if (info.num_components == 1) {
return info.id; return info.id;
} else { } else {
const u32 index_element{element - info.first_element}; return ctx.OpAccessChain(ctx.output_f32, info.id, ctx.ConstU32(element));
return ctx.OpAccessChain(ctx.output_f32, info.id, ctx.ConstU32(index_element));
} }
} }
switch (attr) { switch (attr) {
@ -68,22 +67,21 @@ Id EmitReadConstBufferF32(EmitContext& ctx, const IR::Value& binding, const IR::
throw LogicError("Unreachable instruction"); throw LogicError("Unreachable instruction");
} }
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp) {
const u32 element{static_cast<u32>(attr) % 4};
if (IR::IsParam(attr)) { if (IR::IsParam(attr)) {
const u32 index{u32(attr) - u32(IR::Attribute::Param0)}; const u32 index{u32(attr) - u32(IR::Attribute::Param0)};
const auto& param{ctx.input_params.at(index)}; const auto& param{ctx.input_params.at(index)};
if (!ValidId(param.id)) { if (!ValidId(param.id)) {
// Attribute is disabled or varying component is not written // Attribute is disabled or varying component is not written
return ctx.ConstF32(element == 3 ? 1.0f : 0.0f); return ctx.ConstF32(comp == 3 ? 1.0f : 0.0f);
} }
const Id pointer{ctx.OpAccessChain(param.pointer_type, param.id, ctx.ConstU32(element))}; const Id pointer{ctx.OpAccessChain(param.pointer_type, param.id, ctx.ConstU32(comp))};
return ctx.OpLoad(param.component_type, pointer); return ctx.OpLoad(param.component_type, pointer);
} }
throw NotImplementedException("Read attribute {}", attr); throw NotImplementedException("Read attribute {}", attr);
} }
Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id) { Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp) {
switch (attr) { switch (attr) {
case IR::Attribute::VertexId: case IR::Attribute::VertexId:
return ctx.OpLoad(ctx.U32[1], ctx.vertex_index); return ctx.OpLoad(ctx.U32[1], ctx.vertex_index);
@ -93,9 +91,6 @@ Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id) {
} }
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 element) { void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 element) {
if (attr == IR::Attribute::Param0) {
return;
}
const Id pointer{OutputAttrPointer(ctx, attr, element)}; const Id pointer{OutputAttrPointer(ctx, attr, element)};
ctx.OpStore(pointer, value); ctx.OpStore(pointer, value);
} }

View File

@ -46,9 +46,9 @@ Id EmitReadConstBuffer(EmitContext& ctx, const IR::Value& handle, const IR::Valu
const IR::Value& offset); const IR::Value& offset);
Id EmitReadConstBufferF32(EmitContext& ctx, const IR::Value& handle, const IR::Value& index, Id EmitReadConstBufferF32(EmitContext& ctx, const IR::Value& handle, const IR::Value& index,
const IR::Value& offset); const IR::Value& offset);
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex); Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp);
Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id vertex); Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp);
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 element); void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 comp);
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value); void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value);
void EmitSetSampleMask(EmitContext& ctx, Id value); void EmitSetSampleMask(EmitContext& ctx, Id value);
void EmitSetFragDepth(EmitContext& ctx, Id value); void EmitSetFragDepth(EmitContext& ctx, Id value);

View File

@ -36,7 +36,7 @@ void Name(EmitContext& ctx, Id object, std::string_view format_str, Args&&... ar
} // Anonymous namespace } // Anonymous namespace
EmitContext::EmitContext(const Profile& profile_, IR::Program& program, Bindings& bindings) EmitContext::EmitContext(const Profile& profile_, IR::Program& program, Bindings& bindings)
: Sirit::Module(profile_.supported_spirv), profile{profile_}, stage{program.stage} { : Sirit::Module(profile_.supported_spirv), profile{profile_}, stage{program.info.stage} {
u32& uniform_binding{bindings.unified}; u32& uniform_binding{bindings.unified};
u32& storage_binding{bindings.unified}; u32& storage_binding{bindings.unified};
u32& texture_binding{bindings.unified}; u32& texture_binding{bindings.unified};
@ -98,6 +98,10 @@ void EmitContext::DefineArithmeticTypes() {
u32_zero_value = ConstU32(0U); u32_zero_value = ConstU32(0U);
f32_zero_value = ConstF32(0.0f); f32_zero_value = ConstF32(0.0f);
input_f32 = Name(TypePointer(spv::StorageClass::Input, F32[1]), "input_f32");
input_u32 = Name(TypePointer(spv::StorageClass::Input, U32[1]), "input_u32");
input_s32 = Name(TypePointer(spv::StorageClass::Input, S32[1]), "input_s32");
output_f32 = Name(TypePointer(spv::StorageClass::Output, F32[1]), "output_f32"); output_f32 = Name(TypePointer(spv::StorageClass::Output, F32[1]), "output_f32");
output_u32 = Name(TypePointer(spv::StorageClass::Output, U32[1]), "output_u32"); output_u32 = Name(TypePointer(spv::StorageClass::Output, U32[1]), "output_u32");
} }
@ -107,26 +111,119 @@ void EmitContext::DefineInterfaces(const IR::Program& program) {
DefineOutputs(program); DefineOutputs(program);
} }
Id GetAttributeType(EmitContext& ctx, AmdGpu::NumberFormat fmt) {
switch (fmt) {
case AmdGpu::NumberFormat::Float:
case AmdGpu::NumberFormat::Unorm:
return ctx.F32[4];
case AmdGpu::NumberFormat::Sint:
return ctx.S32[4];
case AmdGpu::NumberFormat::Uint:
return ctx.U32[4];
case AmdGpu::NumberFormat::Sscaled:
return ctx.F32[4];
case AmdGpu::NumberFormat::Uscaled:
return ctx.F32[4];
default:
break;
}
throw InvalidArgument("Invalid attribute type {}", fmt);
}
EmitContext::SpirvAttribute EmitContext::GetAttributeInfo(AmdGpu::NumberFormat fmt, Id id) {
switch (fmt) {
case AmdGpu::NumberFormat::Float:
case AmdGpu::NumberFormat::Unorm:
return {id, input_f32, F32[1], 4};
case AmdGpu::NumberFormat::Uint:
return {id, input_u32, U32[1], 4};
case AmdGpu::NumberFormat::Sint:
return {id, input_s32, S32[1], 4};
case AmdGpu::NumberFormat::Sscaled:
return {id, input_f32, F32[1], 4};
case AmdGpu::NumberFormat::Uscaled:
return {id, input_f32, F32[1], 4};
default:
break;
}
throw InvalidArgument("Invalid attribute type {}", fmt);
}
Id MakeDefaultValue(EmitContext& ctx, u32 default_value) {
switch (default_value) {
case 0:
return ctx.ConstF32(0.f, 0.f, 0.f, 0.f);
case 1:
return ctx.ConstF32(0.f, 0.f, 0.f, 1.f);
case 2:
return ctx.ConstF32(1.f, 1.f, 1.f, 0.f);
case 3:
return ctx.ConstF32(1.f, 1.f, 1.f, 1.f);
default:
UNREACHABLE();
}
}
void EmitContext::DefineInputs(const IR::Program& program) { void EmitContext::DefineInputs(const IR::Program& program) {
const auto& info = program.info;
switch (stage) { switch (stage) {
case Stage::Vertex: case Stage::Vertex:
vertex_index = DefineVariable(U32[1], spv::BuiltIn::VertexIndex, spv::StorageClass::Input); vertex_index = DefineVariable(U32[1], spv::BuiltIn::VertexIndex, spv::StorageClass::Input);
base_vertex = DefineVariable(U32[1], spv::BuiltIn::BaseVertex, spv::StorageClass::Input); base_vertex = DefineVariable(U32[1], spv::BuiltIn::BaseVertex, spv::StorageClass::Input);
for (const auto& input : info.vs_inputs) {
const Id type{GetAttributeType(*this, input.fmt)};
const Id id{DefineInput(type, input.binding)};
Name(id, fmt::format("vs_in_attr{}", input.binding));
input_params[input.binding] = GetAttributeInfo(input.fmt, id);
}
break; break;
case Stage::Fragment:
for (const auto& input : info.ps_inputs) {
if (input.is_default) {
input_params[input.semantic] = {MakeDefaultValue(*this, input.default_value), input_f32, F32[1]};
continue;
}
const IR::Attribute param{IR::Attribute::Param0 + input.param_index};
const u32 num_components = info.loads.NumComponents(param);
const Id type{F32[num_components]};
const Id id{DefineInput(type, input.semantic)};
if (input.is_flat) {
Decorate(id, spv::Decoration::Flat);
}
Name(id, fmt::format("fs_in_attr{}", input.semantic));
input_params[input.semantic] = {id, input_f32, F32[1], num_components};
}
default: default:
break; break;
} }
} }
void EmitContext::DefineOutputs(const IR::Program& program) { void EmitContext::DefineOutputs(const IR::Program& program) {
const auto& info = program.info;
switch (stage) { switch (stage) {
case Stage::Vertex: case Stage::Vertex:
output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output); output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output);
for (u32 i = 0; i < IR::NumParams; i++) {
const IR::Attribute param{IR::Attribute::Param0 + i};
if (!info.stores.GetAny(param)) {
continue;
}
const u32 num_components = info.stores.NumComponents(param);
const Id id{DefineOutput(F32[num_components], i)};
Name(id, fmt::format("out_attr{}", i));
output_params[i] = {id, output_f32, F32[1], num_components};
}
break; break;
case Stage::Fragment: case Stage::Fragment:
frag_color[0] = DefineOutput(F32[4], 0); for (u32 i = 0; i < IR::NumRenderTargets; i++) {
Name(frag_color[0], fmt::format("frag_color{}", 0)); const IR::Attribute mrt{IR::Attribute::RenderTarget0 + i};
interfaces.push_back(frag_color[0]); if (!info.stores.GetAny(mrt)) {
continue;
}
frag_color[i] = DefineOutput(F32[4], i);
Name(frag_color[i], fmt::format("frag_color{}", i));
interfaces.push_back(frag_color[i]);
}
break; break;
default: default:
break; break;

View File

@ -135,6 +135,9 @@ public:
Id u32_zero_value{}; Id u32_zero_value{};
Id f32_zero_value{}; Id f32_zero_value{};
Id input_u32{};
Id input_f32{};
Id input_s32{};
Id output_u32{}; Id output_u32{};
Id output_f32{}; Id output_f32{};
@ -145,25 +148,22 @@ public:
Id base_vertex{}; Id base_vertex{};
std::array<Id, 8> frag_color{}; std::array<Id, 8> frag_color{};
struct InputParamInfo { struct SpirvAttribute {
Id id; Id id;
Id pointer_type; Id pointer_type;
Id component_type; Id component_type;
u32 num_components;
}; };
std::array<InputParamInfo, 32> input_params{}; std::array<SpirvAttribute, 32> input_params{};
std::array<SpirvAttribute, 32> output_params{};
struct ParamElementInfo {
Id id{};
u32 first_element{};
u32 num_components{};
};
std::array<std::array<ParamElementInfo, 4>, 32> output_params{};
private: private:
void DefineArithmeticTypes(); void DefineArithmeticTypes();
void DefineInterfaces(const IR::Program& program); void DefineInterfaces(const IR::Program& program);
void DefineInputs(const IR::Program& program); void DefineInputs(const IR::Program& program);
void DefineOutputs(const IR::Program& program); void DefineOutputs(const IR::Program& program);
SpirvAttribute GetAttributeInfo(AmdGpu::NumberFormat fmt, Id id);
}; };
} // namespace Shader::Backend::SPIRV } // namespace Shader::Backend::SPIRV

View File

@ -32,9 +32,9 @@ namespace Shader::Gcn {
* We take the reverse way, extract the original input semantics from these instructions. * We take the reverse way, extract the original input semantics from these instructions.
**/ **/
std::vector<VertexAttribute> ParseFetchShader(std::span<const u32> code) { std::vector<VertexAttribute> ParseFetchShader(const u32* code) {
std::vector<VertexAttribute> attributes; std::vector<VertexAttribute> attributes;
GcnCodeSlice code_slice(code.data(), code.data() + std::numeric_limits<u32>::max()); GcnCodeSlice code_slice(code, code + std::numeric_limits<u32>::max());
GcnDecodeContext decoder; GcnDecodeContext decoder;
struct VsharpLoad { struct VsharpLoad {

View File

@ -3,7 +3,6 @@
#pragma once #pragma once
#include <span>
#include <vector> #include <vector>
#include "common/types.h" #include "common/types.h"
@ -17,6 +16,6 @@ struct VertexAttribute {
u8 dword_offset; ///< The dword offset of the V# that describes this attribute. u8 dword_offset; ///< The dword offset of the V# that describes this attribute.
}; };
std::vector<VertexAttribute> ParseFetchShader(std::span<const u32> code); std::vector<VertexAttribute> ParseFetchShader(const u32* code);
} // namespace Shader::Gcn } // namespace Shader::Gcn

View File

@ -600,9 +600,9 @@ public:
TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_, TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_,
ObjectPool<Statement>& stmt_pool_, Statement& root_stmt, ObjectPool<Statement>& stmt_pool_, Statement& root_stmt,
IR::AbstractSyntaxList& syntax_list_, std::span<const GcnInst> inst_list_, IR::AbstractSyntaxList& syntax_list_, std::span<const GcnInst> inst_list_,
Stage stage_) Info& info_)
: stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, : stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_},
syntax_list{syntax_list_}, inst_list{inst_list_}, stage{stage_} { syntax_list{syntax_list_}, inst_list{inst_list_}, info{info_} {
Visit(root_stmt, nullptr, nullptr); Visit(root_stmt, nullptr, nullptr);
IR::Block& first_block{*syntax_list.front().data.block}; IR::Block& first_block{*syntax_list.front().data.block};
@ -633,7 +633,7 @@ private:
ensure_block(); ensure_block();
const u32 start = stmt.block->begin_index; const u32 start = stmt.block->begin_index;
const u32 size = stmt.block->end_index - start + 1; const u32 size = stmt.block->end_index - start + 1;
Translate(current_block, stage, inst_list.subspan(start, size)); Translate(current_block, inst_list.subspan(start, size), info);
break; break;
} }
case StatementType::SetVariable: { case StatementType::SetVariable: {
@ -811,17 +811,17 @@ private:
IR::AbstractSyntaxList& syntax_list; IR::AbstractSyntaxList& syntax_list;
const Block dummy_flow_block{}; const Block dummy_flow_block{};
std::span<const GcnInst> inst_list; std::span<const GcnInst> inst_list;
Stage stage; Info& info;
}; };
} // Anonymous namespace } // Anonymous namespace
IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
CFG& cfg, Stage stage) { CFG& cfg, Info& info) {
ObjectPool<Statement> stmt_pool{64}; ObjectPool<Statement> stmt_pool{64};
GotoPass goto_pass{cfg, stmt_pool}; GotoPass goto_pass{cfg, stmt_pool};
Statement& root{goto_pass.RootStatement()}; Statement& root{goto_pass.RootStatement()};
IR::AbstractSyntaxList syntax_list; IR::AbstractSyntaxList syntax_list;
TranslatePass{inst_pool, block_pool, stmt_pool, root, syntax_list, cfg.inst_list, stage}; TranslatePass{inst_pool, block_pool, stmt_pool, root, syntax_list, cfg.inst_list, info};
return syntax_list; return syntax_list;
} }

View File

@ -10,13 +10,13 @@
#include "shader_recompiler/object_pool.h" #include "shader_recompiler/object_pool.h"
namespace Shader { namespace Shader {
enum class Stage : u32; struct Info;
} }
namespace Shader::Gcn { namespace Shader::Gcn {
[[nodiscard]] IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, [[nodiscard]] IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool,
ObjectPool<IR::Block>& block_pool, CFG& cfg, ObjectPool<IR::Block>& block_pool, CFG& cfg,
Stage stage); Info& info);
} // namespace Shader::Gcn } // namespace Shader::Gcn

View File

@ -3,13 +3,15 @@
#include "shader_recompiler/exception.h" #include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/translate/translate.h" #include "shader_recompiler/frontend/translate/translate.h"
#include "shader_recompiler/frontend/fetch_shader.h"
#include "shader_recompiler/runtime_info.h" #include "shader_recompiler/runtime_info.h"
#include "video_core/amdgpu/resource.h"
namespace Shader::Gcn { namespace Shader::Gcn {
Translator::Translator(IR::Block* block_, Stage stage) : block{block_}, ir{*block} { Translator::Translator(IR::Block* block_, Info& info_) : block{block_}, ir{*block}, info{info_} {
IR::VectorReg dst_vreg = IR::VectorReg::V0; IR::VectorReg dst_vreg = IR::VectorReg::V0;
switch (stage) { switch (info.stage) {
case Stage::Vertex: case Stage::Vertex:
// https://github.com/chaotic-cx/mesa-mirror/blob/72326e15/src/amd/vulkan/radv_shader_args.c#L146C1-L146C23 // https://github.com/chaotic-cx/mesa-mirror/blob/72326e15/src/amd/vulkan/radv_shader_args.c#L146C1-L146C23
ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::VertexId)); ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::VertexId));
@ -92,11 +94,38 @@ void Translator::SetDst(const InstOperand& operand, const IR::U32F32& value) {
} }
} }
void Translate(IR::Block* block, Stage stage, std::span<const GcnInst> inst_list) { void Translator::EmitFetch(const GcnInst& inst) {
// Read the pointer to the fetch shader assembly.
const u32 sgpr_base = inst.src[0].code;
const u32* code;
std::memcpy(&code, &info.user_data[sgpr_base], sizeof(code));
// Parse the assembly to generate a list of attributes.
const auto attribs = ParseFetchShader(code);
for (const auto& attrib : attribs) {
IR::VectorReg dst_reg{attrib.dest_vgpr};
const IR::Attribute attr{IR::Attribute::Param0 + attrib.semantic};
for (u32 i = 0; i < attrib.num_elements; i++) {
ir.SetVectorReg(dst_reg++, ir.GetAttribute(attr, i));
}
// Read the V# of the attribute to figure out component number and type.
const auto buffer = info.ReadUd<AmdGpu::Buffer>(attrib.sgpr_base,
attrib.dword_offset);
const u32 num_components = AmdGpu::NumComponents(buffer.data_format);
info.vs_inputs.push_back({
.fmt = buffer.num_format,
.binding = attrib.semantic,
.num_components = std::min<u16>(attrib.num_elements, num_components),
});
}
}
void Translate(IR::Block* block, std::span<const GcnInst> inst_list, Info& info) {
if (inst_list.empty()) { if (inst_list.empty()) {
return; return;
} }
Translator translator{block, stage}; Translator translator{block, info};
for (const auto& inst : inst_list) { for (const auto& inst : inst_list) {
switch (inst.opcode) { switch (inst.opcode) {
case Opcode::S_MOV_B32: case Opcode::S_MOV_B32:
@ -115,6 +144,9 @@ void Translate(IR::Block* block, Stage stage, std::span<const GcnInst> inst_list
translator.V_MUL_F32(inst); translator.V_MUL_F32(inst);
break; break;
case Opcode::S_SWAPPC_B64: case Opcode::S_SWAPPC_B64:
ASSERT(info.stage == Stage::Vertex);
translator.EmitFetch(inst);
break;
case Opcode::S_WAITCNT: case Opcode::S_WAITCNT:
break; // Ignore for now. break; // Ignore for now.
case Opcode::S_BUFFER_LOAD_DWORDX16: case Opcode::S_BUFFER_LOAD_DWORDX16:

View File

@ -7,9 +7,10 @@
#include "shader_recompiler/frontend/instruction.h" #include "shader_recompiler/frontend/instruction.h"
#include "shader_recompiler/ir/basic_block.h" #include "shader_recompiler/ir/basic_block.h"
#include "shader_recompiler/ir/ir_emitter.h" #include "shader_recompiler/ir/ir_emitter.h"
#include "shader_recompiler/runtime_info.h"
namespace Shader { namespace Shader {
enum class Stage : u32; struct Info;
} }
namespace Shader::Gcn { namespace Shader::Gcn {
@ -25,7 +26,9 @@ enum class ConditionOp : u32 {
class Translator { class Translator {
public: public:
explicit Translator(IR::Block* block_, Stage stage); explicit Translator(IR::Block* block_, Info& info);
void EmitFetch(const GcnInst& inst);
// Scalar ALU // Scalar ALU
void S_MOV(const GcnInst& inst); void S_MOV(const GcnInst& inst);
@ -66,8 +69,9 @@ private:
private: private:
IR::Block* block; IR::Block* block;
IR::IREmitter ir; IR::IREmitter ir;
Info& info;
}; };
void Translate(IR::Block* block, Stage stage, std::span<const GcnInst> inst_list); void Translate(IR::Block* block, std::span<const GcnInst> inst_list, Info& info);
} // namespace Shader::Gcn } // namespace Shader::Gcn

View File

@ -20,9 +20,8 @@ void Translator::V_MAC_F32(const GcnInst& inst) {
void Translator::V_CVT_PKRTZ_F16_F32(const GcnInst& inst) { void Translator::V_CVT_PKRTZ_F16_F32(const GcnInst& inst) {
const IR::VectorReg dst_reg{inst.dst[0].code}; const IR::VectorReg dst_reg{inst.dst[0].code};
const IR::Value vec_f32 = ir.CompositeConstruct(ir.FPConvert(16, GetSrc(inst.src[0])), const IR::Value vec_f32 = ir.CompositeConstruct(GetSrc(inst.src[0]), GetSrc(inst.src[1]));
ir.FPConvert(16, GetSrc(inst.src[1]))); ir.SetVectorReg(dst_reg, ir.PackHalf2x16(vec_f32));
ir.SetVectorReg(dst_reg, ir.PackFloat2x16(vec_f32));
} }
void Translator::V_MUL_F32(const GcnInst& inst) { void Translator::V_MUL_F32(const GcnInst& inst) {

View File

@ -7,7 +7,9 @@ namespace Shader::Gcn {
void Translator::V_INTERP_P2_F32(const GcnInst& inst) { void Translator::V_INTERP_P2_F32(const GcnInst& inst) {
const IR::VectorReg dst_reg{inst.dst[0].code}; const IR::VectorReg dst_reg{inst.dst[0].code};
const IR::Attribute attrib{IR::Attribute::Param0 + inst.control.vintrp.attr}; auto& attr = info.ps_inputs.at(inst.control.vintrp.attr);
attr.semantic = inst.control.vintrp.attr;
const IR::Attribute attrib{IR::Attribute::Param0 + attr.param_index};
ir.SetVectorReg(dst_reg, ir.GetAttribute(attrib, inst.control.vintrp.chan)); ir.SetVectorReg(dst_reg, ir.GetAttribute(attrib, inst.control.vintrp.chan));
} }

View File

@ -72,10 +72,12 @@ enum class Attribute : u64 {
LocalInvocationId = 75, LocalInvocationId = 75,
LocalInvocationIndex = 76, LocalInvocationIndex = 76,
FragCoord = 77, FragCoord = 77,
Max,
}; };
constexpr size_t EXP_NUM_POS = 4; constexpr size_t NumAttributes = static_cast<size_t>(Attribute::Max);
constexpr size_t EXP_NUM_PARAM = 32; constexpr size_t NumRenderTargets = 8;
constexpr size_t NumParams = 32;
[[nodiscard]] bool IsParam(Attribute attribute) noexcept; [[nodiscard]] bool IsParam(Attribute attribute) noexcept;
@ -86,7 +88,7 @@ constexpr size_t EXP_NUM_PARAM = 32;
if (result > static_cast<int>(Attribute::Param31)) { if (result > static_cast<int>(Attribute::Param31)) {
throw LogicError("Overflow on register arithmetic"); throw LogicError("Overflow on register arithmetic");
} }
if (result < static_cast<int>(Attribute::Param0)) { if (result < static_cast<int>(Attribute::RenderTarget0)) {
throw LogicError("Underflow on register arithmetic"); throw LogicError("Underflow on register arithmetic");
} }
return static_cast<Attribute>(result); return static_cast<Attribute>(result);

View File

@ -174,18 +174,10 @@ void IREmitter::SetVcc(const U1& value) {
Inst(Opcode::SetVcc, value); Inst(Opcode::SetVcc, value);
} }
F32 IREmitter::GetAttribute(IR::Attribute attribute) {
return GetAttribute(attribute, 0);
}
F32 IREmitter::GetAttribute(IR::Attribute attribute, u32 comp) { F32 IREmitter::GetAttribute(IR::Attribute attribute, u32 comp) {
return Inst<F32>(Opcode::GetAttribute, attribute, Imm32(comp)); return Inst<F32>(Opcode::GetAttribute, attribute, Imm32(comp));
} }
U32 IREmitter::GetAttributeU32(IR::Attribute attribute) {
return GetAttributeU32(attribute, 0);
}
U32 IREmitter::GetAttributeU32(IR::Attribute attribute, u32 comp) { U32 IREmitter::GetAttributeU32(IR::Attribute attribute, u32 comp) {
return Inst<U32>(Opcode::GetAttributeU32, attribute, Imm32(comp)); return Inst<U32>(Opcode::GetAttributeU32, attribute, Imm32(comp));
} }

View File

@ -58,11 +58,9 @@ public:
[[nodiscard]] U1 Condition(IR::Condition cond); [[nodiscard]] U1 Condition(IR::Condition cond);
[[nodiscard]] F32 GetAttribute(IR::Attribute attribute); [[nodiscard]] F32 GetAttribute(Attribute attribute, u32 comp = 0);
[[nodiscard]] F32 GetAttribute(IR::Attribute attribute, u32 comp); [[nodiscard]] U32 GetAttributeU32(Attribute attribute, u32 comp = 0);
[[nodiscard]] U32 GetAttributeU32(IR::Attribute attribute); void SetAttribute(Attribute attribute, const F32& value, u32 comp = 0);
[[nodiscard]] U32 GetAttributeU32(IR::Attribute attribute, u32 comp);
void SetAttribute(IR::Attribute attribute, const F32& value, u32 comp);
[[nodiscard]] U32U64 ReadShared(int bit_size, bool is_signed, const U32& offset); [[nodiscard]] U32U64 ReadShared(int bit_size, bool is_signed, const U32& offset);
void WriteShared(int bit_size, const Value& value, const U32& offset); void WriteShared(int bit_size, const Value& value, const U32& offset);

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "shader_recompiler/ir/program.h"
namespace Shader::Optimization {
void Visit(Info& info, IR::Inst& inst) {
switch (inst.GetOpcode()) {
case IR::Opcode::GetAttribute:
case IR::Opcode::GetAttributeU32: {
info.loads.Set(inst.Arg(0).Attribute(), inst.Arg(1).U32());
break;
}
case IR::Opcode::SetAttribute: {
info.stores.Set(inst.Arg(0).Attribute(), inst.Arg(2).U32());
break;
}
default:
break;
}
}
void CollectShaderInfoPass(IR::Program& program) {
Info& info{program.info};
for (IR::Block* const block : program.post_order_blocks) {
for (IR::Inst& inst : block->Instructions()) {
Visit(info, inst);
}
}
}
} // namespace Shader::Optimization

View File

@ -13,5 +13,6 @@ void IdentityRemovalPass(IR::BlockList& program);
void DeadCodeEliminationPass(IR::BlockList& program); void DeadCodeEliminationPass(IR::BlockList& program);
void ConstantPropagationPass(IR::BlockList& program); void ConstantPropagationPass(IR::BlockList& program);
void ResourceTrackingPass(IR::Program& program); void ResourceTrackingPass(IR::Program& program);
void CollectShaderInfoPass(IR::Program& program);
} // namespace Shader::Optimization } // namespace Shader::Optimization

View File

@ -3,27 +3,22 @@
#pragma once #pragma once
#include <array>
#include <string> #include <string>
#include "shader_recompiler/frontend/instruction.h" #include "shader_recompiler/frontend/instruction.h"
#include "shader_recompiler/ir/abstract_syntax_list.h" #include "shader_recompiler/ir/abstract_syntax_list.h"
#include "shader_recompiler/ir/basic_block.h" #include "shader_recompiler/ir/basic_block.h"
#include "shader_recompiler/runtime_info.h"
namespace Shader {
enum class Stage : u32;
}
namespace Shader::IR { namespace Shader::IR {
static constexpr size_t NumUserDataRegs = 16;
struct Program { struct Program {
explicit Program(const Info&& info_) : info{info_} {}
AbstractSyntaxList syntax_list; AbstractSyntaxList syntax_list;
BlockList blocks; BlockList blocks;
BlockList post_order_blocks; BlockList post_order_blocks;
std::vector<Gcn::GcnInst> ins_list; std::vector<Gcn::GcnInst> ins_list;
std::array<u32, NumUserDataRegs> user_data; Info info;
Stage stage;
}; };
[[nodiscard]] std::string DumpProgram(const Program& program); [[nodiscard]] std::string DumpProgram(const Program& program);

View File

@ -2,7 +2,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <fstream> #include <fstream>
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/frontend/control_flow_graph.h" #include "shader_recompiler/frontend/control_flow_graph.h"
#include "shader_recompiler/frontend/decode.h" #include "shader_recompiler/frontend/decode.h"
#include "shader_recompiler/frontend/structured_control_flow.h" #include "shader_recompiler/frontend/structured_control_flow.h"
@ -30,10 +29,8 @@ IR::BlockList GenerateBlocks(const IR::AbstractSyntaxList& syntax_list) {
return blocks; return blocks;
} }
std::vector<u32> TranslateProgram(ObjectPool<IR::Inst>& inst_pool, IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
ObjectPool<IR::Block>& block_pool, Stage stage, std::span<const u32> token, const Info&& info) {
std::span<const u32, IR::NumUserDataRegs> ud_regs,
std::span<const u32> token) {
// Ensure first instruction is expected. // Ensure first instruction is expected.
constexpr u32 token_mov_vcchi = 0xBEEB03FF; constexpr u32 token_mov_vcchi = 0xBEEB03FF;
ASSERT_MSG(token[0] == token_mov_vcchi, "First instruction is not s_mov_b32 vcc_hi, #imm"); ASSERT_MSG(token[0] == token_mov_vcchi, "First instruction is not s_mov_b32 vcc_hi, #imm");
@ -47,7 +44,7 @@ std::vector<u32> TranslateProgram(ObjectPool<IR::Inst>& inst_pool,
file.close(); file.close();
// Decode and save instructions // Decode and save instructions
IR::Program program; IR::Program program{std::move(info)};
program.ins_list.reserve(token.size()); program.ins_list.reserve(token.size());
while (!slice.atEnd()) { while (!slice.atEnd()) {
program.ins_list.emplace_back(decoder.decodeInstruction(slice)); program.ins_list.emplace_back(decoder.decodeInstruction(slice));
@ -58,11 +55,9 @@ std::vector<u32> TranslateProgram(ObjectPool<IR::Inst>& inst_pool,
Gcn::CFG cfg{gcn_block_pool, program.ins_list}; Gcn::CFG cfg{gcn_block_pool, program.ins_list};
// Structurize control flow graph and create program. // Structurize control flow graph and create program.
program.syntax_list = Shader::Gcn::BuildASL(inst_pool, block_pool, cfg, stage); program.syntax_list = Shader::Gcn::BuildASL(inst_pool, block_pool, cfg, program.info);
program.blocks = GenerateBlocks(program.syntax_list); program.blocks = GenerateBlocks(program.syntax_list);
program.post_order_blocks = Shader::IR::PostOrder(program.syntax_list.front()); program.post_order_blocks = Shader::IR::PostOrder(program.syntax_list.front());
program.stage = stage;
std::ranges::copy(ud_regs, program.user_data.begin());
// Run optimization passes // Run optimization passes
Shader::Optimization::SsaRewritePass(program.post_order_blocks); Shader::Optimization::SsaRewritePass(program.post_order_blocks);
@ -70,14 +65,13 @@ std::vector<u32> TranslateProgram(ObjectPool<IR::Inst>& inst_pool,
Shader::Optimization::IdentityRemovalPass(program.blocks); Shader::Optimization::IdentityRemovalPass(program.blocks);
Shader::Optimization::ResourceTrackingPass(program); Shader::Optimization::ResourceTrackingPass(program);
Shader::Optimization::DeadCodeEliminationPass(program.blocks); Shader::Optimization::DeadCodeEliminationPass(program.blocks);
Shader::Optimization::CollectShaderInfoPass(program);
for (const auto& block : program.blocks) { for (const auto& block : program.blocks) {
fmt::print("{}\n", IR::DumpBlock(*block)); fmt::print("{}\n", IR::DumpBlock(*block));
} }
// TODO: Pass profile from vulkan backend return program;
const auto code = Backend::SPIRV::EmitSPIRV(Profile{}, program);
return code;
} }
} // namespace Shader } // namespace Shader

View File

@ -3,6 +3,8 @@
#pragma once #pragma once
#include "shader_recompiler/ir/basic_block.h"
#include "shader_recompiler/object_pool.h"
#include "shader_recompiler/ir/program.h" #include "shader_recompiler/ir/program.h"
namespace Shader { namespace Shader {
@ -26,9 +28,9 @@ struct BinaryInfo {
u32 crc32; u32 crc32;
}; };
[[nodiscard]] std::vector<u32> TranslateProgram(ObjectPool<IR::Inst>& inst_pool, [[nodiscard]] IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool,
ObjectPool<IR::Block>& block_pool, Stage stage, ObjectPool<IR::Block>& block_pool,
std::span<const u32, IR::NumUserDataRegs> ud_regs, std::span<const u32> code,
std::span<const u32> code); const Info&& info);
} // namespace Shader } // namespace Shader

View File

@ -3,39 +3,16 @@
#pragma once #pragma once
#include <array> #include <span>
#include <boost/container/small_vector.hpp> #include <boost/container/static_vector.hpp>
#include "shader_recompiler/ir/type.h" #include "common/assert.h"
#include "common/types.h"
#include "shader_recompiler/ir/attribute.h"
#include "video_core/amdgpu/pixel_format.h"
namespace Shader { namespace Shader {
enum class AttributeType : u8 { static constexpr size_t NumUserDataRegs = 16;
Float,
SignedInt,
UnsignedInt,
SignedScaled,
UnsignedScaled,
Disabled,
};
enum class InputTopology {
Points,
Lines,
LinesAdjacency,
Triangles,
TrianglesAdjacency,
};
enum class CompareFunction {
Never,
Less,
Equal,
LessThanEqual,
Greater,
NotEqual,
GreaterThanEqual,
Always,
};
enum class Stage : u32 { enum class Stage : u32 {
Vertex, Vertex,
@ -62,78 +39,60 @@ enum class TextureType : u32 {
}; };
constexpr u32 NUM_TEXTURE_TYPES = 7; constexpr u32 NUM_TEXTURE_TYPES = 7;
enum class Interpolation {
Smooth,
Flat,
NoPerspective,
};
struct ConstantBufferDescriptor {
u32 index;
u32 count;
auto operator<=>(const ConstantBufferDescriptor&) const = default;
};
struct TextureDescriptor {
TextureType type;
bool is_eud;
bool is_depth;
bool is_multisample;
bool is_storage;
u32 count;
u32 eud_offset_dwords;
u32 ud_index_dwords;
auto operator<=>(const TextureDescriptor&) const = default;
};
using TextureDescriptors = boost::container::small_vector<TextureDescriptor, 12>;
struct Info { struct Info {
bool uses_workgroup_id{}; explicit Info(std::span<const u32, 16> user_data_) : user_data{user_data_} {}
bool uses_local_invocation_id{};
bool uses_invocation_id{};
bool uses_invocation_info{};
bool uses_sample_id{};
std::array<Interpolation, 32> interpolation{}; struct VsInput {
// VaryingState loads; AmdGpu::NumberFormat fmt;
// VaryingState stores; u16 binding;
// VaryingState passthrough; u16 num_components;
};
boost::container::static_vector<VsInput, 32> vs_inputs{};
std::array<bool, 8> stores_frag_color{}; struct PsInput {
bool stores_sample_mask{}; u32 param_index;
bool stores_frag_depth{}; u32 semantic;
bool is_default;
bool is_flat;
u32 default_value;
};
boost::container::static_vector<PsInput, 32> ps_inputs{};
bool uses_fp16{}; struct AttributeFlags {
bool uses_fp64{}; bool Get(IR::Attribute attrib, u32 comp = 0) const {
bool uses_fp16_denorms_flush{}; return flags[static_cast<size_t>(attrib)] & (1 << comp);
bool uses_fp16_denorms_preserve{}; }
bool uses_fp32_denorms_flush{};
bool uses_fp32_denorms_preserve{};
bool uses_int8{};
bool uses_int16{};
bool uses_int64{};
bool uses_image_1d{};
bool uses_sampled_1d{};
bool uses_subgroup_vote{};
bool uses_subgroup_mask{};
bool uses_derivatives{};
IR::Type used_constant_buffer_types{}; bool GetAny(IR::Attribute attrib) const {
IR::Type used_storage_buffer_types{}; return flags[static_cast<size_t>(attrib)];
IR::Type used_indirect_cbuf_types{}; }
// std::array<u32, MAX_CBUFS> constant_buffer_used_sizes{}; void Set(IR::Attribute attrib, u32 comp = 0) {
u32 used_clip_distances{}; flags[static_cast<size_t>(attrib)] |= (1 << comp);
}
// boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS> u32 NumComponents(IR::Attribute attrib) const {
// constant_buffer_descriptors; const u8 mask = flags[static_cast<size_t>(attrib)];
// boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> ASSERT(mask != 0b1011 || mask != 0b1101);
// storage_buffers_descriptors; TextureBufferDescriptors texture_buffer_descriptors; return std::popcount(mask);
// ImageBufferDescriptors image_buffer_descriptors; }
// TextureDescriptors texture_descriptors;
// ImageDescriptors image_descriptors; std::array<u8, IR::NumAttributes> flags;
};
AttributeFlags loads{};
AttributeFlags stores{};
std::span<const u32, 16> user_data;
Stage stage;
template <typename T>
T ReadUd(u32 ptr_index, u32 dword_offset) const noexcept {
T data;
u32* base;
std::memcpy(&base, &user_data[ptr_index], sizeof(base));
std::memcpy(&data, base + dword_offset, sizeof(T));
return data;
}
}; };
} // namespace Shader } // namespace Shader

View File

@ -33,13 +33,13 @@ struct Liverpool {
static constexpr u32 NumColorBuffers = 8; static constexpr u32 NumColorBuffers = 8;
static constexpr u32 NumViewports = 16; static constexpr u32 NumViewports = 16;
static constexpr u32 NumClipPlanes = 6; static constexpr u32 NumClipPlanes = 6;
static constexpr u32 NumWordsShaderUserData = 16; static constexpr u32 NumShaderUserData = 16;
static constexpr u32 UconfigRegWordOffset = 0xC000; static constexpr u32 UconfigRegWordOffset = 0xC000;
static constexpr u32 ContextRegWordOffset = 0xA000; static constexpr u32 ContextRegWordOffset = 0xA000;
static constexpr u32 ShRegWordOffset = 0x2C00; static constexpr u32 ShRegWordOffset = 0x2C00;
static constexpr u32 NumRegs = 0xD000; static constexpr u32 NumRegs = 0xD000;
using UserData = std::array<u32, NumWordsShaderUserData>; using UserData = std::array<u32, NumShaderUserData>;
struct ShaderProgram { struct ShaderProgram {
u32 address_lo; u32 address_lo;
@ -58,6 +58,14 @@ struct Liverpool {
} }
}; };
union PsInputControl {
u32 raw;
BitField<0, 5, u32> input_offset;
BitField<5, 1, u32> use_default;
BitField<8, 2, u32> default_value;
BitField<10, 1, u32> flat_shade;
};
enum class ShaderExportComp : u32 { enum class ShaderExportComp : u32 {
None = 0, None = 0,
OneComp = 1, OneComp = 1,
@ -552,9 +560,12 @@ struct Liverpool {
INSERT_PADDING_WORDS(1); INSERT_PADDING_WORDS(1);
std::array<ViewportBounds, NumViewports> viewports; std::array<ViewportBounds, NumViewports> viewports;
std::array<ClipUserData, NumClipPlanes> clip_user_data; std::array<ClipUserData, NumClipPlanes> clip_user_data;
INSERT_PADDING_WORDS(0xA1B1 - 0xA187); INSERT_PADDING_WORDS(0xA191 - 0xA187);
std::array<PsInputControl, 32> ps_inputs;
VsOutputConfig vs_output_config; VsOutputConfig vs_output_config;
INSERT_PADDING_WORDS(0xA1C3 - 0xA1B1 - 1); INSERT_PADDING_WORDS(4);
BitField<0, 6, u32> num_interp;
INSERT_PADDING_WORDS(0xA1C3 - 0xA1B6 - 1);
ShaderPosFormat shader_pos_format; ShaderPosFormat shader_pos_format;
ShaderExportFormat z_export_format; ShaderExportFormat z_export_format;
ColorExportFormat color_export_format; ColorExportFormat color_export_format;
@ -631,7 +642,9 @@ static_assert(GFX6_3D_REG_INDEX(viewport_scissors) == 0xA094);
static_assert(GFX6_3D_REG_INDEX(stencil_control) == 0xA10B); static_assert(GFX6_3D_REG_INDEX(stencil_control) == 0xA10B);
static_assert(GFX6_3D_REG_INDEX(viewports) == 0xA10F); static_assert(GFX6_3D_REG_INDEX(viewports) == 0xA10F);
static_assert(GFX6_3D_REG_INDEX(clip_user_data) == 0xA16F); static_assert(GFX6_3D_REG_INDEX(clip_user_data) == 0xA16F);
static_assert(GFX6_3D_REG_INDEX(ps_inputs) == 0xA191);
static_assert(GFX6_3D_REG_INDEX(vs_output_config) == 0xA1B1); static_assert(GFX6_3D_REG_INDEX(vs_output_config) == 0xA1B1);
static_assert(GFX6_3D_REG_INDEX(num_interp) == 0xA1B6);
static_assert(GFX6_3D_REG_INDEX(shader_pos_format) == 0xA1C3); static_assert(GFX6_3D_REG_INDEX(shader_pos_format) == 0xA1C3);
static_assert(GFX6_3D_REG_INDEX(z_export_format) == 0xA1C4); static_assert(GFX6_3D_REG_INDEX(z_export_format) == 0xA1C4);
static_assert(GFX6_3D_REG_INDEX(color_export_format) == 0xA1C5); static_assert(GFX6_3D_REG_INDEX(color_export_format) == 0xA1C5);

View File

@ -2,10 +2,44 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <array> #include <array>
#include "common/assert.h"
#include "video_core/amdgpu/pixel_format.h" #include "video_core/amdgpu/pixel_format.h"
namespace AmdGpu { namespace AmdGpu {
std::string_view NameOf(NumberFormat fmt) {
switch (fmt) {
case NumberFormat::Unorm:
return "Unorm";
case NumberFormat::Snorm:
return "Snorm";
case NumberFormat::Uscaled:
return "Uscaled";
case NumberFormat::Sscaled:
return "Sscaled";
case NumberFormat::Uint:
return "Uint";
case NumberFormat::Sint:
return "Sint";
case NumberFormat::SnormNz:
return "SnormNz";
case NumberFormat::Float:
return "Float";
case NumberFormat::Srgb:
return "Srgb";
case NumberFormat::Ubnorm:
return "Ubnorm";
case NumberFormat::UbnromNz:
return "UbnormNz";
case NumberFormat::Ubint:
return "Ubint";
case NumberFormat::Ubscaled:
return "Unscaled";
default:
UNREACHABLE();
}
}
u32 NumComponents(DataFormat format) { u32 NumComponents(DataFormat format) {
constexpr std::array numComponentsPerElement = { constexpr std::array numComponentsPerElement = {
0, 1, 1, 2, 1, 2, 3, 3, 4, 4, 4, 2, 4, 3, 4, -1, 3, 4, 4, 4, 2, 0, 1, 1, 2, 1, 2, 3, 3, 4, 4, 4, 2, 4, 3, 4, -1, 3, 4, 4, 4, 2,

View File

@ -3,6 +3,8 @@
#pragma once #pragma once
#include <string_view>
#include <fmt/format.h>
#include "common/types.h" #include "common/types.h"
namespace AmdGpu { namespace AmdGpu {
@ -59,6 +61,19 @@ enum class NumberFormat : u32 {
Ubscaled = 13, Ubscaled = 13,
}; };
[[nodiscard]] std::string_view NameOf(NumberFormat fmt);
u32 NumComponents(DataFormat format); u32 NumComponents(DataFormat format);
} // namespace AmdGpu } // namespace AmdGpu
template <>
struct fmt::formatter<AmdGpu::NumberFormat> {
constexpr auto parse(format_parse_context& ctx) {
return ctx.begin();
}
auto format(AmdGpu::NumberFormat fmt, format_context& ctx) const {
return fmt::format_to(ctx.out(), "{}", AmdGpu::NameOf(fmt));
}
};

View File

@ -1,7 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <fstream>
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/recompiler.h" #include "shader_recompiler/recompiler.h"
#include "shader_recompiler/runtime_info.h" #include "shader_recompiler/runtime_info.h"
#include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_instance.h"
@ -11,9 +13,31 @@
namespace Vulkan { namespace Vulkan {
Shader::Info MakeShaderInfo(Shader::Stage stage, std::span<const u32, 16> user_data,
AmdGpu::Liverpool::Regs& regs) {
Shader::Info info{user_data};
info.stage = stage;
switch (stage) {
case Shader::Stage::Fragment: {
for (u32 i = 0; i < regs.num_interp; i++) {
info.ps_inputs.push_back({
.param_index = regs.ps_inputs[i].input_offset.Value(),
.is_default = bool(regs.ps_inputs[i].use_default),
.is_flat = bool(regs.ps_inputs[i].flat_shade),
.default_value = regs.ps_inputs[i].default_value,
});
}
break;
}
default:
break;
}
return info;
}
PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
AmdGpu::Liverpool* liverpool_) AmdGpu::Liverpool* liverpool_)
: instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_}, inst_pool{4096}, : instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_}, inst_pool{8192},
block_pool{512} { block_pool{512} {
const vk::PipelineLayoutCreateInfo layout_info = { const vk::PipelineLayoutCreateInfo layout_info = {
.setLayoutCount = 0U, .setLayoutCount = 0U,
@ -50,8 +74,18 @@ void PipelineCache::BindPipeline() {
// Compile and cache shader. // Compile and cache shader.
const auto data = std::span{token, bininfo.length / sizeof(u32)}; const auto data = std::span{token, bininfo.length / sizeof(u32)};
const auto program = Shader::TranslateProgram(inst_pool, block_pool, stage, pgm.user_data, data); block_pool.ReleaseContents();
return CompileSPV(program, instance.GetDevice()); inst_pool.ReleaseContents();
const auto info = MakeShaderInfo(stage, pgm.user_data, liverpool->regs);
auto program = Shader::TranslateProgram(inst_pool, block_pool, data, std::move(info));
const auto code = Shader::Backend::SPIRV::EmitSPIRV(Shader::Profile{}, program);
static int counter = 0;
std::ofstream file(fmt::format("shader{}.spv", counter++), std::ios::out | std::ios::binary);
file.write((const char*)code.data(), code.size() * sizeof(u32));
file.close();
return CompileSPV(code, instance.GetDevice());
}; };
// Retrieve shader stage modules. // Retrieve shader stage modules.