Skip to content

Commit

Permalink
shader_recompiler: Replace texel buffers with in-shader buffer format…
Browse files Browse the repository at this point in the history
… interpretation (shadps4-emu#2363)

* shader_recompiler: Replace texel buffers with in-shader buffer format interpretation

* shader_recompiler: Move 10/11-bit float conversion to functions and address some comments.

* vulkan: Remove VK_KHR_maintenance5 as it is no longer needed for buffer views.

* shader_recompiler: Add helpers for composites and bitfields in pack/unpack.

* shader_recompiler: Use initializer_list for bitfield insert helper.
  • Loading branch information
squidbus authored Feb 7, 2025
1 parent 78b4f10 commit cfe249d
Show file tree
Hide file tree
Showing 35 changed files with 1,038 additions and 563 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
src/shader_recompiler/ir/passes/hull_shader_transform.cpp
src/shader_recompiler/ir/passes/identity_removal_pass.cpp
src/shader_recompiler/ir/passes/ir_passes.h
src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp
src/shader_recompiler/ir/passes/lower_shared_mem_to_registers.cpp
src/shader_recompiler/ir/passes/resource_tracking_pass.cpp
src/shader_recompiler/ir/passes/ring_access_elimination.cpp
Expand Down
2 changes: 1 addition & 1 deletion externals/sirit
8 changes: 1 addition & 7 deletions src/shader_recompiler/backend/spirv/emit_spirv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct
ctx.AddCapability(spv::Capability::Float64);
}
ctx.AddCapability(spv::Capability::Int64);
if (info.has_storage_images || info.has_image_buffers) {
if (info.has_storage_images) {
ctx.AddCapability(spv::Capability::StorageImageExtendedFormats);
ctx.AddCapability(spv::Capability::StorageImageReadWithoutFormat);
ctx.AddCapability(spv::Capability::StorageImageWriteWithoutFormat);
Expand All @@ -259,12 +259,6 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct
ctx.AddCapability(spv::Capability::ImageReadWriteLodAMD);
}
}
if (info.has_texel_buffers) {
ctx.AddCapability(spv::Capability::SampledBuffer);
}
if (info.has_image_buffers) {
ctx.AddCapability(spv::Capability::ImageBuffer);
}
if (info.has_image_gather) {
ctx.AddCapability(spv::Capability::ImageGatherExtended);
}
Expand Down
224 changes: 192 additions & 32 deletions src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,56 @@

namespace Shader::Backend::SPIRV {

struct R {
R(u32 a, u32 b) : offset(a), size(b) {}
u32 offset;
u32 size;
};
template <bool is_signed, typename... Args>
static std::array<Id, sizeof...(Args)> ExtractBitFields(EmitContext& ctx, const Id value,
const Args... args) {
const auto op_func =
is_signed ? &EmitContext::OpBitFieldSExtract : &EmitContext::OpBitFieldUExtract;
std::array<Id, sizeof...(Args)> result{};
u32 i = 0;
(
[&] {
result[i++] = (ctx.*op_func)(ctx.U32[1], value, ctx.ConstU32(args.offset),
ctx.ConstU32(args.size));
}(),
...);
return result;
}

template <typename... Args>
static Id InsertBitFields(EmitContext& ctx, const std::initializer_list<Id> values,
const Args... args) {
Id result{};
auto it = values.begin();
(
[&] {
if (it == values.begin()) {
result = *it;
} else {
result = ctx.OpBitFieldInsert(ctx.U32[1], result, *it, ctx.ConstU32(args.offset),
ctx.ConstU32(args.size));
}
++it;
}(),
...);
return result;
}

template <u32 num_components>
static std::array<Id, num_components> ExtractComposite(EmitContext& ctx, const VectorIds type,
const Id value) {
std::array<Id, num_components> result{};
for (u32 i = 0; i < num_components; i++) {
result[i] = ctx.OpCompositeExtract(type[1], value, i);
}
return result;
}

Id EmitBitCastU16F16(EmitContext& ctx, Id value) {
return ctx.OpBitcast(ctx.U16, value);
}
Expand Down Expand Up @@ -42,22 +92,6 @@ Id EmitPackFloat2x32(EmitContext& ctx, Id value) {
return ctx.OpBitcast(ctx.F64[1], value);
}

Id EmitPackFloat2x16(EmitContext& ctx, Id value) {
return ctx.OpBitcast(ctx.U32[1], value);
}

Id EmitUnpackFloat2x16(EmitContext& ctx, Id value) {
return ctx.OpBitcast(ctx.F16[2], value);
}

Id EmitPackHalf2x16(EmitContext& ctx, Id value) {
return ctx.OpPackHalf2x16(ctx.U32[1], value);
}

Id EmitUnpackHalf2x16(EmitContext& ctx, Id value) {
return ctx.OpUnpackHalf2x16(ctx.F32[2], value);
}

Id EmitPackUnorm2x16(EmitContext& ctx, Id value) {
return ctx.OpPackUnorm2x16(ctx.U32[1], value);
}
Expand All @@ -75,31 +109,157 @@ Id EmitUnpackSnorm2x16(EmitContext& ctx, Id value) {
}

Id EmitPackUint2x16(EmitContext& ctx, Id value) {
// No SPIR-V instruction for this, do it manually.
const auto x{ctx.OpCompositeExtract(ctx.U32[1], value, 0)};
const auto y{ctx.OpCompositeExtract(ctx.U32[1], value, 1)};
return ctx.OpBitFieldInsert(ctx.U32[1], x, y, ctx.ConstU32(16U), ctx.ConstU32(16U));
const auto unpacked{ctx.OpBitcast(ctx.U32[2], value)};
const auto [x, y] = ExtractComposite<2>(ctx, ctx.U32, unpacked);
return InsertBitFields(ctx, {x, y}, R(0, 16), R(16, 16));
}

Id EmitUnpackUint2x16(EmitContext& ctx, Id value) {
// No SPIR-V instruction for this, do it manually.
const auto x{ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.ConstU32(0U), ctx.ConstU32(16U))};
const auto y{ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.ConstU32(16U), ctx.ConstU32(16U))};
return ctx.OpCompositeConstruct(ctx.U32[2], x, y);
const auto [x, y] = ExtractBitFields<false>(ctx, value, R(0, 16), R(16, 16));
const auto unpacked{ctx.OpCompositeConstruct(ctx.U32[2], x, y)};
return ctx.OpBitcast(ctx.F32[2], unpacked);
}

Id EmitPackSint2x16(EmitContext& ctx, Id value) {
// No SPIR-V instruction for this, do it manually.
const auto x{ctx.OpCompositeExtract(ctx.U32[1], value, 0)};
const auto y{ctx.OpCompositeExtract(ctx.U32[1], value, 1)};
return ctx.OpBitFieldInsert(ctx.U32[1], x, y, ctx.ConstU32(16U), ctx.ConstU32(16U));
return EmitPackUint2x16(ctx, value);
}

Id EmitUnpackSint2x16(EmitContext& ctx, Id value) {
// No SPIR-V instruction for this, do it manually.
const auto x{ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.ConstU32(0U), ctx.ConstU32(16U))};
const auto y{ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.ConstU32(16U), ctx.ConstU32(16U))};
return ctx.OpCompositeConstruct(ctx.U32[2], x, y);
const auto [x, y] = ExtractBitFields<true>(ctx, value, R(0, 16), R(16, 16));
const auto unpacked{ctx.OpCompositeConstruct(ctx.U32[2], x, y)};
return ctx.OpBitcast(ctx.F32[2], unpacked);
}

Id EmitPackHalf2x16(EmitContext& ctx, Id value) {
return ctx.OpPackHalf2x16(ctx.U32[1], value);
}

Id EmitUnpackHalf2x16(EmitContext& ctx, Id value) {
return ctx.OpUnpackHalf2x16(ctx.F32[2], value);
}

Id EmitPackUnorm4x8(EmitContext& ctx, Id value) {
return ctx.OpPackUnorm4x8(ctx.U32[1], value);
}

Id EmitUnpackUnorm4x8(EmitContext& ctx, Id value) {
return ctx.OpUnpackUnorm4x8(ctx.F32[4], value);
}

Id EmitPackSnorm4x8(EmitContext& ctx, Id value) {
return ctx.OpPackSnorm4x8(ctx.U32[1], value);
}

Id EmitUnpackSnorm4x8(EmitContext& ctx, Id value) {
return ctx.OpUnpackSnorm4x8(ctx.F32[4], value);
}

Id EmitPackUint4x8(EmitContext& ctx, Id value) {
const auto unpacked{ctx.OpBitcast(ctx.U32[4], value)};
const auto [x, y, z, w] = ExtractComposite<4>(ctx, ctx.U32, unpacked);
return InsertBitFields(ctx, {x, y, z, w}, R(0, 8), R(8, 8), R(16, 8), R(24, 8));
}

Id EmitUnpackUint4x8(EmitContext& ctx, Id value) {
const auto [x, y, z, w] =
ExtractBitFields<false>(ctx, value, R(0, 8), R(8, 8), R(16, 8), R(24, 8));
const auto unpacked{ctx.OpCompositeConstruct(ctx.U32[4], x, y, z, w)};
return ctx.OpBitcast(ctx.F32[4], unpacked);
}

Id EmitPackSint4x8(EmitContext& ctx, Id value) {
return EmitPackUint4x8(ctx, value);
}

Id EmitUnpackSint4x8(EmitContext& ctx, Id value) {
const auto [x, y, z, w] =
ExtractBitFields<true>(ctx, value, R(0, 8), R(8, 8), R(16, 8), R(24, 8));
const auto unpacked{ctx.OpCompositeConstruct(ctx.U32[4], x, y, z, w)};
return ctx.OpBitcast(ctx.F32[4], unpacked);
}

Id EmitPackUfloat10_11_11(EmitContext& ctx, Id value) {
const auto [x, y, z] = ExtractComposite<3>(ctx, ctx.F32, value);
const auto cvt_x{ctx.OpFunctionCall(ctx.U32[1], ctx.f32_to_uf11, x)};
const auto cvt_y{ctx.OpFunctionCall(ctx.U32[1], ctx.f32_to_uf11, y)};
const auto cvt_z{ctx.OpFunctionCall(ctx.U32[1], ctx.f32_to_uf10, z)};
return InsertBitFields(ctx, {cvt_x, cvt_y, cvt_z}, R(0, 11), R(11, 11), R(22, 10));
}

Id EmitUnpackUfloat10_11_11(EmitContext& ctx, Id value) {
const auto [x, y, z] = ExtractBitFields<false>(ctx, value, R(0, 11), R(11, 11), R(22, 10));
const auto cvt_x{ctx.OpFunctionCall(ctx.F32[1], ctx.uf11_to_f32, x)};
const auto cvt_y{ctx.OpFunctionCall(ctx.F32[1], ctx.uf11_to_f32, y)};
const auto cvt_z{ctx.OpFunctionCall(ctx.F32[1], ctx.uf10_to_f32, z)};
return ctx.OpCompositeConstruct(ctx.F32[3], cvt_x, cvt_y, cvt_z);
}

Id EmitPackUnorm2_10_10_10(EmitContext& ctx, Id value) {
const auto unorm_min{ctx.ConstantComposite(ctx.F32[4], ctx.ConstF32(0.f), ctx.ConstF32(0.f),
ctx.ConstF32(0.f), ctx.ConstF32(0.f))};
const auto unorm_max{ctx.ConstantComposite(ctx.F32[4], ctx.ConstF32(1.f), ctx.ConstF32(1.f),
ctx.ConstF32(1.f), ctx.ConstF32(1.f))};
const auto clamped{ctx.OpFClamp(ctx.F32[4], value, unorm_min, unorm_max)};
const auto unorm_mul{ctx.ConstantComposite(ctx.F32[4], ctx.ConstF32(1023.f),
ctx.ConstF32(1023.f), ctx.ConstF32(1023.f),
ctx.ConstF32(3.f))};
const auto as_float{ctx.OpFMul(ctx.F32[4], clamped, unorm_mul)};
const auto as_uint{ctx.OpConvertFToU(ctx.U32[4], ctx.OpRoundEven(ctx.F32[4], as_float))};
return EmitPackUint2_10_10_10(ctx, ctx.OpBitcast(ctx.F32[4], as_uint));
}

Id EmitUnpackUnorm2_10_10_10(EmitContext& ctx, Id value) {
const auto unpacked{ctx.OpBitcast(ctx.U32[4], EmitUnpackUint2_10_10_10(ctx, value))};
const auto as_float{ctx.OpConvertUToF(ctx.F32[4], unpacked)};
const auto unorm_div{ctx.ConstantComposite(ctx.F32[4], ctx.ConstF32(1023.f),
ctx.ConstF32(1023.f), ctx.ConstF32(1023.f),
ctx.ConstF32(3.f))};
return ctx.OpFDiv(ctx.F32[4], as_float, unorm_div);
}

Id EmitPackSnorm2_10_10_10(EmitContext& ctx, Id value) {
const auto snorm_min{ctx.ConstantComposite(ctx.F32[4], ctx.ConstF32(-1.f), ctx.ConstF32(-1.f),
ctx.ConstF32(-1.f), ctx.ConstF32(-1.f))};
const auto snorm_max{ctx.ConstantComposite(ctx.F32[4], ctx.ConstF32(1.f), ctx.ConstF32(1.f),
ctx.ConstF32(1.f), ctx.ConstF32(1.f))};
const auto clamped{ctx.OpFClamp(ctx.F32[4], value, snorm_min, snorm_max)};
const auto snorm_mul{ctx.ConstantComposite(ctx.F32[4], ctx.ConstF32(511.f), ctx.ConstF32(511.f),
ctx.ConstF32(511.f), ctx.ConstF32(1.f))};
const auto as_float{ctx.OpFMul(ctx.F32[4], clamped, snorm_mul)};
const auto as_sint{ctx.OpConvertFToS(ctx.U32[4], ctx.OpRoundEven(ctx.F32[4], as_float))};
return EmitPackSint2_10_10_10(ctx, ctx.OpBitcast(ctx.F32[4], as_sint));
}

Id EmitUnpackSnorm2_10_10_10(EmitContext& ctx, Id value) {
const auto unpacked{ctx.OpBitcast(ctx.U32[4], EmitUnpackSint2_10_10_10(ctx, value))};
const auto as_float{ctx.OpConvertSToF(ctx.F32[4], unpacked)};
const auto snorm_div{ctx.ConstantComposite(ctx.F32[4], ctx.ConstF32(511.f), ctx.ConstF32(511.f),
ctx.ConstF32(511.f), ctx.ConstF32(1.f))};
return ctx.OpFDiv(ctx.F32[4], as_float, snorm_div);
}

Id EmitPackUint2_10_10_10(EmitContext& ctx, Id value) {
const auto unpacked{ctx.OpBitcast(ctx.U32[4], value)};
const auto [x, y, z, w] = ExtractComposite<4>(ctx, ctx.U32, unpacked);
return InsertBitFields(ctx, {x, y, z, w}, R(0, 10), R(10, 10), R(20, 10), R(30, 2));
}

Id EmitUnpackUint2_10_10_10(EmitContext& ctx, Id value) {
const auto [x, y, z, w] =
ExtractBitFields<false>(ctx, value, R(0, 10), R(10, 10), R(20, 10), R(30, 2));
const auto unpacked{ctx.OpCompositeConstruct(ctx.U32[4], x, y, z, w)};
return ctx.OpBitcast(ctx.F32[4], unpacked);
}

Id EmitPackSint2_10_10_10(EmitContext& ctx, Id value) {
return EmitPackUint2_10_10_10(ctx, value);
}

Id EmitUnpackSint2_10_10_10(EmitContext& ctx, Id value) {
const auto [x, y, z, w] =
ExtractBitFields<true>(ctx, value, R(0, 10), R(10, 10), R(20, 10), R(30, 2));
const auto unpacked{ctx.OpCompositeConstruct(ctx.U32[4], x, y, z, w)};
return ctx.OpBitcast(ctx.F32[4], unpacked);
}

} // namespace Shader::Backend::SPIRV
8 changes: 8 additions & 0 deletions src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ Id EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, I
return EmitCompositeConstruct(ctx, inst, ctx.U32[4], e1, e2, e3, e4);
}

Id EmitCompositeConstructU32x2x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2) {
return EmitCompositeConstruct(ctx, inst, ctx.U32[4], e1, e2);
}

Id EmitCompositeExtractU32x2(EmitContext& ctx, Id composite, u32 index) {
return ctx.OpCompositeExtract(ctx.U32[1], composite, index);
}
Expand Down Expand Up @@ -124,6 +128,10 @@ Id EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, I
return EmitCompositeConstruct(ctx, inst, ctx.F32[4], e1, e2, e3, e4);
}

Id EmitCompositeConstructF32x2x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2) {
return EmitCompositeConstruct(ctx, inst, ctx.F32[4], e1, e2);
}

Id EmitCompositeExtractF32x2(EmitContext& ctx, Id composite, u32 index) {
return ctx.OpCompositeExtract(ctx.F32[1], composite, index);
}
Expand Down
Loading

0 comments on commit cfe249d

Please sign in to comment.