mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
recognize verified pattern fills
Co-authored-by: Juan Pablo Farias <juanpablo.profissional.a.f@gmail.com>
This commit is contained in:
@@ -42640,6 +42640,30 @@ void CheckPs5GameExampleImageClearRuntimeShape() {
|
||||
!ResolveComputeBufferFill(compute, 2, 1, 1, 0x41u, descriptor,
|
||||
packed_clear, size),
|
||||
"excess invocation coverage was accepted as a fill");
|
||||
runtime_resources.uniform_fill.bounded_elements = 65;
|
||||
runtime_resources.buffers[0].dwords[2] = 65;
|
||||
for (u32 oob = 0; oob < 4; ++oob) {
|
||||
runtime_resources.buffers[0].dwords[3] = 0x14204u | (oob << 28u);
|
||||
Require("Ps5GameExampleImageClear", "bounded final workgroup and RDNA2 OOB mode",
|
||||
ResolveComputeBufferFill(compute, 2, 1, 1, 0x41u, descriptor,
|
||||
packed_clear, size) == (oob != 3),
|
||||
"explicit coverage did not respect RDNA2 element bounds");
|
||||
}
|
||||
runtime_resources.buffers[0].dwords[3] = 0x14204u;
|
||||
Require("Ps5GameExampleImageClear", "bounded insufficient dispatch",
|
||||
!ResolveComputeBufferFill(compute, 1, 1, 1, 0x41u, descriptor,
|
||||
packed_clear, size),
|
||||
"a bound was mistaken for actual invocation coverage");
|
||||
Require("Ps5GameExampleImageClear", "bounded index wrap",
|
||||
!ResolveComputeBufferFill(compute, 0x04000001u, 1, 1, 0x41u, descriptor,
|
||||
packed_clear, size),
|
||||
"a wrapped invocation index was accepted as complete coverage");
|
||||
runtime_resources.uniform_fill.bounded_elements = 64;
|
||||
Require("Ps5GameExampleImageClear", "bounded incomplete destination",
|
||||
!ResolveComputeBufferFill(compute, 2, 1, 1, 0x41u, descriptor,
|
||||
packed_clear, size),
|
||||
"a shader bound smaller than the destination was accepted as a fill");
|
||||
runtime_resources = scalar;
|
||||
auto alias = scalar.buffers.front();
|
||||
runtime_resources.buffers.push_back(alias);
|
||||
Require("Ps5GameExampleImageClear", "aliased scalar input",
|
||||
@@ -48607,6 +48631,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--compute-meta-clear-only") == 0) {
|
||||
CheckDynamicRenderingState();
|
||||
CheckPs5GameExampleImageClearRuntimeShape();
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckComputeMetaClearClassification();
|
||||
vulkan.CheckNativeIndirectDispatch();
|
||||
|
||||
@@ -14414,6 +14414,172 @@ void TestComputeImageFill() {
|
||||
Run(mutation);
|
||||
}
|
||||
|
||||
void TestPatternFill() {
|
||||
using namespace ShaderRecompiler::IR;
|
||||
// Bounded DWORD pattern fill, including every instruction through S_ENDPGM.
|
||||
const uint32_t bounded_shader[] = {
|
||||
0xbfa00003u, 0xd7460002u, 0x04010c0au, 0x7da80408u,
|
||||
0xbf88003fu, 0x7e000c09u, 0xbf070980u, 0x858a807eu,
|
||||
0x7e005700u, 0x100000ffu, 0x4f800000u, 0x7e060f00u,
|
||||
0xd5766a00u, 0x02020609u, 0x7d8a0280u, 0x4c020080u,
|
||||
0x02000101u, 0xd56a0001u, 0x00020700u, 0x4c000303u,
|
||||
0x4a020303u, 0x02000101u, 0xd56a0000u, 0x00020500u,
|
||||
0xd5690001u, 0x00020009u, 0x4c060302u, 0x7d8c02f9u,
|
||||
0x06068c02u, 0x7d860609u, 0x87ea6a0cu, 0x50000080u,
|
||||
0xd5286a00u, 0x003200c1u, 0xd5010000u, 0x002a00c1u,
|
||||
0xd5690000u, 0x00020009u, 0x4c000102u, 0x7d0a0080u,
|
||||
0xbe88246au, 0xbf880015u, 0x7d0a0081u, 0xbe8a246au,
|
||||
0xbf88000cu, 0x7d0a0082u, 0xbeea246au, 0xbf880003u,
|
||||
0x7e000207u, 0xe0102000u, 0x80000002u, 0x8afe7e6au,
|
||||
0xbf880003u, 0x7e000206u, 0xe0102000u, 0x80000002u,
|
||||
0xbefe046au, 0x8afe7e0au, 0xbf880003u, 0x7e000205u,
|
||||
0xe0102000u, 0x80000002u, 0xbefe040au, 0x8afe7e08u,
|
||||
0xbf880003u, 0x7e000204u, 0xe0102000u, 0x80000002u,
|
||||
0xbf810000u,
|
||||
};
|
||||
// Pattern fill whose bounds come from dispatch thread dimensions.
|
||||
const uint32_t thread_dimension_shader[] = {
|
||||
0xbfa00002u, 0x7e040c09u, 0x81ea0980u, 0xd7460000u,
|
||||
0x04010c0au, 0x7e020205u, 0x7e045702u, 0x100404ffu,
|
||||
0x4f7ffffeu, 0x7e040f02u, 0x7ed60502u, 0x936a6b6au,
|
||||
0x9aea6b6au, 0x4a04046au, 0xd56a0002u, 0x00020102u,
|
||||
0xd5690002u, 0x00020409u, 0x4c040500u, 0x7d860409u,
|
||||
0x4e060409u, 0x02040702u, 0x7d860409u, 0x4e060409u,
|
||||
0x02040702u, 0x7d840481u, 0x7d8404f9u, 0x06868882u,
|
||||
0x7d8404f9u, 0x06868a83u, 0x02020204u, 0xd5010001u,
|
||||
0x00200d01u, 0xd5010001u, 0x00280f01u, 0xe0102000u,
|
||||
0x80000100u, 0xbf810000u,
|
||||
};
|
||||
// The 64-lane variant uses vector multiply-high for the remainder calculation.
|
||||
const uint32_t thread_dimension_wave64_shader[] = {
|
||||
0xbfa00002u, 0x7e060c09u, 0x4c0412f9u, 0x86860680u,
|
||||
0xd7460000u, 0x04010c0au, 0x7e020205u, 0x7e065703u,
|
||||
0x100606ffu, 0x4f7ffffeu, 0x7e060f03u, 0xd5690002u,
|
||||
0x00020702u, 0xd56a0002u, 0x00020702u, 0x4a040503u,
|
||||
0xd56a0002u, 0x00020102u, 0xd5690002u, 0x00020409u,
|
||||
0x4c040500u, 0x7d860409u, 0x4c0612f9u, 0x86060602u,
|
||||
0x02040702u, 0x7d860409u, 0x4c0612f9u, 0x86060602u,
|
||||
0x02040702u, 0x7d040481u, 0x7d0404f9u, 0x06868882u,
|
||||
0x7d0404f9u, 0x06868a83u, 0x02020204u, 0xd5010001u,
|
||||
0x00200d01u, 0xd5010001u, 0x00280f01u, 0xe0102000u,
|
||||
0x80000100u, 0xbf810000u,
|
||||
};
|
||||
// The 64-lane variant with scalar quotient setup.
|
||||
const uint32_t thread_dimension_wave64_scalar_shader[] = {
|
||||
0xbfa00002u, 0x7e040c09u, 0x81ea0980u, 0xd7460000u,
|
||||
0x04010c0au, 0x7e020205u, 0x7e045702u, 0x100404ffu,
|
||||
0x4f7ffffeu, 0x7e040f02u, 0x7ed60502u, 0x936a6b6au,
|
||||
0x9aea6b6au, 0x816a6a6bu, 0xd56a0002u, 0x0002006au,
|
||||
0xd5690002u, 0x00020409u, 0x4c040500u, 0x7d860409u,
|
||||
0x4c0612f9u, 0x86060602u, 0x02040702u, 0x7d860409u,
|
||||
0x4c0612f9u, 0x86060602u, 0x02040702u, 0x7d040481u,
|
||||
0x7d0404f9u, 0x06868882u, 0x7d0404f9u, 0x06868a83u,
|
||||
0x02020204u, 0xd5010001u, 0x00200d01u, 0xd5010001u,
|
||||
0x00280f01u, 0xe0102000u, 0x80000100u, 0xbf810000u,
|
||||
};
|
||||
// Same layout, with vector arithmetic for the reciprocal correction.
|
||||
auto thread_dimension_wave64_vector_shader = std::to_array(thread_dimension_wave64_scalar_shader);
|
||||
thread_dimension_wave64_vector_shader[13] = 0x4a04046au;
|
||||
thread_dimension_wave64_vector_shader[15] = 0x00020102u;
|
||||
struct PatternCase {
|
||||
std::span<const uint32_t> shader;
|
||||
uint32_t wave_size;
|
||||
bool bounded;
|
||||
size_t literal_word;
|
||||
};
|
||||
const PatternCase cases[] = {
|
||||
{bounded_shader, 64, true, 10},
|
||||
{thread_dimension_shader, 32, false, 8},
|
||||
{thread_dimension_wave64_shader, 64, false, 9},
|
||||
{thread_dimension_wave64_scalar_shader, 64, false, 8},
|
||||
{thread_dimension_wave64_vector_shader, 64, false, 8},
|
||||
};
|
||||
for (const auto &[shader, wave_size, bounded, literal_word] : cases) {
|
||||
std::array<uint32_t, 10> userdata = {
|
||||
0x00200000u, 4u << 16, 64u, 0x14204u,
|
||||
0xff000000u, 0u, 0u, 0x08000000u, 64u, 1u,
|
||||
};
|
||||
ShaderComputeInputInfo compute{};
|
||||
compute.threads_num[0] = 64;
|
||||
compute.threads_num[1] = compute.threads_num[2] = 1;
|
||||
compute.workgroup_register = 10;
|
||||
compute.thread_ids_num = 1;
|
||||
compute.group_id[0] = true;
|
||||
compute.dispatch_thread_dimensions = !bounded;
|
||||
compute.wave_size = wave_size;
|
||||
auto options = MakeCompileOptions(ShaderType::Compute);
|
||||
options.wave_size = compute.wave_size;
|
||||
options.input_info.compute = &compute;
|
||||
options.user_data = userdata;
|
||||
// Recognition derives identity from code, independently of the caller's hash.
|
||||
options.shader_hash = 0x12345678u;
|
||||
const auto Build = [&](std::span<const uint32_t> code) {
|
||||
auto translated = ShaderRecompiler::TranslateProgram(code, options);
|
||||
return ExtractResourcePlan(translated.program);
|
||||
};
|
||||
auto plan = Build(shader);
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceSpecialization specialization;
|
||||
const auto Materialize = [&](bool expected) {
|
||||
Check(MaterializeResources(plan, {.user_data = userdata}, snapshot, specialization),
|
||||
"pattern fill did not materialize");
|
||||
const auto &fill = snapshot.uniform_fill;
|
||||
Check((fill.kind == UniformFillKind::Buffer) == expected,
|
||||
"pattern fill used inactive words or retained a stale result");
|
||||
if (expected)
|
||||
Check(fill.value == userdata[4] && fill.words == 1 &&
|
||||
fill.bounded_elements == (bounded ? userdata[8] : 0u) &&
|
||||
fill.group_stride == std::array<uint32_t, 3>{64, 0, 0},
|
||||
"pattern fill lost its value or coverage contract");
|
||||
};
|
||||
Materialize(true);
|
||||
userdata[4] ^= 0x123456u;
|
||||
Materialize(true);
|
||||
userdata[4] ^= 0x123456u;
|
||||
for (const uint32_t period : {0u, 2u, 3u, 4u, 5u}) {
|
||||
userdata[9] = period;
|
||||
Materialize(false);
|
||||
}
|
||||
for (uint32_t period = 2; period <= 4; ++period) {
|
||||
userdata[9] = period;
|
||||
userdata[3 + period] = userdata[4];
|
||||
Materialize(true);
|
||||
userdata[5] ^= 1u;
|
||||
Materialize(false);
|
||||
userdata[5] ^= 1u;
|
||||
}
|
||||
userdata[5] = userdata[6] = 0;
|
||||
userdata[7] = 0x08000000u;
|
||||
userdata[9] = 1;
|
||||
Materialize(true);
|
||||
userdata[8] = 0;
|
||||
Materialize(!bounded);
|
||||
userdata[8] = 63;
|
||||
Materialize(true); // The renderer owns the descriptor-size equality check.
|
||||
userdata[8] = 64;
|
||||
Materialize(true);
|
||||
std::vector<uint32_t> changed(shader.begin(), shader.end());
|
||||
changed.push_back(0xdeadcafeu); // Non-executable metadata does not change identity.
|
||||
Check(Build(changed).uniform_fill.fill.kind == UniformFillKind::Buffer,
|
||||
"pattern recognition included bytes after S_ENDPGM");
|
||||
changed[literal_word] ^= 0x00800000u;
|
||||
Check(Build(changed).uniform_fill.fill.kind == UniformFillKind::None,
|
||||
"pattern recognition accepted modified executable instructions");
|
||||
for (const uint32_t invalid_input : {0u, 1u, 2u, 3u}) {
|
||||
const auto saved_compute = compute;
|
||||
const auto saved_wave = options.wave_size;
|
||||
if (invalid_input == 0) compute.workgroup_register = 11;
|
||||
if (invalid_input == 1) options.wave_size = wave_size == 64 ? 32 : 64;
|
||||
if (invalid_input == 2) compute.dispatch_thread_dimensions = bounded;
|
||||
if (invalid_input == 3) compute.float_mode ^= 1u;
|
||||
Check(Build(shader).uniform_fill.fill.kind == UniformFillKind::None,
|
||||
"pattern recognition accepted different shader launch inputs");
|
||||
compute = saved_compute;
|
||||
options.wave_size = saved_wave;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestGpuProducedWritableDescriptor() {
|
||||
using namespace ShaderRecompiler::IR;
|
||||
// Reduced from 994af59d50bedca8: the GPU-built BVH header supplies a relative
|
||||
@@ -16430,6 +16596,7 @@ int main() {
|
||||
TestNewShaderRecompilerEarlyZDisabledWhenPixelKillEnabled();
|
||||
TestTypedDescriptorRealWideMoveTranslation();
|
||||
TestComputeImageFill();
|
||||
TestPatternFill();
|
||||
TestGpuProducedWritableDescriptor();
|
||||
TestUniformSelectedWritableDescriptor();
|
||||
TestUniformSelectedDescriptorLoadAddress();
|
||||
|
||||
Reference in New Issue
Block a user