mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
shader: fix embedded vertex fetch swizzles
This commit is contained in:
@@ -553,76 +553,7 @@ void CreatePipelineInternal(
|
||||
EXIT_NOT_IMPLEMENTED(vs_input_info.resources[index].AddTid());
|
||||
EXIT_NOT_IMPLEMENTED(vs_input_info.resources[index].SwizzleEnabled());
|
||||
|
||||
auto log_unsupported_vertex_swizzle = [index, attr_size, registers_num](uint32_t swizzle,
|
||||
uint32_t expected) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("VertexInput: temporary: accepting unsupported dst swizzle at attr %" PRIu32
|
||||
" (attr_size=%" PRIu32 ", regs=%" PRIu32 ", swizzle=0x%03" PRIx32
|
||||
", expected=0x%03" PRIx32 ")\n",
|
||||
static_cast<uint32_t>(index), attr_size, registers_num, swizzle, expected);
|
||||
logged = true;
|
||||
}
|
||||
};
|
||||
|
||||
switch (registers_num) {
|
||||
case 1: {
|
||||
auto swizzle = vs_input_info.resources[index].DstSelX();
|
||||
if (swizzle != DstSel(4)) {
|
||||
log_unsupported_vertex_swizzle(swizzle, DstSel(4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
auto swizzle = vs_input_info.resources[index].DstSelXY();
|
||||
auto expected = (attr_size == 1 ? DstSel(4, 0) : DstSel(4, 5));
|
||||
if (swizzle != expected) {
|
||||
log_unsupported_vertex_swizzle(swizzle, expected);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
auto swizzle = vs_input_info.resources[index].DstSelXYZ();
|
||||
auto expected = DstSel(4, 5, 6);
|
||||
switch (attr_size) {
|
||||
case 1: expected = DstSel(4, 0, 0); break;
|
||||
case 2: expected = DstSel(4, 5, 0); break;
|
||||
default: break;
|
||||
}
|
||||
if (swizzle != expected) {
|
||||
log_unsupported_vertex_swizzle(swizzle, expected);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
auto swizzle = vs_input_info.resources[index].DstSelXYZW();
|
||||
auto expected = DstSel(4, 5, 6, 7);
|
||||
bool supported = false;
|
||||
switch (attr_size) {
|
||||
case 1:
|
||||
expected = DstSel(4, 0, 0, 1);
|
||||
supported = (swizzle == expected);
|
||||
break;
|
||||
case 2:
|
||||
expected = DstSel(4, 5, 0, 1);
|
||||
supported = (swizzle == expected);
|
||||
break;
|
||||
case 3:
|
||||
expected = DstSel(4, 5, 6, 1);
|
||||
supported = (swizzle == expected || swizzle == DstSel(4, 5, 6, 0));
|
||||
break;
|
||||
default:
|
||||
supported = (swizzle == expected || swizzle == DstSel(4, 5, 6, 1) ||
|
||||
swizzle == DstSel(4, 5, 6, 0));
|
||||
break;
|
||||
}
|
||||
if (!supported) {
|
||||
log_unsupported_vertex_swizzle(swizzle, expected);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: EXIT("invalid registers_num");
|
||||
}
|
||||
EXIT_IF(registers_num < 1 || registers_num > 4);
|
||||
}
|
||||
|
||||
vk::PipelineVertexInputStateCreateInfo vertex_input_info {};
|
||||
|
||||
@@ -26,6 +26,28 @@ struct BufferFormatInfo {
|
||||
bool packed_bitfield = false;
|
||||
};
|
||||
|
||||
enum class FormattedSourceKind { Memory, Zero, One, Invalid };
|
||||
|
||||
struct FormattedSource {
|
||||
FormattedSourceKind kind = FormattedSourceKind::Zero;
|
||||
uint32_t component = 0;
|
||||
};
|
||||
|
||||
constexpr FormattedSource ResolveFormattedSource(const BufferFormatInfo& info, uint32_t selector) {
|
||||
if (selector == 0u) return {};
|
||||
if (selector == 1u) return {FormattedSourceKind::One, 0};
|
||||
if (selector < 4u || selector > 7u || info.component_count == 0u) {
|
||||
return {FormattedSourceKind::Invalid, 0};
|
||||
}
|
||||
// Formatted decoding expands source channels before applying the swizzle.
|
||||
return {FormattedSourceKind::Memory, (selector - 4u) % info.component_count};
|
||||
}
|
||||
|
||||
constexpr uint32_t FormattedConstantBits(const BufferFormatInfo& info, FormattedSourceKind kind) {
|
||||
if (kind != FormattedSourceKind::One) return 0;
|
||||
return info.type == ComponentType::Uint || info.type == ComponentType::Sint ? 1u : 0x3f800000u;
|
||||
}
|
||||
|
||||
constexpr Prospero::BufferFormat DecodeTBufferFormat(uint32_t data_format, uint32_t number_format) {
|
||||
return static_cast<Prospero::BufferFormat>(((number_format & 0x7u) << 4u) |
|
||||
(data_format & 0xfu));
|
||||
|
||||
@@ -175,22 +175,10 @@ uint32_t EmitLocalInvocationIndex(EmitterState& state) {
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t VertexInputDefaultComponentU32(EmitterState& state, VertexInputScalarKind kind,
|
||||
uint32_t component) {
|
||||
if ((component & 3u) != 3u) {
|
||||
return ConstantU32(state, 0);
|
||||
}
|
||||
return ConstantU32(state, kind == VertexInputScalarKind::Float ? 0x3f800000u : 1u);
|
||||
}
|
||||
|
||||
uint32_t EmitVertexParameterComponentU32(EmitterState& state, const InputBinding& input,
|
||||
uint32_t component) {
|
||||
const auto count = VertexParameterComponentCount(state, input);
|
||||
const auto count = VertexParameterComponentCount(input);
|
||||
const auto kind = VertexParameterScalarKind(state, input.location);
|
||||
if (component >= count) {
|
||||
return VertexInputDefaultComponentU32(state, kind, component);
|
||||
}
|
||||
|
||||
const auto scalar_type = VertexParameterScalarType(state, kind);
|
||||
uint32_t raw = state.builder.AllocateId();
|
||||
if (count == 1u) {
|
||||
|
||||
@@ -501,7 +501,7 @@ bool PixelParameterIsCustom(const EmitterState& state, uint32_t attr);
|
||||
|
||||
VertexInputScalarKind VertexParameterScalarKind(const EmitterState& state, uint32_t location);
|
||||
|
||||
uint32_t VertexParameterComponentCount(const EmitterState& state, const InputBinding& input);
|
||||
uint32_t VertexParameterComponentCount(const InputBinding& input);
|
||||
|
||||
uint32_t VertexParameterScalarType(EmitterState& state, VertexInputScalarKind kind);
|
||||
|
||||
|
||||
@@ -386,12 +386,8 @@ IR::MemoryInfo RebaseRawComponent(IR::MemoryInfo mem, uint32_t component) {
|
||||
return mem;
|
||||
}
|
||||
|
||||
enum class FormattedSourceKind { Memory, Zero, One };
|
||||
|
||||
struct FormattedSource {
|
||||
FormattedSourceKind kind = FormattedSourceKind::Zero;
|
||||
uint32_t component = 0;
|
||||
};
|
||||
using Format::FormattedSource;
|
||||
using Format::FormattedSourceKind;
|
||||
|
||||
FormattedSource ResolveFormattedSource(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
|
||||
const Format::BufferFormatInfo& info,
|
||||
@@ -403,24 +399,17 @@ FormattedSource ResolveFormattedSource(ValueEmitContext& ctx, const IR::MemoryIn
|
||||
}
|
||||
const auto selector = GetDstSel(ctx.state.program.info.buffers[mem.resource].descriptor_swizzle,
|
||||
output_component);
|
||||
if (selector == 0u) return {};
|
||||
if (selector == 1u) return {FormattedSourceKind::One, 0};
|
||||
if (selector < 4u) {
|
||||
const auto source = Format::ResolveFormattedSource(info, selector);
|
||||
if (source.kind == FormattedSourceKind::Invalid) {
|
||||
ExitDescriptorBindingFailure(ctx.state, IR::DescriptorBindingKind::Buffers, mem.resource,
|
||||
"buffer descriptor has reserved dst_sel");
|
||||
}
|
||||
const auto component = selector - 4u;
|
||||
return component < info.component_count
|
||||
? FormattedSource {FormattedSourceKind::Memory, component}
|
||||
: FormattedSource {};
|
||||
return source;
|
||||
}
|
||||
|
||||
uint32_t FormattedConstant(ValueEmitContext& ctx, const Format::BufferFormatInfo& info,
|
||||
FormattedSourceKind kind) {
|
||||
if (kind != FormattedSourceKind::One) return ConstantU32(ctx.state, 0);
|
||||
const auto integer =
|
||||
info.type == Format::ComponentType::Uint || info.type == Format::ComponentType::Sint;
|
||||
return ConstantU32(ctx.state, integer ? 1u : 0x3f800000u);
|
||||
return ConstantU32(ctx.state, Format::FormattedConstantBits(info, kind));
|
||||
}
|
||||
|
||||
template <typename LoadWordFn, typename LoadSubwordFn>
|
||||
|
||||
@@ -336,15 +336,8 @@ VertexInputScalarKind VertexParameterScalarKind(const EmitterState& state, uint3
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t VertexParameterComponentCount(const EmitterState& state, const InputBinding& input) {
|
||||
uint32_t count = input.component_count;
|
||||
if (state.stage == ShaderType::Vertex && input.location < ShaderVertexInputInfo::RES_MAX &&
|
||||
input.location < static_cast<uint32_t>(state.input_info.vertex->resources_num) &&
|
||||
state.input_info.vertex->resources_dst[input.location].registers_num > 0) {
|
||||
count = static_cast<uint32_t>(
|
||||
state.input_info.vertex->resources_dst[input.location].registers_num);
|
||||
}
|
||||
return std::clamp(count, 1u, 4u);
|
||||
uint32_t VertexParameterComponentCount(const InputBinding& input) {
|
||||
return std::clamp(input.component_count, 1u, 4u);
|
||||
}
|
||||
|
||||
uint32_t VertexParameterScalarType(EmitterState& state, VertexInputScalarKind kind) {
|
||||
@@ -830,7 +823,7 @@ void DefineModule(EmitterState& state) {
|
||||
case IR::StageInputKind::Parameter:
|
||||
if (state.stage == ShaderType::Vertex) {
|
||||
const auto kind = VertexParameterScalarKind(state, input.location);
|
||||
const auto components = VertexParameterComponentCount(state, input);
|
||||
const auto components = VertexParameterComponentCount(input);
|
||||
ptr_type = VertexParameterInputPointerType(state, kind, components);
|
||||
} else if (input.per_vertex) {
|
||||
const auto array_type = state.builder.Type(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "common/assert.h"
|
||||
#include "graphics/shader/recompiler/BufferFormat.h"
|
||||
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -47,10 +48,27 @@ IR::ExportFlags Translator::AddExportInfo(const Decoder::Instruction& inst) {
|
||||
}
|
||||
|
||||
void Translator::TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
|
||||
uint32_t component_count) {
|
||||
uint32_t component_count,
|
||||
const ShaderBufferResource& resource) {
|
||||
const auto format = Format::GetFormatInfo(resource.Format());
|
||||
for (uint32_t component = 0; component < component_count; component++) {
|
||||
const auto value =
|
||||
ir.Emit(IR::ValueOpcode::GetAttribute, {IR::Value(attribute), IR::Value(component)});
|
||||
auto source = Format::FormattedSource {Format::FormattedSourceKind::Memory, component};
|
||||
if (inst.formatted && !inst.typed) {
|
||||
source = Format::ResolveFormattedSource(
|
||||
format, GetDstSel(resource.DstSelXYZW(), component));
|
||||
if (source.kind == Format::FormattedSourceKind::Invalid) {
|
||||
EXIT("invalid formatted vertex input %u at pc 0x%08x", attribute, inst.pc);
|
||||
}
|
||||
}
|
||||
IR::Value value;
|
||||
if (source.kind == Format::FormattedSourceKind::Memory) {
|
||||
value = ir.Emit(IR::ValueOpcode::GetAttribute,
|
||||
{IR::Value(attribute), IR::Value(source.component)});
|
||||
auto& required = program.info.vertex_fetch_components[attribute];
|
||||
required = static_cast<uint8_t>(std::max<uint32_t>(required, source.component + 1u));
|
||||
} else {
|
||||
value = IR::Value(Format::FormattedConstantBits(format, source.kind));
|
||||
}
|
||||
WriteOperand(OffsetOperand(inst.dst, component), value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1204,12 +1204,9 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
|
||||
EXIT("embedded vertex fetch at 0x%08x has no resource for attribute %d",
|
||||
instruction.pc, embedded->attrib_id);
|
||||
}
|
||||
result.info.vertex_fetch_components[static_cast<size_t>(resource)] =
|
||||
static_cast<uint8_t>(std::max<uint32_t>(
|
||||
result.info.vertex_fetch_components[static_cast<size_t>(resource)],
|
||||
embedded->components));
|
||||
translator.TranslateEmbeddedFetch(instruction, static_cast<uint32_t>(resource),
|
||||
embedded->components);
|
||||
embedded->components,
|
||||
options.vertex->resources[resource]);
|
||||
continue;
|
||||
}
|
||||
translator.TranslateInstruction(instruction);
|
||||
|
||||
@@ -15,7 +15,7 @@ public:
|
||||
|
||||
void TranslateInstruction(const Decoder::Instruction& inst);
|
||||
void TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
|
||||
uint32_t component_count);
|
||||
uint32_t component_count, const ShaderBufferResource& resource);
|
||||
void AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
@@ -18433,6 +18433,27 @@ TestCase BufferLoadFormatXyzwResource32_32FloatAppliesXy01() {
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase BufferLoadFormatXyzwResource323232FloatRepeatsSource() {
|
||||
using O = ShaderOpcode;
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 20, 0);
|
||||
AppendBufferLoadOpcode(&code, 0x03, 0, 20);
|
||||
for (u32 i = 0; i < 4; ++i) AppendStoreVgpr(&code, i, i + 4);
|
||||
AppendEnd(&code);
|
||||
TestCase test;
|
||||
test.name = "BufferLoadFormatXyzwResource323232FloatRepeatsSource";
|
||||
test.code = std::move(code);
|
||||
test.initial = {0x3e800000u, 0x3f000000u, 0x3f400000u, 0xdeadbeefu, 0, 0, 0, 0};
|
||||
test.expected = {0x3e800000u, 0x3f000000u, 0x3f400000u, 0xdeadbeefu,
|
||||
0x3e800000u, 0x3f000000u, 0x3f400000u, 0x3e800000u};
|
||||
test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_FORMAT_XYZW,
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.user_data = MakeStructuredStorageBufferData(
|
||||
0, 32, false, BufferFormat(Prospero::BufferFormat::k32_32_32Float));
|
||||
test.has_user_data = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase BufferLoadFormatXyzwResource8_8_8_8UintAppliesWzy1() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -22716,6 +22737,7 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(BufferFormatVariants);
|
||||
AddCase(BufferLoadFormatXyzwSnapshotsOverlappingAddress);
|
||||
AddCase(BufferLoadFormatXyzwResource32_32FloatAppliesXy01);
|
||||
AddCase(BufferLoadFormatXyzwResource323232FloatRepeatsSource);
|
||||
AddCase(BufferLoadFormatXyzwResource8_8_8_8UintAppliesWzy1);
|
||||
AddCase(BufferLoadFormatXyzwInactiveExecPreservesOverlappingAddress);
|
||||
AddCase(BufferFormatStoreVariants);
|
||||
@@ -23087,6 +23109,8 @@ void CheckEmbeddedFetchVertexOffset() {
|
||||
vertex.fetch_buffer_reg = 0;
|
||||
vertex.fetch_attrib_reg = 2;
|
||||
vertex.resources_num = 1;
|
||||
vertex.resources[0].fields[3] =
|
||||
BufferFormat(Prospero::BufferFormat::k32_32_32_32Float) << 12u | DstSel(4, 5, 6, 7);
|
||||
vertex.resources_dst[0].attr_id = 0;
|
||||
vertex.resources_dst[0].registers_num = 4;
|
||||
|
||||
@@ -25966,6 +25990,8 @@ void CheckEmbeddedFetchLaneSpill() {
|
||||
vertex.fetch_embedded = true;
|
||||
vertex.fetch_buffer_reg = 0;
|
||||
vertex.resources_num = 1;
|
||||
vertex.resources[0].fields[3] =
|
||||
BufferFormat(Prospero::BufferFormat::k32_32_32_32Float) << 12u | DstSel(4, 5, 6, 7);
|
||||
vertex.resources_dst[0].attr_id = 0;
|
||||
vertex.resources_dst[0].registers_num = 4;
|
||||
|
||||
|
||||
@@ -9203,6 +9203,84 @@ void TestMergedShaderUserDataSnapshot() {
|
||||
}
|
||||
}
|
||||
|
||||
void TestEmbeddedVertexFormatSwizzle() {
|
||||
using namespace ShaderRecompiler;
|
||||
using namespace ShaderRecompiler::IR;
|
||||
struct Case {
|
||||
Prospero::BufferFormat format;
|
||||
uint32_t swizzle, opcode, components, source_width;
|
||||
std::array<uint32_t, 4> expected;
|
||||
};
|
||||
const Case cases[] = {
|
||||
{Prospero::BufferFormat::k32_32_32Float, DstSel(4, 5, 6, 7), 3, 4, 3,
|
||||
{0x3e800000, 0x3f000000, 0x3f400000, 0x3e800000}},
|
||||
{Prospero::BufferFormat::k32_32_32Float, DstSel(4, 5, 6, 1), 3, 4, 3,
|
||||
{0x3e800000, 0x3f000000, 0x3f400000, 0x3f800000}},
|
||||
{Prospero::BufferFormat::k32_32_32_32UInt, DstSel(7, 6, 5, 0), 3, 4, 4,
|
||||
{0x40000000, 0x3f400000, 0x3f000000, 0}},
|
||||
{Prospero::BufferFormat::k32_32_32_32UInt, DstSel(7, 0, 0, 0), 0, 1, 4,
|
||||
{0x40000000, 0, 0, 0}},
|
||||
{Prospero::BufferFormat::k32UInt, DstSel(1, 0, 1, 0), 3, 4, 0,
|
||||
{1, 0, 1, 0}},
|
||||
{Prospero::BufferFormat::k32Float, DstSel(1, 0, 1, 0), 3, 4, 0,
|
||||
{0x3f800000, 0, 0x3f800000, 0}},
|
||||
{Prospero::BufferFormat::k32_32_32_32Float, DstSel(7, 6, 5, 4), 0x0e, 4, 4,
|
||||
{0x3e800000, 0x3f000000, 0x3f400000, 0x40000000}},
|
||||
};
|
||||
for (const auto &test : cases) {
|
||||
const uint32_t code[] = {EncodeMubuf0(test.opcode), EncodeMubuf1(9, 0, 0),
|
||||
EncodeExp0(0x20, (1u << test.components) - 1u),
|
||||
EncodeExp1(9, 10, 11, 12), EncodeSopp(0x01)};
|
||||
Decoder::Program decoded;
|
||||
Decoder::DecodeProgram(code, decoded);
|
||||
ShaderVertexInputInfo input{};
|
||||
input.resources_num = 1;
|
||||
input.resources[0].fields[3] = (static_cast<uint32_t>(test.format) << 12u) | test.swizzle;
|
||||
input.resources_dst[0].attr_id = 0;
|
||||
input.resources_dst[0].registers_num = static_cast<int>(test.components);
|
||||
Frontend::EmbeddedFetchPlan fetch;
|
||||
fetch.loads.push_back({.pc = 0, .attrib_id = 0, .components = test.components});
|
||||
Frontend::TranslateOptions options{};
|
||||
options.stage = ShaderType::Vertex;
|
||||
options.wave_size = 32;
|
||||
options.user_data_count = 0;
|
||||
options.vertex = &input;
|
||||
options.embedded_fetch = &fetch;
|
||||
auto program = Frontend::TranslateProgram(decoded, CFG::BuildGraph(decoded), options);
|
||||
Check(program.info.vertex_fetch_components[0] == test.source_width,
|
||||
"vertex fetch width follows destination VGPR count instead of selected source channels");
|
||||
const uint32_t source[] = {0x3e800000, 0x3f000000, 0x3f400000, 0x40000000};
|
||||
for (auto *block : program.blocks) {
|
||||
for (auto &inst : *block) {
|
||||
if (inst.GetOpcode() == ValueOpcode::GetAttribute) {
|
||||
Check(inst.Arg(0).U32() == 0 && inst.Arg(1).U32() < 4,
|
||||
"vertex fetch selected an invalid attribute channel");
|
||||
inst.ReplaceUsesWith(Value(source[inst.Arg(1).U32()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
RewriteToSsa(program.blocks);
|
||||
RemoveIdentities(program.blocks);
|
||||
ConstantPropagationPass(program.blocks);
|
||||
uint32_t exports = 0;
|
||||
for (const auto *block : program.blocks) {
|
||||
for (const auto &inst : *block) {
|
||||
if (inst.GetOpcode() != ValueOpcode::SetAttribute) continue;
|
||||
++exports;
|
||||
const auto *value = inst.Arg(0).ResolveInstruction();
|
||||
Check(value != nullptr && value->GetOpcode() == ValueOpcode::CompositeConstructU32x4,
|
||||
"vertex fetch export did not retain its four components");
|
||||
for (uint32_t component = 0; component < 4; ++component) {
|
||||
const auto actual = value->Arg(component).Resolve();
|
||||
Check(actual.IsImmediate() && actual.U32() == test.expected[component],
|
||||
"formatted vertex fetch did not apply the source-channel swizzle");
|
||||
}
|
||||
}
|
||||
}
|
||||
Check(exports == 1, "vertex fetch test did not reach its export");
|
||||
}
|
||||
}
|
||||
|
||||
void TestMeshInputAssembly() {
|
||||
using namespace ShaderRecompiler;
|
||||
using namespace ShaderRecompiler::IR;
|
||||
@@ -12558,6 +12636,7 @@ int main() {
|
||||
TestMeshExportStorage();
|
||||
TestMergedShaderUserDataSnapshot();
|
||||
TestMeshInputAssembly();
|
||||
TestEmbeddedVertexFormatSwizzle();
|
||||
TestNewShaderRecompilerSetpcJumpTable();
|
||||
TestNewShaderRecompilerPrunesUnreachableSetpcMetadata();
|
||||
TestNewShaderRecompilerSetpcDwordJumpTable();
|
||||
|
||||
Reference in New Issue
Block a user