mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-06-23 22:58:33 +02:00
rti: Initial commit
RTI (ray tracing inspector) is a GUI for inspecting acceleration structures and ray tracing dispatches. There are some general classes for application lifetime, gpu resource managment and rendering. The visualization and BVH/node info needs to be implemented by driver/hardware specific backends (By overriding the virtual functions in rti_file_view). Reviewed-by: Natalie Vock <natalie.vock@gmx.de> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/41806>
This commit is contained in:
parent
8c0dc4f88f
commit
a86a30fff2
25 changed files with 5888 additions and 11 deletions
|
|
@ -298,7 +298,7 @@ fedora-release:
|
|||
-D glvnd=enabled
|
||||
-D platforms=x11,wayland
|
||||
EXTRA_OPTION: >
|
||||
-D tools=drm-shim,etnaviv,freedreno,glsl,intel,nir,nouveau,lima,panfrost,imagination
|
||||
-D tools=drm-shim,etnaviv,freedreno,glsl,intel,nir,nouveau,lima,panfrost,imagination,rti
|
||||
-D vulkan-layers=device-select,overlay
|
||||
-D intel-rt=enabled
|
||||
-D imagination-srv=true
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ DEPS=(
|
|||
libglfw3-dev
|
||||
"libllvm${LLVM_VERSION}"
|
||||
libpciaccess-dev
|
||||
libsdl3-dev
|
||||
libunwind-dev
|
||||
libva-dev
|
||||
libvulkan-dev
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ DEPS=(
|
|||
python3-ply
|
||||
python3-pycparser
|
||||
python3-yaml
|
||||
SDL3-devel
|
||||
spirv-tools-devel
|
||||
spirv-llvm-translator-devel
|
||||
vulkan-headers
|
||||
|
|
|
|||
|
|
@ -19,18 +19,18 @@ include:
|
|||
- .gitlab-ci/conditional-build-image-tags.yml
|
||||
|
||||
variables:
|
||||
DEBIAN_BUILD_BASE_TAG: "20260604-rust196"
|
||||
DEBIAN_BUILD_TAG: "20260604-bindgen"
|
||||
DEBIAN_BUILD_BASE_TAG: "20260621-sdl3"
|
||||
DEBIAN_BUILD_TAG: "20260621-sdl3"
|
||||
|
||||
DEBIAN_TEST_BASE_TAG: "20260604-rust196"
|
||||
DEBIAN_TEST_ANDROID_TAG: "20260620-angle-83"
|
||||
DEBIAN_TEST_GL_TAG: "20260620-angle-83"
|
||||
DEBIAN_TEST_VIDEO_TAG: "20260512-vkuprev"
|
||||
DEBIAN_TEST_VK_TAG: "20260604-vkd3d"
|
||||
DEBIAN_TEST_BASE_TAG: "20260621-sdl3"
|
||||
DEBIAN_TEST_ANDROID_TAG: "20260621-sdl3"
|
||||
DEBIAN_TEST_GL_TAG: "20260621-sdl3"
|
||||
DEBIAN_TEST_VIDEO_TAG: "20260621-sdl3"
|
||||
DEBIAN_TEST_VK_TAG: "20260621-sdl3"
|
||||
|
||||
ALPINE_X86_64_BUILD_TAG: "20260604-rust196"
|
||||
|
||||
FEDORA_X86_64_BUILD_TAG: "20260604-f44"
|
||||
FEDORA_X86_64_BUILD_TAG: "20260621-sdl3"
|
||||
|
||||
KERNEL_TAG: "v6.19-mesa-712d"
|
||||
KERNEL_REPO: "gfx-ci/linux"
|
||||
|
|
|
|||
|
|
@ -129,13 +129,14 @@ if with_tools.contains('all')
|
|||
'nir',
|
||||
'nouveau',
|
||||
'panfrost',
|
||||
'rti',
|
||||
'zink',
|
||||
]
|
||||
endif
|
||||
|
||||
with_any_vulkan_layers = get_option('vulkan-layers').length() != 0
|
||||
with_intel_tools = with_tools.contains('intel') or with_tools.contains('intel-ui')
|
||||
with_imgui = with_intel_tools or with_vulkan_overlay_layer
|
||||
with_imgui = with_intel_tools or with_vulkan_overlay_layer or with_tools.contains('rti')
|
||||
|
||||
dri_drivers_path = get_option('dri-drivers-path')
|
||||
if dri_drivers_path == ''
|
||||
|
|
|
|||
|
|
@ -552,7 +552,7 @@ option(
|
|||
value : [],
|
||||
choices : ['amd', 'asahi', 'dlclose-skip', 'drm-shim', 'etnaviv', 'freedreno',
|
||||
'glsl', 'imagination', 'intel', 'intel-ui', 'lima', 'nir', 'nouveau',
|
||||
'panfrost', 'zink', 'all'],
|
||||
'panfrost', 'rti', 'zink', 'all'],
|
||||
description : 'List of tools to build. (Note: `intel-ui` selects `intel`)',
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,3 +9,24 @@ libimgui_core_dep = declare_dependency(
|
|||
link_with : libimgui_core,
|
||||
include_directories : include_directories('.', is_system : true)
|
||||
)
|
||||
|
||||
if with_tools.contains('rti')
|
||||
libimgui_sdl3_vulkan = static_library(
|
||||
'imgui_sdl3_vulkan',
|
||||
files(
|
||||
'backends/imgui_impl_sdl3.cpp',
|
||||
'backends/imgui_impl_vulkan.cpp',
|
||||
'imgui.cpp',
|
||||
'imgui_draw.cpp',
|
||||
'imgui_tables.cpp',
|
||||
'imgui_widgets.cpp',
|
||||
),
|
||||
cpp_args : cpp.get_supported_arguments('-w', '-Wno-error'),
|
||||
install : false
|
||||
)
|
||||
|
||||
libimgui_sdl3_vulkan_dep = declare_dependency(
|
||||
link_with : libimgui_sdl3_vulkan,
|
||||
include_directories : include_directories('.', is_system : true)
|
||||
)
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -9,3 +9,7 @@ endif
|
|||
if with_tools.contains('dlclose-skip')
|
||||
subdir('dlclose-skip')
|
||||
endif
|
||||
|
||||
if with_tools.contains('rti')
|
||||
subdir('rtinspector')
|
||||
endif
|
||||
|
|
|
|||
6
src/tool/rtinspector/.clang-format
Normal file
6
src/tool/rtinspector/.clang-format
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
BasedOnStyle: InheritParentConfig
|
||||
DisableFormat: false
|
||||
|
||||
AccessModifierOffset: -3
|
||||
|
||||
ColumnLimit: 120
|
||||
153
src/tool/rtinspector/main.cpp
Normal file
153
src/tool/rtinspector/main.cpp
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright © 2026 Valve Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "rti_app.h"
|
||||
#include "rti_file_view.h"
|
||||
#include "rti_util.h"
|
||||
|
||||
#include "backends/imgui_impl_sdl3.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
rti_app app = rti_app();
|
||||
int ret = rti_app_init(&app);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (int i = 1; i < argc; i++)
|
||||
app.open_files.push_back(rti_create_file_view(&app, argv[i]));
|
||||
|
||||
VkResult result;
|
||||
bool needs_resize = true;
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
SDL_Event event;
|
||||
|
||||
while (SDL_PollEvent(&event)) {
|
||||
ImGui_ImplSDL3_ProcessEvent(&event);
|
||||
if (event.type == SDL_EVENT_QUIT)
|
||||
done = true;
|
||||
if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(app.window))
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (SDL_GetWindowFlags(app.window) & SDL_WINDOW_MINIMIZED) {
|
||||
SDL_Delay(10);
|
||||
continue;
|
||||
}
|
||||
|
||||
int width, height;
|
||||
SDL_GetWindowSize(app.window, &width, &height);
|
||||
if (app.imgui_window.Width != width || app.imgui_window.Height != height)
|
||||
needs_resize = true;
|
||||
|
||||
if (needs_resize) {
|
||||
rti_app_resize(&app, width, height);
|
||||
needs_resize = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
VkSemaphore image_acquired_semaphore =
|
||||
app.imgui_window.FrameSemaphores[app.imgui_window.SemaphoreIndex].ImageAcquiredSemaphore;
|
||||
VkSemaphore render_complete_semaphore =
|
||||
app.imgui_window.FrameSemaphores[app.imgui_window.SemaphoreIndex].RenderCompleteSemaphore;
|
||||
result = vkAcquireNextImageKHR(app.device, app.imgui_window.Swapchain, UINT64_MAX, image_acquired_semaphore,
|
||||
VK_NULL_HANDLE, &app.imgui_window.FrameIndex);
|
||||
if (result != VK_SUCCESS) {
|
||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
||||
needs_resize = true;
|
||||
continue;
|
||||
}
|
||||
rti_check_vk_result(result);
|
||||
}
|
||||
|
||||
ImGui_ImplVulkanH_Frame *fd = &app.imgui_window.Frames[app.imgui_window.FrameIndex];
|
||||
rti_check_vk_result(vkWaitForFences(app.device, 1, &fd->Fence, VK_TRUE, UINT64_MAX));
|
||||
rti_check_vk_result(vkResetFences(app.device, 1, &fd->Fence));
|
||||
|
||||
rti_check_vk_result(vkResetCommandPool(app.device, fd->CommandPool, 0));
|
||||
|
||||
VkCommandBufferBeginInfo command_buffer_begin_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
};
|
||||
rti_check_vk_result(vkBeginCommandBuffer(fd->CommandBuffer, &command_buffer_begin_info));
|
||||
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
app.command_buffer = fd->CommandBuffer;
|
||||
if (rti_app_run(&app))
|
||||
break;
|
||||
|
||||
ImGui::Render();
|
||||
|
||||
ImDrawData *draw_data = ImGui::GetDrawData();
|
||||
|
||||
VkRenderPassBeginInfo render_pass_begin_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.renderPass = app.imgui_window.RenderPass,
|
||||
.framebuffer = fd->Framebuffer,
|
||||
.renderArea =
|
||||
{
|
||||
.extent =
|
||||
{
|
||||
.width = (uint32_t)app.imgui_window.Width,
|
||||
.height = (uint32_t)app.imgui_window.Height,
|
||||
},
|
||||
},
|
||||
.clearValueCount = 1,
|
||||
.pClearValues = &app.imgui_window.ClearValue,
|
||||
};
|
||||
vkCmdBeginRenderPass(fd->CommandBuffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer);
|
||||
|
||||
vkCmdEndRenderPass(fd->CommandBuffer);
|
||||
|
||||
rti_check_vk_result(vkEndCommandBuffer(fd->CommandBuffer));
|
||||
|
||||
VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
VkSubmitInfo submit_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = &image_acquired_semaphore,
|
||||
.pWaitDstStageMask = &wait_stage,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &fd->CommandBuffer,
|
||||
.signalSemaphoreCount = 1,
|
||||
.pSignalSemaphores = &render_complete_semaphore,
|
||||
};
|
||||
rti_check_vk_result(vkQueueSubmit(app.queue, 1, &submit_info, fd->Fence));
|
||||
|
||||
VkPresentInfoKHR present_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = &render_complete_semaphore,
|
||||
.swapchainCount = 1,
|
||||
.pSwapchains = &app.imgui_window.Swapchain,
|
||||
.pImageIndices = &app.imgui_window.FrameIndex,
|
||||
};
|
||||
result = vkQueuePresentKHR(app.queue, &present_info);
|
||||
if (result != VK_SUCCESS) {
|
||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
||||
needs_resize = true;
|
||||
continue;
|
||||
}
|
||||
rti_check_vk_result(result);
|
||||
}
|
||||
app.imgui_window.SemaphoreIndex = (app.imgui_window.SemaphoreIndex + 1) % app.imgui_window.SemaphoreCount;
|
||||
}
|
||||
|
||||
rti_app_finish(&app);
|
||||
|
||||
return 0;
|
||||
}
|
||||
36
src/tool/rtinspector/meson.build
Normal file
36
src/tool/rtinspector/meson.build
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Copyright © 2026 Valve Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
rti_files = files(
|
||||
'radv/rti_file_view_radv_bvh4.cpp',
|
||||
'radv/rti_file_view_radv_bvh8.cpp',
|
||||
'radv/rti_file_view_radv.cpp',
|
||||
'main.cpp',
|
||||
'rti_app.cpp',
|
||||
'rti_file_view.cpp',
|
||||
'rti_util.cpp',
|
||||
)
|
||||
|
||||
subdir('shaders')
|
||||
|
||||
dep_sdl3 = dependency('sdl3')
|
||||
|
||||
dep_vulkan = dependency('vulkan')
|
||||
|
||||
executable(
|
||||
'rti',
|
||||
[rti_files, spv_shaders],
|
||||
cpp_args: [cpp_msvc_compat_args],
|
||||
gnu_symbol_visibility: 'hidden',
|
||||
link_with: [],
|
||||
include_directories: [
|
||||
inc_include,
|
||||
inc_src,
|
||||
inc_util,
|
||||
inc_radv_bvh,
|
||||
inc_vulkan_bvh,
|
||||
inc_radv_tools,
|
||||
],
|
||||
dependencies: [idep_mesautil, libimgui_sdl3_vulkan_dep, dep_sdl3, dep_vulkan],
|
||||
)
|
||||
850
src/tool/rtinspector/radv/rti_file_view_radv.cpp
Normal file
850
src/tool/rtinspector/radv/rti_file_view_radv.cpp
Normal file
|
|
@ -0,0 +1,850 @@
|
|||
/*
|
||||
* Copyright © 2026 Valve Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "rti_file_view_radv.h"
|
||||
#include "rti_file_view.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <imgui.h>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "imgui_internal.h"
|
||||
|
||||
#include "shaders/rti_shader_interface.h"
|
||||
#include "util/macros.h"
|
||||
#include "util/rti_format.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
#include "compiler/spirv/spirv.h"
|
||||
|
||||
#ifdef HAVE_TBB
|
||||
#include <oneapi/tbb/blocked_range.h>
|
||||
#include <oneapi/tbb/parallel_for.h>
|
||||
#endif
|
||||
|
||||
#include "bvh.h"
|
||||
#include "radv_rti.h"
|
||||
#include "rti_app.h"
|
||||
#include "rti_util.h"
|
||||
|
||||
std::unique_ptr<rti_acceleration_structure>
|
||||
rti_file_view_radv::create_acceleration_structure()
|
||||
{
|
||||
return std::make_unique<rti_acceleration_structure_radv>(this);
|
||||
}
|
||||
|
||||
float
|
||||
rti_file_view_radv::handle_mouse_click(rti_acceleration_structure *_acceleration_structure, rti_ray ray, bool select)
|
||||
{
|
||||
rti_acceleration_structure_radv *acceleration_structure = (rti_acceleration_structure_radv *)_acceleration_structure;
|
||||
|
||||
float tmax = INFINITY;
|
||||
|
||||
if (select)
|
||||
acceleration_structure->radv_ui.selected_node_id = RADV_BVH_INVALID_NODE;
|
||||
|
||||
std::unordered_set<uint32_t> path;
|
||||
if (trace_info->bvh8)
|
||||
bvh8_traverse(acceleration_structure, ray, RADV_BVH_ROOT_NODE, &tmax, path, select);
|
||||
else
|
||||
bvh4_traverse(acceleration_structure, ray, RADV_BVH_ROOT_NODE, &tmax, path, select);
|
||||
|
||||
return tmax;
|
||||
}
|
||||
|
||||
void
|
||||
rti_file_view_radv::load(FILE *file, const rti_header *header)
|
||||
{
|
||||
rti_file_view::load(file, header);
|
||||
|
||||
uint32_t vertex_count = 0;
|
||||
for (uint32_t i = 0; i < acceleration_structures.size(); i++) {
|
||||
rti_acceleration_structure_radv *acceleration_structure =
|
||||
(rti_acceleration_structure_radv *)acceleration_structures[i].get();
|
||||
|
||||
uint32_t primitive_vertex_count = acceleration_structure->header.geometry_type == VK_GEOMETRY_TYPE_TRIANGLES_KHR
|
||||
? 3
|
||||
: RTI_FILLED_CUBE_VERTEX_COUNT;
|
||||
|
||||
if (acceleration_structure->header.geometry_type != VK_GEOMETRY_TYPE_INSTANCES_KHR) {
|
||||
acceleration_structure->first_vertex = vertex_count;
|
||||
acceleration_structure->wireframe_first_vertex = vertex_count;
|
||||
vertex_count += primitive_vertex_count * acceleration_structure->primitive_count;
|
||||
|
||||
if (acceleration_structure->header.geometry_type == VK_GEOMETRY_TYPE_AABBS_KHR) {
|
||||
acceleration_structure->wireframe_first_vertex = vertex_count;
|
||||
vertex_count += RTI_CUBE_VERTEX_COUNT * acceleration_structure->primitive_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rti_vertex *vertices = nullptr;
|
||||
std::shared_ptr<rti_backed_buffer> vertex_buffer = nullptr;
|
||||
if (vertex_count) {
|
||||
vertex_buffer =
|
||||
rti_create_backed_buffer(app, vertex_count * sizeof(rti_vertex), rti_memory_type_device_local,
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, false);
|
||||
vertices = (rti_vertex *)app->upload_memory(vertex_buffer);
|
||||
}
|
||||
|
||||
#ifdef HAVE_TBB
|
||||
tbb::parallel_for(tbb::blocked_range<uint32_t>(0, acceleration_structures.size()),
|
||||
[this, vertices, vertex_buffer](const tbb::blocked_range<uint32_t> &range) {
|
||||
for (uint32_t i = range.begin(); i < range.end(); i++) {
|
||||
#else
|
||||
for (uint32_t i = 0; i < acceleration_structures.size(); i++) {
|
||||
#endif
|
||||
rti_acceleration_structure_radv *acceleration_structure =
|
||||
(rti_acceleration_structure_radv *)acceleration_structures[i].get();
|
||||
|
||||
acceleration_structure->bvh = (const uint8_t *)acceleration_structure->data;
|
||||
|
||||
if (trace_info->bvh8)
|
||||
acceleration_structure->aabb = bvh8_scene_aabb(acceleration_structure);
|
||||
else
|
||||
acceleration_structure->aabb = bvh4_scene_aabb(acceleration_structure);
|
||||
|
||||
acceleration_structure->init_camera();
|
||||
|
||||
if (acceleration_structure->header.geometry_type != VK_GEOMETRY_TYPE_INSTANCES_KHR &&
|
||||
acceleration_structure->primitive_count) {
|
||||
acceleration_structure->ui.vertex_buffer = vertex_buffer;
|
||||
|
||||
if (trace_info->bvh8) {
|
||||
bvh8_get_vertices(acceleration_structure, RADV_BVH_ROOT_NODE,
|
||||
vertices + acceleration_structure->first_vertex,
|
||||
vertices + acceleration_structure->wireframe_first_vertex);
|
||||
} else {
|
||||
bvh4_get_vertices(acceleration_structure, RADV_BVH_ROOT_NODE,
|
||||
vertices + acceleration_structure->first_vertex,
|
||||
vertices + acceleration_structure->wireframe_first_vertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_TBB
|
||||
});
|
||||
#endif
|
||||
|
||||
app->flush_upload_memory();
|
||||
|
||||
bool is_first_tlas = true;
|
||||
for (uint32_t i = 0; i < acceleration_structures.size(); i++) {
|
||||
rti_acceleration_structure_radv *acceleration_structure =
|
||||
(rti_acceleration_structure_radv *)acceleration_structures[i].get();
|
||||
|
||||
if (acceleration_structure->header.geometry_type != VK_GEOMETRY_TYPE_INSTANCES_KHR)
|
||||
continue;
|
||||
|
||||
if (is_first_tlas) {
|
||||
acceleration_structure->ui.opened = true;
|
||||
acceleration_structure->ui.request_focus = true;
|
||||
is_first_tlas = false;
|
||||
}
|
||||
|
||||
acceleration_structure->ui.instance_render_list = std::make_shared<rti_render_list>(app);
|
||||
|
||||
if (trace_info->bvh8)
|
||||
bvh8_get_instances(acceleration_structure, RADV_BVH_ROOT_NODE);
|
||||
|
||||
acceleration_structure->ui.instance_render_list->build();
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < dispatch_count; i++) {
|
||||
auto dispatch_view = std::make_shared<rti_dispatch_view>();
|
||||
|
||||
dispatch_view->ui.settings.width = dispatch_infos[i].dimensions[0];
|
||||
dispatch_view->ui.settings.height = dispatch_infos[i].dimensions[1];
|
||||
dispatch_view->ui.settings.depth = dispatch_infos[i].dimensions[2];
|
||||
dispatch_view->info = dispatch_infos + i;
|
||||
dispatch_view->invocation_count =
|
||||
dispatch_infos[i].dimensions[0] * dispatch_infos[i].dimensions[1] * dispatch_infos[i].dimensions[2];
|
||||
|
||||
dispatch_view->invocations.resize(dispatch_view->invocation_count);
|
||||
|
||||
/* Stupid but fast enough */
|
||||
for (uint32_t j = 0; j < dispatch_view->invocation_count; j++) {
|
||||
if (dispatch_view->invocation_count % (j + 1))
|
||||
continue;
|
||||
|
||||
std::map<uint32_t, uint32_t> sizes;
|
||||
|
||||
uint32_t div = dispatch_view->invocation_count / (j + 1);
|
||||
for (uint32_t k = 0; k < div; k++) {
|
||||
if ((div % (k + 1)) == 0)
|
||||
sizes[k + 1] = div / (k + 1);
|
||||
}
|
||||
|
||||
dispatch_view->valid_dispatch_sizes[j + 1] = sizes;
|
||||
}
|
||||
|
||||
dispatch_views.push_back(dispatch_view);
|
||||
}
|
||||
|
||||
uint64_t data_size =
|
||||
ray_history_data_size - sizeof(radv_rti_ray_history_header) - sizeof(radv_rti_dispatch_info) * dispatch_count;
|
||||
for (uint32_t offset = 0; offset < data_size;) {
|
||||
const radv_packed_token_header *header = (const radv_packed_token_header *)(history_data + offset);
|
||||
if (header->token_type == radv_packed_token_trace_ray) {
|
||||
const radv_packed_trace_ray_token *token = (const radv_packed_trace_ray_token *)(history_data + offset);
|
||||
dispatch_views[token->dispatch_index]->invocations[token->header.launch_index].offsets.push_back(offset);
|
||||
offset += sizeof(radv_packed_trace_ray_token);
|
||||
} else if (header->token_type == radv_packed_token_iteration) {
|
||||
const radv_packed_iteration_token *token = (const radv_packed_iteration_token *)(history_data + offset);
|
||||
dispatch_views[token->dispatch_index]->invocations[token->header.launch_index].offsets.push_back(offset);
|
||||
|
||||
rti_invocation_data *invocation =
|
||||
&dispatch_views[token->dispatch_index]->invocations[token->header.launch_index];
|
||||
|
||||
uint32_t node_type = token->node_id & 0xf;
|
||||
if (node_type == radv_bvh_node_box32)
|
||||
invocation->box_iteration_count++;
|
||||
else if (node_type == radv_bvh_node_instance)
|
||||
invocation->instance_iteration_count++;
|
||||
else
|
||||
invocation->primitive_iteration_count++;
|
||||
|
||||
offset += sizeof(radv_packed_iteration_token);
|
||||
} else if (header->token_type == radv_packed_token_accel_struct) {
|
||||
const radv_packed_accel_struct_token *token = (const radv_packed_accel_struct_token *)(history_data + offset);
|
||||
dispatch_views[token->dispatch_index]->invocations[token->header.launch_index].offsets.push_back(offset);
|
||||
offset += sizeof(radv_packed_accel_struct_token);
|
||||
} else if (header->token_type == radv_packed_token_trace_ray_hit) {
|
||||
offset += sizeof(radv_packed_trace_ray_end_token);
|
||||
} else if (header->token_type == radv_packed_token_trace_ray_miss) {
|
||||
offset += offsetof(radv_packed_trace_ray_end_token, primitive_id);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < dispatch_count; i++) {
|
||||
rti_dispatch_view *dispatch_view = dispatch_views[i].get();
|
||||
for (uint32_t j = 0; j < dispatch_view->invocation_count; j++) {
|
||||
rti_invocation_data *invocation = &dispatch_views[i]->invocations[j];
|
||||
uint32_t iteration_count = invocation->box_iteration_count + invocation->instance_iteration_count +
|
||||
invocation->primitive_iteration_count;
|
||||
dispatch_view->max_iteration_count = MAX2(dispatch_view->max_iteration_count, iteration_count);
|
||||
}
|
||||
dispatch_view->ui.settings.max_iteration_count = dispatch_view->max_iteration_count;
|
||||
}
|
||||
|
||||
app->finish_upload_memory();
|
||||
}
|
||||
|
||||
static const char *node_type_names[16] = {
|
||||
"triangle0", "triangle1", "triangle2", "triangle3", "invalid4", "box", "instance", "invalid7",
|
||||
"triangle4", "triangle5", "triangle6", "triangle7", "invalid12", "invalid13", "invalid14", "invalid15",
|
||||
};
|
||||
|
||||
void
|
||||
rti_file_view_radv::load_driver_specific(FILE *file, const rti_chunk_header *chunk_header, const rti_header *header)
|
||||
{
|
||||
if (chunk_header->type == rti_chunk_type_trace_info_radv) {
|
||||
assert(chunk_header->size == sizeof(radv_rti_trace_info));
|
||||
trace_info = (radv_rti_trace_info *)malloc(sizeof(radv_rti_trace_info));
|
||||
fread(trace_info, sizeof(radv_rti_trace_info), 1, file);
|
||||
return;
|
||||
}
|
||||
|
||||
ray_history_data = malloc(chunk_header->size);
|
||||
fread(ray_history_data, chunk_header->size, 1, file);
|
||||
ray_history_data_size = chunk_header->size;
|
||||
|
||||
history_header = (const radv_rti_ray_history_header *)ray_history_data;
|
||||
dispatch_infos = (const radv_rti_dispatch_info *)(history_header + 1);
|
||||
history_data = (const uint8_t *)(dispatch_infos + history_header->dispatch_count);
|
||||
dispatch_count = history_header->dispatch_count;
|
||||
}
|
||||
|
||||
void
|
||||
rti_file_view_radv::dock_driver_specific(uint32_t left_top, uint32_t left_bottom, uint32_t center, uint32_t right_top,
|
||||
uint32_t right_bottom)
|
||||
{
|
||||
ImGui::DockBuilderDockWindow("dispatches", right_top);
|
||||
|
||||
for (uint32_t i = 0; i < dispatch_count; i++) {
|
||||
char tmp[32];
|
||||
sprintf(tmp, "dispatch[%u]", i);
|
||||
ImGui::DockBuilderDockWindow(tmp, center);
|
||||
}
|
||||
|
||||
ImGui::DockBuilderDockWindow("dispatch", right_bottom);
|
||||
ImGui::DockBuilderDockWindow("ray history", left_top);
|
||||
ImGui::DockBuilderDockWindow("invocation", left_bottom);
|
||||
}
|
||||
|
||||
rti_invocation_data *
|
||||
rti_file_view_radv::get_selected_invocation()
|
||||
{
|
||||
|
||||
if (selected_dispatch && selected_dispatch->ui.selection_x != UINT32_MAX) {
|
||||
uint32_t selection_index = selected_dispatch->ui.selection_x +
|
||||
selected_dispatch->ui.selection_y * selected_dispatch->ui.settings.width +
|
||||
selected_dispatch->ui.settings.z * selected_dispatch->ui.settings.width *
|
||||
selected_dispatch->ui.settings.height;
|
||||
return &selected_dispatch->invocations[selection_index];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
rti_file_view_radv::run()
|
||||
{
|
||||
begin();
|
||||
|
||||
rti_invocation_data *selected_invocation = get_selected_invocation();
|
||||
|
||||
for (auto &_acceleration_structure : acceleration_structures) {
|
||||
rti_acceleration_structure_radv *acceleration_structure =
|
||||
(rti_acceleration_structure_radv *)_acceleration_structure.get();
|
||||
|
||||
if (begin_viewport(acceleration_structure)) {
|
||||
uint32_t primitive_vertex_count =
|
||||
acceleration_structure->header.geometry_type == VK_GEOMETRY_TYPE_TRIANGLES_KHR
|
||||
? 3
|
||||
: RTI_FILLED_CUBE_VERTEX_COUNT;
|
||||
|
||||
if (acceleration_structure->ui.vertex_buffer) {
|
||||
rti_render_task solid_task;
|
||||
solid_task.vertex_buffer = acceleration_structure->ui.vertex_buffer.get();
|
||||
solid_task.first_vertex = acceleration_structure->first_vertex;
|
||||
solid_task.vertex_count = acceleration_structure->primitive_count * primitive_vertex_count;
|
||||
solid_task.flags =
|
||||
rti_visualization_color_to_renderer_color(acceleration_structure->ui.visualization_color);
|
||||
render(solid_task);
|
||||
|
||||
if (acceleration_structure->wireframe_first_vertex != acceleration_structure->first_vertex) {
|
||||
rti_render_task wireframe_task;
|
||||
wireframe_task.type = rti_render_task_type_lines;
|
||||
wireframe_task.vertex_buffer = acceleration_structure->ui.vertex_buffer.get();
|
||||
wireframe_task.first_vertex = acceleration_structure->wireframe_first_vertex;
|
||||
wireframe_task.vertex_count = acceleration_structure->primitive_count * RTI_CUBE_VERTEX_COUNT;
|
||||
wireframe_task.flags = RTI_RENDERER_COLOR_PUSH_CONSTANT;
|
||||
wireframe_task.wait_for_prev = true;
|
||||
render(wireframe_task);
|
||||
} else {
|
||||
rti_render_task wireframe_task = solid_task;
|
||||
wireframe_task.type = rti_render_task_type_wireframe;
|
||||
wireframe_task.flags = RTI_RENDERER_COLOR_PUSH_CONSTANT;
|
||||
wireframe_task.wait_for_prev = true;
|
||||
render(wireframe_task);
|
||||
}
|
||||
} else {
|
||||
rti_render_task tlas_task;
|
||||
tlas_task.type = rti_render_task_type_render_list;
|
||||
tlas_task.render_list = acceleration_structure->ui.instance_render_list.get();
|
||||
tlas_task.flags = rti_visualization_color_to_renderer_color(acceleration_structure->ui.visualization_color);
|
||||
tlas_task.override_flags = true;
|
||||
render(tlas_task);
|
||||
}
|
||||
|
||||
rti_render_task scene_aabb_task;
|
||||
scene_aabb_task.flags = RTI_RENDERER_COLOR_PUSH_CONSTANT;
|
||||
render_aabb(scene_aabb_task, acceleration_structure->aabb);
|
||||
|
||||
if (acceleration_structure->radv_ui.selected_node_id != RADV_BVH_INVALID_NODE) {
|
||||
if (trace_info->bvh8)
|
||||
bvh8_render_selection(acceleration_structure);
|
||||
else
|
||||
bvh4_render_selection(acceleration_structure);
|
||||
}
|
||||
|
||||
render_viewport();
|
||||
|
||||
if (selected_invocation && !selected_invocation->offsets.empty()) {
|
||||
const radv_packed_trace_ray_token *trace_ray =
|
||||
(const radv_packed_trace_ray_token *)(history_data +
|
||||
selected_invocation->offsets[selected_invocation->selected_ray]);
|
||||
|
||||
uint64_t accel_struct_addr = ((uint64_t)trace_ray->accel_struct_hi << 32) | trace_ray->accel_struct_lo;
|
||||
rti_acceleration_structure_radv *trace_accel_struct =
|
||||
(rti_acceleration_structure_radv *)addr_to_acceleration_structure(accel_struct_addr);
|
||||
|
||||
bool draw_ray = acceleration_structure == trace_accel_struct;
|
||||
rti_mat4 transform;
|
||||
|
||||
for (uint32_t i = selected_invocation->selected_ray + 1; i < selected_invocation->offsets.size(); i++) {
|
||||
const radv_packed_token_header *header =
|
||||
(const radv_packed_token_header *)(history_data + selected_invocation->offsets[i]);
|
||||
if (header->token_type == radv_packed_token_trace_ray)
|
||||
break;
|
||||
|
||||
if (header->token_type == radv_packed_token_iteration) {
|
||||
const radv_packed_iteration_token *token = (const radv_packed_iteration_token *)header;
|
||||
|
||||
uint32_t type = token->node_id & 0xf;
|
||||
|
||||
if (type == radv_bvh_node_instance) {
|
||||
rti_mat4 instance_transform;
|
||||
rti_acceleration_structure_radv *blas = nullptr;
|
||||
if (trace_info->bvh8)
|
||||
bvh8_get_instance_info(trace_accel_struct, token->node_id, &instance_transform, &blas);
|
||||
else
|
||||
bvh4_get_instance_info(trace_accel_struct, token->node_id, &instance_transform, &blas);
|
||||
if (blas == acceleration_structure) {
|
||||
draw_ray = true;
|
||||
transform = instance_transform;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (draw_ray) {
|
||||
rti_ray ray = {
|
||||
.origin = {trace_ray->origin[0], trace_ray->origin[1], trace_ray->origin[2]},
|
||||
.direction = {trace_ray->direction[0], trace_ray->direction[1], trace_ray->direction[2]},
|
||||
};
|
||||
ray = rti_ray::transform(ray, transform);
|
||||
draw_point(ray.origin, app->selection_color, 4);
|
||||
draw_line(ray.origin,
|
||||
{trace_ray->origin[0] + trace_ray->direction[0] * trace_ray->tmax,
|
||||
trace_ray->origin[1] + trace_ray->direction[1] * trace_ray->tmax,
|
||||
trace_ray->origin[2] + trace_ray->direction[2] * trace_ray->tmax},
|
||||
app->selection_color, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
end_viewport();
|
||||
}
|
||||
|
||||
rti_acceleration_structure_radv *acceleration_structure =
|
||||
(rti_acceleration_structure_radv *)ui.focused_acceleration_structure;
|
||||
|
||||
begin_bvh_tree();
|
||||
if (acceleration_structure) {
|
||||
|
||||
if (ImGui::BeginTable("BVH", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("node type");
|
||||
ImGui::TableSetupColumn("offset");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
if (trace_info->bvh8)
|
||||
bvh8_draw(acceleration_structure, RADV_BVH_ROOT_NODE);
|
||||
else
|
||||
bvh4_draw(acceleration_structure, RADV_BVH_ROOT_NODE);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
acceleration_structure->radv_ui.open_path.clear();
|
||||
}
|
||||
end_bvh_tree();
|
||||
|
||||
begin_node_info();
|
||||
|
||||
if (acceleration_structure && acceleration_structure->radv_ui.selected_node_id != RADV_BVH_INVALID_NODE) {
|
||||
if (trace_info->bvh8)
|
||||
bvh8_draw_node_info(acceleration_structure, acceleration_structure->radv_ui.selected_node_id);
|
||||
else
|
||||
bvh4_draw_node_info(acceleration_structure, acceleration_structure->radv_ui.selected_node_id);
|
||||
}
|
||||
|
||||
end_node_info();
|
||||
|
||||
ImGui::Begin("dispatches", nullptr, ImGuiWindowFlags_NoFocusOnAppearing);
|
||||
|
||||
if (ImGui::BeginTable("dispatches", 5, ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable)) {
|
||||
ImGui::TableSetupColumn("index");
|
||||
ImGui::TableSetupColumn("type");
|
||||
ImGui::TableSetupColumn("width");
|
||||
ImGui::TableSetupColumn("height");
|
||||
ImGui::TableSetupColumn("depth");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (uint32_t i = 0; i < dispatch_count; i++) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
char tmp[32];
|
||||
sprintf(tmp, "%u", i);
|
||||
if (ImGui::Selectable(tmp, false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SelectOnClick)) {
|
||||
if (dispatch_views[i]->ui.open)
|
||||
dispatch_views[i]->ui.request_focus = true;
|
||||
dispatch_views[i]->ui.open = true;
|
||||
}
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
switch (dispatch_infos[i].type) {
|
||||
case radv_rti_dispatch_type_trace_rays:
|
||||
ImGui::Text("vkCmdTraceRays");
|
||||
break;
|
||||
case radv_rti_dispatch_type_trace_rays_indirect:
|
||||
ImGui::Text("vkCmdTraceRaysIndirect");
|
||||
break;
|
||||
case radv_rti_dispatch_type_trace_rays_indirect2:
|
||||
ImGui::Text("vkCmdTraceRaysIndirect2");
|
||||
break;
|
||||
}
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImGui::Text("%u", dispatch_infos[i].dimensions[0]);
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
ImGui::Text("%u", dispatch_infos[i].dimensions[1]);
|
||||
ImGui::TableSetColumnIndex(4);
|
||||
ImGui::Text("%u", dispatch_infos[i].dimensions[2]);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("dispatch", nullptr, ImGuiWindowFlags_NoFocusOnAppearing);
|
||||
|
||||
if (selected_dispatch) {
|
||||
ImGui::SeparatorText("reshape");
|
||||
|
||||
if (ImGui::Button("reset")) {
|
||||
selected_dispatch->ui.settings.width = selected_dispatch->info->dimensions[0];
|
||||
selected_dispatch->ui.settings.height = selected_dispatch->info->dimensions[1];
|
||||
selected_dispatch->ui.settings.depth = selected_dispatch->info->dimensions[2];
|
||||
selected_dispatch->ui.selection_x = UINT32_MAX;
|
||||
selected_dispatch->ui.selection_y = UINT32_MAX;
|
||||
selected_dispatch->ui.initialize_viewport = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
char tmp[32];
|
||||
sprintf(tmp, "%ux%ux%u", selected_dispatch->ui.settings.width, selected_dispatch->ui.settings.height,
|
||||
selected_dispatch->ui.settings.depth);
|
||||
if (ImGui::BeginMenu(tmp)) {
|
||||
for (const auto &valid_size : selected_dispatch->valid_dispatch_sizes) {
|
||||
sprintf(tmp, "%ux", valid_size.first);
|
||||
if (ImGui::BeginMenu(tmp)) {
|
||||
for (const auto &valid_size2 : valid_size.second) {
|
||||
sprintf(tmp, "%ux%ux%u", valid_size.first, valid_size2.first, valid_size2.second);
|
||||
if (ImGui::MenuItem(tmp)) {
|
||||
selected_dispatch->ui.settings.width = valid_size.first;
|
||||
selected_dispatch->ui.settings.height = valid_size2.first;
|
||||
selected_dispatch->ui.settings.depth = valid_size2.second;
|
||||
selected_dispatch->ui.selection_x = UINT32_MAX;
|
||||
selected_dispatch->ui.selection_y = UINT32_MAX;
|
||||
selected_dispatch->ui.initialize_viewport = true;
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("slice");
|
||||
|
||||
int z = selected_dispatch->ui.settings.z;
|
||||
ImGui::InputInt("z", &z, 1);
|
||||
selected_dispatch->ui.settings.z = MIN2((uint32_t)z, selected_dispatch->ui.settings.depth - 1);
|
||||
|
||||
int max_iteration_count = selected_dispatch->ui.settings.max_iteration_count;
|
||||
ImGui::SliderInt("max iteration count", &max_iteration_count, 0, selected_dispatch->max_iteration_count);
|
||||
selected_dispatch->ui.settings.max_iteration_count = max_iteration_count;
|
||||
|
||||
ImGui::Checkbox("box_iteration_count", &selected_dispatch->ui.settings.show_box_iteration_count);
|
||||
ImGui::Checkbox("instance_iteration_count", &selected_dispatch->ui.settings.show_instance_iteration_count);
|
||||
ImGui::Checkbox("primitive_iteration_count", &selected_dispatch->ui.settings.show_primitive_iteration_count);
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
for (uint32_t i = 0; i < dispatch_count; i++) {
|
||||
rti_dispatch_view *dispatch_view = dispatch_views[i].get();
|
||||
if (!dispatch_view->ui.open)
|
||||
continue;
|
||||
|
||||
char tmp[32];
|
||||
sprintf(tmp, "dispatch[%u]", i);
|
||||
if (!ImGui::Begin(tmp, nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
|
||||
ImGui::End();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dispatch_view->ui.request_focus) {
|
||||
ImGui::SetWindowFocus();
|
||||
dispatch_view->ui.request_focus = false;
|
||||
}
|
||||
|
||||
if (ImGui::IsWindowFocused())
|
||||
selected_dispatch = dispatch_view;
|
||||
|
||||
if (memcmp(&dispatch_view->ui.settings, &dispatch_view->ui.prev_settings, sizeof(dispatch_view->ui.settings))) {
|
||||
std::shared_ptr<ImTextureData> texture_data = std::make_shared<ImTextureData>();
|
||||
texture_data->Create(ImTextureFormat_RGBA32, dispatch_view->ui.settings.width,
|
||||
dispatch_view->ui.settings.height);
|
||||
|
||||
uint32_t base_index =
|
||||
dispatch_view->ui.settings.z * dispatch_view->ui.settings.width * dispatch_view->ui.settings.height;
|
||||
|
||||
for (uint32_t y = 0; y < dispatch_view->ui.settings.height; y++) {
|
||||
for (uint32_t x = 0; x < dispatch_view->ui.settings.width; x++) {
|
||||
rti_invocation_data *invocation =
|
||||
&dispatch_view->invocations[x + y * dispatch_view->ui.settings.width + base_index];
|
||||
uint32_t iteration_count = 0;
|
||||
if (dispatch_view->ui.settings.show_box_iteration_count)
|
||||
iteration_count += invocation->box_iteration_count;
|
||||
if (dispatch_view->ui.settings.show_instance_iteration_count)
|
||||
iteration_count += invocation->instance_iteration_count;
|
||||
if (dispatch_view->ui.settings.show_primitive_iteration_count)
|
||||
iteration_count += invocation->primitive_iteration_count;
|
||||
|
||||
float brightness =
|
||||
fmin((float)iteration_count / (float)dispatch_view->ui.settings.max_iteration_count, 1.0);
|
||||
ImVec4 color = {brightness, brightness, brightness, 1};
|
||||
|
||||
ImU32 *pixel = (ImU32 *)texture_data->GetPixelsAt(x, y);
|
||||
*pixel = ImGui::ColorConvertFloat4ToU32(color);
|
||||
}
|
||||
}
|
||||
ImGui::RegisterUserTexture(texture_data.get());
|
||||
dispatch_view->ui.texture_data.push_back(texture_data);
|
||||
|
||||
dispatch_view->ui.prev_settings = dispatch_view->ui.settings;
|
||||
}
|
||||
|
||||
for (auto it = dispatch_view->ui.destroy_texture_data.begin();
|
||||
it != dispatch_view->ui.destroy_texture_data.end();) {
|
||||
(*it)->UnusedFrames++;
|
||||
if ((*it)->Status == ImTextureStatus_Destroyed) {
|
||||
ImGui::UnregisterUserTexture(it->get());
|
||||
it = dispatch_view->ui.destroy_texture_data.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
while (dispatch_view->ui.texture_data.size() > 1) {
|
||||
if (dispatch_view->ui.texture_data[1]->Status != ImTextureStatus_OK)
|
||||
break;
|
||||
|
||||
if (dispatch_view->ui.texture_data[0]->Status != ImTextureStatus_OK)
|
||||
break;
|
||||
|
||||
dispatch_view->ui.texture_data[0]->Status = ImTextureStatus_WantDestroy;
|
||||
dispatch_view->ui.texture_data[0]->DestroyPixels();
|
||||
dispatch_view->ui.destroy_texture_data.push_back(dispatch_view->ui.texture_data[0]);
|
||||
dispatch_view->ui.texture_data.erase(dispatch_view->ui.texture_data.begin());
|
||||
}
|
||||
|
||||
ImVec2 viewport_offset = ImGui::GetCursorScreenPos();
|
||||
ImVec2 viewport_size = ImGui::GetContentRegionAvail();
|
||||
ImVec2 image_size = ImVec2(dispatch_view->ui.settings.width, dispatch_view->ui.settings.height);
|
||||
|
||||
if (dispatch_view->ui.initialize_viewport) {
|
||||
dispatch_view->ui.initialize_viewport = false;
|
||||
|
||||
dispatch_view->ui.image_scale = fmin(viewport_size.x / image_size.x, viewport_size.y / image_size.y);
|
||||
dispatch_view->ui.viewport_center.x = image_size.x / 2;
|
||||
dispatch_view->ui.viewport_center.y = image_size.y / 2;
|
||||
}
|
||||
|
||||
ImVec2 mouse_position = ImGui::GetMousePos();
|
||||
bool mouse_inside_viewport = mouse_position.x >= viewport_offset.x && mouse_position.y >= viewport_offset.y &&
|
||||
mouse_position.x <= viewport_offset.x + viewport_size.x &&
|
||||
mouse_position.y <= viewport_offset.y + viewport_size.y;
|
||||
|
||||
if (ImGui::IsWindowFocused()) {
|
||||
bool right_mouse_button_pressed = ImGui::IsMouseDown(ImGuiMouseButton_Right);
|
||||
|
||||
/* Detect thet start/end of dragging inside the viewport. */
|
||||
if (!dispatch_view->ui.is_dragging_viewport && right_mouse_button_pressed && mouse_inside_viewport) {
|
||||
dispatch_view->ui.prev_mouse_pos = mouse_position;
|
||||
dispatch_view->ui.is_dragging_viewport = true;
|
||||
}
|
||||
if (dispatch_view->ui.is_dragging_viewport && !right_mouse_button_pressed) {
|
||||
dispatch_view->ui.is_dragging_viewport = false;
|
||||
}
|
||||
|
||||
if (dispatch_view->ui.is_dragging_viewport) {
|
||||
float drag_delta_x = mouse_position.x - dispatch_view->ui.prev_mouse_pos.x;
|
||||
float drag_delta_y = mouse_position.y - dispatch_view->ui.prev_mouse_pos.y;
|
||||
dispatch_view->ui.viewport_center.x -= drag_delta_x / dispatch_view->ui.image_scale;
|
||||
dispatch_view->ui.viewport_center.y -= drag_delta_y / dispatch_view->ui.image_scale;
|
||||
}
|
||||
|
||||
dispatch_view->ui.prev_mouse_pos = mouse_position;
|
||||
|
||||
if (mouse_inside_viewport) {
|
||||
float mouse_wheel = ImGui::GetIO().MouseWheel;
|
||||
dispatch_view->ui.image_scale *= pow(1.1, mouse_wheel);
|
||||
}
|
||||
|
||||
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left, ImGuiInputFlags_None) && mouse_inside_viewport) {
|
||||
float image_x =
|
||||
(mouse_position.x - (viewport_offset.x + viewport_size.x / 2)) / dispatch_view->ui.image_scale +
|
||||
dispatch_view->ui.viewport_center.x;
|
||||
float image_y =
|
||||
(mouse_position.y - (viewport_offset.y + viewport_size.y / 2)) / dispatch_view->ui.image_scale +
|
||||
dispatch_view->ui.viewport_center.y;
|
||||
if (image_x >= 0 && image_x < image_size.x && image_y >= 0 && image_y < image_size.y) {
|
||||
dispatch_view->ui.selection_x = (uint32_t)image_x;
|
||||
dispatch_view->ui.selection_y = (uint32_t)image_y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dispatch_view->ui.texture_data.size() && dispatch_view->ui.texture_data[0]->Status == ImTextureStatus_OK) {
|
||||
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() -
|
||||
dispatch_view->ui.viewport_center.x * dispatch_view->ui.image_scale +
|
||||
viewport_size.x / 2);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() -
|
||||
dispatch_view->ui.viewport_center.y * dispatch_view->ui.image_scale +
|
||||
viewport_size.y / 2);
|
||||
|
||||
ImVec2 global_image_offset = ImGui::GetCursorScreenPos();
|
||||
|
||||
draw_list->AddCallback(ImGui::GetPlatformIO().DrawCallback_SetSamplerNearest);
|
||||
ImGui::Image(
|
||||
dispatch_view->ui.texture_data[0]->GetTexRef(),
|
||||
ImVec2(image_size.x * dispatch_view->ui.image_scale, image_size.y * dispatch_view->ui.image_scale));
|
||||
draw_list->AddCallback(ImGui::GetPlatformIO().DrawCallback_SetSamplerLinear);
|
||||
|
||||
if (dispatch_view->ui.selection_x != UINT32_MAX) {
|
||||
ImU32 selection_color = ImGui::ColorConvertFloat4ToU32(
|
||||
ImVec4(app->selection_color.x, app->selection_color.y, app->selection_color.z, 1));
|
||||
ImVec2 tl = {
|
||||
global_image_offset.x + (dispatch_view->ui.selection_x + 0) * dispatch_view->ui.image_scale,
|
||||
global_image_offset.y + (dispatch_view->ui.selection_y + 0) * dispatch_view->ui.image_scale,
|
||||
};
|
||||
ImVec2 br = {
|
||||
global_image_offset.x + (dispatch_view->ui.selection_x + 1) * dispatch_view->ui.image_scale,
|
||||
global_image_offset.y + (dispatch_view->ui.selection_y + 1) * dispatch_view->ui.image_scale,
|
||||
};
|
||||
float thickness = fmax(1.0, 0.2 * dispatch_view->ui.image_scale);
|
||||
draw_list->AddRect(tl, br, selection_color, thickness, 0, thickness);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
ImGui::Begin("invocation", nullptr, ImGuiWindowFlags_NoFocusOnAppearing);
|
||||
if (selected_invocation) {
|
||||
for (uint32_t i = 0; i < selected_invocation->offsets.size(); i++) {
|
||||
const radv_packed_token_header *header =
|
||||
(const radv_packed_token_header *)(history_data + selected_invocation->offsets[i]);
|
||||
if (header->token_type != radv_packed_token_trace_ray)
|
||||
continue;
|
||||
|
||||
const radv_packed_trace_ray_token *token = (const radv_packed_trace_ray_token *)header;
|
||||
|
||||
char tmp[32];
|
||||
sprintf(tmp, "trace_ray[%u]", i);
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::Selectable(tmp, i == selected_invocation->selected_ray))
|
||||
selected_invocation->selected_ray = i;
|
||||
|
||||
if (ImGui::BeginTable(tmp, 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableNextColumnText("accel_struct");
|
||||
ImGui::TableNextColumn();
|
||||
uint64_t addr = ((uint64_t)token->accel_struct_hi << 32) | token->accel_struct_lo;
|
||||
sprintf(tmp, "0x%" PRIx64, addr);
|
||||
if (ImGui::TextLink(tmp)) {
|
||||
rti_acceleration_structure *acceleration_structure = addr_to_acceleration_structure(addr);
|
||||
if (acceleration_structure->ui.opened)
|
||||
acceleration_structure->ui.request_focus = true;
|
||||
acceleration_structure->ui.opened = true;
|
||||
}
|
||||
|
||||
ImGui::TableNextColumnText("origin");
|
||||
ImGui::TableNextColumnText("(%f, %f, %f)", token->origin[0], token->origin[1], token->origin[2]);
|
||||
|
||||
ImGui::TableNextColumnText("direction");
|
||||
ImGui::TableNextColumnText("(%f, %f, %f)", token->direction[0], token->direction[1], token->direction[2]);
|
||||
|
||||
ImGui::TableNextColumnText("tmin/tmax");
|
||||
ImGui::TableNextColumnText("(%f, %f)", token->tmin, token->tmax);
|
||||
|
||||
ImGui::TableNextColumnText("sbt_offset");
|
||||
ImGui::TableNextColumnText("%u", token->sbt_offset);
|
||||
|
||||
ImGui::TableNextColumnText("sbt_stride");
|
||||
ImGui::TableNextColumnText("%u", token->sbt_stride);
|
||||
|
||||
ImGui::TableNextColumnText("miss_index");
|
||||
ImGui::TableNextColumnText("%u", token->miss_index);
|
||||
|
||||
ImGui::TableNextColumnText("cull_mask");
|
||||
ImGui::TableNextColumnText("0x%x", token->cull_mask);
|
||||
|
||||
ImGui::TableNextColumnText("flags");
|
||||
ImGui::TableNextColumnText(
|
||||
"%s%s%s%s%s%s%s%s%s%s", (token->flags & SpvRayFlagsOpaqueKHRMask) ? "Opaque " : "",
|
||||
(token->flags & SpvRayFlagsNoOpaqueKHRMask) ? "NoOpaque " : "",
|
||||
(token->flags & SpvRayFlagsTerminateOnFirstHitKHRMask) ? "TerminateOnFirstHit " : "",
|
||||
(token->flags & SpvRayFlagsSkipClosestHitShaderKHRMask) ? "SkipClosestHitShader " : "",
|
||||
(token->flags & SpvRayFlagsCullBackFacingTrianglesKHRMask) ? "CullBackFacingTriangles " : "",
|
||||
(token->flags & SpvRayFlagsCullFrontFacingTrianglesKHRMask) ? "CullFrontFacingTriangles " : "",
|
||||
(token->flags & SpvRayFlagsCullOpaqueKHRMask) ? "CullOpaque " : "",
|
||||
(token->flags & SpvRayFlagsCullNoOpaqueKHRMask) ? "CullNoOpaque " : "",
|
||||
(token->flags & SpvRayFlagsSkipTrianglesKHRMask) ? "SkipTriangles " : "",
|
||||
(token->flags & SpvRayFlagsSkipAABBsKHRMask) ? "SkipAABBs " : "");
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("ray history", nullptr, ImGuiWindowFlags_NoFocusOnAppearing);
|
||||
|
||||
if (selected_invocation && !selected_invocation->offsets.empty()) {
|
||||
const radv_packed_trace_ray_token *trace_ray =
|
||||
(const radv_packed_trace_ray_token *)(history_data +
|
||||
selected_invocation->offsets[selected_invocation->selected_ray]);
|
||||
uint64_t accel_struct_addr = ((uint64_t)trace_ray->accel_struct_hi << 32) | trace_ray->accel_struct_lo;
|
||||
|
||||
if (ImGui::BeginTable("history", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg)) {
|
||||
for (uint32_t i = selected_invocation->selected_ray + 1; i < selected_invocation->offsets.size(); i++) {
|
||||
const radv_packed_token_header *header =
|
||||
(const radv_packed_token_header *)(history_data + selected_invocation->offsets[i]);
|
||||
if (header->token_type == radv_packed_token_trace_ray)
|
||||
break;
|
||||
|
||||
if (header->token_type == radv_packed_token_iteration) {
|
||||
const radv_packed_iteration_token *token = (const radv_packed_iteration_token *)header;
|
||||
|
||||
uint32_t offset = (token->node_id & (~0xf)) << 3;
|
||||
uint32_t type = token->node_id & 0xf;
|
||||
|
||||
rti_acceleration_structure_radv *acceleration_structure =
|
||||
(rti_acceleration_structure_radv *)addr_to_acceleration_structure(accel_struct_addr);
|
||||
|
||||
char selectable_label[32];
|
||||
sprintf(selectable_label, "%s##%x", node_type_names[type], i);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(selectable_label,
|
||||
token->node_id == acceleration_structure->radv_ui.selected_node_id,
|
||||
ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SelectOnClick)) {
|
||||
if (acceleration_structure->ui.opened)
|
||||
acceleration_structure->ui.request_focus = true;
|
||||
acceleration_structure->ui.opened = true;
|
||||
acceleration_structure->radv_ui.selected_node_id = token->node_id;
|
||||
}
|
||||
ImGui::TableNextColumnText("0x%x", offset);
|
||||
} else if (header->token_type == radv_packed_token_accel_struct) {
|
||||
const radv_packed_accel_struct_token *token = (const radv_packed_accel_struct_token *)header;
|
||||
accel_struct_addr = token->accel_struct;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
end();
|
||||
}
|
||||
|
||||
std::unique_ptr<rti_file_view>
|
||||
rti_create_file_view_radv()
|
||||
{
|
||||
return std::make_unique<rti_file_view_radv>();
|
||||
}
|
||||
177
src/tool/rtinspector/radv/rti_file_view_radv.h
Normal file
177
src/tool/rtinspector/radv/rti_file_view_radv.h
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Copyright © 2026 Valve Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <imgui.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
#include "bvh.h"
|
||||
#include "radv_rti.h"
|
||||
#include "rti_file_view.h"
|
||||
#include "rti_util.h"
|
||||
#include <unordered_set>
|
||||
|
||||
struct rti_acceleration_structure_radv : public rti_acceleration_structure {
|
||||
rti_acceleration_structure_radv(rti_file_view *view): rti_acceleration_structure(view)
|
||||
{
|
||||
}
|
||||
|
||||
struct {
|
||||
uint32_t selected_node_id = RADV_BVH_INVALID_NODE;
|
||||
bool scroll_to_selected_node = false;
|
||||
std::unordered_set<uint32_t> open_path;
|
||||
} radv_ui;
|
||||
|
||||
const uint8_t *bvh;
|
||||
|
||||
uint32_t first_vertex;
|
||||
uint32_t wireframe_first_vertex;
|
||||
};
|
||||
|
||||
struct rti_dispatch_view_settings {
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t depth = 0;
|
||||
uint32_t z = 0;
|
||||
uint32_t max_iteration_count = 0;
|
||||
bool show_box_iteration_count = true;
|
||||
bool show_instance_iteration_count = true;
|
||||
bool show_primitive_iteration_count = true;
|
||||
};
|
||||
|
||||
struct rti_invocation_data {
|
||||
std::vector<uint32_t> offsets;
|
||||
uint32_t selected_ray = 0;
|
||||
uint32_t box_iteration_count = 0;
|
||||
uint32_t instance_iteration_count = 0;
|
||||
uint32_t primitive_iteration_count = 0;
|
||||
};
|
||||
|
||||
struct rti_dispatch_view {
|
||||
struct {
|
||||
bool open = false;
|
||||
bool request_focus = false;
|
||||
|
||||
bool initialize_viewport = true;
|
||||
|
||||
std::vector<std::shared_ptr<ImTextureData>> texture_data;
|
||||
std::vector<std::shared_ptr<ImTextureData>> destroy_texture_data;
|
||||
|
||||
float image_scale;
|
||||
rti_vec2 viewport_center;
|
||||
bool is_dragging_viewport = false;
|
||||
ImVec2 prev_mouse_pos;
|
||||
uint32_t selection_x = UINT32_MAX;
|
||||
uint32_t selection_y = UINT32_MAX;
|
||||
|
||||
rti_dispatch_view_settings prev_settings;
|
||||
rti_dispatch_view_settings settings;
|
||||
} ui;
|
||||
|
||||
const radv_rti_dispatch_info *info;
|
||||
|
||||
uint32_t invocation_count;
|
||||
uint32_t max_iteration_count = 0;
|
||||
|
||||
std::vector<rti_invocation_data> invocations;
|
||||
|
||||
std::map<uint32_t, std::map<uint32_t, uint32_t>> valid_dispatch_sizes;
|
||||
};
|
||||
|
||||
struct rti_file_view_radv : public rti_file_view {
|
||||
std::vector<std::shared_ptr<rti_dispatch_view>> dispatch_views;
|
||||
|
||||
rti_dispatch_view *selected_dispatch = nullptr;
|
||||
|
||||
void *ray_history_data = nullptr;
|
||||
const radv_rti_ray_history_header *history_header = nullptr;
|
||||
const radv_rti_dispatch_info *dispatch_infos = nullptr;
|
||||
const uint8_t *history_data = nullptr;
|
||||
uint32_t dispatch_count = 0;
|
||||
uint64_t ray_history_data_size;
|
||||
|
||||
radv_rti_trace_info *trace_info;
|
||||
|
||||
virtual std::unique_ptr<rti_acceleration_structure> create_acceleration_structure() override;
|
||||
|
||||
virtual void load(FILE *file, const rti_header *header) override;
|
||||
|
||||
virtual void load_driver_specific(FILE *file, const rti_chunk_header *chunk_header,
|
||||
const rti_header *header) override;
|
||||
|
||||
virtual void dock_driver_specific(uint32_t left_top, uint32_t left_bottom, uint32_t center, uint32_t right_top,
|
||||
uint32_t right_bottom) override;
|
||||
|
||||
virtual float handle_mouse_click(rti_acceleration_structure *acceleration_structure, rti_ray ray,
|
||||
bool select) override;
|
||||
|
||||
virtual void run() override;
|
||||
|
||||
rti_invocation_data *get_selected_invocation();
|
||||
|
||||
/* bvh4 */
|
||||
void bvh4_traverse(rti_acceleration_structure_radv *acceleration_structure, rti_ray ray, uint32_t id, float *tmax,
|
||||
const std::unordered_set<uint32_t> &path, bool select);
|
||||
|
||||
rti_aabb bvh4_scene_aabb(rti_acceleration_structure_radv *acceleration_structure);
|
||||
|
||||
void bvh4_get_vertices(rti_acceleration_structure_radv *acceleration_structure, uint32_t id, rti_vertex *vertices,
|
||||
rti_vertex *wireframe_vertices);
|
||||
|
||||
void bvh4_get_instances(rti_acceleration_structure_radv *tlas, uint32_t id);
|
||||
|
||||
void bvh4_render_selection(rti_acceleration_structure_radv *acceleration_structure);
|
||||
|
||||
void bvh4_draw(rti_acceleration_structure_radv *acceleration_structure, uint32_t id);
|
||||
|
||||
void bvh4_draw_node_info(const rti_acceleration_structure_radv *acceleration_structure, uint32_t id);
|
||||
|
||||
void bvh4_get_instance_info(const rti_acceleration_structure_radv *acceleration_structure, uint32_t id,
|
||||
rti_mat4 *transform, rti_acceleration_structure_radv **blas);
|
||||
|
||||
/* bvh8 */
|
||||
void bvh8_traverse(rti_acceleration_structure_radv *acceleration_structure, rti_ray ray, uint32_t id, float *tmax,
|
||||
const std::unordered_set<uint32_t> &path, bool select);
|
||||
|
||||
rti_aabb bvh8_scene_aabb(rti_acceleration_structure_radv *acceleration_structure);
|
||||
|
||||
void bvh8_get_vertices(rti_acceleration_structure_radv *acceleration_structure, uint32_t id, rti_vertex *vertices,
|
||||
rti_vertex *wireframe_vertices);
|
||||
|
||||
void bvh8_get_instances(rti_acceleration_structure_radv *tlas, uint32_t id);
|
||||
|
||||
void bvh8_render_selection(rti_acceleration_structure_radv *acceleration_structure);
|
||||
|
||||
void bvh8_draw(rti_acceleration_structure_radv *acceleration_structure, uint32_t id);
|
||||
|
||||
void bvh8_draw_node_info(const rti_acceleration_structure_radv *acceleration_structure, uint32_t id);
|
||||
|
||||
void bvh8_get_instance_info(const rti_acceleration_structure_radv *acceleration_structure, uint32_t id,
|
||||
rti_mat4 *transform, rti_acceleration_structure_radv **blas);
|
||||
};
|
||||
|
||||
static inline uint64_t
|
||||
radv_node_to_addr(uint64_t node)
|
||||
{
|
||||
node &= ~7ull;
|
||||
node <<= 19;
|
||||
return ((int64_t)node) >> 16;
|
||||
}
|
||||
|
||||
static inline rti_mat4
|
||||
mat3x4_to_rti(mat3x4 m)
|
||||
{
|
||||
rti_mat4 result;
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
for (uint32_t j = 0; j < 4; j++) {
|
||||
result.elements[i + j * 4] = m.values[i][j];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
490
src/tool/rtinspector/radv/rti_file_view_radv_bvh4.cpp
Normal file
490
src/tool/rtinspector/radv/rti_file_view_radv_bvh4.cpp
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
* Copyright © 2026 Valve Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "rti_file_view.h"
|
||||
#include "rti_file_view_radv.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <imgui.h>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "shaders/rti_shader_interface.h"
|
||||
#include "util/half_float.h"
|
||||
#include "util/rti_format.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
#include "bvh.h"
|
||||
#include "rti_app.h"
|
||||
#include "rti_util.h"
|
||||
#include "vk_bvh.h"
|
||||
|
||||
static radv_bvh_box32_node
|
||||
radv_get_box_node(const void *node, uint32_t type)
|
||||
{
|
||||
if (type == radv_bvh_node_box32)
|
||||
return *(const radv_bvh_box32_node *)node;
|
||||
|
||||
const radv_bvh_box16_node *box16 = (const radv_bvh_box16_node *)node;
|
||||
|
||||
radv_bvh_box32_node box32;
|
||||
memset(&box32, 0, sizeof(box32));
|
||||
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
box32.children[i] = box16->children[i];
|
||||
box32.coords[i] = {
|
||||
.min =
|
||||
{
|
||||
.x = _mesa_half_to_float(box16->coords[i].min_x),
|
||||
.y = _mesa_half_to_float(box16->coords[i].min_y),
|
||||
.z = _mesa_half_to_float(box16->coords[i].min_z),
|
||||
},
|
||||
.max =
|
||||
{
|
||||
.x = _mesa_half_to_float(box16->coords[i].max_x),
|
||||
.y = _mesa_half_to_float(box16->coords[i].max_y),
|
||||
.z = _mesa_half_to_float(box16->coords[i].max_z),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return box32;
|
||||
}
|
||||
|
||||
static rti_acceleration_structure_radv *
|
||||
radv_bvh_instance_node_get_blas(rti_file_view *view, const radv_bvh_instance_node *node)
|
||||
{
|
||||
return (rti_acceleration_structure_radv *)view->addr_to_acceleration_structure(radv_node_to_addr(node->bvh_ptr));
|
||||
}
|
||||
|
||||
static rti_aabb
|
||||
vk_aabb_to_rti(vk_aabb aabb)
|
||||
{
|
||||
return {
|
||||
.min = {aabb.min.x, aabb.min.y, aabb.min.z},
|
||||
.max = {aabb.max.x, aabb.max.y, aabb.max.z},
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
rti_file_view_radv::bvh4_traverse(rti_acceleration_structure_radv *acceleration_structure, rti_ray ray, uint32_t id,
|
||||
float *tmax, const std::unordered_set<uint32_t> &path, bool select)
|
||||
{
|
||||
uint32_t offset = (id & (~0x7)) << 3;
|
||||
uint32_t type = id & 0x7;
|
||||
|
||||
std::unordered_set<uint32_t> path_copy = path;
|
||||
if (select)
|
||||
path_copy.insert(id);
|
||||
|
||||
radv_bvh_box32_node node = radv_get_box_node(acceleration_structure->bvh + offset, type);
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
if (node.children[i] == RADV_BVH_INVALID_NODE)
|
||||
continue;
|
||||
|
||||
uint32_t child_offset = (node.children[i] & (~0x7)) << 3;
|
||||
uint32_t child_type = node.children[i] & 0x7;
|
||||
rti_aabb aabb = vk_aabb_to_rti(node.coords[i]);
|
||||
float t = rti_aabb::intersect_ray(aabb, ray);
|
||||
if (t >= *tmax)
|
||||
continue;
|
||||
|
||||
if (child_type == radv_bvh_node_box32 || child_type == radv_bvh_node_box16) {
|
||||
bvh4_traverse(acceleration_structure, ray, node.children[i], tmax, path_copy, select);
|
||||
} else if (child_type == radv_bvh_node_triangle) {
|
||||
const radv_bvh_triangle_node *triangle_node =
|
||||
(const radv_bvh_triangle_node *)(acceleration_structure->bvh + child_offset);
|
||||
|
||||
rti_triangle triangle;
|
||||
for (uint32_t j = 0; j < 3; j++) {
|
||||
triangle.vertices[j] = {triangle_node->coords[j][0], triangle_node->coords[j][1],
|
||||
triangle_node->coords[j][2]};
|
||||
}
|
||||
|
||||
t = rti_triangle::intersect_ray(triangle, ray);
|
||||
|
||||
if (t < *tmax) {
|
||||
*tmax = t;
|
||||
if (select) {
|
||||
acceleration_structure->radv_ui.selected_node_id = node.children[i];
|
||||
acceleration_structure->radv_ui.open_path = path_copy;
|
||||
acceleration_structure->radv_ui.scroll_to_selected_node = true;
|
||||
}
|
||||
}
|
||||
} else if (child_type == radv_bvh_node_aabb) {
|
||||
if (t < *tmax) {
|
||||
*tmax = t;
|
||||
if (select) {
|
||||
acceleration_structure->radv_ui.selected_node_id = node.children[i];
|
||||
acceleration_structure->radv_ui.open_path = path_copy;
|
||||
acceleration_structure->radv_ui.scroll_to_selected_node = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const radv_bvh_instance_node *instance =
|
||||
(const radv_bvh_instance_node *)(acceleration_structure->bvh + child_offset);
|
||||
rti_ray object_ray = rti_ray::transform(ray, mat3x4_to_rti(instance->wto_matrix));
|
||||
rti_acceleration_structure_radv *blas = radv_bvh_instance_node_get_blas(this, instance);
|
||||
float blas_tmax = INFINITY;
|
||||
std::unordered_set<uint32_t> blas_path;
|
||||
bvh4_traverse(blas, object_ray, RADV_BVH_ROOT_NODE, &blas_tmax, blas_path, false);
|
||||
if (blas_tmax < INFINITY) {
|
||||
if (select) {
|
||||
acceleration_structure->radv_ui.selected_node_id = node.children[i];
|
||||
acceleration_structure->radv_ui.open_path = path_copy;
|
||||
acceleration_structure->radv_ui.scroll_to_selected_node = true;
|
||||
}
|
||||
*tmax = blas_tmax;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rti_aabb
|
||||
rti_file_view_radv::bvh4_scene_aabb(rti_acceleration_structure_radv *acceleration_structure)
|
||||
{
|
||||
rti_aabb aabb;
|
||||
|
||||
uint32_t root_offset = (RADV_BVH_ROOT_NODE & (~0x7)) << 3;
|
||||
uint32_t root_type = RADV_BVH_ROOT_NODE & 0x7;
|
||||
assert(root_type == radv_bvh_node_box32);
|
||||
|
||||
bool first = true;
|
||||
const radv_bvh_box32_node *root_node = (const radv_bvh_box32_node *)(acceleration_structure->bvh + root_offset);
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
if (root_node->children[i] == RADV_BVH_INVALID_NODE)
|
||||
continue;
|
||||
|
||||
rti_aabb child_aabb = vk_aabb_to_rti(root_node->coords[i]);
|
||||
if (first) {
|
||||
aabb = child_aabb;
|
||||
first = false;
|
||||
} else {
|
||||
aabb = rti_aabb::combine(aabb, child_aabb);
|
||||
}
|
||||
}
|
||||
|
||||
return aabb;
|
||||
}
|
||||
|
||||
void
|
||||
rti_file_view_radv::bvh4_get_vertices(rti_acceleration_structure_radv *acceleration_structure, uint32_t id,
|
||||
rti_vertex *vertices, rti_vertex *wireframe_vertices)
|
||||
{
|
||||
uint32_t offset = (id & (~0x7)) << 3;
|
||||
uint32_t type = id & 0x7;
|
||||
radv_bvh_box32_node box_node = radv_get_box_node(acceleration_structure->bvh + offset, type);
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
if (box_node.children[i] == RADV_BVH_INVALID_NODE)
|
||||
continue;
|
||||
|
||||
uint32_t child_offset = (box_node.children[i] & (~0x7)) << 3;
|
||||
uint32_t child_type = box_node.children[i] & 0x7;
|
||||
if (child_type == radv_bvh_node_box32 || child_type == radv_bvh_node_box16) {
|
||||
bvh4_get_vertices(acceleration_structure, box_node.children[i], vertices, wireframe_vertices);
|
||||
} else if (child_type == radv_bvh_node_triangle) {
|
||||
const radv_bvh_triangle_node *node =
|
||||
(const radv_bvh_triangle_node *)(acceleration_structure->bvh + child_offset);
|
||||
|
||||
uint32_t geometry_index = node->geometry_id_and_flags & 0xffffff;
|
||||
|
||||
uint32_t dst_index =
|
||||
acceleration_structure->primitive_counts_exclusive_sum[node->geometry_id_and_flags & 0xfffffff] +
|
||||
node->triangle_id;
|
||||
for (uint32_t j = 0; j < 3; j++) {
|
||||
vertices[dst_index * 3 + j].position = {node->coords[j][0], node->coords[j][1], node->coords[j][2]};
|
||||
vertices[dst_index * 3 + j].geometry_index = geometry_index;
|
||||
vertices[dst_index * 3 + j].primitive_index = node->triangle_id;
|
||||
}
|
||||
} else if (child_type == radv_bvh_node_aabb) {
|
||||
const radv_bvh_aabb_node *node = (const radv_bvh_aabb_node *)(acceleration_structure->bvh + child_offset);
|
||||
uint32_t dst_index =
|
||||
acceleration_structure->primitive_counts_exclusive_sum[node->geometry_id_and_flags & 0xfffffff] +
|
||||
node->primitive_id;
|
||||
rti_aabb aabb = vk_aabb_to_rti(box_node.coords[i]);
|
||||
rti_generate_cube_vertices(wireframe_vertices + dst_index * RTI_CUBE_VERTEX_COUNT, aabb);
|
||||
rti_generate_filled_cube_vertices(vertices + dst_index * RTI_FILLED_CUBE_VERTEX_COUNT, aabb,
|
||||
node->geometry_id_and_flags & 0xffffff, node->primitive_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rti_file_view_radv::bvh4_get_instances(rti_acceleration_structure_radv *tlas, uint32_t id)
|
||||
{
|
||||
uint32_t offset = (id & (~0x7)) << 3;
|
||||
uint32_t type = id & 0x7;
|
||||
|
||||
if (type == radv_bvh_node_box32 || type == radv_bvh_node_box16) {
|
||||
radv_bvh_box32_node node = radv_get_box_node(tlas->bvh + offset, type);
|
||||
for (uint32_t i = 0; i < 4; i++)
|
||||
if (node.children[i] != RADV_BVH_INVALID_NODE)
|
||||
bvh4_get_instances(tlas, node.children[i]);
|
||||
} else if (type == radv_bvh_node_instance) {
|
||||
const radv_bvh_instance_node *node = (const radv_bvh_instance_node *)(tlas->bvh + offset);
|
||||
rti_acceleration_structure_radv *blas = radv_bvh_instance_node_get_blas(this, node);
|
||||
|
||||
uint32_t primitive_vertex_count =
|
||||
blas->header.geometry_type == VK_GEOMETRY_TYPE_TRIANGLES_KHR ? 3 : RTI_FILLED_CUBE_VERTEX_COUNT;
|
||||
|
||||
rti_render_task solid_task;
|
||||
solid_task.vertex_buffer = blas->ui.vertex_buffer.get();
|
||||
solid_task.first_vertex = blas->first_vertex;
|
||||
solid_task.vertex_count = blas->primitive_count * primitive_vertex_count;
|
||||
solid_task.params.transform = mat3x4_to_rti(node->otw_matrix);
|
||||
solid_task.params.color = rti_index_to_color(node->instance_id);
|
||||
solid_task.flags = RTI_RENDERER_COLOR_PUSH_CONSTANT;
|
||||
tlas->ui.instance_render_list->tasks.push_back(solid_task);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *node_type_names[8] = {
|
||||
"triangle0", "triangle1", "triangle2", "triangle3", "box16", "box32", "instance", "aabb",
|
||||
};
|
||||
|
||||
void
|
||||
rti_file_view_radv::bvh4_draw(rti_acceleration_structure_radv *acceleration_structure, uint32_t id)
|
||||
{
|
||||
uint32_t offset = (id & (~0x7)) << 3;
|
||||
uint32_t type = id & 0x7;
|
||||
|
||||
bool selected = acceleration_structure->radv_ui.selected_node_id == id;
|
||||
|
||||
ImGuiTreeNodeFlags node_flags =
|
||||
ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
|
||||
if (type != radv_bvh_node_box32 && type != radv_bvh_node_box16)
|
||||
node_flags |= ImGuiTreeNodeFlags_Leaf;
|
||||
if (selected)
|
||||
node_flags |= ImGuiTreeNodeFlags_Selected;
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
if (acceleration_structure->radv_ui.open_path.find(id) != acceleration_structure->radv_ui.open_path.end())
|
||||
ImGui::SetNextItemOpen(true);
|
||||
|
||||
char tree_node_id[32];
|
||||
sprintf(tree_node_id, "%x %x", type, offset);
|
||||
|
||||
bool open = ImGui::TreeNodeEx(tree_node_id, node_flags, "%s", node_type_names[type]);
|
||||
|
||||
if (ImGui::IsItemClicked())
|
||||
acceleration_structure->radv_ui.selected_node_id = id;
|
||||
|
||||
ImGui::TableNextColumnText("0x%x", offset);
|
||||
|
||||
if (open) {
|
||||
if (type == radv_bvh_node_box32 || type == radv_bvh_node_box16) {
|
||||
radv_bvh_box32_node box_node = radv_get_box_node(acceleration_structure->bvh + offset, type);
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
if (box_node.children[i] != RADV_BVH_INVALID_NODE)
|
||||
bvh4_draw(acceleration_structure, box_node.children[i]);
|
||||
}
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (acceleration_structure->radv_ui.scroll_to_selected_node && selected) {
|
||||
ImGui::SetScrollHereY();
|
||||
acceleration_structure->radv_ui.scroll_to_selected_node = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rti_file_view_radv::bvh4_draw_node_info(const rti_acceleration_structure_radv *acceleration_structure, uint32_t id)
|
||||
{
|
||||
|
||||
uint32_t offset = (acceleration_structure->radv_ui.selected_node_id & (~0x7)) << 3;
|
||||
uint32_t type = acceleration_structure->radv_ui.selected_node_id & 0x7;
|
||||
|
||||
if (type == radv_bvh_node_box32 || type == radv_bvh_node_box16) {
|
||||
radv_bvh_box32_node node = radv_get_box_node(acceleration_structure->bvh + offset, type);
|
||||
|
||||
char tmp[64];
|
||||
sprintf(tmp, "box%u node (0x%x)", type == radv_bvh_node_box32 ? 32 : 16, offset);
|
||||
|
||||
ImGui::SeparatorText(tmp);
|
||||
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
sprintf(tmp, "child[%u]", i);
|
||||
ImGui::SeparatorText(tmp);
|
||||
|
||||
sprintf(tmp, "child[%u] properties", i);
|
||||
|
||||
if (ImGui::BeginTable(tmp, 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableNextColumnText("id");
|
||||
ImGui::TableNextColumnText("0x%x", node.children[i]);
|
||||
|
||||
ImGui::TableNextColumnText("aabb");
|
||||
ImGui::TableNextColumnText("(%f, %f, %f) (%f, %f, %f)", node.coords[i].min.x, node.coords[i].min.y,
|
||||
node.coords[i].min.z, node.coords[i].max.x, node.coords[i].max.y,
|
||||
node.coords[i].max.z);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
} else if (type == radv_bvh_node_instance) {
|
||||
const radv_bvh_instance_node *node = (const radv_bvh_instance_node *)(acceleration_structure->bvh + offset);
|
||||
|
||||
char tmp[64];
|
||||
sprintf(tmp, "instance node (0x%x)", offset);
|
||||
|
||||
ImGui::SeparatorText(tmp);
|
||||
|
||||
if (ImGui::BeginTable("instance node properties", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableNextColumnText("bvh_ptr");
|
||||
ImGui::TableNextColumn();
|
||||
sprintf(tmp, "0x%" PRIx64, node->bvh_ptr);
|
||||
if (ImGui::TextLink(tmp)) {
|
||||
rti_acceleration_structure_radv *blas = radv_bvh_instance_node_get_blas(this, node);
|
||||
if (blas->ui.opened)
|
||||
blas->ui.request_focus = true;
|
||||
blas->ui.opened = true;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
ImGui::TableNextColumnText("wto_matrix.row[%u]", i);
|
||||
ImGui::TableNextColumnText("(%f, %f, %f, %f)", node->wto_matrix.values[i][0], node->wto_matrix.values[i][1],
|
||||
node->wto_matrix.values[i][2], node->wto_matrix.values[i][3]);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
ImGui::TableNextColumnText("otw_matrix.row[%u]", i);
|
||||
ImGui::TableNextColumnText("(%f, %f, %f, %f)", node->otw_matrix.values[i][0], node->otw_matrix.values[i][1],
|
||||
node->otw_matrix.values[i][2], node->otw_matrix.values[i][3]);
|
||||
}
|
||||
|
||||
ImGui::TableNextColumnText("user_data");
|
||||
ImGui::TableNextColumnText("0x%x", node->custom_instance_and_mask & 0xFFFFFF);
|
||||
|
||||
ImGui::TableNextColumnText("mask");
|
||||
ImGui::TableNextColumnText("0x%x", node->custom_instance_and_mask >> 24);
|
||||
|
||||
ImGui::TableNextColumnText("sbt_offset");
|
||||
ImGui::TableNextColumnText("0x%x", node->sbt_offset_and_flags & 0xFFFFFF);
|
||||
|
||||
ImGui::TableNextColumnText("flags");
|
||||
ImGui::TableNextColumnText(
|
||||
"%s%s%s%s", (node->sbt_offset_and_flags & RADV_INSTANCE_FORCE_OPAQUE) ? "force_opaque " : "",
|
||||
(node->sbt_offset_and_flags & RADV_INSTANCE_NO_FORCE_NOT_OPAQUE) ? "no_force_not_opaque " : "",
|
||||
(node->sbt_offset_and_flags & RADV_INSTANCE_TRIANGLE_FACING_CULL_DISABLE) ? "triangle_facing_cull_disable "
|
||||
: "",
|
||||
(node->sbt_offset_and_flags & RADV_INSTANCE_TRIANGLE_FLIP_FACING) ? "triangle_flip_facing " : "");
|
||||
|
||||
ImGui::TableNextColumnText("instance_id");
|
||||
ImGui::TableNextColumnText("%u", node->instance_id);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
} else if (type == radv_bvh_node_triangle) {
|
||||
const radv_bvh_triangle_node *node = (const radv_bvh_triangle_node *)(acceleration_structure->bvh + offset);
|
||||
|
||||
char tmp[64];
|
||||
sprintf(tmp, "triangle node (0x%x)", offset);
|
||||
|
||||
ImGui::SeparatorText(tmp);
|
||||
|
||||
if (ImGui::BeginTable("triangle node properties", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
ImGui::TableNextColumnText("v%u", i);
|
||||
ImGui::TableNextColumnText("(%f, %f, %f)", node->coords[i][0], node->coords[i][1], node->coords[i][2]);
|
||||
}
|
||||
|
||||
ImGui::TableNextColumnText("triangle_id");
|
||||
ImGui::TableNextColumnText("%u", node->triangle_id);
|
||||
|
||||
ImGui::TableNextColumnText("geometry_id");
|
||||
ImGui::TableNextColumnText("%u", node->geometry_id_and_flags & 0xfffffff);
|
||||
|
||||
ImGui::TableNextColumnText("flags");
|
||||
ImGui::TableNextColumnText("%s", (node->geometry_id_and_flags & RADV_GEOMETRY_OPAQUE) ? "opaque " : "");
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
} else {
|
||||
const radv_bvh_aabb_node *node = (const radv_bvh_aabb_node *)(acceleration_structure->bvh + offset);
|
||||
|
||||
char tmp[64];
|
||||
sprintf(tmp, "aabb node (0x%x)", offset);
|
||||
|
||||
ImGui::SeparatorText(tmp);
|
||||
|
||||
if (ImGui::BeginTable("aabb node properties", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableNextColumnText("primitive_id");
|
||||
ImGui::TableNextColumnText("%u", node->primitive_id);
|
||||
|
||||
ImGui::TableNextColumnText("geometry_id");
|
||||
ImGui::TableNextColumnText("%u", node->geometry_id_and_flags & 0xfffffff);
|
||||
|
||||
ImGui::TableNextColumnText("flags");
|
||||
ImGui::TableNextColumnText("%s", (node->geometry_id_and_flags & RADV_GEOMETRY_OPAQUE) ? "opaque " : "");
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rti_file_view_radv::bvh4_render_selection(rti_acceleration_structure_radv *acceleration_structure)
|
||||
{
|
||||
rti_render_task selection_task;
|
||||
selection_task.type = rti_render_task_type_thick_wireframe;
|
||||
selection_task.vertex_buffer = acceleration_structure->ui.vertex_buffer.get();
|
||||
selection_task.vertex_count = 3;
|
||||
selection_task.params.color = app->selection_color;
|
||||
selection_task.flags = RTI_RENDERER_COLOR_PUSH_CONSTANT;
|
||||
selection_task.wait_for_prev = true;
|
||||
|
||||
if (acceleration_structure->wireframe_first_vertex != acceleration_structure->first_vertex) {
|
||||
selection_task.type = rti_render_task_type_thick_lines;
|
||||
selection_task.vertex_count = RTI_CUBE_VERTEX_COUNT;
|
||||
}
|
||||
|
||||
uint32_t offset = (acceleration_structure->radv_ui.selected_node_id & (~0x7)) << 3;
|
||||
uint32_t type = acceleration_structure->radv_ui.selected_node_id & 0x7;
|
||||
|
||||
if (type == radv_bvh_node_box32 || type == radv_bvh_node_box16) {
|
||||
radv_bvh_box32_node node = radv_get_box_node(acceleration_structure->bvh + offset, type);
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
if (node.children[i] != RADV_BVH_INVALID_NODE) {
|
||||
render_aabb(selection_task, vk_aabb_to_rti(node.coords[i]));
|
||||
selection_task.wait_for_prev = false;
|
||||
}
|
||||
}
|
||||
} else if (type == radv_bvh_node_triangle) {
|
||||
const radv_bvh_triangle_node *node = (const radv_bvh_triangle_node *)(acceleration_structure->bvh + offset);
|
||||
uint32_t index = acceleration_structure->primitive_counts_exclusive_sum[node->geometry_id_and_flags & 0xfffffff] +
|
||||
node->triangle_id;
|
||||
selection_task.first_vertex = acceleration_structure->wireframe_first_vertex + index * 3;
|
||||
render(selection_task);
|
||||
selection_task.wait_for_prev = false;
|
||||
} else if (type == radv_bvh_node_aabb) {
|
||||
const radv_bvh_aabb_node *node = (const radv_bvh_aabb_node *)(acceleration_structure->bvh + offset);
|
||||
uint32_t index = acceleration_structure->primitive_counts_exclusive_sum[node->geometry_id_and_flags & 0xfffffff] +
|
||||
node->primitive_id;
|
||||
selection_task.first_vertex = acceleration_structure->wireframe_first_vertex + index * RTI_CUBE_VERTEX_COUNT;
|
||||
render(selection_task);
|
||||
selection_task.wait_for_prev = false;
|
||||
} else {
|
||||
const radv_bvh_instance_node *node = (const radv_bvh_instance_node *)(acceleration_structure->bvh + offset);
|
||||
selection_task.params.transform = mat3x4_to_rti(node->otw_matrix);
|
||||
render_aabb(selection_task, radv_bvh_instance_node_get_blas(this, node)->aabb);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rti_file_view_radv::bvh4_get_instance_info(const rti_acceleration_structure_radv *acceleration_structure, uint32_t id,
|
||||
rti_mat4 *transform, rti_acceleration_structure_radv **blas)
|
||||
{
|
||||
|
||||
uint32_t offset = (id & (~0x7)) << 3;
|
||||
const radv_bvh_instance_node *node = (const radv_bvh_instance_node *)(acceleration_structure->bvh + offset);
|
||||
*blas = radv_bvh_instance_node_get_blas(this, node);
|
||||
*transform = mat3x4_to_rti(node->wto_matrix);
|
||||
}
|
||||
736
src/tool/rtinspector/radv/rti_file_view_radv_bvh8.cpp
Normal file
736
src/tool/rtinspector/radv/rti_file_view_radv_bvh8.cpp
Normal file
|
|
@ -0,0 +1,736 @@
|
|||
/*
|
||||
* Copyright © 2026 Valve Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "rti_file_view.h"
|
||||
#include "rti_file_view_radv.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <imgui.h>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "shaders/rti_shader_interface.h"
|
||||
#include "util/bitset.h"
|
||||
#include "util/macros.h"
|
||||
#include "util/rti_format.h"
|
||||
#include "util/u_math.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
#include "bvh.h"
|
||||
#include "rti_app.h"
|
||||
#include "rti_util.h"
|
||||
|
||||
struct radv_bvh8_box_node_iterator {
|
||||
const radv_gfx12_box_node *node;
|
||||
uint32_t internal_id;
|
||||
uint32_t primitive_id;
|
||||
int32_t valid_child_count_minus_one;
|
||||
int32_t index = -1;
|
||||
|
||||
radv_bvh8_box_node_iterator(const radv_gfx12_box_node *node): node(node)
|
||||
{
|
||||
internal_id = node->internal_base_id;
|
||||
primitive_id = node->primitive_base_id;
|
||||
valid_child_count_minus_one = node->child_count_exponents >> 28;
|
||||
if (valid_child_count_minus_one == 0xF)
|
||||
valid_child_count_minus_one = -1;
|
||||
}
|
||||
|
||||
uint32_t next()
|
||||
{
|
||||
index++;
|
||||
if (index > (int32_t)valid_child_count_minus_one)
|
||||
return RADV_BVH_INVALID_NODE;
|
||||
|
||||
uint32_t child_type = (node->children[index].dword2 >> 24) & 0xf;
|
||||
uint32_t child_size = node->children[index].dword2 >> 28;
|
||||
|
||||
uint32_t child_id;
|
||||
if (child_type == radv_bvh_node_box32) {
|
||||
child_id = internal_id;
|
||||
internal_id += (child_size * RADV_GFX12_BVH_NODE_SIZE) >> 3;
|
||||
} else {
|
||||
child_id = primitive_id;
|
||||
primitive_id += (child_size * RADV_GFX12_BVH_NODE_SIZE) >> 3;
|
||||
}
|
||||
|
||||
return child_id | child_type;
|
||||
}
|
||||
};
|
||||
|
||||
static rti_aabb
|
||||
radv_bvh8_box_child_get_aabb(radv_gfx12_box_child child, vec3 origin, uint32_t child_count_exponents)
|
||||
{
|
||||
rti_aabb aabb;
|
||||
|
||||
uint32_t exponents[3] = {
|
||||
child_count_exponents & 0xff,
|
||||
(child_count_exponents >> 8) & 0xff,
|
||||
(child_count_exponents >> 16) & 0xff,
|
||||
};
|
||||
float extent[3] = {
|
||||
uif(exponents[0] << 23),
|
||||
uif(exponents[1] << 23),
|
||||
uif(exponents[2] << 23),
|
||||
};
|
||||
|
||||
aabb.min.x = (float)(child.dword0 & 0xfff) / 0x1000 * extent[0] + origin.x;
|
||||
aabb.min.y = (float)((child.dword0 >> 12) & 0xfff) / 0x1000 * extent[1] + origin.y;
|
||||
aabb.min.z = (float)(child.dword1 & 0xfff) / 0x1000 * extent[2] + origin.z;
|
||||
aabb.max.x = (float)(((child.dword1 >> 12) & 0xfff) + 1) / 0x1000 * extent[0] + origin.x;
|
||||
aabb.max.y = (float)((child.dword2 & 0xfff) + 1) / 0x1000 * extent[1] + origin.y;
|
||||
aabb.max.z = (float)(((child.dword2 >> 12) & 0xfff) + 1) / 0x1000 * extent[2] + origin.z;
|
||||
|
||||
return aabb;
|
||||
}
|
||||
|
||||
struct radv_bvh8_primitive_node_info {
|
||||
uint32_t geometry_index_base_bits_div_2;
|
||||
uint32_t geometry_index_bits_div_2;
|
||||
uint32_t triangle_pair_count_minus_one;
|
||||
uint32_t primitive_index_base_bits;
|
||||
uint32_t primitive_index_bits;
|
||||
uint32_t indices_midpoint;
|
||||
uint32_t vertex_bits_minus_one[3];
|
||||
uint32_t trailing_zero_bits;
|
||||
rti_triangle triangles[16];
|
||||
|
||||
uint32_t procedural_mask = 0;
|
||||
|
||||
radv_bvh8_primitive_node_info(const BITSET_WORD *node)
|
||||
{
|
||||
geometry_index_base_bits_div_2 = BITSET_EXTRACT(node, 20, 4);
|
||||
geometry_index_bits_div_2 = BITSET_EXTRACT(node, 24, 4);
|
||||
triangle_pair_count_minus_one = BITSET_EXTRACT(node, 28, 3);
|
||||
primitive_index_base_bits = BITSET_EXTRACT(node, 32, 5);
|
||||
primitive_index_bits = BITSET_EXTRACT(node, 37, 5);
|
||||
indices_midpoint = BITSET_EXTRACT(node, 42, 10);
|
||||
vertex_bits_minus_one[0] = BITSET_EXTRACT(node, 0, 5);
|
||||
vertex_bits_minus_one[1] = BITSET_EXTRACT(node, 5, 5);
|
||||
vertex_bits_minus_one[2] = BITSET_EXTRACT(node, 10, 5);
|
||||
trailing_zero_bits = BITSET_EXTRACT(node, 15, 5);
|
||||
|
||||
uint32_t geometry_id_base = BITSET_EXTRACT(node, indices_midpoint - geometry_index_base_bits_div_2 * 2,
|
||||
geometry_index_base_bits_div_2 * 2);
|
||||
uint32_t primitive_id_base = BITSET_EXTRACT(node, indices_midpoint, primitive_index_base_bits);
|
||||
|
||||
for (uint32_t i = 0; i < (triangle_pair_count_minus_one + 1) * 2; i++) {
|
||||
uint32_t pair_offset = 1024 - (i / 2 + 1) * RADV_GFX12_PRIMITIVE_NODE_PAIR_DESC_SIZE;
|
||||
uint32_t vertex_indices_offset = pair_offset + ((i % 2) == 0 ? 17 : 3);
|
||||
|
||||
uint32_t vertex_indices[] = {
|
||||
BITSET_EXTRACT(node, vertex_indices_offset + 0, 4),
|
||||
BITSET_EXTRACT(node, vertex_indices_offset + 4, 4),
|
||||
BITSET_EXTRACT(node, vertex_indices_offset + 8, 4),
|
||||
};
|
||||
|
||||
if (vertex_indices[0] == 0 && vertex_indices[1] == 0 && vertex_indices[2] == 0) {
|
||||
triangles[i].primitive_index = RTI_PRIMITIVE_INDEX_INACTIVE;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool is_procedural = vertex_indices[0] == 0xf && vertex_indices[1] == 0xf;
|
||||
if (is_procedural)
|
||||
procedural_mask |= BITFIELD_BIT(i);
|
||||
|
||||
uint32_t payload_size[3] = {
|
||||
vertex_bits_minus_one[0] + 1,
|
||||
vertex_bits_minus_one[1] + 1,
|
||||
vertex_bits_minus_one[2] + 1,
|
||||
};
|
||||
uint32_t total_payload_size = payload_size[0] + payload_size[1] + payload_size[2];
|
||||
|
||||
uint32_t prefix_size[3] = {
|
||||
32 - trailing_zero_bits - payload_size[0],
|
||||
32 - trailing_zero_bits - payload_size[1],
|
||||
32 - trailing_zero_bits - payload_size[2],
|
||||
};
|
||||
uint32_t total_prefix_size = prefix_size[0] + prefix_size[1] + prefix_size[2];
|
||||
uint32_t prefix[3] = {
|
||||
BITSET_EXTRACT(node, RADV_GFX12_PRIMITIVE_NODE_HEADER_SIZE, prefix_size[0]) << (32 - prefix_size[0]),
|
||||
BITSET_EXTRACT(node, RADV_GFX12_PRIMITIVE_NODE_HEADER_SIZE + prefix_size[0], prefix_size[1])
|
||||
<< (32 - prefix_size[1]),
|
||||
BITSET_EXTRACT(node, RADV_GFX12_PRIMITIVE_NODE_HEADER_SIZE + prefix_size[0] + prefix_size[1], prefix_size[2])
|
||||
<< (32 - prefix_size[2]),
|
||||
};
|
||||
|
||||
for (uint32_t j = 0; j < (is_procedural ? 0 : 3); j++) {
|
||||
uint32_t payload[3] = {
|
||||
BITSET_EXTRACT(
|
||||
node,
|
||||
RADV_GFX12_PRIMITIVE_NODE_HEADER_SIZE + total_prefix_size + total_payload_size * vertex_indices[j],
|
||||
payload_size[0])
|
||||
<< trailing_zero_bits,
|
||||
BITSET_EXTRACT(node,
|
||||
RADV_GFX12_PRIMITIVE_NODE_HEADER_SIZE + total_prefix_size +
|
||||
total_payload_size * vertex_indices[j] + payload_size[0],
|
||||
payload_size[1])
|
||||
<< trailing_zero_bits,
|
||||
BITSET_EXTRACT(node,
|
||||
RADV_GFX12_PRIMITIVE_NODE_HEADER_SIZE + total_prefix_size +
|
||||
total_payload_size * vertex_indices[j] + payload_size[0] + payload_size[1],
|
||||
payload_size[2])
|
||||
<< trailing_zero_bits,
|
||||
};
|
||||
triangles[i].vertices[j].x = uif(prefix[0] + payload[0]);
|
||||
triangles[i].vertices[j].y = uif(prefix[1] + payload[1]);
|
||||
triangles[i].vertices[j].z = uif(prefix[2] + payload[2]);
|
||||
}
|
||||
|
||||
uint32_t geometry_index = geometry_id_base;
|
||||
uint32_t primitive_index = primitive_id_base;
|
||||
if (i) {
|
||||
geometry_index =
|
||||
(geometry_id_base & ~BITFIELD64_MASK(geometry_index_bits_div_2 * 2)) |
|
||||
BITSET_EXTRACT(node,
|
||||
indices_midpoint - geometry_index_base_bits_div_2 * 2 - geometry_index_bits_div_2 * 2 * i,
|
||||
geometry_index_bits_div_2 * 2);
|
||||
|
||||
primitive_index =
|
||||
(primitive_id_base & ~BITFIELD64_MASK(primitive_index_bits)) |
|
||||
BITSET_EXTRACT(node, indices_midpoint + primitive_index_base_bits + primitive_index_bits * (i - 1),
|
||||
primitive_index_bits);
|
||||
};
|
||||
triangles[i].geometry_index = geometry_index;
|
||||
triangles[i].primitive_index = primitive_index;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static rti_mat4
|
||||
radv_bvh8_instance_node_get_transform(const radv_gfx12_instance_node *node)
|
||||
{
|
||||
rti_mat4 inv_transform = mat3x4_to_rti(node->wto_matrix);
|
||||
rti_mat4 transform;
|
||||
util_invert_mat4x4(transform.elements, inv_transform.elements);
|
||||
return transform;
|
||||
}
|
||||
|
||||
static rti_acceleration_structure_radv *
|
||||
radv_bvh8_instance_node_get_blas(rti_file_view *view, const radv_gfx12_instance_node *node)
|
||||
{
|
||||
return (rti_acceleration_structure_radv *)view->addr_to_acceleration_structure(
|
||||
radv_node_to_addr(node->pointer_flags_bvh_addr));
|
||||
}
|
||||
|
||||
void
|
||||
rti_file_view_radv::bvh8_traverse(rti_acceleration_structure_radv *acceleration_structure, rti_ray ray, uint32_t id,
|
||||
float *tmax, const std::unordered_set<uint32_t> &path, bool select)
|
||||
{
|
||||
uint32_t offset = (id & (~0xf)) << 3;
|
||||
|
||||
std::unordered_set<uint32_t> path_copy = path;
|
||||
if (select)
|
||||
path_copy.insert(id);
|
||||
|
||||
const radv_gfx12_box_node *node = (const radv_gfx12_box_node *)(acceleration_structure->bvh + offset);
|
||||
radv_bvh8_box_node_iterator iterator = node;
|
||||
for (uint32_t child_id = iterator.next(); child_id != RADV_BVH_INVALID_NODE; child_id = iterator.next()) {
|
||||
uint32_t child_offset = (child_id & (~0xf)) << 3;
|
||||
uint32_t child_type = child_id & 0xf;
|
||||
rti_aabb aabb =
|
||||
radv_bvh8_box_child_get_aabb(node->children[iterator.index], node->origin, node->child_count_exponents);
|
||||
float t = rti_aabb::intersect_ray(aabb, ray);
|
||||
if (t >= *tmax)
|
||||
continue;
|
||||
|
||||
if (child_type == radv_bvh_node_box32) {
|
||||
bvh8_traverse(acceleration_structure, ray, child_id, tmax, path_copy, select);
|
||||
} else if (child_type != radv_bvh_node_instance) {
|
||||
const BITSET_WORD *node = (const BITSET_WORD *)(acceleration_structure->bvh + child_offset);
|
||||
radv_bvh8_primitive_node_info info(node);
|
||||
for (uint32_t i = 0; i < (info.triangle_pair_count_minus_one + 1) * 2; i++) {
|
||||
rti_triangle triangle = info.triangles[i];
|
||||
if (triangle.primitive_index == RTI_PRIMITIVE_INDEX_INACTIVE)
|
||||
continue;
|
||||
|
||||
if (!(info.procedural_mask & BITFIELD_BIT(i)))
|
||||
t = rti_triangle::intersect_ray(triangle, ray);
|
||||
|
||||
if (t < *tmax) {
|
||||
*tmax = t;
|
||||
if (select) {
|
||||
acceleration_structure->radv_ui.selected_node_id = child_id;
|
||||
acceleration_structure->radv_ui.open_path = path_copy;
|
||||
acceleration_structure->radv_ui.scroll_to_selected_node = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const radv_gfx12_instance_node *instance =
|
||||
(const radv_gfx12_instance_node *)(acceleration_structure->bvh + child_offset);
|
||||
rti_ray object_ray = rti_ray::transform(ray, mat3x4_to_rti(instance->wto_matrix));
|
||||
rti_acceleration_structure_radv *blas = radv_bvh8_instance_node_get_blas(this, instance);
|
||||
float blas_tmax = INFINITY;
|
||||
std::unordered_set<uint32_t> blas_path;
|
||||
bvh8_traverse(blas, object_ray, RADV_BVH_ROOT_NODE, &blas_tmax, blas_path, false);
|
||||
if (blas_tmax < INFINITY) {
|
||||
if (select) {
|
||||
acceleration_structure->radv_ui.selected_node_id = child_id;
|
||||
acceleration_structure->radv_ui.open_path = path_copy;
|
||||
acceleration_structure->radv_ui.scroll_to_selected_node = true;
|
||||
}
|
||||
*tmax = blas_tmax;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rti_aabb
|
||||
rti_file_view_radv::bvh8_scene_aabb(rti_acceleration_structure_radv *acceleration_structure)
|
||||
{
|
||||
rti_aabb aabb;
|
||||
|
||||
uint32_t root_offset = (RADV_BVH_ROOT_NODE & (~0xf)) << 3;
|
||||
uint32_t root_type = RADV_BVH_ROOT_NODE & 0xf;
|
||||
assert(root_type == radv_bvh_node_box32);
|
||||
const radv_gfx12_box_node *root_node = (const radv_gfx12_box_node *)(acceleration_structure->bvh + root_offset);
|
||||
for (uint32_t i = 0; i <= root_node->child_count_exponents >> 28; i++) {
|
||||
rti_aabb child_aabb =
|
||||
radv_bvh8_box_child_get_aabb(root_node->children[i], root_node->origin, root_node->child_count_exponents);
|
||||
if (i == 0) {
|
||||
aabb = child_aabb;
|
||||
} else {
|
||||
aabb = rti_aabb::combine(aabb, child_aabb);
|
||||
}
|
||||
}
|
||||
|
||||
return aabb;
|
||||
}
|
||||
|
||||
void
|
||||
rti_file_view_radv::bvh8_get_vertices(rti_acceleration_structure_radv *acceleration_structure, uint32_t id,
|
||||
rti_vertex *vertices, rti_vertex *wireframe_vertices)
|
||||
{
|
||||
uint32_t offset = (id & (~0xf)) << 3;
|
||||
const radv_gfx12_box_node *box_node = (const radv_gfx12_box_node *)(acceleration_structure->bvh + offset);
|
||||
radv_bvh8_box_node_iterator iterator = box_node;
|
||||
for (uint32_t child_id = iterator.next(); child_id != RADV_BVH_INVALID_NODE; child_id = iterator.next()) {
|
||||
uint32_t child_offset = (child_id & (~0xf)) << 3;
|
||||
uint32_t child_type = child_id & 0xf;
|
||||
if (child_type == radv_bvh_node_box32) {
|
||||
bvh8_get_vertices(acceleration_structure, child_id, vertices, wireframe_vertices);
|
||||
} else if (child_type != radv_bvh_node_instance) {
|
||||
const BITSET_WORD *node = (const BITSET_WORD *)(acceleration_structure->bvh + child_offset);
|
||||
radv_bvh8_primitive_node_info info(node);
|
||||
for (uint32_t i = 0; i < (info.triangle_pair_count_minus_one + 1) * 2; i++) {
|
||||
rti_triangle triangle = info.triangles[i];
|
||||
if (triangle.primitive_index == RTI_PRIMITIVE_INDEX_INACTIVE)
|
||||
continue;
|
||||
|
||||
uint32_t dst_index = acceleration_structure->primitive_counts_exclusive_sum[triangle.geometry_index] +
|
||||
triangle.primitive_index;
|
||||
|
||||
if (info.procedural_mask & BITFIELD_BIT(i)) {
|
||||
rti_aabb aabb = radv_bvh8_box_child_get_aabb(box_node->children[iterator.index], box_node->origin,
|
||||
box_node->child_count_exponents);
|
||||
rti_generate_cube_vertices(wireframe_vertices + dst_index * RTI_CUBE_VERTEX_COUNT, aabb);
|
||||
rti_generate_filled_cube_vertices(vertices + dst_index * RTI_FILLED_CUBE_VERTEX_COUNT, aabb,
|
||||
triangle.geometry_index, triangle.primitive_index);
|
||||
continue;
|
||||
}
|
||||
|
||||
vertices[dst_index * 3 + 0].position = triangle.vertices[0];
|
||||
vertices[dst_index * 3 + 0].geometry_index = triangle.geometry_index;
|
||||
vertices[dst_index * 3 + 0].primitive_index = triangle.primitive_index;
|
||||
|
||||
vertices[dst_index * 3 + 1].position = triangle.vertices[1];
|
||||
vertices[dst_index * 3 + 1].geometry_index = triangle.geometry_index;
|
||||
vertices[dst_index * 3 + 1].primitive_index = triangle.primitive_index;
|
||||
|
||||
vertices[dst_index * 3 + 2].position = triangle.vertices[2];
|
||||
vertices[dst_index * 3 + 2].geometry_index = triangle.geometry_index;
|
||||
vertices[dst_index * 3 + 2].primitive_index = triangle.primitive_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rti_file_view_radv::bvh8_get_instances(rti_acceleration_structure_radv *tlas, uint32_t id)
|
||||
{
|
||||
uint32_t offset = (id & (~0xf)) << 3;
|
||||
uint32_t type = id & 0xf;
|
||||
|
||||
if (type == radv_bvh_node_box32) {
|
||||
const radv_gfx12_box_node *node = (const radv_gfx12_box_node *)(tlas->bvh + offset);
|
||||
radv_bvh8_box_node_iterator iterator = node;
|
||||
for (uint32_t child_id = iterator.next(); child_id != RADV_BVH_INVALID_NODE; child_id = iterator.next())
|
||||
bvh8_get_instances(tlas, child_id);
|
||||
} else if (type == radv_bvh_node_instance) {
|
||||
const radv_gfx12_instance_node *node = (const radv_gfx12_instance_node *)(tlas->bvh + offset);
|
||||
const radv_gfx12_instance_node_user_data *user_data = (const radv_gfx12_instance_node_user_data *)(node + 1);
|
||||
rti_acceleration_structure_radv *blas = radv_bvh8_instance_node_get_blas(this, node);
|
||||
|
||||
uint32_t primitive_vertex_count =
|
||||
blas->header.geometry_type == VK_GEOMETRY_TYPE_TRIANGLES_KHR ? 3 : RTI_FILLED_CUBE_VERTEX_COUNT;
|
||||
|
||||
rti_render_task solid_task;
|
||||
solid_task.vertex_buffer = blas->ui.vertex_buffer.get();
|
||||
solid_task.first_vertex = blas->first_vertex;
|
||||
solid_task.vertex_count = blas->primitive_count * primitive_vertex_count;
|
||||
solid_task.params.transform = radv_bvh8_instance_node_get_transform(node);
|
||||
solid_task.params.color = rti_index_to_color(user_data->instance_index);
|
||||
solid_task.flags = RTI_RENDERER_COLOR_PUSH_CONSTANT;
|
||||
tlas->ui.instance_render_list->tasks.push_back(solid_task);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *node_type_names[16] = {
|
||||
"triangle0", "triangle1", "triangle2", "triangle3", "invalid4", "box", "instance", "invalid7",
|
||||
"triangle4", "triangle5", "triangle6", "triangle7", "invalid12", "invalid13", "invalid14", "invalid15",
|
||||
};
|
||||
|
||||
void
|
||||
rti_file_view_radv::bvh8_draw(rti_acceleration_structure_radv *acceleration_structure, uint32_t id)
|
||||
{
|
||||
uint32_t offset = (id & (~0xf)) << 3;
|
||||
uint32_t type = id & 0xf;
|
||||
|
||||
bool selected = acceleration_structure->radv_ui.selected_node_id == id;
|
||||
|
||||
ImGuiTreeNodeFlags node_flags =
|
||||
ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
|
||||
if (type != radv_bvh_node_box32)
|
||||
node_flags |= ImGuiTreeNodeFlags_Leaf;
|
||||
if (selected)
|
||||
node_flags |= ImGuiTreeNodeFlags_Selected;
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
if (acceleration_structure->radv_ui.open_path.find(id) != acceleration_structure->radv_ui.open_path.end())
|
||||
ImGui::SetNextItemOpen(true);
|
||||
|
||||
char tree_node_id[32];
|
||||
sprintf(tree_node_id, "%x %x", type, offset);
|
||||
|
||||
bool open = ImGui::TreeNodeEx(tree_node_id, node_flags, "%s", node_type_names[type]);
|
||||
|
||||
if (ImGui::IsItemClicked())
|
||||
acceleration_structure->radv_ui.selected_node_id = id;
|
||||
|
||||
ImGui::TableNextColumnText("0x%x", offset);
|
||||
|
||||
if (open) {
|
||||
if (type == radv_bvh_node_box32) {
|
||||
const radv_gfx12_box_node *node = (const radv_gfx12_box_node *)(acceleration_structure->bvh + offset);
|
||||
radv_bvh8_box_node_iterator iterator = node;
|
||||
for (uint32_t child_id = iterator.next(); child_id != RADV_BVH_INVALID_NODE; child_id = iterator.next())
|
||||
bvh8_draw(acceleration_structure, child_id);
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (acceleration_structure->radv_ui.scroll_to_selected_node && selected) {
|
||||
ImGui::SetScrollHereY();
|
||||
acceleration_structure->radv_ui.scroll_to_selected_node = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rti_amd_draw_box8_child_info(const char *table_id, radv_gfx12_box_child child, vec3 origin,
|
||||
uint32_t child_count_exponents)
|
||||
{
|
||||
if (ImGui::BeginTable(table_id, 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableNextColumnText("cull_flags");
|
||||
ImGui::TableNextColumnText("0x%x", child.dword0 >> 24);
|
||||
|
||||
ImGui::TableNextColumnText("cull_mask");
|
||||
ImGui::TableNextColumnText("0x%x", child.dword1 >> 24);
|
||||
|
||||
ImGui::TableNextColumnText("type");
|
||||
ImGui::TableNextColumnText("%s", node_type_names[(child.dword2 >> 24) & 0xF]);
|
||||
|
||||
ImGui::TableNextColumnText("stride_128b");
|
||||
ImGui::TableNextColumnText("0x%x", child.dword1 >> 28);
|
||||
|
||||
struct rti_aabb aabb = radv_bvh8_box_child_get_aabb(child, origin, child_count_exponents);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
bool min_open = ImGui::TreeNodeEx("min");
|
||||
ImGui::TableNextColumnText("(0x%x, 0x%x, 0x%x)", (child.dword0 >> 0) & 0xfff, (child.dword0 >> 12) & 0xfff,
|
||||
(child.dword1 >> 0) & 0xfff);
|
||||
if (min_open) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumnText("(%f, %f, %f)", aabb.min.x, aabb.min.y, aabb.min.z);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
bool max_open = ImGui::TreeNodeEx("max");
|
||||
ImGui::TableNextColumnText("(0x%x, 0x%x, 0x%x)", (child.dword1 >> 12) & 0xfff, (child.dword2 >> 0) & 0xfff,
|
||||
(child.dword2 >> 12) & 0xfff);
|
||||
if (max_open) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumnText("(%f, %f, %f)", aabb.max.x, aabb.max.y, aabb.max.z);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rti_file_view_radv::bvh8_draw_node_info(const rti_acceleration_structure_radv *acceleration_structure, uint32_t id)
|
||||
{
|
||||
|
||||
uint32_t offset = (acceleration_structure->radv_ui.selected_node_id & (~0xf)) << 3;
|
||||
uint32_t type = acceleration_structure->radv_ui.selected_node_id & 0xf;
|
||||
|
||||
if (type == radv_bvh_node_box32) {
|
||||
const radv_gfx12_box_node *node = (const radv_gfx12_box_node *)(acceleration_structure->bvh + offset);
|
||||
|
||||
char tmp[64];
|
||||
sprintf(tmp, "box8 node (0x%x)", offset);
|
||||
|
||||
ImGui::SeparatorText(tmp);
|
||||
|
||||
if (ImGui::BeginTable("box8 node properties", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableNextColumnText("internal_base_id");
|
||||
ImGui::TableNextColumnText("0x%x", node->internal_base_id);
|
||||
|
||||
ImGui::TableNextColumnText("primitive_base_id");
|
||||
ImGui::TableNextColumnText("0x%x", node->primitive_base_id);
|
||||
|
||||
ImGui::TableNextColumnText("origin");
|
||||
ImGui::TableNextColumnText("(%f, %f, %f)", node->origin.x, node->origin.y, node->origin.z);
|
||||
|
||||
ImGui::TableNextColumnText("child_count - 1");
|
||||
ImGui::TableNextColumnText("%u", node->child_count_exponents >> 28);
|
||||
|
||||
ImGui::TableNextColumnText("exponents");
|
||||
ImGui::TableNextColumnText("(0x%x, 0x%x, 0x%x)", (node->child_count_exponents >> 0) & 0xFF,
|
||||
(node->child_count_exponents >> 8) & 0xFF,
|
||||
(node->child_count_exponents >> 16) & 0xFF);
|
||||
|
||||
ImGui::TableNextColumnText("obb_matrix_index");
|
||||
ImGui::TableNextColumnText("0x%x", node->obb_matrix_index);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i <= node->child_count_exponents >> 28; i++) {
|
||||
sprintf(tmp, "child[%u]", i);
|
||||
ImGui::SeparatorText(tmp);
|
||||
|
||||
sprintf(tmp, "child[%u] properties", i);
|
||||
rti_amd_draw_box8_child_info(tmp, node->children[i], node->origin, node->child_count_exponents);
|
||||
}
|
||||
} else if (type == radv_bvh_node_instance) {
|
||||
const radv_gfx12_instance_node *node = (const radv_gfx12_instance_node *)(acceleration_structure->bvh + offset);
|
||||
|
||||
char tmp[64];
|
||||
sprintf(tmp, "instance node (0x%x)", offset);
|
||||
|
||||
ImGui::SeparatorText(tmp);
|
||||
|
||||
if (ImGui::BeginTable("instance node properties", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
ImGui::TableNextColumnText("wto_matrix.row[%u]", i);
|
||||
ImGui::TableNextColumnText("(%f, %f, %f, %f)", node->wto_matrix.values[i][0], node->wto_matrix.values[i][1],
|
||||
node->wto_matrix.values[i][2], node->wto_matrix.values[i][3]);
|
||||
}
|
||||
|
||||
ImGui::TableNextColumnText("pointer_flags_bvh_addr");
|
||||
ImGui::TableNextColumn();
|
||||
sprintf(tmp, "0x%" PRIx64, node->pointer_flags_bvh_addr);
|
||||
if (ImGui::TextLink(tmp)) {
|
||||
rti_acceleration_structure_radv *blas = radv_bvh8_instance_node_get_blas(this, node);
|
||||
if (blas->ui.opened)
|
||||
blas->ui.request_focus = true;
|
||||
blas->ui.opened = true;
|
||||
}
|
||||
|
||||
ImGui::TableNextColumnText("pointer_flags");
|
||||
ImGui::TableNextColumnText(
|
||||
"%s%s%s%s%s%s", (node->pointer_flags_bvh_addr & RADV_BLAS_POINTER_FORCE_OPAQUE) ? "force_opaque " : "",
|
||||
(node->pointer_flags_bvh_addr & RADV_BLAS_POINTER_FORCE_NON_OPAQUE) ? "force_non_opaque " : "",
|
||||
(node->pointer_flags_bvh_addr & RADV_BLAS_POINTER_DISABLE_TRI_CULL) ? "disable_tri_cull " : "",
|
||||
(node->pointer_flags_bvh_addr & RADV_BLAS_POINTER_FLIP_FACING) ? "flip_facing " : "",
|
||||
(node->pointer_flags_bvh_addr & RADV_BLAS_POINTER_SKIP_TRIANGLES) ? "skip_triangles " : "",
|
||||
(node->pointer_flags_bvh_addr & RADV_BLAS_POINTER_SKIP_AABBS) ? "skip_aabbs " : "");
|
||||
|
||||
ImGui::TableNextColumnText("cull_mask");
|
||||
ImGui::TableNextColumnText("0x%x", node->cull_mask_user_data >> 24);
|
||||
|
||||
ImGui::TableNextColumnText("user_data");
|
||||
ImGui::TableNextColumnText("0x%x", node->cull_mask_user_data & 0xFFFFFF);
|
||||
|
||||
ImGui::TableNextColumnText("origin");
|
||||
ImGui::TableNextColumnText("(%f, %f, %f)", node->origin.x, node->origin.y, node->origin.z);
|
||||
|
||||
ImGui::TableNextColumnText("child_count - 1");
|
||||
ImGui::TableNextColumnText("%u", node->child_count_exponents >> 28);
|
||||
|
||||
ImGui::TableNextColumnText("exponents");
|
||||
ImGui::TableNextColumnText("(0x%x, 0x%x, 0x%x)", (node->child_count_exponents >> 0) & 0xFF,
|
||||
(node->child_count_exponents >> 8) & 0xFF,
|
||||
(node->child_count_exponents >> 16) & 0xFF);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i <= node->child_count_exponents >> 28; i++) {
|
||||
sprintf(tmp, "child[%u]", i);
|
||||
ImGui::SeparatorText(tmp);
|
||||
|
||||
sprintf(tmp, "child[%u] properties", i);
|
||||
rti_amd_draw_box8_child_info(tmp, node->children[i], node->origin, node->child_count_exponents);
|
||||
}
|
||||
} else {
|
||||
const BITSET_WORD *node = (const BITSET_WORD *)(acceleration_structure->bvh + offset);
|
||||
radv_bvh8_primitive_node_info info(node);
|
||||
|
||||
char tmp[64];
|
||||
sprintf(tmp, "primitive node (0x%x)", offset);
|
||||
|
||||
ImGui::SeparatorText(tmp);
|
||||
|
||||
if (ImGui::BeginTable("primitive node properties", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableNextColumnText("vertex_bits - 1");
|
||||
ImGui::TableNextColumnText("(%u, %u, %u)", info.vertex_bits_minus_one[0], info.vertex_bits_minus_one[1],
|
||||
info.vertex_bits_minus_one[2]);
|
||||
|
||||
ImGui::TableNextColumnText("trailing_zero_bits");
|
||||
ImGui::TableNextColumnText("%u", info.trailing_zero_bits);
|
||||
|
||||
ImGui::TableNextColumnText("geometry_index_base_bits / 2");
|
||||
ImGui::TableNextColumnText("%u", info.geometry_index_base_bits_div_2);
|
||||
|
||||
ImGui::TableNextColumnText("geometry_index_bits / 2");
|
||||
ImGui::TableNextColumnText("%u", info.geometry_index_bits_div_2);
|
||||
|
||||
ImGui::TableNextColumnText("triangle_pair_count - 1");
|
||||
ImGui::TableNextColumnText("%u", info.triangle_pair_count_minus_one);
|
||||
|
||||
ImGui::TableNextColumnText("primitive_index_base_bits");
|
||||
ImGui::TableNextColumnText("%u", info.primitive_index_base_bits);
|
||||
|
||||
ImGui::TableNextColumnText("primitive_index_bits");
|
||||
ImGui::TableNextColumnText("%u", info.primitive_index_bits);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i <= info.triangle_pair_count_minus_one; i++) {
|
||||
sprintf(tmp, "pair_desc[%u]", i);
|
||||
ImGui::SeparatorText(tmp);
|
||||
|
||||
uint32_t pair_offset = 1024 - (i + 1) * RADV_GFX12_PRIMITIVE_NODE_PAIR_DESC_SIZE;
|
||||
|
||||
sprintf(tmp, "pair_desc[%u] properties", i);
|
||||
if (ImGui::BeginTable(tmp, 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableNextColumnText("flags");
|
||||
ImGui::TableNextColumnText("%s%s%s%s%s", BITSET_EXTRACT(node, pair_offset + 0, 1) ? "prim_range_stop " : "",
|
||||
BITSET_EXTRACT(node, pair_offset + 15, 1) ? "tri0_double_sided " : "",
|
||||
BITSET_EXTRACT(node, pair_offset + 16, 1) ? "tri0_opaque " : "",
|
||||
BITSET_EXTRACT(node, pair_offset + 1, 1) ? "tri1_double_sided " : "",
|
||||
BITSET_EXTRACT(node, pair_offset + 2, 1) ? "tri1_opaque " : "");
|
||||
|
||||
for (uint32_t j = 0; j < 2; j++) {
|
||||
uint32_t triangle_index = i * 2 + j;
|
||||
uint32_t vertex_indices_offset = pair_offset + (triangle_index == 0 ? 17 : 3);
|
||||
|
||||
sprintf(tmp, "tri%u vertices", j);
|
||||
ImGui::TableNextColumn();
|
||||
bool vertices_open = ImGui::TreeNodeEx(tmp);
|
||||
uint32_t vertex_indices[] = {
|
||||
BITSET_EXTRACT(node, vertex_indices_offset + 0, 4),
|
||||
BITSET_EXTRACT(node, vertex_indices_offset + 4, 4),
|
||||
BITSET_EXTRACT(node, vertex_indices_offset + 8, 4),
|
||||
};
|
||||
ImGui::TableNextColumnText("(%u, %u, %u)", vertex_indices[0], vertex_indices[1], vertex_indices[2]);
|
||||
if (vertices_open) {
|
||||
for (uint32_t k = 0; k < 3; k++) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumnText("(%f, %f, %f)", info.triangles[triangle_index].vertices[k].x,
|
||||
info.triangles[triangle_index].vertices[k].y,
|
||||
info.triangles[triangle_index].vertices[k].z);
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::TableNextColumnText("tri%u geometry_index", j);
|
||||
ImGui::TableNextColumnText("0x%x", info.triangles[triangle_index].geometry_index);
|
||||
|
||||
ImGui::TableNextColumnText("tri%u primitive_index", j);
|
||||
ImGui::TableNextColumnText("0x%x", info.triangles[triangle_index].primitive_index);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rti_file_view_radv::bvh8_render_selection(rti_acceleration_structure_radv *acceleration_structure)
|
||||
{
|
||||
rti_render_task selection_task;
|
||||
selection_task.type = rti_render_task_type_thick_wireframe;
|
||||
selection_task.vertex_buffer = acceleration_structure->ui.vertex_buffer.get();
|
||||
selection_task.vertex_count = 3;
|
||||
selection_task.params.color = app->selection_color;
|
||||
selection_task.flags = RTI_RENDERER_COLOR_PUSH_CONSTANT;
|
||||
selection_task.wait_for_prev = true;
|
||||
|
||||
if (acceleration_structure->wireframe_first_vertex != acceleration_structure->first_vertex) {
|
||||
selection_task.type = rti_render_task_type_thick_lines;
|
||||
selection_task.vertex_count = RTI_CUBE_VERTEX_COUNT;
|
||||
}
|
||||
|
||||
uint32_t offset = (acceleration_structure->radv_ui.selected_node_id & (~0xf)) << 3;
|
||||
uint32_t type = acceleration_structure->radv_ui.selected_node_id & 0xf;
|
||||
|
||||
if (type == radv_bvh_node_box32) {
|
||||
const radv_gfx12_box_node *node = (const radv_gfx12_box_node *)(acceleration_structure->bvh + offset);
|
||||
radv_bvh8_box_node_iterator iterator = node;
|
||||
for (uint32_t child_id = iterator.next(); child_id != RADV_BVH_INVALID_NODE; child_id = iterator.next()) {
|
||||
render_aabb(selection_task, radv_bvh8_box_child_get_aabb(node->children[iterator.index], node->origin,
|
||||
node->child_count_exponents));
|
||||
selection_task.wait_for_prev = false;
|
||||
}
|
||||
} else if (type != radv_bvh_node_instance) {
|
||||
const BITSET_WORD *node = (const BITSET_WORD *)(acceleration_structure->bvh + offset);
|
||||
radv_bvh8_primitive_node_info info(node);
|
||||
for (uint32_t i = 0; i < (info.triangle_pair_count_minus_one + 1) * 2; i++) {
|
||||
rti_triangle triangle = info.triangles[i];
|
||||
if (triangle.primitive_index == RTI_PRIMITIVE_INDEX_INACTIVE)
|
||||
continue;
|
||||
|
||||
uint32_t index =
|
||||
acceleration_structure->primitive_counts_exclusive_sum[triangle.geometry_index] + triangle.primitive_index;
|
||||
if (info.procedural_mask & BITFIELD_BIT(i))
|
||||
selection_task.first_vertex = index * RTI_CUBE_VERTEX_COUNT;
|
||||
else
|
||||
selection_task.first_vertex = index * 3;
|
||||
|
||||
selection_task.first_vertex += acceleration_structure->wireframe_first_vertex;
|
||||
|
||||
render(selection_task);
|
||||
selection_task.wait_for_prev = false;
|
||||
}
|
||||
} else {
|
||||
const radv_gfx12_instance_node *node = (const radv_gfx12_instance_node *)(acceleration_structure->bvh + offset);
|
||||
selection_task.params.transform = radv_bvh8_instance_node_get_transform(node);
|
||||
render_aabb(selection_task, radv_bvh8_instance_node_get_blas(this, node)->aabb);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rti_file_view_radv::bvh8_get_instance_info(const rti_acceleration_structure_radv *acceleration_structure, uint32_t id,
|
||||
rti_mat4 *transform, rti_acceleration_structure_radv **blas)
|
||||
{
|
||||
|
||||
uint32_t offset = (id & (~0xf)) << 3;
|
||||
const radv_gfx12_instance_node *node = (const radv_gfx12_instance_node *)(acceleration_structure->bvh + offset);
|
||||
*blas = radv_bvh8_instance_node_get_blas(this, node);
|
||||
*transform = mat3x4_to_rti(node->wto_matrix);
|
||||
}
|
||||
791
src/tool/rtinspector/rti_app.cpp
Normal file
791
src/tool/rtinspector/rti_app.cpp
Normal file
|
|
@ -0,0 +1,791 @@
|
|||
/*
|
||||
* Copyright © 2026 Valve Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "rti_app.h"
|
||||
#include "rti_file_view.h"
|
||||
#include "rti_util.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "shaders/rti_shader_interface.h"
|
||||
#include "util/os_time.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
#include <SDL3/SDL_dialog.h>
|
||||
#include <SDL3/SDL_stdinc.h>
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
|
||||
#include "backends/imgui_impl_sdl3.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
|
||||
static const uint32_t renderer_vert_spv[] = {
|
||||
#include "shaders/renderer_vert.spv.h"
|
||||
};
|
||||
|
||||
static const uint32_t renderer_frag_spv[] = {
|
||||
#include "shaders/renderer_frag.spv.h"
|
||||
};
|
||||
|
||||
#include "util/macros.h"
|
||||
|
||||
int
|
||||
rti_app_init(rti_app *app)
|
||||
{
|
||||
int64_t start_time = os_time_get_nano();
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
|
||||
app->window = SDL_CreateWindow("Ray tracing inspector", 720, 400,
|
||||
SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_MAXIMIZED);
|
||||
if (app->window == NULL) {
|
||||
fprintf(stderr, "rti: Failed to create window: %s\n", SDL_GetError());
|
||||
rti_app_finish(app);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t sdl_extensions_count = 0;
|
||||
const char *const *sdl_extensions = SDL_Vulkan_GetInstanceExtensions(&sdl_extensions_count);
|
||||
|
||||
uint32_t api_version = VK_API_VERSION_1_0;
|
||||
vkEnumerateInstanceVersion(&api_version);
|
||||
|
||||
if (api_version < VK_API_VERSION_1_4) {
|
||||
fprintf(stderr, "rti: VK_API_VERSION_1_4 is required but not supported.\n");
|
||||
rti_app_finish(app);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t layer_property_count = 0;
|
||||
vkEnumerateInstanceLayerProperties(&layer_property_count, nullptr);
|
||||
std::vector<VkLayerProperties> layer_properties(layer_property_count);
|
||||
vkEnumerateInstanceLayerProperties(&layer_property_count, layer_properties.data());
|
||||
|
||||
bool has_validation = false;
|
||||
for (const VkLayerProperties &properties : layer_properties) {
|
||||
if (!strcmp("VK_LAYER_KHRONOS_validation", properties.layerName)) {
|
||||
has_validation = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_validation)
|
||||
fprintf(stderr, "rti: Enabling VK_LAYER_KHRONOS_validation.\n");
|
||||
|
||||
VkApplicationInfo app_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
.pApplicationName = "mesa.rti",
|
||||
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
|
||||
.pEngineName = "mesa.rti",
|
||||
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
|
||||
.apiVersion = api_version,
|
||||
};
|
||||
|
||||
const char *layers[] = {"VK_LAYER_KHRONOS_validation"};
|
||||
|
||||
VkInstanceCreateInfo instance_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
.pApplicationInfo = &app_info,
|
||||
.enabledLayerCount = (uint32_t)(has_validation ? ARRAY_SIZE(layers) : 0),
|
||||
.ppEnabledLayerNames = layers,
|
||||
.enabledExtensionCount = sdl_extensions_count,
|
||||
.ppEnabledExtensionNames = sdl_extensions,
|
||||
};
|
||||
|
||||
rti_check_vk_result(vkCreateInstance(&instance_info, nullptr, &app->instance));
|
||||
|
||||
VkSurfaceKHR surface;
|
||||
if (!SDL_Vulkan_CreateSurface(app->window, app->instance, nullptr, &surface)) {
|
||||
fprintf(stderr, "rti: Failed to create VkSurfaceKHR: %s\n", SDL_GetError());
|
||||
rti_app_finish(app);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t physical_device_count = 0;
|
||||
rti_check_vk_result(vkEnumeratePhysicalDevices(app->instance, &physical_device_count, nullptr));
|
||||
std::vector<VkPhysicalDevice> physical_devices(physical_device_count);
|
||||
rti_check_vk_result(vkEnumeratePhysicalDevices(app->instance, &physical_device_count, physical_devices.data()));
|
||||
|
||||
for (VkPhysicalDevice pdev : physical_devices) {
|
||||
uint32_t extension_property_count = 0;
|
||||
vkEnumerateDeviceExtensionProperties(pdev, nullptr, &extension_property_count, nullptr);
|
||||
std::vector<VkExtensionProperties> extension_properties(extension_property_count);
|
||||
vkEnumerateDeviceExtensionProperties(pdev, nullptr, &extension_property_count, extension_properties.data());
|
||||
|
||||
bool has_multi_draw = false;
|
||||
for (const VkExtensionProperties &properties : extension_properties) {
|
||||
if (!strcmp(VK_EXT_MULTI_DRAW_EXTENSION_NAME, properties.extensionName)) {
|
||||
has_multi_draw = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_multi_draw)
|
||||
continue;
|
||||
|
||||
VkPhysicalDeviceMultiDrawFeaturesEXT multi_draw_features = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceVulkan11Features features11 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
|
||||
.pNext = &multi_draw_features,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceVulkan12Features features12 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
|
||||
.pNext = &features11,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceVulkan13Features features13 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
|
||||
.pNext = &features12,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceVulkan14Features features14 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES,
|
||||
.pNext = &features13,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceFeatures2 features2 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
.pNext = &features14,
|
||||
};
|
||||
vkGetPhysicalDeviceFeatures2(pdev, &features2);
|
||||
|
||||
if (!features2.features.fillModeNonSolid || !features2.features.wideLines || !features11.shaderDrawParameters ||
|
||||
!features12.scalarBlockLayout || !features13.dynamicRendering || !features14.maintenance5 ||
|
||||
!multi_draw_features.multiDraw)
|
||||
continue;
|
||||
|
||||
uint32_t queue_family_property_count = 0;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(pdev, &queue_family_property_count, nullptr);
|
||||
std::vector<VkQueueFamilyProperties> queue_family_properties(queue_family_property_count);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(pdev, &queue_family_property_count, queue_family_properties.data());
|
||||
|
||||
for (uint32_t i = 0; i < queue_family_property_count; i++) {
|
||||
if (queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||||
app->queue_family_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
app->pdev = pdev;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!app->pdev) {
|
||||
fprintf(stderr, "rti: Failed to find suitable VkPhysicalDevice\n");
|
||||
rti_app_finish(app);
|
||||
return 1;
|
||||
}
|
||||
|
||||
VkPhysicalDeviceMultiDrawPropertiesEXT multi_draw_properties = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceProperties2 properties2 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
||||
.pNext = &multi_draw_properties,
|
||||
};
|
||||
|
||||
vkGetPhysicalDeviceProperties2(app->pdev, &properties2);
|
||||
|
||||
VkSampleCountFlags sample_counts = properties2.properties.limits.framebufferColorSampleCounts &
|
||||
properties2.properties.limits.framebufferDepthSampleCounts;
|
||||
app->sample_count = 1;
|
||||
if (sample_counts & VK_SAMPLE_COUNT_16_BIT)
|
||||
app->sample_count = 16;
|
||||
else if (sample_counts & VK_SAMPLE_COUNT_8_BIT)
|
||||
app->sample_count = 8;
|
||||
else if (sample_counts & VK_SAMPLE_COUNT_4_BIT)
|
||||
app->sample_count = 4;
|
||||
else if (sample_counts & VK_SAMPLE_COUNT_2_BIT)
|
||||
app->sample_count = 2;
|
||||
|
||||
fprintf(stderr, "rti: Using GPU: %s\n", properties2.properties.deviceName);
|
||||
|
||||
float thick_line_width = fmin(3.0, properties2.properties.limits.lineWidthRange[1]);
|
||||
|
||||
app->max_multi_draw_count = multi_draw_properties.maxMultiDrawCount;
|
||||
|
||||
float priority = 1.0;
|
||||
VkDeviceQueueCreateInfo queue_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
.queueFamilyIndex = app->queue_family_index,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &priority,
|
||||
};
|
||||
|
||||
const char *extensions[] = {VK_EXT_MULTI_DRAW_EXTENSION_NAME, VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
||||
|
||||
VkPhysicalDeviceFeatures features = {
|
||||
.fillModeNonSolid = true,
|
||||
.wideLines = true,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceVulkan11Features features11 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
|
||||
.shaderDrawParameters = true,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceVulkan12Features features12 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
|
||||
.pNext = &features11,
|
||||
.scalarBlockLayout = true,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceVulkan13Features features13 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
|
||||
.pNext = &features12,
|
||||
.dynamicRendering = true,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceVulkan14Features features14 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES,
|
||||
.pNext = &features13,
|
||||
.maintenance5 = true,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceMultiDrawFeaturesEXT multi_draw_features = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT,
|
||||
.pNext = &features14,
|
||||
.multiDraw = true,
|
||||
};
|
||||
|
||||
VkDeviceCreateInfo device_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
.pNext = &multi_draw_features,
|
||||
.queueCreateInfoCount = 1,
|
||||
.pQueueCreateInfos = &queue_info,
|
||||
.enabledExtensionCount = ARRAY_SIZE(extensions),
|
||||
.ppEnabledExtensionNames = extensions,
|
||||
.pEnabledFeatures = &features,
|
||||
};
|
||||
rti_check_vk_result(vkCreateDevice(app->pdev, &device_info, nullptr, &app->device));
|
||||
|
||||
vkGetDeviceQueue(app->device, app->queue_family_index, 0, &app->queue);
|
||||
|
||||
app->vkCmdDrawMultiEXT = (PFN_vkCmdDrawMultiEXT)vkGetDeviceProcAddr(app->device, "vkCmdDrawMultiEXT");
|
||||
|
||||
VkDescriptorPoolSize pool_sizes[] = {
|
||||
{VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE + RTI_MAX_OPEN_VIEWS},
|
||||
{VK_DESCRIPTOR_TYPE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE},
|
||||
};
|
||||
VkDescriptorPoolCreateInfo pool_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
|
||||
.maxSets = IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE + IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE +
|
||||
RTI_MAX_OPEN_VIEWS,
|
||||
.poolSizeCount = ARRAY_SIZE(pool_sizes),
|
||||
.pPoolSizes = pool_sizes,
|
||||
};
|
||||
|
||||
VkDescriptorPool descriptor_pool;
|
||||
rti_check_vk_result(vkCreateDescriptorPool(app->device, &pool_info, nullptr, &descriptor_pool));
|
||||
|
||||
VkFormat request_formats[] = {VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM,
|
||||
VK_FORMAT_R8G8B8_UNORM};
|
||||
VkColorSpaceKHR color_space = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
|
||||
|
||||
VkClearColorValue clear_color_value = {
|
||||
.float32 = {1, 1, 1, 1},
|
||||
};
|
||||
|
||||
VkClearValue clear_value = {
|
||||
.color = clear_color_value,
|
||||
};
|
||||
|
||||
app->imgui_window.ClearValue = clear_value;
|
||||
app->imgui_window.Surface = surface;
|
||||
app->imgui_window.SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(app->pdev, surface, request_formats,
|
||||
ARRAY_SIZE(request_formats), color_space);
|
||||
|
||||
VkPresentModeKHR present_modes[] = {VK_PRESENT_MODE_FIFO_KHR};
|
||||
app->imgui_window.PresentMode =
|
||||
ImGui_ImplVulkanH_SelectPresentMode(app->pdev, surface, present_modes, ARRAY_SIZE(present_modes));
|
||||
|
||||
int w, h;
|
||||
SDL_GetWindowSize(app->window, &w, &h);
|
||||
ImGui_ImplVulkanH_CreateOrResizeWindow(app->instance, app->pdev, app->device, &app->imgui_window,
|
||||
app->queue_family_index, nullptr, w, h, 2, 0);
|
||||
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
// io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||
io.Fonts->AddFontDefaultVector();
|
||||
io.IniFilename = nullptr;
|
||||
|
||||
ImGui::StyleColorsLight();
|
||||
|
||||
ImGuiStyle *style = &ImGui::GetStyle();
|
||||
style->WindowBorderSize = style->_MainScale;
|
||||
style->FrameBorderSize = style->_MainScale;
|
||||
style->PopupBorderSize = style->_MainScale;
|
||||
style->WindowRounding = 8.0f;
|
||||
style->ChildRounding = 8.0f;
|
||||
style->FrameRounding = 8.0f;
|
||||
style->PopupRounding = 8.0f;
|
||||
style->GrabRounding = 8.0f;
|
||||
style->ImageBorderSize = 0;
|
||||
style->WindowPadding = ImVec2(8, 4);
|
||||
style->ItemSpacing = ImVec2(8, 6);
|
||||
style->DockingNodeHasCloseButton = false;
|
||||
style->Colors[ImGuiCol_DockingEmptyBg] = ImVec4(1, 1, 1, 1);
|
||||
|
||||
ImGui_ImplSDL3_InitForVulkan(app->window);
|
||||
ImGui_ImplVulkan_InitInfo init_info = {};
|
||||
init_info.Instance = app->instance;
|
||||
init_info.PhysicalDevice = app->pdev;
|
||||
init_info.Device = app->device;
|
||||
init_info.QueueFamily = app->queue_family_index;
|
||||
init_info.Queue = app->queue;
|
||||
init_info.PipelineCache = VK_NULL_HANDLE;
|
||||
init_info.DescriptorPool = descriptor_pool;
|
||||
init_info.MinImageCount = 2;
|
||||
init_info.ImageCount = app->imgui_window.ImageCount;
|
||||
init_info.Allocator = nullptr;
|
||||
init_info.PipelineInfoMain.RenderPass = app->imgui_window.RenderPass;
|
||||
init_info.PipelineInfoMain.Subpass = 0;
|
||||
init_info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
init_info.CheckVkResultFn = nullptr;
|
||||
ImGui_ImplVulkan_Init(&init_info);
|
||||
|
||||
VkCommandPoolCreateInfo command_pool_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.queueFamilyIndex = app->queue_family_index,
|
||||
};
|
||||
rti_check_vk_result(vkCreateCommandPool(app->device, &command_pool_info, nullptr, &app->staging_command_pool));
|
||||
|
||||
/* Start uploading data to the GPU before compiling pipelines to reduce startup time. */
|
||||
app->cube_vertex_buffer =
|
||||
rti_create_backed_buffer(app, RTI_CUBE_VERTEX_COUNT * sizeof(rti_vertex), rti_memory_type_device_local,
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, false);
|
||||
rti_vertex *cube_vertices = (rti_vertex *)app->upload_memory(app->cube_vertex_buffer);
|
||||
rti_generate_cube_vertices(cube_vertices, {.min = {0, 0, 0}, .max = {1, 1, 1}});
|
||||
|
||||
app->filled_cube_vertex_buffer =
|
||||
rti_create_backed_buffer(app, RTI_FILLED_CUBE_VERTEX_COUNT * sizeof(rti_vertex), rti_memory_type_device_local,
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, false);
|
||||
rti_vertex *filled_cube_vertices = (rti_vertex *)app->upload_memory(app->filled_cube_vertex_buffer);
|
||||
rti_generate_filled_cube_vertices(filled_cube_vertices, {.min = {0, 0, 0}, .max = {1, 1, 1}}, 0, 0);
|
||||
|
||||
app->flush_upload_memory();
|
||||
|
||||
VkDescriptorSetLayoutBinding binding = {
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
};
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo set_layout_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = 1,
|
||||
.pBindings = &binding,
|
||||
};
|
||||
|
||||
rti_check_vk_result(vkCreateDescriptorSetLayout(app->device, &set_layout_info, nullptr, &app->renderer_set_layout));
|
||||
|
||||
VkPushConstantRange push_constant_range = {
|
||||
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.size = sizeof(rti_push_constants),
|
||||
};
|
||||
|
||||
VkPipelineLayoutCreateInfo pipeline_layout_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.setLayoutCount = 1,
|
||||
.pSetLayouts = &app->renderer_set_layout,
|
||||
.pushConstantRangeCount = 1,
|
||||
.pPushConstantRanges = &push_constant_range,
|
||||
};
|
||||
|
||||
rti_check_vk_result(
|
||||
vkCreatePipelineLayout(app->device, &pipeline_layout_info, nullptr, &app->renderer_pipeline_layout));
|
||||
|
||||
VkFormat color_attachment_format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
VkPipelineRenderingCreateInfo rendering_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachmentFormats = &color_attachment_format,
|
||||
.depthAttachmentFormat = VK_FORMAT_D32_SFLOAT,
|
||||
};
|
||||
|
||||
VkShaderModuleCreateInfo module_infos[2] = {
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
.codeSize = sizeof(renderer_vert_spv),
|
||||
.pCode = renderer_vert_spv,
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
.codeSize = sizeof(renderer_frag_spv),
|
||||
.pCode = renderer_frag_spv,
|
||||
},
|
||||
};
|
||||
|
||||
VkPipelineShaderStageCreateInfo stage_infos[2] = {
|
||||
{.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.pNext = &module_infos[0],
|
||||
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
||||
.pName = "main"},
|
||||
{.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.pNext = &module_infos[1],
|
||||
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.pName = "main"},
|
||||
};
|
||||
|
||||
VkVertexInputBindingDescription vertex_binding_info = {
|
||||
.binding = 0,
|
||||
.stride = sizeof(rti_vertex),
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
|
||||
};
|
||||
|
||||
VkVertexInputAttributeDescription vertex_attribute_infos[] = {
|
||||
{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
||||
.offset = offsetof(rti_vertex, position),
|
||||
},
|
||||
{
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32_UINT,
|
||||
.offset = offsetof(rti_vertex, geometry_index),
|
||||
},
|
||||
{
|
||||
.location = 2,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32_UINT,
|
||||
.offset = offsetof(rti_vertex, primitive_index),
|
||||
},
|
||||
};
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertex_input_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = 1,
|
||||
.pVertexBindingDescriptions = &vertex_binding_info,
|
||||
.vertexAttributeDescriptionCount = ARRAY_SIZE(vertex_attribute_infos),
|
||||
.pVertexAttributeDescriptions = vertex_attribute_infos,
|
||||
};
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||
};
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewpoirt_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.viewportCount = 1,
|
||||
.scissorCount = 1,
|
||||
};
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterization_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.polygonMode = VK_POLYGON_MODE_FILL,
|
||||
.cullMode = VK_CULL_MODE_NONE,
|
||||
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
||||
.lineWidth = 1,
|
||||
};
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisample_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.rasterizationSamples = (VkSampleCountFlagBits)app->sample_count,
|
||||
.minSampleShading = 1.0,
|
||||
};
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo depth_stencil_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
.depthTestEnable = true,
|
||||
.depthWriteEnable = true,
|
||||
.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL,
|
||||
.minDepthBounds = 0.0,
|
||||
.maxDepthBounds = 1.0,
|
||||
};
|
||||
|
||||
VkPipelineColorBlendAttachmentState color_blend_attachment_info = {
|
||||
.colorWriteMask =
|
||||
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
|
||||
};
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo color_blend_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &color_blend_attachment_info,
|
||||
};
|
||||
|
||||
VkDynamicState dynamic_states[] = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR,
|
||||
};
|
||||
|
||||
VkPipelineDynamicStateCreateInfo dynamic_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.dynamicStateCount = ARRAY_SIZE(dynamic_states),
|
||||
.pDynamicStates = dynamic_states,
|
||||
};
|
||||
|
||||
VkGraphicsPipelineCreateInfo graphics_pipeline_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = &rendering_info,
|
||||
.stageCount = ARRAY_SIZE(stage_infos),
|
||||
.pStages = stage_infos,
|
||||
.pVertexInputState = &vertex_input_info,
|
||||
.pInputAssemblyState = &input_assembly_info,
|
||||
.pViewportState = &viewpoirt_info,
|
||||
.pRasterizationState = &rasterization_info,
|
||||
.pMultisampleState = &multisample_info,
|
||||
.pDepthStencilState = &depth_stencil_info,
|
||||
.pColorBlendState = &color_blend_info,
|
||||
.pDynamicState = &dynamic_info,
|
||||
.layout = app->renderer_pipeline_layout,
|
||||
};
|
||||
|
||||
rti_check_vk_result(
|
||||
vkCreateGraphicsPipelines(app->device, VK_NULL_HANDLE, 1, &graphics_pipeline_info, nullptr, &app->fill_pipeline));
|
||||
|
||||
rasterization_info.polygonMode = VK_POLYGON_MODE_LINE;
|
||||
rti_check_vk_result(vkCreateGraphicsPipelines(app->device, VK_NULL_HANDLE, 1, &graphics_pipeline_info, nullptr,
|
||||
&app->wireframe_pipeline));
|
||||
|
||||
input_assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
||||
rti_check_vk_result(vkCreateGraphicsPipelines(app->device, VK_NULL_HANDLE, 1, &graphics_pipeline_info, nullptr,
|
||||
&app->lines_pipeline));
|
||||
|
||||
rasterization_info.lineWidth = thick_line_width;
|
||||
input_assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
rti_check_vk_result(vkCreateGraphicsPipelines(app->device, VK_NULL_HANDLE, 1, &graphics_pipeline_info, nullptr,
|
||||
&app->thick_wireframe_pipeline));
|
||||
|
||||
input_assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
||||
rti_check_vk_result(vkCreateGraphicsPipelines(app->device, VK_NULL_HANDLE, 1, &graphics_pipeline_info, nullptr,
|
||||
&app->thick_lines_pipeline));
|
||||
|
||||
app->finish_upload_memory();
|
||||
|
||||
int64_t end_time = os_time_get_nano();
|
||||
fprintf(stderr, "rti: Creating window state took %.2fms\n", (end_time - start_time) / 1000000.0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
rti_app_finish(rti_app *app)
|
||||
{
|
||||
vkDeviceWaitIdle(app->device);
|
||||
|
||||
vkDestroyPipeline(app->device, app->fill_pipeline, nullptr);
|
||||
vkDestroyPipeline(app->device, app->wireframe_pipeline, nullptr);
|
||||
vkDestroyPipelineLayout(app->device, app->renderer_pipeline_layout, nullptr);
|
||||
vkDestroyDescriptorSetLayout(app->device, app->renderer_set_layout, nullptr);
|
||||
|
||||
vkDestroyCommandPool(app->device, app->staging_command_pool, nullptr);
|
||||
|
||||
/* Cleanup anything potentially using Vulkan first. */
|
||||
app->open_files.clear();
|
||||
|
||||
ImGui_ImplVulkan_Shutdown();
|
||||
ImGui_ImplSDL3_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
SDL_DestroyWindow(app->window);
|
||||
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void
|
||||
rti_app_resize(rti_app *app, uint32_t width, uint32_t height)
|
||||
{
|
||||
ImGui_ImplVulkan_SetMinImageCount(2);
|
||||
ImGui_ImplVulkanH_CreateOrResizeWindow(app->instance, app->pdev, app->device, &app->imgui_window,
|
||||
app->queue_family_index, nullptr, width, height, 2, 0);
|
||||
}
|
||||
|
||||
static SDLCALL void
|
||||
rti_open_file_cb(void *userdata, const char *const *filelist, int filter)
|
||||
{
|
||||
rti_app *app = (rti_app *)userdata;
|
||||
|
||||
if (!filelist)
|
||||
return;
|
||||
|
||||
for (uint32_t i = 0; filelist[i]; i++) {
|
||||
/* O(N^2) should be fine here. Focus the file instead of opening it again if it was already opened. */
|
||||
bool already_opened = false;
|
||||
for (const auto &view : app->open_files) {
|
||||
if (view->path == filelist[i]) {
|
||||
already_opened = true;
|
||||
view->ui.request_focus = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!already_opened)
|
||||
app->open_files.push_back(rti_create_file_view(app, filelist[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static const SDL_DialogFileFilter rti_file_filters[] = {
|
||||
{"RTI file", "rti"},
|
||||
};
|
||||
|
||||
bool
|
||||
rti_app_run(rti_app *app)
|
||||
{
|
||||
if (!app->last_run_time_valid) {
|
||||
app->last_run_time = std::chrono::high_resolution_clock::now();
|
||||
app->last_run_time_valid = true;
|
||||
}
|
||||
std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now();
|
||||
uint64_t dt_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(now - app->last_run_time).count();
|
||||
app->dt = dt_ns / 1000000000.0;
|
||||
app->last_run_time = now;
|
||||
|
||||
rti_vec3 selection_colors[] = {
|
||||
{228.0 / 255.0, 3.0 / 255.0, 3.0 / 255.0}, {255.0 / 255.0, 140.0 / 255.0, 0.0 / 255.0},
|
||||
{255.0 / 255.0, 237.0 / 255.0, 0.0 / 255.0}, {0.0 / 255.0, 128.0 / 255.0, 38.0 / 255.0},
|
||||
{0.0 / 255.0, 76.0 / 255.0, 255.0 / 255.0}, {115.0 / 255.0, 41.0 / 255.0, 130.0 / 255.0},
|
||||
};
|
||||
float selection_color = app->t * 6.0;
|
||||
float selection_color_fract = selection_color - (uint64_t)selection_color;
|
||||
app->selection_color.x =
|
||||
selection_colors[((uint64_t)selection_color + 0) % ARRAY_SIZE(selection_colors)].x * (1 - selection_color_fract) +
|
||||
selection_colors[((uint64_t)selection_color + 1) % ARRAY_SIZE(selection_colors)].x * selection_color_fract;
|
||||
app->selection_color.y =
|
||||
selection_colors[((uint64_t)selection_color + 0) % ARRAY_SIZE(selection_colors)].y * (1 - selection_color_fract) +
|
||||
selection_colors[((uint64_t)selection_color + 1) % ARRAY_SIZE(selection_colors)].y * selection_color_fract;
|
||||
app->selection_color.z =
|
||||
selection_colors[((uint64_t)selection_color + 0) % ARRAY_SIZE(selection_colors)].z * (1 - selection_color_fract) +
|
||||
selection_colors[((uint64_t)selection_color + 1) % ARRAY_SIZE(selection_colors)].z * selection_color_fract;
|
||||
|
||||
ImGuiKeyChord exit_chord = ImGuiMod_Ctrl | ImGuiKey_Q;
|
||||
if (ImGui::Shortcut(exit_chord, ImGuiInputFlags_RouteGlobal))
|
||||
return true;
|
||||
|
||||
ImGuiKeyChord open_chord = ImGuiMod_Ctrl | ImGuiKey_O;
|
||||
if (ImGui::Shortcut(open_chord, ImGuiInputFlags_RouteGlobal)) {
|
||||
SDL_ShowOpenFileDialog(rti_open_file_cb, app, app->window, rti_file_filters, ARRAY_SIZE(rti_file_filters),
|
||||
nullptr, false);
|
||||
}
|
||||
|
||||
if (ImGui::BeginMainMenuBar()) {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("Open", ImGui::GetKeyChordName(open_chord))) {
|
||||
SDL_ShowOpenFileDialog(rti_open_file_cb, app, app->window, rti_file_filters, ARRAY_SIZE(rti_file_filters),
|
||||
nullptr, false);
|
||||
}
|
||||
if (ImGui::MenuItem("Exit", ImGui::GetKeyChordName(exit_chord)))
|
||||
return true;
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
uint32_t framerate = 0;
|
||||
if (app->dt != 0)
|
||||
framerate = (uint32_t)(1 / app->dt);
|
||||
|
||||
char tmp[32];
|
||||
sprintf(tmp, "%u fps", framerate);
|
||||
|
||||
ImVec2 framerate_text_size = ImGui::CalcTextSize(tmp);
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - framerate_text_size.x - 10);
|
||||
ImGui::Text("%s", tmp);
|
||||
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
ImGuiWindowClass top_level_class;
|
||||
top_level_class.ClassId = ImHashStr("top_level_class");
|
||||
top_level_class.DockingAllowUnclassed = false;
|
||||
|
||||
ImGuiID root_dockspace_id = ImGui::GetID("root_dockspace");
|
||||
ImGui::DockSpaceOverViewport(root_dockspace_id, nullptr, ImGuiDockNodeFlags_NoWindowMenuButton, &top_level_class);
|
||||
|
||||
for (const auto &view : app->open_files) {
|
||||
ImGui::SetNextWindowClass(&top_level_class);
|
||||
ImGui::SetNextWindowDockID(root_dockspace_id, ImGuiCond_FirstUseEver);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
ImGui::Begin(view->path.filename().c_str());
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
view->run();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
app->t += app->dt;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void *
|
||||
rti_app::upload_memory(std::shared_ptr<rti_backed_buffer> dst)
|
||||
{
|
||||
std::shared_ptr<rti_backed_buffer> staging_buffer =
|
||||
rti_create_backed_buffer(this, dst->size, rti_memory_type_host_visible, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, true);
|
||||
pending_staging_buffers.push_back({staging_buffer, dst});
|
||||
return staging_buffer->map;
|
||||
}
|
||||
|
||||
void
|
||||
rti_app::flush_upload_memory()
|
||||
{
|
||||
if (pending_staging_buffers.empty())
|
||||
return;
|
||||
|
||||
assert(!staging_command_buffer);
|
||||
|
||||
VkCommandBufferAllocateInfo command_buffer_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = staging_command_pool,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount = 1,
|
||||
};
|
||||
rti_check_vk_result(vkAllocateCommandBuffers(device, &command_buffer_info, &staging_command_buffer));
|
||||
|
||||
VkCommandBufferBeginInfo begin_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT,
|
||||
};
|
||||
rti_check_vk_result(vkBeginCommandBuffer(staging_command_buffer, &begin_info));
|
||||
|
||||
for (const auto &staging_buffer : pending_staging_buffers) {
|
||||
VkBufferCopy copy = {
|
||||
.size = staging_buffer.first->size,
|
||||
};
|
||||
vkCmdCopyBuffer(staging_command_buffer, staging_buffer.first->buffer, staging_buffer.second->buffer, 1, ©);
|
||||
}
|
||||
|
||||
rti_check_vk_result(vkEndCommandBuffer(staging_command_buffer));
|
||||
|
||||
VkSubmitInfo submit_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &staging_command_buffer,
|
||||
};
|
||||
rti_check_vk_result(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE));
|
||||
}
|
||||
|
||||
void
|
||||
rti_app::finish_upload_memory()
|
||||
{
|
||||
if (!staging_command_buffer)
|
||||
return;
|
||||
|
||||
rti_check_vk_result(vkQueueWaitIdle(queue));
|
||||
|
||||
vkFreeCommandBuffers(device, staging_command_pool, 1, &staging_command_buffer);
|
||||
staging_command_buffer = VK_NULL_HANDLE;
|
||||
|
||||
pending_staging_buffers.clear();
|
||||
}
|
||||
76
src/tool/rtinspector/rti_app.h
Normal file
76
src/tool/rtinspector/rti_app.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright © 2026 Valve Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "vulkan/vulkan_core.h"
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "backends/imgui_impl_vulkan.h"
|
||||
|
||||
#include "rti_file_view.h"
|
||||
#include "rti_util.h"
|
||||
|
||||
#define RTI_MAX_OPEN_VIEWS 4096
|
||||
|
||||
#define RTI_CUBE_VERTEX_COUNT (2 * 4 * 2 + 4 * 2)
|
||||
#define RTI_FILLED_CUBE_VERTEX_COUNT (3 * 2 * 6)
|
||||
|
||||
struct rti_app {
|
||||
SDL_Window *window;
|
||||
VkInstance instance;
|
||||
VkPhysicalDevice pdev;
|
||||
VkDevice device;
|
||||
VkQueue queue;
|
||||
uint32_t queue_family_index;
|
||||
ImGui_ImplVulkanH_Window imgui_window;
|
||||
std::vector<std::unique_ptr<rti_file_view>> open_files;
|
||||
VkCommandBuffer command_buffer;
|
||||
|
||||
PFN_vkCmdDrawMultiEXT vkCmdDrawMultiEXT;
|
||||
|
||||
VkCommandPool staging_command_pool;
|
||||
VkCommandBuffer staging_command_buffer = VK_NULL_HANDLE;
|
||||
std::vector<std::pair<std::shared_ptr<rti_backed_buffer>, std::shared_ptr<rti_backed_buffer>>>
|
||||
pending_staging_buffers;
|
||||
|
||||
VkDescriptorSetLayout renderer_set_layout;
|
||||
VkPipelineLayout renderer_pipeline_layout;
|
||||
VkPipeline fill_pipeline;
|
||||
VkPipeline wireframe_pipeline;
|
||||
VkPipeline thick_wireframe_pipeline;
|
||||
VkPipeline lines_pipeline;
|
||||
VkPipeline thick_lines_pipeline;
|
||||
|
||||
uint32_t sample_count;
|
||||
uint32_t max_multi_draw_count;
|
||||
|
||||
std::shared_ptr<rti_backed_buffer> cube_vertex_buffer;
|
||||
std::shared_ptr<rti_backed_buffer> filled_cube_vertex_buffer;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point last_run_time;
|
||||
bool last_run_time_valid = false;
|
||||
float t = 0;
|
||||
float dt;
|
||||
|
||||
rti_vec3 selection_color;
|
||||
|
||||
void *upload_memory(std::shared_ptr<rti_backed_buffer> dst);
|
||||
|
||||
void flush_upload_memory();
|
||||
void finish_upload_memory();
|
||||
};
|
||||
|
||||
int rti_app_init(rti_app *app);
|
||||
|
||||
void rti_app_finish(rti_app *app);
|
||||
|
||||
void rti_app_resize(rti_app *app, uint32_t width, uint32_t height);
|
||||
|
||||
bool rti_app_run(rti_app *app);
|
||||
1404
src/tool/rtinspector/rti_file_view.cpp
Normal file
1404
src/tool/rtinspector/rti_file_view.cpp
Normal file
File diff suppressed because it is too large
Load diff
259
src/tool/rtinspector/rti_file_view.h
Normal file
259
src/tool/rtinspector/rti_file_view.h
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
* Copyright © 2026 Valve Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#include "rti_util.h"
|
||||
|
||||
#include "shaders/rti_shader_interface.h"
|
||||
#include "util/rti_format.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
struct rti_app;
|
||||
|
||||
enum rti_render_task_type {
|
||||
rti_render_task_type_solid,
|
||||
rti_render_task_type_wireframe,
|
||||
rti_render_task_type_thick_wireframe,
|
||||
rti_render_task_type_lines,
|
||||
rti_render_task_type_thick_lines,
|
||||
rti_render_task_type_render_list,
|
||||
};
|
||||
|
||||
struct rti_render_list;
|
||||
|
||||
struct rti_render_task {
|
||||
rti_render_task_type type = rti_render_task_type_solid;
|
||||
union {
|
||||
const rti_backed_buffer *vertex_buffer = nullptr;
|
||||
rti_render_list *render_list;
|
||||
};
|
||||
uint32_t first_vertex = 0;
|
||||
uint32_t vertex_count = 0;
|
||||
rti_render_params params;
|
||||
uint32_t flags = 0;
|
||||
bool wait_for_prev = false;
|
||||
bool override_flags = false;
|
||||
};
|
||||
|
||||
struct rti_render_list {
|
||||
rti_app *app;
|
||||
std::vector<rti_render_task> tasks;
|
||||
std::shared_ptr<rti_backed_buffer> constants = nullptr;
|
||||
VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
|
||||
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
|
||||
|
||||
rti_render_list(rti_app *app): app(app)
|
||||
{
|
||||
}
|
||||
|
||||
~rti_render_list();
|
||||
|
||||
void build();
|
||||
};
|
||||
|
||||
struct rti_imgui_texture {
|
||||
VkDescriptorSet descriptor_set;
|
||||
|
||||
rti_imgui_texture(VkDescriptorSet descriptor_set): descriptor_set(descriptor_set)
|
||||
{
|
||||
}
|
||||
|
||||
~rti_imgui_texture();
|
||||
};
|
||||
|
||||
struct rti_viewport_state {
|
||||
std::shared_ptr<rti_imgui_texture> surface = nullptr;
|
||||
std::shared_ptr<rti_backed_image> resolved_image = nullptr;
|
||||
std::shared_ptr<rti_backed_image> cb = nullptr;
|
||||
std::shared_ptr<rti_backed_image> db = nullptr;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
std::shared_ptr<rti_render_list> render_list = nullptr;
|
||||
};
|
||||
|
||||
enum rti_visualization_color {
|
||||
rti_visualization_color_primitive_index,
|
||||
rti_visualization_color_geometry_index,
|
||||
rti_visualization_color_instance_index,
|
||||
};
|
||||
|
||||
static inline uint32_t
|
||||
rti_visualization_color_to_renderer_color(rti_visualization_color color)
|
||||
{
|
||||
switch (color) {
|
||||
case rti_visualization_color_primitive_index:
|
||||
return RTI_RENDERER_COLOR_PRIMITIVE_INDEX;
|
||||
case rti_visualization_color_geometry_index:
|
||||
return RTI_RENDERER_COLOR_GEOMETRY_INDEX;
|
||||
case rti_visualization_color_instance_index:
|
||||
return RTI_RENDERER_COLOR_PUSH_CONSTANT;
|
||||
}
|
||||
return RTI_RENDERER_COLOR_PRIMITIVE_INDEX;
|
||||
}
|
||||
|
||||
static inline rti_vec3
|
||||
rti_index_to_color(uint32_t index)
|
||||
{
|
||||
return {
|
||||
.x = ((index * 0xd83f0930) >> 24) / 255.0f,
|
||||
.y = ((index * 0x8fa9836b) >> 24) / 255.0f,
|
||||
.z = ((index * 0x3037f8ad) >> 24) / 255.0f,
|
||||
};
|
||||
}
|
||||
|
||||
enum rti_camera_type {
|
||||
rti_camera_type_first_person,
|
||||
rti_camera_type_pivot,
|
||||
};
|
||||
|
||||
struct rti_file_view;
|
||||
|
||||
struct rti_acceleration_structure {
|
||||
rti_app *app = nullptr;
|
||||
rti_file_view *view = nullptr;
|
||||
rti_acceleration_structure_header header;
|
||||
char *name = nullptr;
|
||||
void *data = nullptr;
|
||||
|
||||
uint32_t *primitive_counts = nullptr;
|
||||
uint32_t *primitive_counts_exclusive_sum = nullptr;
|
||||
uint32_t primitive_count = 0;
|
||||
|
||||
rti_aabb aabb;
|
||||
|
||||
struct {
|
||||
bool opened = false;
|
||||
bool request_focus = false;
|
||||
bool prev_focused = false;
|
||||
|
||||
rti_viewport_state viewport;
|
||||
std::vector<rti_viewport_state> pending_viewports;
|
||||
|
||||
std::shared_ptr<rti_backed_buffer> vertex_buffer = nullptr;
|
||||
std::shared_ptr<rti_render_list> instance_render_list = nullptr;
|
||||
|
||||
rti_camera_type camera_type = rti_camera_type_first_person;
|
||||
float camera_phi = 0;
|
||||
float camera_theta = 0;
|
||||
rti_vec3 camera_pivot = {0, 0, 0};
|
||||
float camera_pivot_distance = 0;
|
||||
bool camera_pivot_valid = false;
|
||||
rti_vec3 camera_position = {0, 0, 0};
|
||||
float camera_speed = 0.3;
|
||||
float camera_near = 0.0001;
|
||||
float camera_far = 5.0;
|
||||
float camera_fov = 60.0;
|
||||
|
||||
rti_visualization_color visualization_color = rti_visualization_color_primitive_index;
|
||||
|
||||
bool is_right_dragging_viewport;
|
||||
bool is_middle_dragging_viewport;
|
||||
ImVec2 prev_mouse_pos;
|
||||
float middle_drag_start_z_distance;
|
||||
} ui;
|
||||
|
||||
rti_acceleration_structure(rti_file_view *view);
|
||||
|
||||
virtual ~rti_acceleration_structure()
|
||||
{
|
||||
free(name);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void init_camera();
|
||||
|
||||
void load(FILE *file, uint32_t chunk_size);
|
||||
};
|
||||
|
||||
struct rti_file_view {
|
||||
rti_app *app;
|
||||
std::filesystem::path path;
|
||||
|
||||
std::vector<std::unique_ptr<rti_acceleration_structure>> acceleration_structures;
|
||||
std::map<uint64_t, uint32_t> address_map;
|
||||
|
||||
uint32_t up_axis = 1 + 3; /* +x, +y, +z, -x, -y, -z */
|
||||
|
||||
struct {
|
||||
ImGuiID dockspace_id;
|
||||
ImGuiWindowClass dockspace_class;
|
||||
bool request_focus = true;
|
||||
bool initialized;
|
||||
VkAccelerationStructureTypeKHR filter_acceleration_structure_type;
|
||||
rti_acceleration_structure *focused_acceleration_structure = nullptr;
|
||||
} ui;
|
||||
|
||||
struct {
|
||||
rti_acceleration_structure *acceleration_structure;
|
||||
rti_mat4 projection_matrix;
|
||||
rti_mat4 inv_camera_rotation;
|
||||
rti_mat4 view_projection_matrix;
|
||||
ImVec2 viewport_offset;
|
||||
ImVec2 viewport_size;
|
||||
bool render;
|
||||
rti_vec3 camera_pivot;
|
||||
} rendering_state;
|
||||
|
||||
virtual void load(FILE *file, const rti_header *header);
|
||||
|
||||
virtual void load_driver_specific(FILE *file, const rti_chunk_header *chunk_header, const rti_header *header)
|
||||
{
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<rti_acceleration_structure> create_acceleration_structure() = 0;
|
||||
|
||||
virtual void dock_driver_specific(uint32_t left_top, uint32_t left_bottom, uint32_t center, uint32_t right_top,
|
||||
uint32_t right_bottom)
|
||||
{
|
||||
}
|
||||
|
||||
virtual float handle_mouse_click(rti_acceleration_structure *acceleration_structure, rti_ray ray, bool select)
|
||||
{
|
||||
return INFINITY;
|
||||
}
|
||||
|
||||
virtual void run() = 0;
|
||||
|
||||
/* Helper functions for updating and drawing the UI */
|
||||
void begin();
|
||||
void end();
|
||||
|
||||
bool begin_viewport(rti_acceleration_structure *acceleration_structure);
|
||||
|
||||
void render(const rti_render_task &task);
|
||||
void render_aabb(const rti_render_task &base_task, rti_aabb aabb, bool fill = false);
|
||||
|
||||
void render_viewport();
|
||||
|
||||
void draw_point(rti_vec3 pos, rti_vec3 color, float stroke);
|
||||
void draw_line(rti_vec3 start, rti_vec3 end, rti_vec3 color, float stroke);
|
||||
|
||||
void end_viewport();
|
||||
|
||||
void begin_bvh_tree();
|
||||
void end_bvh_tree();
|
||||
|
||||
void begin_node_info();
|
||||
void end_node_info();
|
||||
|
||||
rti_acceleration_structure *addr_to_acceleration_structure(uint64_t address);
|
||||
};
|
||||
|
||||
std::unique_ptr<rti_file_view> rti_create_file_view(rti_app *app, const char *file);
|
||||
|
||||
/* Create functions for rti_file_views specialized for drivers. */
|
||||
|
||||
std::unique_ptr<rti_file_view> rti_create_file_view_radv();
|
||||
428
src/tool/rtinspector/rti_util.cpp
Normal file
428
src/tool/rtinspector/rti_util.cpp
Normal file
|
|
@ -0,0 +1,428 @@
|
|||
/*
|
||||
* Copyright © 2026 Valve Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "rti_util.h"
|
||||
#include "rti_app.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
void
|
||||
rti_check_vk_result_internal(VkResult result, const char *file, uint32_t line)
|
||||
{
|
||||
if (result != VK_SUCCESS) {
|
||||
fprintf(stderr, "rti: VkResult(%i) at %s:%u\n", result, file, line);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
rti_find_memory_index(rti_app *app, VkMemoryPropertyFlags flags)
|
||||
{
|
||||
VkPhysicalDeviceMemoryProperties mem_properties;
|
||||
vkGetPhysicalDeviceMemoryProperties(app->pdev, &mem_properties);
|
||||
|
||||
/* Try to find an exact match first. */
|
||||
for (uint32_t i = 0; i < mem_properties.memoryTypeCount; ++i) {
|
||||
if (mem_properties.memoryTypes[i].propertyFlags == flags)
|
||||
return i;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < mem_properties.memoryTypeCount; ++i) {
|
||||
if ((mem_properties.memoryTypes[i].propertyFlags & flags) == flags)
|
||||
return i;
|
||||
}
|
||||
fprintf(stderr, "rti: Unsupported memory properties\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rti_backed_buffer::~rti_backed_buffer()
|
||||
{
|
||||
vkDestroyBuffer(app->device, buffer, NULL);
|
||||
|
||||
if (map) {
|
||||
VkMemoryUnmapInfo unmap_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_UNMAP_INFO,
|
||||
.memory = memory,
|
||||
};
|
||||
vkUnmapMemory2(app->device, &unmap_info);
|
||||
}
|
||||
|
||||
vkFreeMemory(app->device, memory, NULL);
|
||||
}
|
||||
|
||||
std::shared_ptr<rti_backed_buffer>
|
||||
rti_create_backed_buffer(rti_app *app, uint64_t size, enum rti_memory_type memory_type, VkBufferUsageFlags usage,
|
||||
bool map)
|
||||
{
|
||||
VkMemoryPropertyFlags memory_property_flags = 0;
|
||||
switch (memory_type) {
|
||||
case rti_memory_type_device_local:
|
||||
memory_property_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
break;
|
||||
case rti_memory_type_host_visible:
|
||||
memory_property_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
break;
|
||||
case rti_memory_type_host_visible_cached:
|
||||
memory_property_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
|
||||
break;
|
||||
}
|
||||
uint32_t memory_type_index = rti_find_memory_index(app, memory_property_flags);
|
||||
|
||||
std::shared_ptr<rti_backed_buffer> buffer = std::make_shared<rti_backed_buffer>();
|
||||
buffer->app = app;
|
||||
buffer->size = size;
|
||||
|
||||
VkBufferCreateInfo buffer_create_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.size = size,
|
||||
.usage = usage,
|
||||
};
|
||||
rti_check_vk_result(vkCreateBuffer(app->device, &buffer_create_info, NULL, &buffer->buffer));
|
||||
|
||||
VkDeviceBufferMemoryRequirements buffer_mem_req_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS,
|
||||
.pCreateInfo = &buffer_create_info,
|
||||
};
|
||||
VkMemoryRequirements2 mem_reqs = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,
|
||||
};
|
||||
vkGetDeviceBufferMemoryRequirements(app->device, &buffer_mem_req_info, &mem_reqs);
|
||||
|
||||
VkMemoryAllocateInfo alloc_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = mem_reqs.memoryRequirements.size,
|
||||
.memoryTypeIndex = memory_type_index,
|
||||
};
|
||||
rti_check_vk_result(vkAllocateMemory(app->device, &alloc_info, NULL, &buffer->memory));
|
||||
|
||||
VkBindBufferMemoryInfo bind_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO,
|
||||
.buffer = buffer->buffer,
|
||||
.memory = buffer->memory,
|
||||
};
|
||||
rti_check_vk_result(vkBindBufferMemory2(app->device, 1, &bind_info));
|
||||
|
||||
if (map) {
|
||||
VkMemoryMapInfo mem_map_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_MAP_INFO,
|
||||
.memory = buffer->memory,
|
||||
.size = VK_WHOLE_SIZE,
|
||||
};
|
||||
rti_check_vk_result(vkMapMemory2(app->device, &mem_map_info, &buffer->map));
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
rti_backed_image::~rti_backed_image()
|
||||
{
|
||||
vkDestroyImageView(app->device, image_view, nullptr);
|
||||
vkDestroyImage(app->device, image, nullptr);
|
||||
vkFreeMemory(app->device, memory, nullptr);
|
||||
}
|
||||
|
||||
std::shared_ptr<rti_backed_image>
|
||||
rti_create_backed_image(rti_app *app, VkExtent3D extent, const std::vector<VkFormat> &formats,
|
||||
VkFormatFeatureFlags format_features, VkImageUsageFlags usage,
|
||||
VkImageAspectFlagBits aspect_mask, uint32_t samples, uint32_t levels)
|
||||
{
|
||||
VkFormat format = formats[0];
|
||||
for (VkFormat candidate_format : formats) {
|
||||
VkFormatProperties format_properties;
|
||||
vkGetPhysicalDeviceFormatProperties(app->pdev, candidate_format, &format_properties);
|
||||
if ((format_properties.optimalTilingFeatures & format_features) == format_features) {
|
||||
format = candidate_format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<rti_backed_image> image = std::make_shared<rti_backed_image>();
|
||||
image->app = app;
|
||||
|
||||
VkImageCreateInfo image_create_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = format,
|
||||
.extent = extent,
|
||||
.mipLevels = levels,
|
||||
.arrayLayers = 1,
|
||||
.samples = (VkSampleCountFlagBits)samples,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = usage,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 1,
|
||||
.pQueueFamilyIndices = &app->queue_family_index,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
};
|
||||
|
||||
rti_check_vk_result(vkCreateImage(app->device, &image_create_info, nullptr, &image->image));
|
||||
|
||||
VkDeviceImageMemoryRequirements memory_requirements_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS,
|
||||
.pCreateInfo = &image_create_info,
|
||||
.planeAspect = aspect_mask,
|
||||
};
|
||||
VkMemoryRequirements2 memory_requirements = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,
|
||||
};
|
||||
vkGetDeviceImageMemoryRequirements(app->device, &memory_requirements_info, &memory_requirements);
|
||||
|
||||
uint32_t memory_type_index = rti_find_memory_index(app, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
|
||||
VkMemoryAllocateInfo alloc_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = memory_requirements.memoryRequirements.size,
|
||||
.memoryTypeIndex = memory_type_index,
|
||||
};
|
||||
rti_check_vk_result(vkAllocateMemory(app->device, &alloc_info, NULL, &image->memory));
|
||||
|
||||
rti_check_vk_result(vkBindImageMemory(app->device, image->image, image->memory, 0));
|
||||
|
||||
VkComponentMapping view_component_mapping = {
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
};
|
||||
|
||||
VkImageSubresourceRange subresource_range = {
|
||||
.aspectMask = aspect_mask,
|
||||
.levelCount = levels,
|
||||
.layerCount = 1,
|
||||
};
|
||||
|
||||
VkImageViewCreateInfo image_view_create_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = image->image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = format,
|
||||
.components = view_component_mapping,
|
||||
.subresourceRange = subresource_range,
|
||||
};
|
||||
rti_check_vk_result(vkCreateImageView(app->device, &image_view_create_info, nullptr, &image->image_view));
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void
|
||||
rti_generate_cube_vertices(rti_vertex *vertices, rti_aabb aabb)
|
||||
{
|
||||
*(vertices++) = {.position = {aabb.min.x, aabb.min.y, aabb.min.z}};
|
||||
*(vertices++) = {.position = {aabb.max.x, aabb.min.y, aabb.min.z}};
|
||||
*(vertices++) = {.position = {aabb.max.x, aabb.min.y, aabb.min.z}};
|
||||
*(vertices++) = {.position = {aabb.max.x, aabb.max.y, aabb.min.z}};
|
||||
*(vertices++) = {.position = {aabb.max.x, aabb.max.y, aabb.min.z}};
|
||||
*(vertices++) = {.position = {aabb.min.x, aabb.max.y, aabb.min.z}};
|
||||
*(vertices++) = {.position = {aabb.min.x, aabb.max.y, aabb.min.z}};
|
||||
*(vertices++) = {.position = {aabb.min.x, aabb.min.y, aabb.min.z}};
|
||||
*(vertices++) = {.position = {aabb.min.x, aabb.min.y, aabb.max.z}};
|
||||
*(vertices++) = {.position = {aabb.max.x, aabb.min.y, aabb.max.z}};
|
||||
*(vertices++) = {.position = {aabb.max.x, aabb.min.y, aabb.max.z}};
|
||||
*(vertices++) = {.position = {aabb.max.x, aabb.max.y, aabb.max.z}};
|
||||
*(vertices++) = {.position = {aabb.max.x, aabb.max.y, aabb.max.z}};
|
||||
*(vertices++) = {.position = {aabb.min.x, aabb.max.y, aabb.max.z}};
|
||||
*(vertices++) = {.position = {aabb.min.x, aabb.max.y, aabb.max.z}};
|
||||
*(vertices++) = {.position = {aabb.min.x, aabb.min.y, aabb.max.z}};
|
||||
*(vertices++) = {.position = {aabb.min.x, aabb.min.y, aabb.min.z}};
|
||||
*(vertices++) = {.position = {aabb.min.x, aabb.min.y, aabb.max.z}};
|
||||
*(vertices++) = {.position = {aabb.max.x, aabb.min.y, aabb.min.z}};
|
||||
*(vertices++) = {.position = {aabb.max.x, aabb.min.y, aabb.max.z}};
|
||||
*(vertices++) = {.position = {aabb.max.x, aabb.max.y, aabb.min.z}};
|
||||
*(vertices++) = {.position = {aabb.max.x, aabb.max.y, aabb.max.z}};
|
||||
*(vertices++) = {.position = {aabb.min.x, aabb.max.y, aabb.min.z}};
|
||||
*(vertices++) = {.position = {aabb.min.x, aabb.max.y, aabb.max.z}};
|
||||
}
|
||||
|
||||
void
|
||||
rti_generate_filled_cube_vertices(rti_vertex *vertices, rti_aabb aabb, uint32_t geometry_index,
|
||||
uint32_t primitive_index)
|
||||
{
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.min.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.min.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.max.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.max.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.max.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.min.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.min.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.min.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.max.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.max.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.max.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.min.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.min.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.min.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.min.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.min.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.min.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.min.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.max.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.max.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.max.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.max.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.max.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.max.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.min.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.min.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.max.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.max.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.max.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.min.x, aabb.min.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.min.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.min.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.max.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.max.y, aabb.max.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.max.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
*(vertices++) = {
|
||||
.position = {aabb.max.x, aabb.min.y, aabb.min.z},
|
||||
.geometry_index = geometry_index,
|
||||
.primitive_index = primitive_index,
|
||||
};
|
||||
}
|
||||
262
src/tool/rtinspector/rti_util.h
Normal file
262
src/tool/rtinspector/rti_util.h
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* Copyright © 2026 Valve Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
#include <cstdint>
|
||||
#include <imgui.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "util/macros.h"
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
void rti_check_vk_result_internal(VkResult result, const char *file, uint32_t line);
|
||||
|
||||
#define rti_check_vk_result(result) rti_check_vk_result_internal(result, __FILE__, __LINE__)
|
||||
|
||||
struct rti_app;
|
||||
|
||||
struct rti_backed_buffer {
|
||||
rti_app *app;
|
||||
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||
VkBuffer buffer = VK_NULL_HANDLE;
|
||||
void *map = nullptr;
|
||||
uint64_t size = 0;
|
||||
|
||||
~rti_backed_buffer();
|
||||
};
|
||||
|
||||
enum rti_memory_type {
|
||||
rti_memory_type_device_local,
|
||||
rti_memory_type_host_visible,
|
||||
rti_memory_type_host_visible_cached,
|
||||
};
|
||||
|
||||
std::shared_ptr<rti_backed_buffer> rti_create_backed_buffer(rti_app *app, uint64_t size,
|
||||
enum rti_memory_type memory_type, VkBufferUsageFlags usage,
|
||||
bool map);
|
||||
|
||||
struct rti_backed_image {
|
||||
rti_app *app = nullptr;
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||
VkImageView image_view = VK_NULL_HANDLE;
|
||||
|
||||
~rti_backed_image();
|
||||
};
|
||||
|
||||
std::shared_ptr<rti_backed_image> rti_create_backed_image(rti_app *app, VkExtent3D extent,
|
||||
const std::vector<VkFormat> &formats,
|
||||
VkFormatFeatureFlags format_features, VkImageUsageFlags usage,
|
||||
VkImageAspectFlagBits aspect_mask, uint32_t samples,
|
||||
uint32_t levels);
|
||||
|
||||
struct rti_vec2 {
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
};
|
||||
|
||||
struct rti_vec3 {
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
float z = 0;
|
||||
|
||||
static inline rti_vec3 sub(rti_vec3 a, rti_vec3 b)
|
||||
{
|
||||
return {
|
||||
.x = a.x - b.x,
|
||||
.y = a.y - b.y,
|
||||
.z = a.z - b.z,
|
||||
};
|
||||
}
|
||||
|
||||
static inline rti_vec3 cross(rti_vec3 a, rti_vec3 b)
|
||||
{
|
||||
return {
|
||||
.x = a.y * b.z - a.z * b.y,
|
||||
.y = a.z * b.x - a.x * b.z,
|
||||
.z = a.x * b.y - a.y * b.x,
|
||||
};
|
||||
}
|
||||
|
||||
static inline float dot(rti_vec3 a, rti_vec3 b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
};
|
||||
|
||||
struct rti_vec4 {
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
float z = 0;
|
||||
float w = 0;
|
||||
};
|
||||
|
||||
struct rti_mat4 {
|
||||
float elements[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
|
||||
|
||||
static inline rti_mat4 zero()
|
||||
{
|
||||
return {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
|
||||
}
|
||||
|
||||
static inline rti_mat4 mul(rti_mat4 a, rti_mat4 b)
|
||||
{
|
||||
rti_mat4 result = rti_mat4::zero();
|
||||
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
for (uint32_t j = 0; j < 4; j++) {
|
||||
for (uint32_t k = 0; k < 4; k++)
|
||||
result.elements[i + j * 4] += a.elements[i + k * 4] * b.elements[k + j * 4];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline rti_mat4 transpose(rti_mat4 m)
|
||||
{
|
||||
rti_mat4 result;
|
||||
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
for (uint32_t j = 0; j < 4; j++)
|
||||
result.elements[i + j * 4] = m.elements[j + i * 4];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline rti_vec4 mul_vec4(rti_mat4 a, rti_vec4 v)
|
||||
{
|
||||
return (rti_vec4){
|
||||
.x = a.elements[0 + 0 * 4] * v.x + a.elements[0 + 1 * 4] * v.y + a.elements[0 + 2 * 4] * v.z +
|
||||
a.elements[0 + 3 * 4] * v.w,
|
||||
.y = a.elements[1 + 0 * 4] * v.x + a.elements[1 + 1 * 4] * v.y + a.elements[1 + 2 * 4] * v.z +
|
||||
a.elements[1 + 3 * 4] * v.w,
|
||||
.z = a.elements[2 + 0 * 4] * v.x + a.elements[2 + 1 * 4] * v.y + a.elements[2 + 2 * 4] * v.z +
|
||||
a.elements[2 + 3 * 4] * v.w,
|
||||
.w = a.elements[3 + 0 * 4] * v.x + a.elements[3 + 1 * 4] * v.y + a.elements[3 + 2 * 4] * v.z +
|
||||
a.elements[3 + 3 * 4] * v.w,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct rti_ray {
|
||||
rti_vec3 origin;
|
||||
rti_vec3 direction;
|
||||
|
||||
static inline rti_ray transform(rti_ray ray, rti_mat4 transform)
|
||||
{
|
||||
rti_vec4 origin = {ray.origin.x, ray.origin.y, ray.origin.z, 1};
|
||||
origin = rti_mat4::mul_vec4(transform, origin);
|
||||
|
||||
rti_vec4 direction = {ray.direction.x, ray.direction.y, ray.direction.z, 0};
|
||||
direction = rti_mat4::mul_vec4(transform, direction);
|
||||
|
||||
return {
|
||||
.origin = {origin.x, origin.y, origin.z},
|
||||
.direction = {direction.x, direction.y, direction.z},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct rti_aabb {
|
||||
rti_vec3 min;
|
||||
rti_vec3 max;
|
||||
|
||||
static inline rti_aabb combine(rti_aabb a, rti_aabb b)
|
||||
{
|
||||
return {
|
||||
.min =
|
||||
{
|
||||
.x = MIN2(a.min.x, b.min.x),
|
||||
.y = MIN2(a.min.y, b.min.y),
|
||||
.z = MIN2(a.min.z, b.min.z),
|
||||
},
|
||||
.max =
|
||||
{
|
||||
.x = MAX2(a.max.x, b.max.x),
|
||||
.y = MAX2(a.max.y, b.max.y),
|
||||
.z = MAX2(a.max.z, b.max.z),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
static inline float intersect_ray(rti_aabb aabb, rti_ray ray)
|
||||
{
|
||||
float t1 = (aabb.min.x - ray.origin.x) / ray.direction.x;
|
||||
float t2 = (aabb.max.x - ray.origin.x) / ray.direction.x;
|
||||
float t3 = (aabb.min.y - ray.origin.y) / ray.direction.y;
|
||||
float t4 = (aabb.max.y - ray.origin.y) / ray.direction.y;
|
||||
float t5 = (aabb.min.z - ray.origin.z) / ray.direction.z;
|
||||
float t6 = (aabb.max.z - ray.origin.z) / ray.direction.z;
|
||||
|
||||
float tmin = fmax(fmax(fmin(t1, t2), fmin(t3, t4)), fmin(t5, t6));
|
||||
float tmax = fmin(fmin(fmax(t1, t2), fmax(t3, t4)), fmax(t5, t6));
|
||||
|
||||
if (tmax < 0 || tmax < tmin)
|
||||
return INFINITY;
|
||||
|
||||
return tmin;
|
||||
}
|
||||
};
|
||||
|
||||
struct rti_triangle {
|
||||
rti_vec3 vertices[3];
|
||||
uint32_t geometry_index = 0;
|
||||
uint32_t primitive_index = 0;
|
||||
|
||||
static inline float intersect_ray(rti_triangle triangle, rti_ray ray)
|
||||
{
|
||||
rti_vec3 edge1 = rti_vec3::sub(triangle.vertices[1], triangle.vertices[0]);
|
||||
rti_vec3 edge2 = rti_vec3::sub(triangle.vertices[2], triangle.vertices[0]);
|
||||
rti_vec3 direction_cross_edge2 = rti_vec3::cross(ray.direction, edge2);
|
||||
float det = rti_vec3::dot(edge1, direction_cross_edge2);
|
||||
rti_vec3 s = rti_vec3::sub(ray.origin, triangle.vertices[0]);
|
||||
float u = rti_vec3::dot(s, direction_cross_edge2) / det;
|
||||
rti_vec3 s_cross_edge1 = rti_vec3::cross(s, edge1);
|
||||
float v = rti_vec3::dot(ray.direction, s_cross_edge1) / det;
|
||||
if (u < 0 || v < 0 || u + v > 1)
|
||||
return INFINITY;
|
||||
|
||||
float t = rti_vec3::dot(edge2, s_cross_edge1) / det;
|
||||
if (t < 0)
|
||||
t = INFINITY;
|
||||
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
#define RTI_PRIMITIVE_INDEX_INACTIVE 0xffffffff
|
||||
|
||||
struct rti_vertex {
|
||||
rti_vec3 position;
|
||||
uint32_t geometry_index;
|
||||
uint32_t primitive_index;
|
||||
};
|
||||
|
||||
void rti_generate_cube_vertices(rti_vertex *vertices, rti_aabb aabb);
|
||||
|
||||
void rti_generate_filled_cube_vertices(rti_vertex *vertices, rti_aabb aabb, uint32_t geometry_index,
|
||||
uint32_t primitive_index);
|
||||
|
||||
/* Helpers to remove some imgui boilerplate. */
|
||||
namespace ImGui {
|
||||
|
||||
static inline void
|
||||
TableNextColumnText(const char *fmt, ...)
|
||||
{
|
||||
TableNextColumn();
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
TextV(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
}; // namespace ImGui
|
||||
35
src/tool/rtinspector/shaders/meson.build
Normal file
35
src/tool/rtinspector/shaders/meson.build
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Copyright © 2026 Valve Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
shaders = [
|
||||
[
|
||||
'renderer.vert',
|
||||
'renderer_vert',
|
||||
],
|
||||
[
|
||||
'renderer.frag',
|
||||
'renderer_frag',
|
||||
],
|
||||
]
|
||||
|
||||
shader_includes = files(
|
||||
)
|
||||
|
||||
spv_shaders = []
|
||||
foreach s : shaders
|
||||
command = [
|
||||
prog_glslang, '-V', '--target-env', 'spirv1.5',
|
||||
'-x', '-o', '@OUTPUT@', '@INPUT@', glslang_depfile, glslang_quiet,
|
||||
]
|
||||
|
||||
_spv_name = '@0@.spv.h'.format(s[1])
|
||||
spv_shaders += custom_target(
|
||||
_spv_name,
|
||||
input : s[0],
|
||||
output : _spv_name,
|
||||
command : command,
|
||||
depfile : f'@_spv_name@.d',
|
||||
depend_files: shader_includes
|
||||
)
|
||||
endforeach
|
||||
52
src/tool/rtinspector/shaders/renderer.frag
Normal file
52
src/tool/rtinspector/shaders/renderer.frag
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright © 2026 Valve Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#version 460
|
||||
|
||||
#extension GL_GOOGLE_include_directive : require
|
||||
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int16 : require
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int32 : require
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require
|
||||
#extension GL_EXT_scalar_block_layout : require
|
||||
|
||||
#include "rti_shader_interface.h"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
layout(location = 0) flat in uint32_t index;
|
||||
layout(location = 1) flat in uint32_t param_index;
|
||||
|
||||
layout(push_constant) uniform block
|
||||
{
|
||||
rti_push_constants consts;
|
||||
};
|
||||
|
||||
layout(scalar, set = 0, binding = 0) readonly buffer SSBO
|
||||
{
|
||||
rti_render_params params[];
|
||||
}
|
||||
state;
|
||||
|
||||
void
|
||||
main()
|
||||
{
|
||||
rti_render_params params = state.params[param_index];
|
||||
|
||||
out_color = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
uint32_t color_calculation = consts.flags & RTI_RENDERER_FLAG_COLOR_MASK;
|
||||
if (color_calculation == RTI_RENDERER_COLOR_PUSH_CONSTANT) {
|
||||
out_color.rgb = params.color;
|
||||
} else if (color_calculation == RTI_RENDERER_COLOR_GEOMETRY_INDEX ||
|
||||
color_calculation == RTI_RENDERER_COLOR_PRIMITIVE_INDEX) {
|
||||
out_color.r = ((index * 0xd83f0930) >> 24) / 255.0;
|
||||
out_color.g = ((index * 0x8fa9836b) >> 24) / 255.0;
|
||||
out_color.b = ((index * 0x3037f8ad) >> 24) / 255.0;
|
||||
}
|
||||
}
|
||||
53
src/tool/rtinspector/shaders/renderer.vert
Normal file
53
src/tool/rtinspector/shaders/renderer.vert
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright © 2026 Valve Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#version 460
|
||||
|
||||
#extension GL_GOOGLE_include_directive : require
|
||||
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int16 : require
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int32 : require
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require
|
||||
#extension GL_EXT_scalar_block_layout : require
|
||||
|
||||
#include "rti_shader_interface.h"
|
||||
|
||||
layout(location = 0) in vec3 position;
|
||||
layout(location = 1) in uint32_t geometry_index;
|
||||
layout(location = 2) in uint32_t primitive_index;
|
||||
|
||||
layout(location = 0) flat out uint32_t out_index;
|
||||
layout(location = 1) flat out uint32_t out_param_index;
|
||||
|
||||
layout(push_constant) uniform block
|
||||
{
|
||||
rti_push_constants consts;
|
||||
};
|
||||
|
||||
layout(scalar, set = 0, binding = 0) readonly buffer SSBO
|
||||
{
|
||||
rti_render_params params[];
|
||||
}
|
||||
state;
|
||||
|
||||
void
|
||||
main()
|
||||
{
|
||||
uint32_t param_index = consts.first_param + gl_DrawID;
|
||||
rti_render_params params = state.params[param_index];
|
||||
|
||||
gl_Position = consts.transform * (params.transform * vec4(position, 1.0));
|
||||
|
||||
uint32_t color_calculation = consts.flags & RTI_RENDERER_FLAG_COLOR_MASK;
|
||||
if (color_calculation == RTI_RENDERER_COLOR_GEOMETRY_INDEX)
|
||||
out_index = geometry_index;
|
||||
else
|
||||
out_index = primitive_index;
|
||||
|
||||
out_param_index = param_index;
|
||||
}
|
||||
41
src/tool/rtinspector/shaders/rti_shader_interface.h
Normal file
41
src/tool/rtinspector/shaders/rti_shader_interface.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright © 2026 Valve Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef RTI_SHADER_INTERFACE_H
|
||||
#define RTI_SHADER_INTERFACE_H
|
||||
|
||||
#ifdef VULKAN
|
||||
#else
|
||||
#include "rti_util.h"
|
||||
#define mat4 rti_mat4
|
||||
#define vec3 rti_vec3
|
||||
#endif
|
||||
|
||||
struct rti_render_params {
|
||||
mat4 transform;
|
||||
vec3 color;
|
||||
};
|
||||
|
||||
struct rti_push_constants {
|
||||
mat4 transform;
|
||||
uint32_t first_param;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
#define RTI_RENDERER_COLOR_PUSH_CONSTANT 0
|
||||
#define RTI_RENDERER_COLOR_PRIMITIVE_INDEX 1
|
||||
#define RTI_RENDERER_COLOR_GEOMETRY_INDEX 2
|
||||
#define RTI_RENDERER_COLOR_BIT_COUNT 2
|
||||
|
||||
#define RTI_RENDERER_FLAG_COLOR_MASK ((1u << RTI_RENDERER_COLOR_BIT_COUNT) - 1)
|
||||
|
||||
#ifdef VULKAN
|
||||
#else
|
||||
#undef mat4
|
||||
#undef vec3
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Reference in a new issue