From 08f4f6ed6098a74f7a009766c14a4363eb082634 Mon Sep 17 00:00:00 2001 From: Hirek193 Date: Sun, 25 Oct 2026 15:35:26 +0100 Subject: [PATCH] Vulkan ray tracing: include tracks and roads, build triangle strips and fans --- docs/opengl46-renderer.md | 6 +-- rendering/common/scenewalk.cpp | 2 + rendering/vulkan14/vulkan14frame.cpp | 2 +- rendering/vulkan14/vulkan14raytracing.cpp | 46 ++++++++++++++++++----- rendering/vulkan14/vulkan14raytracing.h | 3 ++ 5 files changed, 46 insertions(+), 13 deletions(-) diff --git a/docs/opengl46-renderer.md b/docs/opengl46-renderer.md index 15d0814d..24c653ad 100644 --- a/docs/opengl46-renderer.md +++ b/docs/opengl46-renderer.md @@ -252,9 +252,9 @@ the environment map only. | `gfx.raytracing yes/no` | `no` | Vulkan renderer only, on a graphics card with ray queries (`VK_KHR_ray_query`): what the screen space reflections don't find is traced through the scenery around the camera instead of reflecting the sky. Read at the start; with `gfx.ssr no` all the reflections of the scenery are traced | | `gfx.raytracing.range` | `500` | 10..5000: the scenery within this distance from the camera takes part in the traced reflections, and the rays are this long, metres | -The traced reflections hold what the environment map held before, as `gfx.reflections.fidelity` says: the tracks and the -terrain (0), the models within `gfx.reflections.range_instances` (1) and the vehicles within -`gfx.reflections.range_vehicles` (2, the default now), except the vehicle of the cab. `gfx.reflections.fidelity` and the +The traced reflections hold what the environment map held before, as `gfx.reflections.fidelity` says: the terrain +and the large shapes of the scenery (0), the tracks, the roads and the models within `gfx.reflections.range_instances` (1) +and the vehicles within `gfx.reflections.range_vehicles` (2, the default now), except the vehicle of the cab. `gfx.reflections.fidelity` and the two ranges have no other use now; `gfx.reflections.framerate` still says how often the environment map (the sky) is drawn. The point hit is lit by the sky, by the sun where nothing stands between them (a ray of its own) and by its own light; the lamps and headlights don't light it. Each part of a model gets its structure for the tracing the first time it's met (a few a frame, so a new place fills in over some frames), and loses it when it diff --git a/rendering/common/scenewalk.cpp b/rendering/common/scenewalk.cpp index cd90d82c..1c77644c 100644 --- a/rendering/common/scenewalk.cpp +++ b/rendering/common/scenewalk.cpp @@ -482,6 +482,8 @@ void scene_walk::Render(cell_sequence::iterator First, cell_sequence::iterator L // opaque non-instanced shapes for (auto const &shape : cell->m_shapesopaque) Render(shape, false); + // tracks: the ray traced reflections (the one user of this mode) show the trackbed and the platforms made of roads + Render(std::begin(cell->m_paths), std::end(cell->m_paths)); // post-render cleanup ::glPopMatrix(); diff --git a/rendering/vulkan14/vulkan14frame.cpp b/rendering/vulkan14/vulkan14frame.cpp index d4d6450d..10553829 100644 --- a/rendering/vulkan14/vulkan14frame.cpp +++ b/rendering/vulkan14/vulkan14frame.cpp @@ -1821,7 +1821,7 @@ void vulkan14_renderer::gather_ray_draw(gfx::geometry_handle const &Geometry) return; } auto const &chunk{static_cast(m_banks[Geometry.bank - 1])->last()}; - if ((chunk.topology != VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) || (chunk.vertex_address == 0)) + if (((chunk.topology != VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) && (chunk.topology != VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP) && (chunk.topology != VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN)) || (chunk.vertex_address == 0)) { return; } diff --git a/rendering/vulkan14/vulkan14raytracing.cpp b/rendering/vulkan14/vulkan14raytracing.cpp index e1afc0dc..966a1936 100644 --- a/rendering/vulkan14/vulkan14raytracing.cpp +++ b/rendering/vulkan14/vulkan14raytracing.cpp @@ -205,7 +205,7 @@ void ray_tracing::clear(std::uint64_t const Value) auto const handle{m_device->handle()}; for (auto &entry : m_bottoms) { - m_deletions->push(Value, [handle, structure = entry.second.handle, storage = entry.second.storage]() { destroy_structure(handle, structure); }); + m_deletions->push(Value, [handle, structure = entry.second.handle, storage = entry.second.storage, indices = entry.second.indices]() { destroy_structure(handle, structure); }); } m_bottoms.clear(); m_bottombytes = 0; @@ -440,11 +440,37 @@ void ray_tracing::build(command_stream *Commands, std::uint32_t const Slot, std: } auto const &source{draw.source}; auto const indexed{source.indices != VK_NULL_HANDLE}; - auto const primitives{source.count / 3}; + // the strips and the fans (the tracks and the roads are strips) get triangles of their own, as indices kept with the structure + auto const strip{source.topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP}; + auto const fan{source.topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN}; + if ((strip || fan) && indexed) + { + continue; + } + auto const primitives{(strip || fan) ? (source.count >= 3 ? source.count - 2 : 0) : source.count / 3}; if ((primitives == 0) || (source.vertex_address == 0) || (indexed && (source.index_address == 0)) || (source.vertexcount == 0)) { continue; } + std::shared_ptr generated; + if (strip || fan) + { + auto const bytes{sizeof(std::uint32_t) * 3 * static_cast(primitives)}; + generated = std::make_shared(); + if ((false == generated->create(*m_memory, bytes, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, memory_kind::upload, "ray tracing strip indices")) || + (generated->mapped() == nullptr)) + { + continue; + } + auto *indices{reinterpret_cast(generated->mapped())}; + for (std::uint32_t triangle = 0; triangle < primitives; ++triangle) + { + indices[triangle * 3 + 0] = (strip ? triangle : 0); + indices[triangle * 3 + 1] = triangle + 1; + indices[triangle * 3 + 2] = triangle + 2; + } + generated->flush(0, bytes); + } pending_build build{}; build.geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR; build.geometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR; @@ -454,8 +480,8 @@ void ray_tracing::build(command_stream *Commands, std::uint32_t const Slot, std: triangles_data.vertexData.deviceAddress = source.vertex_address + vertex_stride * source.vertexfirst; triangles_data.vertexStride = vertex_stride; triangles_data.maxVertex = source.vertexcount - 1; - triangles_data.indexType = (indexed ? VK_INDEX_TYPE_UINT32 : VK_INDEX_TYPE_NONE_KHR); - triangles_data.indexData.deviceAddress = (indexed ? source.index_address + sizeof(std::uint32_t) * source.first : 0); + triangles_data.indexType = ((indexed || generated) ? VK_INDEX_TYPE_UINT32 : VK_INDEX_TYPE_NONE_KHR); + triangles_data.indexData.deviceAddress = (generated ? generated->address() : indexed ? source.index_address + sizeof(std::uint32_t) * source.first : 0); build.range.primitiveCount = primitives; build.info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; build.info.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; @@ -486,7 +512,7 @@ void ray_tracing::build(command_stream *Commands, std::uint32_t const Slot, std: { // the data of the chunk changed: the old structure goes once the frames which read it are done m_bottombytes -= lookup->second.bytes; - m_deletions->push(Value, [handle, old = lookup->second.handle, oldstorage = lookup->second.storage]() { destroy_structure(handle, old); }); + m_deletions->push(Value, [handle, old = lookup->second.handle, oldstorage = lookup->second.storage, oldindices = lookup->second.indices]() { destroy_structure(handle, old); }); } VkAccelerationStructureDeviceAddressInfoKHR addressinfo{}; addressinfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR; @@ -497,7 +523,9 @@ void ray_tracing::build(command_stream *Commands, std::uint32_t const Slot, std: entry.address = vkGetAccelerationStructureDeviceAddressKHR(handle, &addressinfo); entry.generation = source.generation; entry.used = Frame; - entry.bytes = sizes.accelerationStructureSize; + entry.bytes = sizes.accelerationStructureSize + (generated ? generated->size() : 0); + entry.index_address = (generated ? generated->address() : 0); + entry.indices = std::move(generated); m_bottombytes += entry.bytes; build.info.dstAccelerationStructure = structure; build.scratch = scratchsize; @@ -520,7 +548,7 @@ void ray_tracing::build(command_stream *Commands, std::uint32_t const Slot, std: if (it->second.handle == build.info.dstAccelerationStructure) { m_bottombytes -= it->second.bytes; - m_deletions->push(Value, [handle, old = it->second.handle, oldstorage = it->second.storage]() { destroy_structure(handle, old); }); + m_deletions->push(Value, [handle, old = it->second.handle, oldstorage = it->second.storage, oldindices = it->second.indices]() { destroy_structure(handle, old); }); m_bottoms.erase(it); break; } @@ -589,7 +617,7 @@ void ray_tracing::build(command_stream *Commands, std::uint32_t const Slot, std: } auto &entry{records[written]}; entry.vertices = source.vertex_address + vertex_stride * source.vertexfirst; - entry.indices = (source.indices != VK_NULL_HANDLE ? source.index_address + sizeof(std::uint32_t) * source.first : 0); + entry.indices = (lookup->second.index_address != 0 ? lookup->second.index_address : source.indices != VK_NULL_HANDLE ? source.index_address + sizeof(std::uint32_t) * source.first : 0); entry.texture = texture; auto const alpha{(texture != 0) && (draw.cutoff > 0.f)}; entry.flags = (alpha ? record_alpha : 0); @@ -702,7 +730,7 @@ void ray_tracing::build(command_stream *Commands, std::uint32_t const Slot, std: if (it->second.used + bottom_lifetime < Frame) { m_bottombytes -= it->second.bytes; - m_deletions->push(Value, [handle, old = it->second.handle, oldstorage = it->second.storage]() { destroy_structure(handle, old); }); + m_deletions->push(Value, [handle, old = it->second.handle, oldstorage = it->second.storage, oldindices = it->second.indices]() { destroy_structure(handle, old); }); it = m_bottoms.erase(it); } else diff --git a/rendering/vulkan14/vulkan14raytracing.h b/rendering/vulkan14/vulkan14raytracing.h index 3f8534f8..e8587c91 100644 --- a/rendering/vulkan14/vulkan14raytracing.h +++ b/rendering/vulkan14/vulkan14raytracing.h @@ -97,6 +97,9 @@ class ray_tracing std::uint64_t generation{0}; // of the data of the chunk it was built from std::uint64_t used{0}; // the frame it was last met in VkDeviceSize bytes{0}; + // the triangles of a strip or a fan, which the structure is built from and the shading reads + std::shared_ptr indices; + VkDeviceAddress index_address{0}; }; struct frame_data {