mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-30 01:01:21 +00:00
shader: add BVH8 intersections and instance transforms
This commit is contained in:
1 parent
bfd4c34b88
commit
5eab0ec738
21 files changed
+964
-15
No files matched your search
@@ -1,3 +1,306 @@
|
||||
TestCase TrinityBvhIntersections(bool compressed, bool bary, bool sorted,
|
||||
bool wide_sort, u32 heuristic = 0, bool triangles_first = false,
|
||||
u32 wave_size = 32, bool edges = false) {
|
||||
TestCase test;
|
||||
test.name = compressed ? (sorted ? "TrinityCompressedSorted" : "TrinityCompressedUnsorted")
|
||||
: (sorted ? "TrinityFp32Sorted" : "TrinityFp32Unsorted");
|
||||
if (!bary) test.name = "TrinityTriangleIds";
|
||||
if (heuristic == 1) test.name = "TrinityLargestInterval";
|
||||
if (heuristic == 2) test.name = "TrinityClosestMidpoint";
|
||||
if (triangles_first) test.name = "TrinityTrianglesFirst";
|
||||
if (wave_size == 64) test.name = "TrinityBvhWave64";
|
||||
if (edges) test.name = compressed ? "TrinityCompressedEdges" : "TrinityFp32Edges";
|
||||
constexpr u32 NodeStart = 4096, OutputStart = 2048, GuestBase = 0x10000;
|
||||
constexpr u32 Invalid = ~0u, Infinity = 0x7f800000, Flag = 0x8484, Shape = 0x12345678;
|
||||
const auto bits = [](float x) { return std::bit_cast<u32>(x); };
|
||||
test.initial.resize(NodeStart + 256 * 16);
|
||||
auto& data = test.initial;
|
||||
const std::array<float, 12> vertices {-1,-1,2, 1,-1,2, 0,1,2, 2,1,2};
|
||||
for (u32 i = 0; i < vertices.size(); ++i) data[NodeStart + i] = bits(vertices[i]);
|
||||
data[NodeStart + 14] = Shape;
|
||||
data[NodeStart + 15] = Flag;
|
||||
const std::array<float, 12> matrix {2,0,0,1, 0,3,0,-2, 0,0,4,3};
|
||||
for (u32 i = 0; i < matrix.size(); ++i) data[NodeStart + 16 + i] = bits(matrix[i]);
|
||||
data[NodeStart + 28] = 0x1000;
|
||||
data[NodeStart + 29] = 9u << 22; // Force opaque and flip facing.
|
||||
data[NodeStart + 30] = 0x98;
|
||||
data[NodeStart + 31] = 0x04123456;
|
||||
const std::array<float, 8> near {5,1,3,7,4,2,6,8};
|
||||
const std::array<float, 8> length {1,8,3,5,7,2,4,6};
|
||||
const std::array<u32, 8> children {128,138,171,190,256,266,299,318};
|
||||
const auto set_bits = [&](u32 base, u32 offset, u32 count, u32 value) {
|
||||
for (u32 bit = 0; bit < count; ++bit)
|
||||
data[base + (offset + bit) / 32] |= ((value >> bit) & 1) << ((offset + bit) % 32);
|
||||
};
|
||||
for (u32 slot = 0; slot < 2; ++slot) {
|
||||
const u32 base = NodeStart + (3 + slot * (compressed ? 1 : 2)) * 16;
|
||||
if (compressed) {
|
||||
set_bits(base, 0, 29, slot ? 32 : 16);
|
||||
for (u32 i = 0; i < 4; ++i) set_bits(base, 29 + i * 3, 3, children[slot * 4 + i] & 7);
|
||||
set_bits(base, 56, 8, 127); set_bits(base, 64, 8, 127); set_bits(base, 72, 8, 130);
|
||||
}
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
const u32 index = slot * 4 + i;
|
||||
if (compressed) {
|
||||
// Exact integer minima; positive maxima round outward within one quantization step.
|
||||
const std::array<u32, 6> planes {0x30000, 0x30000, u32(near[index]) * 8192,
|
||||
0x10000, 0x10000, u32(near[index] + length[index]) * 8192};
|
||||
for (u32 axis = 0; axis < 6; ++axis) set_bits(base, 80 + i * 108 + axis * 18, 18, planes[axis]);
|
||||
} else {
|
||||
data[base + i] = children[index];
|
||||
const std::array<float, 6> bounds {-1,-1,near[index], 1,1,near[index] + length[index]};
|
||||
for (u32 axis = 0; axis < 6; ++axis) data[base + 4 + i * 6 + axis] = bits(bounds[axis]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (compressed) {
|
||||
std::copy_n(data.begin() + NodeStart + 3 * 16, 16, data.begin() + NodeStart + 10 * 16);
|
||||
} else {
|
||||
constexpr std::array<u32, 4> lo {0x4500, 0x3c00, 0x4200, 0x4700};
|
||||
constexpr std::array<u32, 4> hi {0x4600, 0x4880, 0x4600, 0x4a00};
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
data[NodeStart + 160 + i] = children[i];
|
||||
data[NodeStart + 164 + i * 3] = 0xbc00bc00;
|
||||
data[NodeStart + 165 + i * 3] = 0x3c000000 | lo[i];
|
||||
data[NodeStart + 166 + i * 3] = (hi[i] << 16) | 0x3c00;
|
||||
}
|
||||
}
|
||||
if (edges) {
|
||||
// Slot zero ends exactly at the descriptor limit; slot one lies beyond it.
|
||||
std::copy_n(data.begin() + NodeStart + 3 * 16, compressed ? 16 : 32,
|
||||
data.begin() + NodeStart + (compressed ? 253 : 252) * 16);
|
||||
for (u32 index : {12u, 13u})
|
||||
std::copy_n(data.begin() + NodeStart, 16, data.begin() + NodeStart + index * 16);
|
||||
data[NodeStart + 12 * 16 + 15] = 0xc4c4; // Opaque procedural pair.
|
||||
data[NodeStart + 13 * 16 + 15] = 0x9494; // Opaque, double-sided pair.
|
||||
if (compressed) {
|
||||
std::copy_n(data.begin() + NodeStart + 3 * 16, 16, data.begin() + NodeStart + 11 * 16);
|
||||
set_bits(NodeStart + 11 * 16, 41, 4, 1);
|
||||
set_bits(NodeStart + 11 * 16, 45, 4, 2);
|
||||
set_bits(NodeStart + 11 * 16, 49, 4, 3);
|
||||
}
|
||||
}
|
||||
std::vector<std::array<u32, 16>> expected(wave_size);
|
||||
for (u32 lane = 0; lane < wave_size; ++lane) {
|
||||
const u32 which = lane % 16, input = lane * 16;
|
||||
auto& out = expected[lane];
|
||||
out.fill(Invalid);
|
||||
data[input + 2] = bits(20); data[input + 3] = 4; data[input + 9] = bits(1);
|
||||
const auto miss = [&](u32 slot, bool invalid = false) {
|
||||
out[slot * 4] = Infinity; out[slot * 4 + 1] = bits(1);
|
||||
out[slot * 4 + 2] = !invalid && bary ? 0 : Invalid; out[slot * 4 + 3] = 0;
|
||||
};
|
||||
const auto hit = [&](u32 slot, float u, float v) {
|
||||
out[slot * 4] = bits(-8); out[slot * 4 + 1] = bits(-4);
|
||||
out[slot * 4 + 2] = bary ? bits(u) : Flag + slot;
|
||||
out[slot * 4 + 3] = bary ? bits(v) : 1;
|
||||
};
|
||||
if (which <= 2 || which >= 9 && which <= 14) {
|
||||
data[input + 10] = which == 1 || which == 2 ? 1 : 0;
|
||||
miss(0); miss(1);
|
||||
out[8] = Shape;
|
||||
if (which == 1) {
|
||||
data[input + 4] = bits(1); hit(1, 2, 1); out[9] = Shape;
|
||||
} else {
|
||||
hit(0, 1, 1);
|
||||
if (which == 2) { miss(1); out[9] = Shape; }
|
||||
}
|
||||
if (which == 9 || which == 10 || which == 11) {
|
||||
if (which == 9) data[input + 4] = 0x7fc00000;
|
||||
if (which == 10) data[input + 10] = 0xfff8;
|
||||
if (which == 11) data[input + 1] = 0x41u << 22;
|
||||
miss(0, true); miss(1, true); out[8] = out[9] = Invalid;
|
||||
} else if (which == 12) {
|
||||
data[input + 1] = 0x100u << 22; miss(0);
|
||||
} else if (which == 13) {
|
||||
data[input + 1] = 8u << 22; out[0] = bits(8); out[1] = bits(4);
|
||||
} else if (which == 14) {
|
||||
data[input + 1] = 2u << 22;
|
||||
if (bary) { out[2] = bits(-1); out[3] = bits(-1); }
|
||||
}
|
||||
} else if (which >= 3 && which <= 5) {
|
||||
data[input + 10] = which == 3 ? (3 * 8 + (compressed ? 3 : 2))
|
||||
: which == 4 ? (3 * 8 + (compressed ? 4 : 5)) : (10 * 8 + 4);
|
||||
std::array<u32, 8> order {0,1,2,3,4,5,6,7};
|
||||
const auto compare = [&](u32 x, u32 y) {
|
||||
const bool xt = (children[x] & 7) < 2, yt = (children[y] & 7) < 2;
|
||||
if (triangles_first && xt != yt) return xt;
|
||||
if (heuristic == 1) return length[x] > length[y];
|
||||
if (heuristic == 2) return 2 * near[x] + length[x] < 2 * near[y] + length[y];
|
||||
return near[x] < near[y];
|
||||
};
|
||||
if (sorted) {
|
||||
if (wide_sort && which == 3) std::stable_sort(order.begin(), order.end(), compare);
|
||||
else {
|
||||
std::stable_sort(order.begin(), order.begin() + 4, compare);
|
||||
std::stable_sort(order.begin() + 4, order.end(), compare);
|
||||
}
|
||||
}
|
||||
for (u32 i = 0; i < (which == 3 ? 8u : 4u); ++i) out[i] = children[order[i]];
|
||||
} else if (which == 6 || which == 7 || which == 15) {
|
||||
data[input + 10] = 14;
|
||||
if (which == 15) { data[input] = 8; data[input + 10] = 6; }
|
||||
data[input + 4] = bits(1); data[input + 5] = bits(2); data[input + 6] = bits(3);
|
||||
if (which == 7) data[input + 3] = 2;
|
||||
for (u32 i = 0; i < 10; ++i) out[i] = 0xabc00000 + i;
|
||||
out[2] = 0x1000; out[3] = 9u << 22; out[6] = 0x123456; out[7] = which == 7 ? Invalid : 0x98;
|
||||
} else data[input + 10] = 7;
|
||||
if (edges) {
|
||||
switch (which) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 5:
|
||||
if (which == 0) data[input] = 1; // Misaligned encoded base must not read across a block.
|
||||
if (which == 1) data[input + 4] = Infinity;
|
||||
if (which == 2) data[input + 2] = 0x7fc00000;
|
||||
if (which == 5) { data[input + 1] = 0x2000; data[input + 10] = 0; }
|
||||
miss(0, true); miss(1, true); out[8] = out[9] = Invalid;
|
||||
break;
|
||||
case 3:
|
||||
data[input + 10] = compressed ? 253 * 8 + 3 : 252 * 8 + 2;
|
||||
for (u32 i = 4; i < 8; ++i) out[i] = Invalid;
|
||||
break;
|
||||
case 4:
|
||||
if (compressed) {
|
||||
data[input + 10] = 11 * 8 + 4; data[input + 1] = 0x40u << 22;
|
||||
out[0] = out[2] = Invalid;
|
||||
} else {
|
||||
data[input + 2] = bits(1);
|
||||
for (u32 i = 0; i < 4; ++i) out[i] = Invalid;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
data[input + 1] = 8u << 22; out[3] = 1u << 22;
|
||||
break;
|
||||
case 7:
|
||||
data[input + 1] = 2u << 22; out[3] = 10u << 22;
|
||||
break;
|
||||
case 9:
|
||||
case 10:
|
||||
data[input + 10] = 12 * 8;
|
||||
data[input + 4] = bits(100);
|
||||
out[8] = Shape; miss(0); miss(1);
|
||||
if (which == 9) { out[0] = bits(0); out[1] = bits(1); out[2] = bits(-1); out[3] = bits(1); }
|
||||
else data[input + 1] = 0x200u << 22;
|
||||
break;
|
||||
case 11:
|
||||
data[input + 1] = 0x10u << 22; miss(0); miss(1); out[8] = Shape;
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
data[input + 1] = (which == 12 ? 0x14u : 0x10u) << 22;
|
||||
if (which == 13) data[input + 10] = 13 * 8;
|
||||
hit(0, 1, 1);
|
||||
break;
|
||||
case 14:
|
||||
// The compressed +X bound rounds outward beyond exactly one.
|
||||
if (compressed) {
|
||||
data[input + 1] = 0; data[input + 4] = bits(1.0f + 0x1p-17f);
|
||||
data[input + 10] = 3 * 8 + 4;
|
||||
out.fill(Invalid);
|
||||
for (u32 i = 0; i < 4; ++i) out[i] = children[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (u32 i = 0; i < 6; ++i) out[10 + i] = data[input + 4 + i];
|
||||
if (which == 6 || which == 7 || which == 15) {
|
||||
out[10] = bits(3); out[11] = bits(4); out[12] = bits(15); out[15] = bits(4);
|
||||
}
|
||||
if (lane % 32 >= 16) {
|
||||
for (u32 i = 0; i < 10; ++i) out[i] = 0xabc00000 + i;
|
||||
for (u32 i = 0; i < 6; ++i) out[10 + i] = data[input + 4 + i];
|
||||
}
|
||||
}
|
||||
test.expected = data;
|
||||
for (u32 lane = 0; lane < wave_size; ++lane)
|
||||
std::copy(expected[lane].begin(), expected[lane].end(), test.expected.begin() + OutputStart + lane * 16);
|
||||
auto& code = test.code;
|
||||
AppendSMovLiteral(&code, 8, GuestBase >> 8);
|
||||
AppendSMovLiteral(&code, 9, (sorted ? 1u << 31 : 0) | (triangles_first ? 1u << 20 : 0) | (heuristic << 21));
|
||||
AppendSMovLiteral(&code, 10, edges ? 253 : 255);
|
||||
AppendSMovLiteral(&code, 11, 0x80c00000 | (bary ? 1u << 24 : 0) |
|
||||
(compressed ? 1u << 20 : 0) | (wide_sort ? 1u << 21 : 0));
|
||||
code.push_back(EncodeVop2(0x1a, 1, InlineU32(6), 0));
|
||||
for (u32 offset : {0u,4u,8u})
|
||||
code.insert(code.end(), {EncodeMubuf0(offset == 8 ? 0x0f : 0x0e, offset * 4), EncodeMubuf1(4 + offset, 0, 1)});
|
||||
for (u32 i = 0; i < 10; ++i) AppendVMovLiteral(&code, 32 + i, 0xabc00000 + i);
|
||||
AppendSMovLiteral(&code, 126, 0x0000ffff);
|
||||
if (wave_size == 64) AppendSMovLiteral(&code, 127, 0x0000ffff);
|
||||
code.insert(code.end(), {EncodeMimg0(0xe5, 15, 2, false, 0, true), EncodeMimg1(32, 4, 2),
|
||||
0x08070605, 0x00000e0b});
|
||||
code.push_back(EncodeSop1(0x04, 126, 193));
|
||||
code.push_back(EncodeVop2(0x1a, 2, InlineU32(4), 0));
|
||||
for (u32 i = 0; i < 10; ++i) AppendStoreVgprAtLaneDwordOffset(&code, 32 + i, 2, OutputStart + i);
|
||||
for (u32 i = 0; i < 6; ++i) AppendStoreVgprAtLaneDwordOffset(&code, 8 + i, 2, OutputStart + 10 + i);
|
||||
AppendEnd(&code);
|
||||
test.bda_mappings = {{GuestBase, NodeStart * sizeof(u32)}};
|
||||
test.compute_info.wave_size = wave_size;
|
||||
test.compute_info.threads_num[0] = wave_size;
|
||||
test.compute_info.threads_num[1] = test.compute_info.threads_num[2] = 1;
|
||||
test.compute_info.thread_ids_num = 1;
|
||||
test.has_compute_info = true;
|
||||
test.decoded_counts = {{"IMAGE_BVH8_INTERSECT_RAY", 1}};
|
||||
test.required_spirv = {"trinity_bvh_intersect"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase TrinityBvhAliases() {
|
||||
auto test = TrinityBvhIntersections(false, true, false, false);
|
||||
test.name = "TrinityBvhRegisterAliases";
|
||||
constexpr u32 OutputStart = 2048;
|
||||
for (u32 lane = 0; lane < 32; ++lane) {
|
||||
auto* out = test.expected.data() + OutputStart + lane * 16;
|
||||
const auto* input = test.initial.data() + lane * 16;
|
||||
const bool active = lane < 16, instance = active && (lane == 6 || lane == 7 || lane == 15);
|
||||
if (!active) {
|
||||
for (u32 i = 0; i < 6; ++i) out[4 + i] = input[i];
|
||||
} else if (instance) {
|
||||
out[4] = input[0]; out[5] = input[1];
|
||||
out[8] = out[10]; out[9] = out[11];
|
||||
} else {
|
||||
// A non-instance writes its shape IDs into registers formerly holding ray XY.
|
||||
out[10] = out[8]; out[11] = out[9];
|
||||
}
|
||||
}
|
||||
auto& code = test.code;
|
||||
code.clear();
|
||||
AppendSMovLiteral(&code, 8, 0x100);
|
||||
AppendSMovLiteral(&code, 9, 0);
|
||||
AppendSMovLiteral(&code, 10, 255);
|
||||
AppendSMovLiteral(&code, 11, 0x81c00000);
|
||||
code.push_back(EncodeVop2(0x1a, 50, InlineU32(6), 0));
|
||||
code.push_back(EncodeVop2(0x1a, 51, InlineU32(4), 0));
|
||||
for (u32 i = 0; i < 10; ++i) AppendVMovLiteral(&code, i, 0xabc00000 + i);
|
||||
for (u32 offset : {0u, 4u, 8u})
|
||||
code.insert(code.end(), {EncodeMubuf0(offset == 8 ? 0x0f : 0x0e, offset * 4),
|
||||
EncodeMubuf1(4 + offset, 0, 50)});
|
||||
AppendSMovLiteral(&code, 126, 0x0000ffff);
|
||||
code.insert(code.end(), {EncodeMimg0(0xe5, 15, 2, false, 0, true), EncodeMimg1(0, 4, 2),
|
||||
0x08070605, 0x00000e0b});
|
||||
code.push_back(EncodeSop1(0x04, 126, 193));
|
||||
for (u32 i = 0; i < 10; ++i) AppendStoreVgprAtLaneDwordOffset(&code, i, 51, OutputStart + i);
|
||||
for (u32 i = 0; i < 6; ++i) AppendStoreVgprAtLaneDwordOffset(&code, 8 + i, 51, OutputStart + 10 + i);
|
||||
AppendEnd(&code);
|
||||
return test;
|
||||
}
|
||||
|
||||
std::vector<TestCase> TrinityBvhCases() {
|
||||
return {TrinityBvhIntersections(false, true, false, false),
|
||||
TrinityBvhIntersections(false, true, true, true),
|
||||
TrinityBvhIntersections(true, true, false, false),
|
||||
TrinityBvhIntersections(true, true, true, false),
|
||||
TrinityBvhIntersections(false, false, true, true),
|
||||
TrinityBvhIntersections(false, true, true, true, 1),
|
||||
TrinityBvhIntersections(false, true, true, true, 2),
|
||||
TrinityBvhIntersections(true, true, true, true, 0, true),
|
||||
TrinityBvhIntersections(true, true, true, true, 0, false, 64),
|
||||
TrinityBvhIntersections(false, true, false, false, 0, false, 32, true),
|
||||
TrinityBvhIntersections(true, true, false, false, 0, false, 32, true),
|
||||
TrinityBvhAliases()};
|
||||
}
|
||||
|
||||
TestCase TrinityStack(u32 wave_size, u32 capacity, bool storage = false) {
|
||||
TestCase test;
|
||||
static constexpr const char* names[2][3] = {
|
||||
|
||||
Reference in new issue
Block a user