shader: fix packed image address reads

This commit is contained in:
nmzik
2026-09-05 11:32:32 +04:00
parent 51afce3a97
commit 2ec0e9e8f3
5 changed files with 112 additions and 39 deletions
@@ -1,5 +1,6 @@
#include "graphics/guest_gpu/gpu_format.h"
#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/frontend/decode/ImageOps.h"
#include <algorithm>
#include <bit>
@@ -47,45 +48,14 @@ bool HasFlag(const IR::MemoryInfo& mem, uint32_t flag) {
return (mem.image_sample_flags & flag) != 0u;
}
bool UsesA16(const IR::MemoryInfo& mem) {
return HasFlag(mem, Decoder::ImageSampleFlagA16);
}
bool ComponentUsesA16(const IR::MemoryInfo& mem, uint32_t component) {
if (!UsesA16(mem)) return false;
uint32_t cursor = 0;
if (HasFlag(mem, Decoder::ImageSampleFlagOffset)) {
if (component == cursor) return false;
cursor++;
}
if (HasFlag(mem, Decoder::ImageSampleFlagBias)) {
if (component == cursor) return true;
cursor++;
}
if (HasFlag(mem, Decoder::ImageSampleFlagCompare) && component == cursor) return false;
return true;
}
uint32_t HalfComponent(const IR::MemoryInfo& mem, uint32_t component) {
if (!UsesA16(mem)) return component * 2u;
uint32_t half = 0;
for (uint32_t index = 0; index < component; index++) {
const auto width = ComponentUsesA16(mem, index) ? 1u : 2u;
if (width == 2u && (half & 1u) != 0u) half++;
half += width;
}
if (!ComponentUsesA16(mem, component) && (half & 1u) != 0u) half++;
return half;
}
uint32_t AddressU32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::Inst& address,
uint32_t component) {
const auto half = HalfComponent(mem, component);
const auto packed = half / 2u;
const auto layout = Decoder::ImageAddressComponentLayout(mem.image_sample_flags, component);
const auto packed = layout.bit_offset / 32u;
if (packed >= address.NumArgs()) return ConstantU32(ctx.state, 0);
auto value = ctx.Def(address.Arg(packed));
if (ComponentUsesA16(mem, component)) {
if ((half & 1u) != 0u) {
if (layout.bit_width == 16u) {
if ((layout.bit_offset & 31u) != 0u) {
value = Binary(ctx.state, OpShiftRightLogical, TypeU32(ctx.state), value,
ConstantU32(ctx.state, 16));
}
@@ -98,7 +68,7 @@ uint32_t AddressU32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::
uint32_t AddressF32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::Inst& address,
uint32_t component) {
const auto value = AddressU32(ctx, mem, address, component);
return ComponentUsesA16(mem, component)
return Decoder::ImageAddressComponentLayout(mem.image_sample_flags, component).bit_width == 16u
? EmitF16BitsToF32(ctx.state, value)
: Unary(ctx.state, OpBitcast, TypeF32(ctx.state), value);
}