mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-22 20:06:54 +02:00
graphics: encode packed UNorm buffer stores before writing skinning normals
This commit is contained in:
@@ -706,26 +706,66 @@ uint32_t FormattedOutOfBoundsValue(ValueEmitContext& ctx, const IR::MemoryInfo&
|
|||||||
return ConstructU32Composite(ctx.state, components, values);
|
return ConstructU32Composite(ctx.state, components, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StoreFormattedInBounds(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
|
uint32_t EncodeFormattedStoreComponent(ValueEmitContext& ctx,
|
||||||
const PreparedFormattedMemory& plan, uint32_t component,
|
const Format::BufferFormatInfo& info,
|
||||||
uint32_t data) {
|
uint32_t component, uint32_t data) {
|
||||||
if (component >= plan.info.component_count) return;
|
const auto bits = info.component_bits[component];
|
||||||
const auto bits = plan.info.component_bits[component];
|
if (info.type == Format::ComponentType::Unorm) {
|
||||||
if (bits == 16u && (plan.info.type == Format::ComponentType::Snorm ||
|
const auto value = EmitBitCastF32U32(ctx.state, data);
|
||||||
plan.info.type == Format::ComponentType::Float)) {
|
const auto clamped = EmitFPMin32(
|
||||||
|
ctx.state, EmitFPMax32(ctx.state, value, ConstantF32Value(ctx.state, 0.0f)),
|
||||||
|
ConstantF32Value(ctx.state, 1.0f));
|
||||||
|
const auto scaled = EmitFPMul32(
|
||||||
|
ctx.state, clamped, ConstantF32Value(ctx.state, float((1u << bits) - 1u)));
|
||||||
|
return Unary(ctx.state, spv::OpConvertFToU, TypeU32(ctx.state),
|
||||||
|
EmitFPRoundEven32(ctx.state, scaled));
|
||||||
|
}
|
||||||
|
if (bits == 16u && (info.type == Format::ComponentType::Snorm ||
|
||||||
|
info.type == Format::ComponentType::Float)) {
|
||||||
const auto value = EmitBitCastF32U32(ctx.state, data);
|
const auto value = EmitBitCastF32U32(ctx.state, data);
|
||||||
const auto pair = EmitCompositeConstructF32x2(ctx.state, value,
|
const auto pair = EmitCompositeConstructF32x2(ctx.state, value,
|
||||||
ConstantF32Value(ctx.state, 0.0f));
|
ConstantF32Value(ctx.state, 0.0f));
|
||||||
data = plan.info.type == Format::ComponentType::Float
|
return info.type == Format::ComponentType::Float ? EmitPackHalf2x16(ctx.state, pair)
|
||||||
? EmitPackHalf2x16(ctx.state, pair)
|
: EmitPackSnorm2x16(ctx.state, pair);
|
||||||
: EmitPackSnorm2x16(ctx.state, pair);
|
|
||||||
}
|
|
||||||
if (bits == 8u || bits == 16u) {
|
|
||||||
StoreSubwordInBounds(ctx, mem, plan.resource, plan.addresses[component],
|
|
||||||
plan.indices[component], bits, data);
|
|
||||||
} else {
|
|
||||||
StoreWordInBounds(ctx, plan.resource, plan.indices[component], data);
|
|
||||||
}
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StoreFormattedPrepared(ValueEmitContext& ctx, const IR::Inst& inst,
|
||||||
|
const IR::MemoryInfo& mem, const MemoryResourceAccess& resource,
|
||||||
|
const Format::BufferFormatInfo& info, uint32_t data,
|
||||||
|
uint32_t components) {
|
||||||
|
// RDNA2 formatted stores transfer the entire format, zero-filling missing VGPRs.
|
||||||
|
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, info, info.component_count,
|
||||||
|
FormattedAccess::Store);
|
||||||
|
EmitIfCondition(ctx.state, plan.in_bounds, [&]() {
|
||||||
|
auto packed = ConstantU32(ctx.state, 0);
|
||||||
|
for (uint32_t component = 0; component < info.component_count; component++) {
|
||||||
|
auto value = ConstantU32(ctx.state, 0);
|
||||||
|
if (component < components) {
|
||||||
|
value = data;
|
||||||
|
if (components != 1u) {
|
||||||
|
value = ctx.state.builder.AllocateId();
|
||||||
|
ctx.state.builder.AddFunction(spv::OpCompositeExtract, TypeU32(ctx.state),
|
||||||
|
value, data, component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value = EncodeFormattedStoreComponent(ctx, info, component, value);
|
||||||
|
const auto bits = info.component_bits[component];
|
||||||
|
if (info.packed_bitfield) {
|
||||||
|
packed = EmitBitFieldInsert(
|
||||||
|
ctx.state, packed, value,
|
||||||
|
ConstantU32(ctx.state, info.component_bit_offset[component]),
|
||||||
|
ConstantU32(ctx.state, bits));
|
||||||
|
} else if (bits == 8u || bits == 16u) {
|
||||||
|
StoreSubwordInBounds(ctx, mem, resource, plan.addresses[component],
|
||||||
|
plan.indices[component], bits, value);
|
||||||
|
} else {
|
||||||
|
StoreWordInBounds(ctx, resource, plan.indices[component], value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (info.packed_bitfield) StoreWordInBounds(ctx, resource, plan.indices[0], packed);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormattedStore(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem) {
|
void FormattedStore(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem) {
|
||||||
@@ -737,11 +777,7 @@ void FormattedStore(ValueEmitContext& ctx, const IR::Inst& inst, const IR::Memor
|
|||||||
StoreWordPrepared(ctx, inst, RebaseRawComponent(mem, 0u), resource, data);
|
StoreWordPrepared(ctx, inst, RebaseRawComponent(mem, 0u), resource, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, info, 1u,
|
StoreFormattedPrepared(ctx, inst, mem, resource, info, data, 1u);
|
||||||
FormattedAccess::Store);
|
|
||||||
EmitIfCondition(ctx.state, plan.in_bounds, [&]() {
|
|
||||||
StoreFormattedInBounds(ctx, mem, plan, 0u, data);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -859,16 +895,7 @@ void StoreWideBuffer(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t compo
|
|||||||
const auto info = Format::GetFormatInfo(
|
const auto info = Format::GetFormatInfo(
|
||||||
mem.formatted ? BufferFormat(ctx, mem) : Prospero::BufferFormat::kInvalid);
|
mem.formatted ? BufferFormat(ctx, mem) : Prospero::BufferFormat::kInvalid);
|
||||||
if (info.type != Format::ComponentType::Unknown) {
|
if (info.type != Format::ComponentType::Unknown) {
|
||||||
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, info, components,
|
StoreFormattedPrepared(ctx, inst, mem, resource, info, composite, components);
|
||||||
FormattedAccess::Store);
|
|
||||||
EmitIfCondition(state, plan.in_bounds, [&]() {
|
|
||||||
for (uint32_t component = 0; component < components; component++) {
|
|
||||||
const auto data = state.builder.AllocateId();
|
|
||||||
state.builder.AddFunction(spv::OpCompositeExtract, TypeU32(state), data,
|
|
||||||
composite, component);
|
|
||||||
StoreFormattedInBounds(ctx, mem, plan, component, data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (uint32_t component = 0; component < components; component++) {
|
for (uint32_t component = 0; component < components; component++) {
|
||||||
|
|||||||
@@ -23770,7 +23770,7 @@ TestCase BufferLoadFormatXChecksOnlyTransferredComponent() {
|
|||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCase BufferStoreFormatXChecksOnlyTransferredComponent() {
|
TestCase BufferStoreFormatXRejectsPartialRecord() {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
std::vector<u32> code;
|
std::vector<u32> code;
|
||||||
@@ -23781,10 +23781,10 @@ TestCase BufferStoreFormatXChecksOnlyTransferredComponent() {
|
|||||||
AppendEnd(&code);
|
AppendEnd(&code);
|
||||||
|
|
||||||
TestCase test;
|
TestCase test;
|
||||||
test.name = "BufferStoreFormatXChecksOnlyTransferredComponent";
|
test.name = "BufferStoreFormatXRejectsPartialRecord";
|
||||||
test.code = std::move(code);
|
test.code = std::move(code);
|
||||||
test.initial = {0x11111111u, 0x22222222u, 0x33333333u, 0x44444444u};
|
test.initial = {0x11111111u, 0x22222222u, 0x33333333u, 0x44444444u};
|
||||||
test.expected = {0x11111111u, 0x22222222u, 0x33333333u, 0xaaaaaaaau};
|
test.expected = test.initial;
|
||||||
test.storage_buffer_range_dwords = 4;
|
test.storage_buffer_range_dwords = 4;
|
||||||
test.user_data = MakeStructuredStorageBufferData(
|
test.user_data = MakeStructuredStorageBufferData(
|
||||||
0, 4, false, BufferFormat(Prospero::BufferFormat::k32_32_32_32Float));
|
0, 4, false, BufferFormat(Prospero::BufferFormat::k32_32_32_32Float));
|
||||||
@@ -23817,7 +23817,7 @@ TestCase BufferLoadFormatXyChecksOnlyTransferredComponents() {
|
|||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCase BufferStoreFormatXyChecksOnlyTransferredComponents() {
|
TestCase BufferStoreFormatXyRejectsPartialRecord() {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
std::vector<u32> code;
|
std::vector<u32> code;
|
||||||
@@ -23829,10 +23829,10 @@ TestCase BufferStoreFormatXyChecksOnlyTransferredComponents() {
|
|||||||
AppendEnd(&code);
|
AppendEnd(&code);
|
||||||
|
|
||||||
TestCase test;
|
TestCase test;
|
||||||
test.name = "BufferStoreFormatXyChecksOnlyTransferredComponents";
|
test.name = "BufferStoreFormatXyRejectsPartialRecord";
|
||||||
test.code = std::move(code);
|
test.code = std::move(code);
|
||||||
test.initial = {0x11111111u, 0x22222222u, 0x33333333u, 0x44444444u};
|
test.initial = {0x11111111u, 0x22222222u, 0x33333333u, 0x44444444u};
|
||||||
test.expected = {0x11111111u, 0x22222222u, 0xaaaaaaaau, 0xbbbbbbbbu};
|
test.expected = test.initial;
|
||||||
test.storage_buffer_range_dwords = 4;
|
test.storage_buffer_range_dwords = 4;
|
||||||
test.user_data = MakeStructuredStorageBufferData(
|
test.user_data = MakeStructuredStorageBufferData(
|
||||||
0, 4, false, BufferFormat(Prospero::BufferFormat::k32_32_32_32Float));
|
0, 4, false, BufferFormat(Prospero::BufferFormat::k32_32_32_32Float));
|
||||||
@@ -24147,6 +24147,60 @@ TestCase BufferStoreFormatXResource16UintPreservesAdjacentLanes() {
|
|||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestCase BufferStoreFormatXyzwPackedUnormSkinningVectors() {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
|
// The captured skinning dispatch stores a compressed quaternion with W = 1.
|
||||||
|
constexpr std::array<float, 12> values = {
|
||||||
|
0.25f, 0.5f, 0.75f, 1.0f,
|
||||||
|
-1.0f, 0.0f, 2.0f, 1.0f,
|
||||||
|
0.5f, 0.5f, 0.5f, 0.5f};
|
||||||
|
std::vector<u32> code;
|
||||||
|
for (u32 record = 0; record < 3; record++) {
|
||||||
|
for (u32 component = 0; component < 4; component++) {
|
||||||
|
AppendVMovLiteral(&code, component,
|
||||||
|
std::bit_cast<u32>(values[record * 4 + component]));
|
||||||
|
}
|
||||||
|
AppendVMovU32(&code, 20, record);
|
||||||
|
code.push_back(EncodeMubuf0(0x07u, 0, true, false));
|
||||||
|
code.push_back(EncodeMubuf1(0, 0, 20));
|
||||||
|
}
|
||||||
|
AppendEnd(&code);
|
||||||
|
|
||||||
|
TestCase test;
|
||||||
|
test.name = "BufferStoreFormatXyzwPackedUnormSkinningVectors";
|
||||||
|
test.code = std::move(code);
|
||||||
|
test.initial = std::vector<u32>(4, 0xdeadbeefu);
|
||||||
|
test.expected = {0xeff80100u, 0xfff00000u, 0xa0080200u, 0xdeadbeefu};
|
||||||
|
test.user_data = MakeStructuredStorageBufferData(
|
||||||
|
4, 3, false, BufferFormat(Prospero::BufferFormat::k10_10_10_2UNorm));
|
||||||
|
test.has_user_data = true;
|
||||||
|
test.opcodes = {O::V_MOV_B32, O::BUFFER_STORE_FORMAT_XYZW, O::S_ENDPGM};
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestCase BufferStoreFormatXZeroFillsPackedRecord() {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
|
std::vector<u32> code;
|
||||||
|
AppendVMovLiteral(&code, 0, std::bit_cast<u32>(0.5f));
|
||||||
|
AppendVMovU32(&code, 20, 4);
|
||||||
|
code.push_back(EncodeMubuf0(0x04u));
|
||||||
|
code.push_back(EncodeMubuf1(0, 0, 20));
|
||||||
|
AppendEnd(&code);
|
||||||
|
|
||||||
|
TestCase test;
|
||||||
|
test.name = "BufferStoreFormatXZeroFillsPackedRecord";
|
||||||
|
test.code = std::move(code);
|
||||||
|
test.initial = {0x11111111u, 0xffffffffu, 0x33333333u};
|
||||||
|
test.expected = {0x11111111u, 0x00000200u, 0x33333333u};
|
||||||
|
test.user_data = MakeStructuredStorageBufferData(
|
||||||
|
0, 12, false, BufferFormat(Prospero::BufferFormat::k10_10_10_2UNorm));
|
||||||
|
test.has_user_data = true;
|
||||||
|
test.opcodes = {O::V_MOV_B32, O::BUFFER_STORE_FORMAT_X, O::S_ENDPGM};
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
TestCase BufferStoreFormatXyzwFloat16ConvertsComponents() {
|
TestCase BufferStoreFormatXyzwFloat16ConvertsComponents() {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
@@ -29090,9 +29144,9 @@ std::vector<TestCase> MakeCases() {
|
|||||||
AddCase(BufferLoadFormatXyzwRejectsPartialRecord);
|
AddCase(BufferLoadFormatXyzwRejectsPartialRecord);
|
||||||
AddCase(BufferStoreFormatXyzwDropsPartialRecord);
|
AddCase(BufferStoreFormatXyzwDropsPartialRecord);
|
||||||
AddCase(BufferLoadFormatXChecksOnlyTransferredComponent);
|
AddCase(BufferLoadFormatXChecksOnlyTransferredComponent);
|
||||||
AddCase(BufferStoreFormatXChecksOnlyTransferredComponent);
|
AddCase(BufferStoreFormatXRejectsPartialRecord);
|
||||||
AddCase(BufferLoadFormatXyChecksOnlyTransferredComponents);
|
AddCase(BufferLoadFormatXyChecksOnlyTransferredComponents);
|
||||||
AddCase(BufferStoreFormatXyChecksOnlyTransferredComponents);
|
AddCase(BufferStoreFormatXyRejectsPartialRecord);
|
||||||
AddCase(BufferStoreVariants);
|
AddCase(BufferStoreVariants);
|
||||||
AddCase(BufferFormatVariants);
|
AddCase(BufferFormatVariants);
|
||||||
AddCase(BufferLoadFormatXyzwSnapshotsOverlappingAddress);
|
AddCase(BufferLoadFormatXyzwSnapshotsOverlappingAddress);
|
||||||
@@ -29103,6 +29157,8 @@ std::vector<TestCase> MakeCases() {
|
|||||||
AddCase(BufferFormatStoreVariants);
|
AddCase(BufferFormatStoreVariants);
|
||||||
AddCase(BufferStoreFormatXResource16UintWritesHalfword);
|
AddCase(BufferStoreFormatXResource16UintWritesHalfword);
|
||||||
AddCase(BufferStoreFormatXResource16UintPreservesAdjacentLanes);
|
AddCase(BufferStoreFormatXResource16UintPreservesAdjacentLanes);
|
||||||
|
AddCase(BufferStoreFormatXyzwPackedUnormSkinningVectors);
|
||||||
|
AddCase(BufferStoreFormatXZeroFillsPackedRecord);
|
||||||
AddCase(BufferStoreFormatXyzwFloat16ConvertsComponents);
|
AddCase(BufferStoreFormatXyzwFloat16ConvertsComponents);
|
||||||
AddCase(BufferStoreFormatXyzwSnorm16CapturedSkinningVectors);
|
AddCase(BufferStoreFormatXyzwSnorm16CapturedSkinningVectors);
|
||||||
AddCase(BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords);
|
AddCase(BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords);
|
||||||
@@ -33836,15 +33892,19 @@ int main(int argc, char **argv) {
|
|||||||
RunCase(&vulkan, VectorVopcCmpxOrderedCapturedExecMask());
|
RunCase(&vulkan, VectorVopcCmpxOrderedCapturedExecMask());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (argc == 2 && std::strcmp(argv[1], "--buffer-snorm-store-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--buffer-format-store-only") == 0) {
|
||||||
VulkanHarness vulkan;
|
VulkanHarness vulkan;
|
||||||
|
RunCase(&vulkan, BufferStoreFormatXyzwPackedUnormSkinningVectors());
|
||||||
|
RunCase(&vulkan, BufferStoreFormatXZeroFillsPackedRecord());
|
||||||
|
RunCase(&vulkan, BufferStoreFormatXyzwFloat16ConvertsComponents());
|
||||||
RunCase(&vulkan, BufferStoreFormatXyzwSnorm16CapturedSkinningVectors());
|
RunCase(&vulkan, BufferStoreFormatXyzwSnorm16CapturedSkinningVectors());
|
||||||
RunCase(&vulkan, BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords());
|
RunCase(&vulkan, BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords());
|
||||||
RunCase(&vulkan, BufferStoreFormatXResource16UintWritesHalfword());
|
RunCase(&vulkan, BufferStoreFormatXResource16UintWritesHalfword());
|
||||||
RunCase(&vulkan, BufferStoreFormatXResource16UintPreservesAdjacentLanes());
|
RunCase(&vulkan, BufferStoreFormatXResource16UintPreservesAdjacentLanes());
|
||||||
RunCase(&vulkan, BufferStoreFormatXyResource88UintWritesBytes());
|
RunCase(&vulkan, BufferStoreFormatXyResource88UintWritesBytes());
|
||||||
RunCase(&vulkan, BufferStoreFormatXyzwDropsPartialRecord());
|
RunCase(&vulkan, BufferStoreFormatXyzwDropsPartialRecord());
|
||||||
RunCase(&vulkan, BufferStoreFormatXChecksOnlyTransferredComponent());
|
RunCase(&vulkan, BufferStoreFormatXRejectsPartialRecord());
|
||||||
|
RunCase(&vulkan, BufferStoreFormatXyRejectsPartialRecord());
|
||||||
RunCase(&vulkan, BufferFormatStoreVariants());
|
RunCase(&vulkan, BufferFormatStoreVariants());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user