shader: share formatted loads and support packed D16 XYZW buffers

Co-authored-by: fxpw <fxpw@outlook.com>
This commit is contained in:
nmzik
2026-10-14 04:09:57 +02:00
parent 1905b28b3c
commit 6abfe3bbe4
8 changed files with 170 additions and 40 deletions
@@ -741,30 +741,58 @@ uint32_t FormattedOutOfBoundsValue(ValueEmitContext& ctx, const IR::MemoryInfo&
return ConstructU32Composite(ctx.state, components, values);
}
uint32_t FormattedLoad(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem) {
return EmitValueOrZeroIfCondition(ctx.state, ctx.Arg(inst, inst.NumArgs() - 1), [&]() {
const auto resource = PrepareMemoryResourceAccess(ctx.state, mem);
const auto info = Format::GetFormatInfo(BufferFormat(ctx, mem));
if (info.type == Format::ComponentType::Unknown) {
return LoadWordPrepared(ctx, inst, RebaseRawComponent(mem, 0u), resource);
}
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, info);
const auto value = EmitValueOrDefaultIfCondition(
ctx.state, plan.in_bounds, TypeU32(ctx.state),
FormattedOutOfBoundsValue(ctx, mem, plan, 1u),
[&]() { return LoadFormattedInBounds(ctx, mem, plan, 0u); });
if (mem.data_bits != 16u) {
return value;
uint32_t LoadFormattedPrepared(ValueEmitContext& ctx, const IR::Inst& inst,
const IR::MemoryInfo& mem, const MemoryResourceAccess& resource,
const Format::BufferFormatInfo& info, uint32_t components) {
auto& state = ctx.state;
const auto count = mem.data_bits == 16u ? mem.component_count : components;
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, info);
const auto value = EmitValueOrDefaultIfCondition(
state, plan.in_bounds, TypeU32Composite(state, count),
FormattedOutOfBoundsValue(ctx, mem, plan, count), [&]() {
std::array<uint32_t, 4> values {};
for (uint32_t component = 0; component < count; component++) {
values[component] = LoadFormattedInBounds(ctx, mem, plan, component);
}
return ConstructU32Composite(state, count, values);
});
if (mem.data_bits != 16u) return value;
const bool integer = info.type == Format::ComponentType::Uint ||
info.type == Format::ComponentType::Sint;
const auto scalar = [&](uint32_t component) {
if (component >= count) return integer ? ConstantU32(state, 0u) : ConstantF32(state, 0);
auto result = value;
if (count != 1u) {
result = state.builder.AllocateId();
state.builder.AddFunction(spv::OpCompositeExtract, TypeU32(state), result, value,
component);
}
if (info.type == Format::ComponentType::Uint) {
return EmitUMin32(ctx.state, value, ConstantU32(ctx.state, 0xffffu));
return EmitUMin32(state, result, ConstantU32(state, 0xffffu));
}
if (info.type == Format::ComponentType::Sint) {
const auto lower = EmitSMax32(ctx.state, value, ConstantU32(ctx.state, 0xffff8000u));
return EmitSMin32(ctx.state, lower, ConstantU32(ctx.state, 0x7fffu));
const auto lower = EmitSMax32(state, result, ConstantU32(state, 0xffff8000u));
return EmitSMin32(state, lower, ConstantU32(state, 0x7fffu));
}
return EmitConvertF16F32(ctx.state, EmitBitCastF32U32(ctx.state, value));
});
return EmitBitCastF32U32(state, result);
};
std::array<uint32_t, 4> packed {};
for (uint32_t word = 0; word < components; word++) {
const auto low = scalar(word * 2u);
const auto high = scalar(word * 2u + 1u);
if (integer) {
packed[word] = Binary(
state, spv::OpBitwiseOr, TypeU32(state),
Binary(state, spv::OpBitwiseAnd, TypeU32(state), low, ConstantU32(state, 0xffffu)),
Binary(state, spv::OpShiftLeftLogical, TypeU32(state), high, ConstantU32(state, 16u)));
} else {
const auto pair = state.builder.AllocateId();
state.builder.AddFunction(spv::OpCompositeConstruct, TypeF32Vector(state, 2), pair,
low, high);
packed[word] = EmitPackHalf2x16(state, pair);
}
}
return ConstructU32Composite(state, components, packed);
}
void StoreFormattedPrepared(ValueEmitContext& ctx, const IR::Inst& inst,
@@ -1042,10 +1070,13 @@ uint32_t LoadIndirectFormattedX(ValueEmitContext& ctx, const IR::Inst& inst) {
uint32_t LoadBuffer(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t components) {
auto& state = ctx.state;
const auto mem = ctx.Memory(inst);
if (mem.kind == IR::ResourceKind::IndirectBuffer && mem.formatted) {
return LoadIndirectFormattedX(ctx, inst);
}
return EmitValueOrDefaultIfCondition(
state, ctx.Arg(inst, inst.NumArgs() - 1), TypeU32Composite(state, components),
ConstantU32CompositeZero(state, components), [&]() {
const auto mem = ctx.Memory(inst);
if (mem.kind == IR::ResourceKind::IndirectBuffer) {
return LoadIndirectBuffer(ctx, inst, components);
}
@@ -1053,16 +1084,7 @@ uint32_t LoadBuffer(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t compon
const auto info = Format::GetFormatInfo(
mem.formatted ? BufferFormat(ctx, mem) : Prospero::BufferFormat::kInvalid);
if (info.type != Format::ComponentType::Unknown) {
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, info);
return EmitValueOrDefaultIfCondition(
state, plan.in_bounds, TypeU32Composite(state, components),
FormattedOutOfBoundsValue(ctx, mem, plan, components), [&]() {
std::array<uint32_t, 4> values {};
for (uint32_t component = 0; component < components; component++) {
values[component] = LoadFormattedInBounds(ctx, mem, plan, component);
}
return ConstructU32Composite(state, components, values);
});
return LoadFormattedPrepared(ctx, inst, mem, resource, info, components);
}
std::array<uint32_t, 4> values {};
for (uint32_t component = 0; component < components; component++) {
@@ -1422,7 +1444,8 @@ void EmitLoadMemory(ValueEmitContext& ctx, const IR::Inst& inst) {
if (mem.kind == IR::ResourceKind::FlatLocal)
value = LoadLocalFlat(ctx, inst);
else if (buffer_components > 1u ||
(buffer_components == 1u && mem.kind == IR::ResourceKind::IndirectBuffer && !mem.formatted))
(op == IR::ValueOpcode::LoadBufferU32 &&
(mem.kind == IR::ResourceKind::IndirectBuffer || mem.formatted)))
value = LoadBuffer(ctx, inst, buffer_components);
else if (shared_components > 1u)
value = LoadWideShared(ctx, inst, shared_components);
@@ -1432,9 +1455,6 @@ void EmitLoadMemory(ValueEmitContext& ctx, const IR::Inst& inst) {
mem.kind != IR::ResourceKind::Scratch)
value = LoadBda(ctx, GuestAddress(ctx, inst, mem), ctx.Arg(inst, inst.NumArgs() - 1),
address_info.data_bits, mem.coherent);
else if (op == IR::ValueOpcode::LoadBufferU32 && mem.formatted)
value = mem.kind == IR::ResourceKind::IndirectBuffer ? LoadIndirectFormattedX(ctx, inst)
: FormattedLoad(ctx, inst, mem);
else if (inst.GetType() == IR::Type::U8)
value = LoadSubword(ctx, inst, mem, 8, false);
else if (inst.GetType() == IR::Type::U16)
@@ -66,6 +66,7 @@ constexpr MemoryOpcodeInfo MUBUF_OPCODE_LIST[] = {
{0x59u, Opcode::BUFFER_ATOMIC_AND_X2, 2, 32},
{0x5au, Opcode::BUFFER_ATOMIC_OR_X2, 2, 32},
{0x80u, Opcode::BUFFER_LOAD_FORMAT_D16_X, 1, 16, false, false, true},
{0x83u, Opcode::BUFFER_LOAD_FORMAT_D16_XYZW, 2, 16, false, false, true},
{0x84u, Opcode::BUFFER_STORE_FORMAT_D16_X, 1, 16, false, false, true},
};
@@ -304,6 +305,9 @@ void DecodeMubuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
}
DecodeVectorGpr(vdata, inst.dst);
if (inst.opcode == Opcode::BUFFER_LOAD_FORMAT_D16_XYZW) {
inst.data_components = 4u;
}
if (inst.opcode == Opcode::BUFFER_LOAD_FORMAT_D16_X ||
inst.opcode == Opcode::BUFFER_LOAD_SHORT_D16 ||
inst.opcode == Opcode::BUFFER_LOAD_SHORT_D16_HI) {
@@ -631,6 +631,7 @@ std::string InstructionToString(const Instruction& inst) {
case Opcode::BUFFER_LOAD_FORMAT_XYZ:
case Opcode::BUFFER_LOAD_FORMAT_XYZW:
case Opcode::BUFFER_LOAD_FORMAT_D16_X:
case Opcode::BUFFER_LOAD_FORMAT_D16_XYZW:
case Opcode::BUFFER_STORE_FORMAT_X:
case Opcode::BUFFER_STORE_FORMAT_XY:
case Opcode::BUFFER_STORE_FORMAT_XYZ:
@@ -547,6 +547,7 @@ enum class Opcode {
BUFFER_LOAD_FORMAT_XYZ,
BUFFER_LOAD_FORMAT_XYZW,
BUFFER_LOAD_FORMAT_D16_X,
BUFFER_LOAD_FORMAT_D16_XYZW,
BUFFER_STORE_FORMAT_X,
BUFFER_STORE_FORMAT_XY,
BUFFER_STORE_FORMAT_XYZ,
@@ -987,6 +987,7 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) {
case Decoder::Opcode::BUFFER_LOAD_FORMAT_XYZ:
case Decoder::Opcode::BUFFER_LOAD_FORMAT_XYZW:
case Decoder::Opcode::BUFFER_LOAD_FORMAT_D16_X:
case Decoder::Opcode::BUFFER_LOAD_FORMAT_D16_XYZW:
case Decoder::Opcode::TBUFFER_LOAD_FORMAT_X:
case Decoder::Opcode::TBUFFER_LOAD_FORMAT_XY:
case Decoder::Opcode::TBUFFER_LOAD_FORMAT_XYZ:
@@ -473,8 +473,11 @@ void ValidateProgram(const Program& program, bool require_ssa) {
!memory.SupportsIndirectBufferLoad(inst.GetOpcode())) {
return Fail("indirect buffer requires a scalar, raw DWORD x1/x2/x3/x4, or formatted X load");
}
const bool packed_formatted_load =
BufferAccessOf(inst.GetOpcode()) == BufferAccess::Read && memory.formatted &&
memory.data_bits == 16u && memory.component_count == 4u && buffer_components == 2u;
if (buffer_components > 1u &&
(!vector_buffer || memory.data_bits != 32u ||
(!vector_buffer || (memory.data_bits != 32u && !packed_formatted_load) ||
memory.data_dwords != buffer_components || memory.component_index != 0u)) {
return Fail(fmt::format("{} has inconsistent native-wide metadata",
ValueOpcodeName(inst.GetOpcode())));
@@ -1534,14 +1534,23 @@ void ApplyResourceSpecialization(Program& program, const ResourceSpecialization&
if (memory.formatted && !memory.typed) {
const auto& buffer = buffers[memory.resource];
const auto format = Format::GetFormatInfo(buffer.descriptor_format);
for (uint32_t component = 0; component < count; component++) {
const bool d16 = memory.data_bits == 16u;
const auto components = d16 ? memory.component_count : count;
std::array<uint32_t, 4> constants {};
for (uint32_t component = 0; component < components; component++) {
if (format.type == Format::ComponentType::Unknown ||
GetDstSel(buffer.descriptor_swizzle, component) != 1u) continue;
const auto one = Format::FormattedConstantBits(
auto one = Format::FormattedConstantBits(
format, Format::FormattedSourceKind::One);
if (d16 && one == 0x3f800000u) one = 0x3c00u;
constants[d16 ? component / 2u : component] |=
one << (d16 ? (component % 2u) * 16u : 0u);
}
for (uint32_t component = 0; component < count; component++) {
if (constants[component] == 0u) continue;
values[component] = Value(&*block->PrependNewInst(
it, ValueOpcode::SelectU32,
{inst.Arg(inst.NumArgs() - 1), Value(one), Value(0u)}));
{inst.Arg(inst.NumArgs() - 1), Value(constants[component]), Value(0u)}));
}
}
Value result = values[0];