shader: fix embedded vertex fetch swizzles

This commit is contained in:
nmzik
2026-09-05 16:00:46 +04:00
parent 9c00c7077b
commit 81a5dd612f
11 changed files with 163 additions and 120 deletions
@@ -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: