shader: add BVH8 intersections and instance transforms

This commit is contained in:
nmzik committed 2026-10-29 18:37:29 +01:00
1 parent bfd4c34b88
commit 5eab0ec738
21 files changed
+964 -15

No files matched your search

@@ -281,6 +281,7 @@ Opcode DecodeMimgOpcode(uint32_t opcode, const MimgSampleInfo* sample, const Mim
case 0x09u: return Opcode::IMAGE_STORE_MIP;
case 0x0eu: return Opcode::IMAGE_GET_RESINFO;
case 0x60u: return Opcode::IMAGE_GET_LOD;
case 0xe5u: return Opcode::IMAGE_BVH8_INTERSECT_RAY;
case 0xe6u:
case 0xe7u: return Opcode::IMAGE_BVH_INTERSECT_RAY;
default: return Opcode::UNSUPPORTED;
@@ -297,6 +298,7 @@ uint32_t DecodeMimgAddressComponents(uint32_t opcode, ImageDimension dimension,
}
switch (opcode) {
case 0xe5u:
case 0xe6u: return 11u;
case 0xe7u: return 12u;
case 0x0eu: return 1u;
@@ -427,6 +429,36 @@ void DecodeMimg(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
"BVH intersection requires full-float ray DWORDs and R128/dmask:0xf");
}
if (opcode == 0xe5u) {
inst.data_components = inst.data_dwords = 10;
if (a16 || d16 || !r128 || inst.dmask != 0xfu ||
(nsa_dwords != 0u && nsa_dwords != 2u) || vdata > 246u ||
(nsa_dwords == 0u && vaddr > 245u)) {
SetUnsupported(inst, Family::MIMG, opcode, "invalid BVH8 ray or destination encoding");
}
if (nsa_dwords == 2u) {
const auto origin = inst.image_nsa_addr[3], direction = inst.image_nsa_addr[4];
const auto node = inst.image_nsa_addr[5];
if (origin > 253u || direction > 253u)
SetUnsupported(inst, Family::MIMG, opcode, "BVH8 ray registers exceed VGPR range");
for (uint32_t i = 0; i < 3; ++i) {
inst.image_nsa_addr[3 + i] = origin + i;
inst.image_nsa_addr[6 + i] = direction + i;
}
inst.image_nsa_addr[9] = node;
}
const auto origin = nsa_dwords != 0 ? inst.image_nsa_addr[3] : vaddr + 4;
const auto direction = nsa_dwords != 0 ? inst.image_nsa_addr[6] : vaddr + 7;
bool overlapping = origin < direction + 3 && direction < origin + 3;
for (const auto output : {2u, 3u, 6u, 7u}) {
const auto reg = vdata + output;
overlapping |= (reg >= origin && reg < origin + 3) ||
(reg >= direction && reg < direction + 3);
}
if (overlapping)
SetUnsupported(inst, Family::MIMG, opcode, "overlapping BVH8 instance outputs are unsupported");
}
DecodeVectorGpr(vdata, inst.dst);
DecodeVectorGpr(vaddr, inst.src0);
DecodeScalarSource(srsrc * 4u, pc, inst.src1);
@@ -701,6 +701,7 @@ enum class Opcode {
DS_WRITE_ADDTID_B32,
DS_READ_ADDTID_B32,
IMAGE_BVH_INTERSECT_RAY,
IMAGE_BVH8_INTERSECT_RAY,
IMAGE_GET_RESINFO,
IMAGE_GET_LOD,
IMAGE_LOAD,
@@ -326,7 +326,8 @@ IR::Value Translator::MakeImageAddress(const Decoder::Instruction& inst,
inst.image_sample_flags, inst.image_dimension, inst.image_address_components);
EXIT_IF(count > components.size());
const auto nsa_components =
std::min(inst.image_nsa_dwords * 4u, Decoder::MaxImageNsaAddressComponents);
inst.opcode == Decoder::Opcode::IMAGE_BVH8_INTERSECT_RAY && inst.image_nsa_dwords != 0u
? 10u : std::min(inst.image_nsa_dwords * 4u, Decoder::MaxImageNsaAddressComponents);
for (uint32_t index = 0; index < count; index++) {
if (index != 0u && index - 1u < nsa_components) {
components[index] =
@@ -948,6 +949,31 @@ void Translator::DS_PERMUTE(const Decoder::Instruction& inst, bool backward) {
{ReadU32(inst.src1), address, ir.GetExec()}));
}
void Translator::IMAGE_BVH8(const Decoder::Instruction& inst) {
std::array<IR::U32, 10> previous;
for (uint32_t i = 0; i < previous.size(); ++i)
previous[i] = ReadU32(OffsetOperand(inst.dst, i));
const auto result = ir.Emit(IR::ValueOpcode::Bvh8Intersect,
{ConstructU32x4(inst.src1, 4), MakeImageAddress(inst, inst.src0), ir.GetExec()});
const auto get = [&](uint32_t i) {
return IR::U32(ir.Emit(IR::ValueOpcode::Bvh8Extract, {result, IR::Value(i)}));
};
const auto instance = ir.INotEqual(get(16), IR::U32(IR::Value(0u)));
for (uint32_t i = 0; i < 10; ++i) {
auto value = get(i);
if (i == 0 || i == 1 || i == 4 || i == 5 || i == 8 || i == 9)
value = ir.Select(instance, previous[i], value);
WriteOperand(OffsetOperand(inst.dst, i), value);
}
for (uint32_t i = 0; i < 6; ++i) {
auto reg = inst.image_nsa_dwords != 0 ? inst.image_nsa_addr[3 + i] : inst.src0.reg + 4 + i;
auto target = inst.src0;
target.reg = reg;
// Non-instance results may alias the ray inputs.
WriteOperand(target, ir.Select(instance, get(10 + i), ReadU32(target)));
}
}
void Translator::EmitMemory(const Decoder::Instruction& inst) {
switch (inst.opcode) {
// Translated scalar loads have no cache of their own to invalidate.
@@ -956,6 +982,7 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) {
case Decoder::Opcode::BUFFER_GL1_INV:
ir.Emit(IR::ValueOpcode::DataCacheInvalidate);
return;
case Decoder::Opcode::IMAGE_BVH8_INTERSECT_RAY: return IMAGE_BVH8(inst);
case Decoder::Opcode::IMAGE_BVH_INTERSECT_RAY: {
const auto result = ir.Emit(IR::ValueOpcode::BvhIntersect,
{ConstructU32x4(inst.src1, 4), MakeImageAddress(inst, inst.src0), ir.GetExec()},
@@ -105,6 +105,7 @@ private:
void WriteSharedU32(uint32_t width, IR::U32 address, const std::array<IR::Value, 4>& values,
const IR::MemoryInfo& memory, uint32_t pc);
void DS_READ(const Decoder::Instruction& inst);
void IMAGE_BVH8(const Decoder::Instruction& inst);
void DS_BVH_STACK(const Decoder::Instruction& inst);
void DS_READ2(const Decoder::Instruction& inst);
void DS_WRITE(const Decoder::Instruction& inst);