feat(shader): implement image_bvh64_intersect_ray
MIMG 0xe7 is the node test of image_bvh_intersect_ray with a 64-bit node pointer: the pointer takes the first two address dwords (low, high), so the instruction addresses 12 dwords, or 9 with A16. The node index and address are computed from the whole pointer. A zero base in the T# is a null BVH only for the 32-bit form: with 64-bit pointers the T# may cover all of memory from base 0 and the pointer then carries the node address, so only the T# size bounds the node. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
e440e2c22b
commit
bf635fae68
|
|
@ -0,0 +1,304 @@
|
|||
#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
|
||||
#include "prx/libc/include/GuestAllocations.hpp"
|
||||
#include "Recompiler.hpp"
|
||||
#include "VulkanTestDevice.hpp"
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
using AgcDriver::Graphics::Require;
|
||||
using ShaderRecompiler::ShaderStage;
|
||||
|
||||
constexpr std::uint32_t Threads = 32;
|
||||
constexpr std::uint32_t Record = 16;
|
||||
constexpr std::uint32_t Forms = 4;
|
||||
constexpr std::size_t BlockBytes = 65536;
|
||||
constexpr std::uint64_t NodeBytes = 64;
|
||||
constexpr std::uint32_t LastNode = 6;
|
||||
constexpr std::uint32_t Invalid = 0xffffffffu;
|
||||
constexpr std::uint32_t BoxSort = 0x80000000u;
|
||||
constexpr std::uint32_t TriangleId = 0x00abc123u;
|
||||
constexpr std::array<std::uint32_t, 4> Box32Children{0x1000u, 0x1001u, 0x1002u, 0x1003u};
|
||||
constexpr std::array<std::uint32_t, 4> Box16Children{0x2000u, 0x2001u, 0x2002u, 0x2003u};
|
||||
constexpr std::array<std::uint32_t, 4> PastEndChildren{0x3000u, 0x3001u, 0x3002u, 0x3003u};
|
||||
constexpr std::array<const char*, Forms> FormNames{"image_bvh64_intersect_ray", "image_bvh_intersect_ray", "image_bvh64_intersect_ray a16", "image_bvh_intersect_ray a16"};
|
||||
|
||||
alignas(256) std::array<std::uint32_t, Threads * Record> Input{};
|
||||
alignas(256) std::array<std::uint32_t, Threads * Record> Output{};
|
||||
|
||||
constexpr std::array<std::uint32_t, 82> MakeCode() {
|
||||
std::array<std::uint32_t, 82> code{};
|
||||
std::size_t at = 0;
|
||||
code[at++] = 0x34020086u;
|
||||
for (std::uint32_t dword = 0; dword < Record; ++dword) {
|
||||
code[at++] = 0xe0301000u | (dword * 4u);
|
||||
code[at++] = 0x80000001u | ((4u + dword) << 8u);
|
||||
}
|
||||
code[at++] = 0xbf8c3f70u;
|
||||
code[at++] = 0xf19c9f01u;
|
||||
code[at++] = 0x00031405u;
|
||||
code[at++] = 0xf1989f07u;
|
||||
code[at++] = 0x00021804u;
|
||||
code[at++] = 0x0a090807u;
|
||||
code[at++] = 0x0e0d0c0bu;
|
||||
code[at++] = 0x0000100fu;
|
||||
code[at++] = 0xf19c9f05u;
|
||||
code[at++] = 0x40031c05u;
|
||||
code[at++] = 0x09080706u;
|
||||
code[at++] = 0x1312110au;
|
||||
code[at++] = 0xf1989f05u;
|
||||
code[at++] = 0x40022004u;
|
||||
code[at++] = 0x0a090807u;
|
||||
code[at++] = 0x00131211u;
|
||||
for (std::uint32_t dword = 0; dword < Record; ++dword) {
|
||||
code[at++] = 0xe0701000u | (dword * 4u);
|
||||
code[at++] = 0x80010001u | ((20u + dword) << 8u);
|
||||
}
|
||||
code[at++] = 0xbf810000u;
|
||||
return code;
|
||||
}
|
||||
|
||||
alignas(256) constexpr std::array<std::uint32_t, 82> Code = MakeCode();
|
||||
|
||||
struct Ray {
|
||||
float extent;
|
||||
std::array<float, 3> origin;
|
||||
std::array<float, 3> direction;
|
||||
std::array<float, 3> inverse;
|
||||
};
|
||||
|
||||
struct Case {
|
||||
std::uint32_t node;
|
||||
Ray ray;
|
||||
std::array<std::uint32_t, 4> expected;
|
||||
};
|
||||
|
||||
constexpr float Infinity = std::numeric_limits<float>::infinity();
|
||||
constexpr std::array<float, 3> Diagonal{0.25f, 0.5f, 1.0f};
|
||||
constexpr std::array<float, 3> DiagonalInverse{4.0f, 2.0f, 1.0f};
|
||||
constexpr Ray Long{100.0f, {0.0f, 0.0f, 0.0f}, Diagonal, DiagonalInverse};
|
||||
constexpr Ray Short{2.0f, {0.0f, 0.0f, 0.0f}, Diagonal, DiagonalInverse};
|
||||
constexpr Ray Forward{100.0f, {0.25f, 0.25f, 0.0f}, {0.0f, 0.0f, 1.0f}, {Infinity, Infinity, 1.0f}};
|
||||
|
||||
constexpr std::uint32_t Pointer(std::uint32_t node, std::uint32_t type) {
|
||||
return (node << 3u) | type;
|
||||
}
|
||||
|
||||
constexpr std::array<Case, 8> Cases{{
|
||||
{Pointer(2, 5), Long, {Box32Children[2], Box32Children[0], Invalid, Invalid}},
|
||||
{Pointer(4, 4), Long, {Box16Children[2], Box16Children[0], Invalid, Invalid}},
|
||||
{Pointer(1, 0), Forward, {0xc0a00000u, 0xbf800000u, TriangleId, 1u}},
|
||||
{Pointer(1, 1), Forward, {0x7f800000u, 0x3f800000u, TriangleId, 0u}},
|
||||
{Pointer(1, 7), Long, {Invalid, Invalid, Invalid, Invalid}},
|
||||
{Pointer(6, 5), Long, {Invalid, Invalid, Invalid, Invalid}},
|
||||
{Pointer(7, 0), Forward, {Invalid, Invalid, Invalid, Invalid}},
|
||||
{Pointer(2, 5), Short, {Box32Children[2], Invalid, Invalid, Invalid}},
|
||||
}};
|
||||
|
||||
constexpr std::array<std::array<float, 6>, 4> Boxes{{
|
||||
{1.0f, 2.0f, 4.0f, 2.0f, 4.0f, 8.0f},
|
||||
{10.0f, 0.0f, 0.0f, 11.0f, 1.0f, 1.0f},
|
||||
{0.25f, 0.5f, 1.0f, 0.5f, 1.0f, 2.0f},
|
||||
{-2.0f, -4.0f, -8.0f, -1.0f, -2.0f, -4.0f},
|
||||
}};
|
||||
|
||||
constexpr std::array<float, 12> TriangleVertices{0.0f, 0.0f, 5.0f, 1.0f, 0.0f, 5.0f, 0.0f, 1.0f, 5.0f, 1.0f, 1.0f, 5.0f};
|
||||
|
||||
std::uint32_t Bits(float value) {
|
||||
return std::bit_cast<std::uint32_t>(value);
|
||||
}
|
||||
|
||||
std::uint32_t Half(float value) {
|
||||
const auto bits = Bits(value);
|
||||
const auto sign = (bits >> 16u) & 0x8000u;
|
||||
const auto exponent = (bits >> 23u) & 0xffu;
|
||||
const auto mantissa = bits & 0x7fffffu;
|
||||
if (exponent == 0u && mantissa == 0u) return sign;
|
||||
if (exponent == 0xffu && mantissa == 0u) return sign | 0x7c00u;
|
||||
Require(exponent >= 113u && exponent <= 142u && (mantissa & 0x1fffu) == 0u, "bvh64 intersect ray: a test value is not a float16");
|
||||
return sign | ((exponent - 112u) << 10u) | (mantissa >> 13u);
|
||||
}
|
||||
|
||||
class GuestBlock {
|
||||
public:
|
||||
GuestBlock() {
|
||||
constexpr std::uintptr_t hint = std::uintptr_t{1} << 40u;
|
||||
#ifdef _WIN32
|
||||
for (std::uintptr_t attempt = 0; block == nullptr && attempt < 16u; ++attempt) {
|
||||
block = static_cast<std::uint8_t*>(VirtualAlloc(reinterpret_cast<void*>(hint + (attempt << 32u)), BlockBytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||
}
|
||||
if (block == nullptr) block = static_cast<std::uint8_t*>(VirtualAlloc(nullptr, BlockBytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||
#else
|
||||
void* mapped = mmap(reinterpret_cast<void*>(hint), BlockBytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
block = mapped == MAP_FAILED ? nullptr : static_cast<std::uint8_t*>(mapped);
|
||||
#endif
|
||||
Require(block != nullptr, "bvh64 intersect ray: cannot allocate the guest block");
|
||||
std::memset(block, 0, BlockBytes);
|
||||
GuestAllocations::Mutation().Add(block, BlockBytes, true, false);
|
||||
}
|
||||
|
||||
~GuestBlock() {
|
||||
GuestAllocations::Mutation().Remove(block);
|
||||
#ifdef _WIN32
|
||||
VirtualFree(block, 0, MEM_RELEASE);
|
||||
#else
|
||||
munmap(block, BlockBytes);
|
||||
#endif
|
||||
}
|
||||
|
||||
GuestBlock(const GuestBlock&) = delete;
|
||||
GuestBlock& operator=(const GuestBlock&) = delete;
|
||||
|
||||
std::uint64_t Address() const { return static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(block)); }
|
||||
|
||||
void Write(std::uint32_t node, std::uint32_t dword, std::uint32_t value) {
|
||||
std::memcpy(block + node * NodeBytes + dword * 4u, &value, sizeof(value));
|
||||
}
|
||||
|
||||
private:
|
||||
std::uint8_t* block = nullptr;
|
||||
};
|
||||
|
||||
void WriteBox32(GuestBlock& guest, std::uint32_t node, const std::array<std::uint32_t, 4>& children) {
|
||||
for (std::uint32_t child = 0; child < 4u; ++child) {
|
||||
guest.Write(node, child, children[child]);
|
||||
for (std::uint32_t index = 0; index < 6u; ++index) guest.Write(node, 4u + child * 6u + index, Bits(Boxes[child][index]));
|
||||
}
|
||||
}
|
||||
|
||||
void WriteBox16(GuestBlock& guest, std::uint32_t node, const std::array<std::uint32_t, 4>& children) {
|
||||
for (std::uint32_t child = 0; child < 4u; ++child) {
|
||||
guest.Write(node, child, children[child]);
|
||||
for (std::uint32_t index = 0; index < 6u; index += 2u) {
|
||||
const auto position = child * 6u + index;
|
||||
guest.Write(node, 4u + position / 2u, Half(Boxes[child][index]) | (Half(Boxes[child][index + 1u]) << 16u));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteTriangles(GuestBlock& guest, std::uint32_t node) {
|
||||
for (std::uint32_t index = 0; index < TriangleVertices.size(); ++index) guest.Write(node, index, Bits(TriangleVertices[index]));
|
||||
guest.Write(node, 15u, TriangleId);
|
||||
}
|
||||
|
||||
void FillNodes(GuestBlock& guest) {
|
||||
WriteTriangles(guest, 1u);
|
||||
WriteBox32(guest, 2u, Box32Children);
|
||||
WriteBox16(guest, 4u, Box16Children);
|
||||
WriteBox32(guest, 6u, PastEndChildren);
|
||||
WriteTriangles(guest, 7u);
|
||||
}
|
||||
|
||||
void FillInput(std::uint64_t nodeBias) {
|
||||
for (std::uint32_t tid = 0; tid < Threads; ++tid) {
|
||||
const auto& test = Cases[tid % Cases.size()];
|
||||
const auto& ray = test.ray;
|
||||
const std::uint64_t node64 = nodeBias + test.node;
|
||||
auto* words = &Input[tid * Record];
|
||||
words[0] = test.node;
|
||||
words[1] = static_cast<std::uint32_t>(node64);
|
||||
words[2] = static_cast<std::uint32_t>(node64 >> 32u);
|
||||
words[3] = Bits(ray.extent);
|
||||
for (std::uint32_t axis = 0; axis < 3u; ++axis) {
|
||||
words[4u + axis] = Bits(ray.origin[axis]);
|
||||
words[7u + axis] = Bits(ray.direction[axis]);
|
||||
words[10u + axis] = Bits(ray.inverse[axis]);
|
||||
}
|
||||
words[13] = Half(ray.direction[0]) | (Half(ray.direction[1]) << 16u);
|
||||
words[14] = Half(ray.direction[2]) | (Half(ray.inverse[0]) << 16u);
|
||||
words[15] = Half(ray.inverse[1]) | (Half(ray.inverse[2]) << 16u);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<std::uint32_t, 4> BufferDescriptor(const void* data, std::uint32_t bytes) {
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(data);
|
||||
return {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu), bytes, 0x01016facu};
|
||||
}
|
||||
|
||||
std::array<std::uint32_t, 4> BvhDescriptor(std::uint64_t base, std::uint64_t lastNode) {
|
||||
return {static_cast<std::uint32_t>(base >> 8u), static_cast<std::uint32_t>((base >> 40u) & 0xffu) | BoxSort, static_cast<std::uint32_t>(lastNode), static_cast<std::uint32_t>((lastNode >> 32u) & 0x3ffu)};
|
||||
}
|
||||
|
||||
void Run(AgcDriver::VulkanDevice& device, const GuestBlock& guest, bool addressInPointer) {
|
||||
const auto address = guest.Address();
|
||||
FillInput(addressInPointer ? address >> 3u : 0u);
|
||||
Output.fill(0xdeadbeefu);
|
||||
std::vector<std::uint32_t> userData(16, 0u);
|
||||
const auto input = BufferDescriptor(Input.data(), static_cast<std::uint32_t>(Input.size() * 4u));
|
||||
const auto output = BufferDescriptor(Output.data(), static_cast<std::uint32_t>(Output.size() * 4u));
|
||||
const auto narrow = BvhDescriptor(address, LastNode);
|
||||
const auto wide = addressInPointer ? BvhDescriptor(0u, address / NodeBytes + LastNode) : narrow;
|
||||
std::copy(input.begin(), input.end(), userData.begin());
|
||||
std::copy(output.begin(), output.end(), userData.begin() + 4);
|
||||
std::copy(narrow.begin(), narrow.end(), userData.begin() + 8);
|
||||
std::copy(wide.begin(), wide.end(), userData.begin() + 12);
|
||||
const std::span<const std::uint32_t> code(Code);
|
||||
const std::array<ShaderRecompiler::MemoryRegion, 1> memory{{{reinterpret_cast<std::uintptr_t>(code.data()), std::as_bytes(code)}}};
|
||||
const ShaderRecompiler::ShaderComputeStageInfo compute{{Threads, 1, 1}, 0u, {false, false, false}, false, 1};
|
||||
ShaderRecompiler::RecompileRequest request{
|
||||
{ShaderStage::Compute, reinterpret_cast<std::uintptr_t>(code.data()), code, 0, {}},
|
||||
{32, 0, userData, compute, std::nullopt, std::nullopt, memory},
|
||||
device.Target(),
|
||||
{0, 0, 0, 128}
|
||||
};
|
||||
request.useCache = false;
|
||||
const auto result = ShaderRecompiler::Recompile(request);
|
||||
device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast<std::uintptr_t>(code.data()));
|
||||
device.WaitIdle();
|
||||
}
|
||||
|
||||
void Check(bool addressInPointer) {
|
||||
const std::string mode = addressInPointer ? "address in the node pointer" : "base in the descriptor";
|
||||
for (std::uint32_t tid = 0; tid < Threads; ++tid) {
|
||||
const auto& expected = Cases[tid % Cases.size()].expected;
|
||||
for (std::uint32_t form = 0; form < Forms; ++form) {
|
||||
for (std::uint32_t dword = 0; dword < 4u; ++dword) {
|
||||
const auto actual = Output[tid * Record + form * 4u + dword];
|
||||
Require(actual == expected[dword], std::string(FormNames[form]) + ", " + mode + ": thread " + std::to_string(tid) + " dword " + std::to_string(dword) + " is " + std::to_string(actual) + ", expected " + std::to_string(expected[dword]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
try {
|
||||
if (ShaderRecompiler::RayTracingStrict() || ShaderRecompiler::RayTracingMiss()) {
|
||||
std::puts("skipped, APS5_RAYTRACING replaces the node test");
|
||||
return VulkanTestSkipped;
|
||||
}
|
||||
const auto device = OpenVulkanTestDevice();
|
||||
if (!device) return VulkanTestSkipped;
|
||||
GuestBlock guest;
|
||||
Require((guest.Address() >> 35u) != 0u, "bvh64 intersect ray: the guest block is not above 32 GiB, the node pointer would fit 32 bits");
|
||||
FillNodes(guest);
|
||||
for (const bool addressInPointer : {true, false}) {
|
||||
Run(*device, guest, addressInPointer);
|
||||
Check(addressInPointer);
|
||||
}
|
||||
std::puts("bvh64 intersect ray tests passed");
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << error.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,12 +6,20 @@
|
|||
|
||||
using namespace ShaderRecompiler;
|
||||
static void Require(bool value) { if (!value) throw std::runtime_error("ray tracing miss regression"); }
|
||||
int main() {
|
||||
const std::array<std::uint32_t, 2> code{0xF1981F01u, 0x00000000u};
|
||||
struct Form {
|
||||
std::uint32_t word0;
|
||||
std::uint32_t word1;
|
||||
RdnaOpcode opcode;
|
||||
std::uint32_t addressDwords;
|
||||
};
|
||||
static void Check(const Form& form) {
|
||||
const std::array<std::uint32_t, 2> code{form.word0, form.word1};
|
||||
const RdnaInstruction instruction = DecodeRdnaMimg(0u, code, 0u);
|
||||
Require(instruction.op == RdnaOpcode::ImageBvhIntersectRay);
|
||||
Require(instruction.op == form.opcode);
|
||||
Require(instruction.family == RdnaInstructionFamily::MIMG);
|
||||
Require(IsImageOpcode(instruction.op));
|
||||
Require(instruction.imageAddressComponents == form.addressDwords);
|
||||
Require(GetRdnaImageAddressDwordCount(instruction.imageSampleFlags, instruction.imageAddressComponents) == form.addressDwords);
|
||||
IrProgram program;
|
||||
auto& block = program.CreateBlock();
|
||||
program.SetEntryBlock(block);
|
||||
|
|
@ -26,8 +34,14 @@ int main() {
|
|||
Require(thrown);
|
||||
} else {
|
||||
context.TranslateInstruction(instruction);
|
||||
if (RayTracingMiss()) {
|
||||
for (auto* value : block.Instructions()) Require(value->Opcode() != IrOpcode::ImageBvhIntersectRay);
|
||||
}
|
||||
bool emitted = false;
|
||||
for (auto* value : block.Instructions()) emitted = emitted || value->Opcode() == IrOpcode::ImageBvhIntersectRay;
|
||||
Require(emitted != RayTracingMiss());
|
||||
}
|
||||
}
|
||||
int main() {
|
||||
Check({0xF1981F01u, 0x00000000u, RdnaOpcode::ImageBvhIntersectRay, 11u});
|
||||
Check({0xF1981F01u, 0x40000000u, RdnaOpcode::ImageBvhIntersectRay, 8u});
|
||||
Check({0xF19C1F01u, 0x00000000u, RdnaOpcode::ImageBvh64IntersectRay, 12u});
|
||||
Check({0xF19C1F01u, 0x40000000u, RdnaOpcode::ImageBvh64IntersectRay, 9u});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ std::uint32_t estimatedSpirvWords(const RdnaInstruction& instruction) {
|
|||
case RdnaOpcode::DsPermuteB32:
|
||||
return 1200u;
|
||||
case RdnaOpcode::ImageBvhIntersectRay:
|
||||
case RdnaOpcode::ImageBvh64IntersectRay:
|
||||
return 2000u;
|
||||
default:
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -892,6 +892,7 @@ enum class RdnaOpcode : std::uint16_t {
|
|||
ImageGather4LO,
|
||||
ImageGather4O,
|
||||
ImageBvhIntersectRay,
|
||||
ImageBvh64IntersectRay,
|
||||
VInterpP1F32,
|
||||
VInterpP2F32,
|
||||
VInterpMovF32,
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ constexpr ImageOpcodeInfo imageOpcodes[] = {
|
|||
{0x0eu, RdnaOpcode::ImageGetResinfo, nullptr, 0, false, false, false},
|
||||
{0x60u, RdnaOpcode::ImageGetLod, nullptr, 0, false, false, false},
|
||||
{0xe6u, RdnaOpcode::ImageBvhIntersectRay, "image_bvh_intersect_ray", 0, false, false, false},
|
||||
{0xe7u, RdnaOpcode::ImageBvh64IntersectRay, "image_bvh64_intersect_ray", 0, false, false, false},
|
||||
};
|
||||
|
||||
const ImageOpcodeInfo& lookupOpcode(std::uint32_t opcode) {
|
||||
|
|
@ -306,8 +307,9 @@ RdnaInstruction DecodeRdnaMimg(std::uint32_t programCounter, std::span<const std
|
|||
if (d16 && !(info.sample || info.gather || opcode == 0u || opcode == 1u || opcode == 8u || opcode == 9u)) {
|
||||
throw std::runtime_error("MIMG opcode does not support D16");
|
||||
}
|
||||
const bool rayQuery = info.opcode == RdnaOpcode::ImageBvhIntersectRay;
|
||||
std::uint32_t components = rayQuery ? (a16 ? 8u : 11u) : opcode == 0x0Eu ? 1u : coordinateCount(dimension);
|
||||
const bool rayQuery64 = info.opcode == RdnaOpcode::ImageBvh64IntersectRay;
|
||||
const bool rayQuery = rayQuery64 || info.opcode == RdnaOpcode::ImageBvhIntersectRay;
|
||||
std::uint32_t components = rayQuery ? (a16 ? 8u : 11u) + (rayQuery64 ? 1u : 0u) : opcode == 0x0Eu ? 1u : coordinateCount(dimension);
|
||||
if (opcode == 1u || opcode == 4u || opcode == 5u || opcode == 9u || opcode == 0x0bu) {
|
||||
++components;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -828,6 +828,7 @@ bool IsImageOpcode(RdnaOpcode opcode) {
|
|||
case RdnaOpcode::ImageGather4LO:
|
||||
case RdnaOpcode::ImageGather4O:
|
||||
case RdnaOpcode::ImageBvhIntersectRay:
|
||||
case RdnaOpcode::ImageBvh64IntersectRay:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1201,35 +1201,41 @@ void EmitImageBvhIntersectRay(SpirvValueEmitContext& ctx, const IrValue& inst) {
|
|||
const IrValue* address = ctx.ImageAddress(inst.Argument(1));
|
||||
const auto component = [&](std::uint32_t index) { return ctx.Arg(*address, index); };
|
||||
const bool a16 = (ctx.Memory(inst).imageSampleFlags & RdnaImageSampleFlagA16) != 0u;
|
||||
const bool node64 = ctx.Memory(inst).addressIsFull;
|
||||
const std::uint32_t ray = node64 ? 2u : 1u;
|
||||
|
||||
const auto node = component(0);
|
||||
const auto type = op(spv::OpBitwiseAnd, u32, node, uint(7u));
|
||||
const auto nodeIndex = op(spv::OpShiftRightLogical, u64, widen(node), BdaConstant(state, 3u));
|
||||
const auto nodeLow = component(0);
|
||||
const auto node = node64 ? pair(nodeLow, component(1)) : widen(nodeLow);
|
||||
const auto type = op(spv::OpBitwiseAnd, u32, nodeLow, uint(7u));
|
||||
const auto nodeIndex = op(spv::OpShiftRightLogical, u64, node, BdaConstant(state, 3u));
|
||||
const auto baseUnits = pair(word[0], op(spv::OpBitwiseAnd, u32, word[1], uint(0xffu)));
|
||||
const auto lastNode = pair(word[2], op(spv::OpBitwiseAnd, u32, word[3], uint(0x3ffu)));
|
||||
const auto boxGrow = op(spv::OpBitwiseAnd, u32, op(spv::OpShiftRightLogical, u32, word[1], uint(23u)), uint(0xffu));
|
||||
const auto boxSort = op(spv::OpINotEqual, boolean, op(spv::OpBitwiseAnd, u32, word[1], uint(0x80000000u)), uint(0u));
|
||||
const auto barycentrics = op(spv::OpINotEqual, boolean, op(spv::OpBitwiseAnd, u32, word[3], uint(1u << 24u)), uint(0u));
|
||||
const auto nodeAddress = op(spv::OpIAdd, u64, op(spv::OpShiftLeftLogical, u64, baseUnits, BdaConstant(state, 8u)), op(spv::OpShiftLeftLogical, u64, widen(op(spv::OpBitwiseAnd, u32, node, uint(~7u))), BdaConstant(state, 3u)));
|
||||
const auto present = op(spv::OpINotEqual, boolean, baseUnits, BdaConstant(state, 0u));
|
||||
const auto valid = both(present, op(spv::OpULessThanEqual, boolean, nodeIndex, lastNode));
|
||||
const auto nodeAddress = op(spv::OpIAdd, u64, op(spv::OpShiftLeftLogical, u64, baseUnits, BdaConstant(state, 8u)), op(spv::OpShiftLeftLogical, u64, op(spv::OpBitwiseAnd, u64, node, BdaConstant(state, ~std::uint64_t{7u})), BdaConstant(state, 3u)));
|
||||
const auto within = [&](spv::Op compare) {
|
||||
const auto inRange = op(compare, boolean, nodeIndex, lastNode);
|
||||
return node64 ? inRange : both(op(spv::OpINotEqual, boolean, baseUnits, BdaConstant(state, 0u)), inRange);
|
||||
};
|
||||
const auto valid = within(spv::OpULessThanEqual);
|
||||
const auto isTriangle = both(valid, op(spv::OpULessThanEqual, boolean, type, uint(1u)));
|
||||
const auto isBox16 = both(valid, op(spv::OpIEqual, boolean, type, uint(4u)));
|
||||
const auto isBox32 = both(both(present, op(spv::OpULessThan, boolean, nodeIndex, lastNode)), op(spv::OpIEqual, boolean, type, uint(5u)));
|
||||
const auto isBox32 = both(within(spv::OpULessThan), op(spv::OpIEqual, boolean, type, uint(5u)));
|
||||
|
||||
const auto extent = asFloat(component(1));
|
||||
const auto extent = asFloat(component(ray));
|
||||
std::array<std::uint32_t, 3> origin{};
|
||||
std::array<std::uint32_t, 3> direction{};
|
||||
std::array<std::uint32_t, 3> inverse{};
|
||||
for (std::uint32_t axis = 0; axis < 3u; ++axis) {
|
||||
origin[axis] = asFloat(component(2u + axis));
|
||||
origin[axis] = asFloat(component(ray + 1u + axis));
|
||||
if (!a16) {
|
||||
direction[axis] = asFloat(component(5u + axis));
|
||||
inverse[axis] = asFloat(component(8u + axis));
|
||||
direction[axis] = asFloat(component(ray + 4u + axis));
|
||||
inverse[axis] = asFloat(component(ray + 7u + axis));
|
||||
continue;
|
||||
}
|
||||
const auto half = [&](std::uint32_t index) {
|
||||
const auto packed = component(5u + index / 2u);
|
||||
const auto packed = component(ray + 4u + index / 2u);
|
||||
return EmitF16BitsToF32(state, index % 2u == 0u ? op(spv::OpBitwiseAnd, u32, packed, uint(0xffffu)) : op(spv::OpShiftRightLogical, u32, packed, uint(16u)));
|
||||
};
|
||||
direction[axis] = half(axis);
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ bool TranslationContext::imageBvhIntersectRay(const RdnaInstruction& inst) {
|
|||
memory.kind = ResourceKind::Global;
|
||||
memory.dataDwords = 4u;
|
||||
memory.imageSampleFlags = inst.imageA16 ? RdnaImageSampleFlagA16 : 0u;
|
||||
memory.addressIsFull = inst.op == RdnaOpcode::ImageBvh64IntersectRay;
|
||||
IrValue* descriptor = constructU32x4(inst.source1, 4u);
|
||||
IrValue* address = makeImageAddress(inst, inst.source0);
|
||||
IrValue& result = ir.Emit(IrOpcode::ImageBvhIntersectRay, IrOpcodeType(IrOpcode::ImageBvhIntersectRay), {descriptor, address, &ir.GetExec()}, addMemoryInfo(memory, inst.programCounter));
|
||||
|
|
|
|||
|
|
@ -432,6 +432,7 @@ bool TranslationContext::emitMemory(const RdnaInstruction& inst) {
|
|||
case RdnaOpcode::ImageGetLod:
|
||||
return imageGetLod(inst);
|
||||
case RdnaOpcode::ImageBvhIntersectRay:
|
||||
case RdnaOpcode::ImageBvh64IntersectRay:
|
||||
return imageBvhIntersectRay(inst);
|
||||
|
||||
default:
|
||||
|
|
|
|||
Loading…
Reference in New Issue