mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 03:16:54 +02:00
shader: encode SNORM16 formatted stores for skinning buffers
This commit is contained in:
@@ -514,31 +514,6 @@ void StoreWord(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo mem)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormattedStorePrepared(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
|
|
||||||
uint32_t component, const MemoryResourceAccess& resource,
|
|
||||||
uint32_t data) {
|
|
||||||
const auto info = Format::GetFormatInfo(BufferFormat(ctx, mem));
|
|
||||||
if (info.type == Format::ComponentType::Unknown) {
|
|
||||||
StoreWordPrepared(ctx, inst, RebaseRawComponent(mem, component), resource, data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (component >= info.component_count) return;
|
|
||||||
const auto bits = info.component_bits[component];
|
|
||||||
const auto component_mem = RebaseFormattedComponent(mem, info, component);
|
|
||||||
if (bits == 8u || bits == 16u) {
|
|
||||||
StoreSubwordPrepared(ctx, inst, component_mem, resource, bits, data);
|
|
||||||
} else {
|
|
||||||
StoreWordPrepared(ctx, inst, component_mem, resource, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FormattedStore(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem) {
|
|
||||||
EmitIfCondition(ctx.state, ctx.Arg(inst, inst.NumArgs() - 1), [&]() {
|
|
||||||
const auto resource = PrepareMemoryResourceAccess(ctx.state, mem);
|
|
||||||
FormattedStorePrepared(ctx, inst, mem, 0u, resource, ctx.Arg(inst, inst.NumArgs() - 2));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
spv::Op SpirvAtomicOpcode(IR::ValueOpcode opcode) {
|
spv::Op SpirvAtomicOpcode(IR::ValueOpcode opcode) {
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case IR::ValueOpcode::BufferAtomicCmpSwap32: return spv::OpAtomicCompareExchange;
|
case IR::ValueOpcode::BufferAtomicCmpSwap32: return spv::OpAtomicCompareExchange;
|
||||||
@@ -729,6 +704,11 @@ void StoreFormattedInBounds(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
|
|||||||
uint32_t data) {
|
uint32_t data) {
|
||||||
if (component >= plan.info.component_count) return;
|
if (component >= plan.info.component_count) return;
|
||||||
const auto bits = plan.info.component_bits[component];
|
const auto bits = plan.info.component_bits[component];
|
||||||
|
if (plan.info.type == Format::ComponentType::Snorm && bits == 16u) {
|
||||||
|
const auto value = EmitBitCastF32U32(ctx.state, data);
|
||||||
|
data = EmitPackSnorm2x16(
|
||||||
|
ctx.state, EmitCompositeConstructF32x2(ctx.state, value, ConstantF32Value(ctx.state, 0.0f)));
|
||||||
|
}
|
||||||
if (bits == 8u || bits == 16u) {
|
if (bits == 8u || bits == 16u) {
|
||||||
StoreSubwordInBounds(ctx, mem, plan.resource, plan.addresses[component],
|
StoreSubwordInBounds(ctx, mem, plan.resource, plan.addresses[component],
|
||||||
plan.indices[component], bits, data);
|
plan.indices[component], bits, data);
|
||||||
@@ -737,6 +717,23 @@ void StoreFormattedInBounds(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FormattedStore(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem) {
|
||||||
|
EmitIfCondition(ctx.state, ctx.Arg(inst, inst.NumArgs() - 1), [&]() {
|
||||||
|
const auto resource = PrepareMemoryResourceAccess(ctx.state, mem);
|
||||||
|
const auto data = ctx.Arg(inst, inst.NumArgs() - 2);
|
||||||
|
const auto info = Format::GetFormatInfo(BufferFormat(ctx, mem));
|
||||||
|
if (info.type == Format::ComponentType::Unknown) {
|
||||||
|
StoreWordPrepared(ctx, inst, RebaseRawComponent(mem, 0u), resource, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, info, 1u,
|
||||||
|
FormattedAccess::Store);
|
||||||
|
EmitIfCondition(ctx.state, plan.in_bounds, [&]() {
|
||||||
|
StoreFormattedInBounds(ctx, mem, plan, 0u, data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t LoadWideBuffer(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t components) {
|
uint32_t LoadWideBuffer(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t components) {
|
||||||
auto& state = ctx.state;
|
auto& state = ctx.state;
|
||||||
return EmitValueOrDefaultIfCondition(
|
return EmitValueOrDefaultIfCondition(
|
||||||
|
|||||||
@@ -22382,6 +22382,63 @@ TestCase BufferStoreFormatXResource16UintPreservesAdjacentLanes() {
|
|||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestCase BufferStoreFormatXyzwSnorm16CapturedSkinningVectors() {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
|
// Normal and tangent from the character skinning dispatch (eadfd178c07feebd).
|
||||||
|
constexpr std::array<u32, 8> values = {
|
||||||
|
0x3d98f7c5u, 0xbedae712u, 0xbf66a19eu, 0x3f800000u,
|
||||||
|
0x3f7e6baeu, 0xbd339a7du, 0x3dd0a2e2u, 0xbf800000u};
|
||||||
|
std::vector<u32> code;
|
||||||
|
for (u32 record = 0; record < 2; record++) {
|
||||||
|
for (u32 component = 0; component < 4; component++) {
|
||||||
|
AppendVMovLiteral(&code, component, 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 = "BufferStoreFormatXyzwSnorm16CapturedSkinningVectors";
|
||||||
|
test.code = std::move(code);
|
||||||
|
test.initial = std::vector<u32>(4, 0xdeadbeefu);
|
||||||
|
test.expected = {0xc947098fu, 0x7fff8cb0u, 0xfa637f35u, 0x80010d0au};
|
||||||
|
test.user_data = MakeStructuredStorageBufferData(
|
||||||
|
8, 2, false, BufferFormat(Prospero::BufferFormat::k16_16_16_16SNorm));
|
||||||
|
test.has_user_data = true;
|
||||||
|
test.opcodes = {O::V_MOV_B32, O::BUFFER_STORE_FORMAT_XYZW, O::S_ENDPGM};
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestCase BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords() {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
|
constexpr std::array<float, 9> values = {
|
||||||
|
-2.0f, -1.0f, -0.75f, -0.25f, 0.0f, 0.25f, 0.75f, 1.0f, 2.0f};
|
||||||
|
std::vector<u32> code;
|
||||||
|
for (u32 i = 0; i < values.size(); i++) {
|
||||||
|
AppendVMovLiteral(&code, 0, std::bit_cast<u32>(values[i]));
|
||||||
|
AppendVMovU32(&code, 20, i * 2 + 2);
|
||||||
|
code.push_back(EncodeMubuf0(0x04u));
|
||||||
|
code.push_back(EncodeMubuf1(0, 0, 20));
|
||||||
|
}
|
||||||
|
AppendEnd(&code);
|
||||||
|
|
||||||
|
TestCase test;
|
||||||
|
test.name = "BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords";
|
||||||
|
test.code = std::move(code);
|
||||||
|
test.initial = std::vector<u32>(6, 0xdeadbeefu);
|
||||||
|
test.expected = {0x8001beefu, 0xa0018001u, 0x0000e000u,
|
||||||
|
0x5fff2000u, 0x7fff7fffu, 0xdeadbeefu};
|
||||||
|
test.user_data = MakeStructuredStorageBufferData(
|
||||||
|
0, 24, false, BufferFormat(Prospero::BufferFormat::k16SNorm));
|
||||||
|
test.has_user_data = true;
|
||||||
|
test.opcodes = {O::V_MOV_B32, O::BUFFER_STORE_FORMAT_X, O::S_ENDPGM};
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
TestCase BufferLoadFormatXyResource88UintExtractsBytes() {
|
TestCase BufferLoadFormatXyResource88UintExtractsBytes() {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
@@ -26901,6 +26958,8 @@ std::vector<TestCase> MakeCases() {
|
|||||||
AddCase(BufferFormatStoreVariants);
|
AddCase(BufferFormatStoreVariants);
|
||||||
AddCase(BufferStoreFormatXResource16UintWritesHalfword);
|
AddCase(BufferStoreFormatXResource16UintWritesHalfword);
|
||||||
AddCase(BufferStoreFormatXResource16UintPreservesAdjacentLanes);
|
AddCase(BufferStoreFormatXResource16UintPreservesAdjacentLanes);
|
||||||
|
AddCase(BufferStoreFormatXyzwSnorm16CapturedSkinningVectors);
|
||||||
|
AddCase(BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords);
|
||||||
AddCase(BufferLoadFormatXResource8UintZeroExtendsByte);
|
AddCase(BufferLoadFormatXResource8UintZeroExtendsByte);
|
||||||
AddCase(BufferLoadFormatXyResource88UintExtractsBytes);
|
AddCase(BufferLoadFormatXyResource88UintExtractsBytes);
|
||||||
AddCase(BufferLoadFormatXyResource8888UnormConvertsFirstTwoComponents);
|
AddCase(BufferLoadFormatXyResource8888UnormConvertsFirstTwoComponents);
|
||||||
@@ -31512,6 +31571,18 @@ int main(int argc, char **argv) {
|
|||||||
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
||||||
EnsureConfigInitialized();
|
EnsureConfigInitialized();
|
||||||
CheckLeastRecentlyUsedCacheOrdering();
|
CheckLeastRecentlyUsedCacheOrdering();
|
||||||
|
if (argc == 2 && std::strcmp(argv[1], "--buffer-snorm-store-only") == 0) {
|
||||||
|
VulkanHarness vulkan;
|
||||||
|
RunCase(&vulkan, BufferStoreFormatXyzwSnorm16CapturedSkinningVectors());
|
||||||
|
RunCase(&vulkan, BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords());
|
||||||
|
RunCase(&vulkan, BufferStoreFormatXResource16UintWritesHalfword());
|
||||||
|
RunCase(&vulkan, BufferStoreFormatXResource16UintPreservesAdjacentLanes());
|
||||||
|
RunCase(&vulkan, BufferStoreFormatXyResource88UintWritesBytes());
|
||||||
|
RunCase(&vulkan, BufferStoreFormatXyzwDropsPartialRecord());
|
||||||
|
RunCase(&vulkan, BufferStoreFormatXChecksOnlyTransferredComponent());
|
||||||
|
RunCase(&vulkan, BufferFormatStoreVariants());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (argc == 2 && std::strcmp(argv[1], "--s-ashr-i64-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--s-ashr-i64-only") == 0) {
|
||||||
VulkanHarness vulkan;
|
VulkanHarness vulkan;
|
||||||
RunCase(&vulkan, ScalarAshrI64Edges(false));
|
RunCase(&vulkan, ScalarAshrI64Edges(false));
|
||||||
|
|||||||
Reference in New Issue
Block a user