17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-11 20:16:47 +01:00

Replace cubemap scenery reflections with SSR, add Vulkan ray traced reflections

This commit is contained in:
2026-10-25 13:54:49 +01:00
parent 89f11895fc
commit f20b1a3071
45 changed files with 2725 additions and 174 deletions

View File

@@ -69,7 +69,9 @@ constexpr std::uint32_t LOCAL_SHADOW_LAMPS_TEX = 22;
constexpr std::uint32_t HEADLIGHT_SHADOW_TEX = 23;
// the depth of the opaque scene drawn before its colour (the stage P3: the contact shadows; the soft particles of P6)
constexpr std::uint32_t SCENE_DEPTH_TEX = 24;
constexpr std::uint32_t PASS_BINDING_COUNT = 25;
// the image of the last frame of the main view with its mipmaps, which the screen space reflections take their colour from
constexpr std::uint32_t SSR_HISTORY_TEX = 25;
constexpr std::uint32_t PASS_BINDING_COUNT = 26;
// the offsets of the bindings above with dynamic offsets, in the order of the bindings
constexpr std::uint32_t PASS_DYNAMIC_OFFSETS = 6;
// the textures of a material take the bindings of their units (#pragma texture(name, unit, ...)), 0 .. MAX_TEXTURES - 1
@@ -94,6 +96,14 @@ constexpr std::uint32_t POST_EXPOSURE_SSBO = 4;
// those the shader declares from 0 on
constexpr std::uint32_t COMPUTE_SET = 1;
// the ray traced reflections (gfx.raytracing, vk14::ray_tracing): set 2 of the pipelines of the scene, one for each frame in flight,
// written with the frame: the acceleration structure of the scenery around the camera, the records of its instances, and the
// textures they read (an array of as many as the device allows, partly bound, the last binding)
constexpr std::uint32_t RT_SET = 2;
constexpr std::uint32_t RT_SCENE = 0;
constexpr std::uint32_t RT_RECORDS = 1;
constexpr std::uint32_t RT_TEXTURES = 2;
// the push constants of the graphics pipelines of the scene and of the post-processing (vk14::draw_constants): the draw, and
// the parameters of the special shaders (the celestial bodies, the post-processing)
constexpr std::uint32_t PUSH_CONSTANTS_SIZE = 112;
@@ -115,7 +125,8 @@ namespace vk14
{
// the bindings above as #define lines, with the constants of the game the shaders share with the opengl renderer (the sizes of
// the blocks, the cluster grid), and MASZYNA_CONSTANTS, which tells shaders/vk14/common.glsl not to write its own
std::string binding_preamble();
// the blocks, the cluster grid), and MASZYNA_CONSTANTS, which tells shaders/vk14/common.glsl not to write its own. Raytracing:
// the shaders of the scene trace the reflections the screen doesn't hold (REFLECTION_RAYS, with the extensions it needs)
std::string binding_preamble(bool const Raytracing = false);
} // namespace vk14

View File

@@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "rendering/vulkan14/vulkan14capabilities.h"
#include <algorithm>
#include <cstring>
namespace vk14
@@ -239,6 +240,38 @@ void query_capabilities(VkPhysicalDevice Physical, feature_chain &Queried, capab
Caps.sampler_filter_minmax = (Queried.v12.samplerFilterMinmax == VK_TRUE);
Caps.shader_output_layer = (Queried.v12.shaderOutputLayer == VK_TRUE);
// the ray queries: the structures of the extensions are asked about only where the device has the extensions
if (hasextension(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME) && hasextension(VK_KHR_RAY_QUERY_EXTENSION_NAME) && hasextension(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME))
{
VkPhysicalDeviceAccelerationStructureFeaturesKHR acceleration{};
acceleration.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR;
VkPhysicalDeviceRayQueryFeaturesKHR rayquery{};
rayquery.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR;
acceleration.pNext = &rayquery;
VkPhysicalDeviceFeatures2 features{};
features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
features.pNext = &acceleration;
vkGetPhysicalDeviceFeatures2(Physical, &features);
VkPhysicalDeviceAccelerationStructurePropertiesKHR accelerationproperties{};
accelerationproperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_PROPERTIES_KHR;
VkPhysicalDeviceVulkan12Properties indexing{};
indexing.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES;
indexing.pNext = &accelerationproperties;
VkPhysicalDeviceProperties2 properties2{};
properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
properties2.pNext = &indexing;
vkGetPhysicalDeviceProperties2(Physical, &properties2);
Caps.scratch_alignment = std::max<std::uint32_t>(accelerationproperties.minAccelerationStructureScratchOffsetAlignment, 1);
// the textures of the scenery the reflections are traced through, beside the other bindings of the stage
// (combined image samplers: they count as samplers too)
auto const textures{std::min({Caps.limits.maxPerStageDescriptorSampledImages, Caps.limits.maxDescriptorSetSampledImages, Caps.limits.maxPerStageDescriptorSamplers,
Caps.limits.maxDescriptorSetSamplers, Caps.limits.maxPerStageResources})};
Caps.max_indexed_textures = std::min<std::uint32_t>(textures > 64 ? textures - 64 : 0, 65536);
Caps.ray_query = (acceleration.accelerationStructure == VK_TRUE) && (rayquery.rayQuery == VK_TRUE) && (Queried.v12.bufferDeviceAddress == VK_TRUE) &&
(Queried.v12.runtimeDescriptorArray == VK_TRUE) && (Queried.v12.descriptorBindingPartiallyBound == VK_TRUE) && (Queried.v12.descriptorBindingVariableDescriptorCount == VK_TRUE) &&
(Queried.v12.shaderSampledImageArrayNonUniformIndexing == VK_TRUE) && (Caps.max_indexed_textures >= 256);
}
// can the renderer run on it
if (!Caps.ext_swapchain)
{
@@ -294,6 +327,7 @@ std::string capability_report(capabilities const &Caps)
line(std::string("shader output layer ") + flag(Caps.shader_output_layer));
line(std::string("fill mode non solid ") + flag(Caps.fill_mode_non_solid));
line(std::string("pipeline statistics ") + flag(Caps.pipeline_statistics_query));
line(std::string("ray queries ") + flag(Caps.ray_query));
return report;
}

View File

@@ -93,6 +93,12 @@ struct capabilities
bool pipeline_statistics_query{false};
bool sampler_filter_minmax{false};
bool shader_output_layer{false};
// ray queries in the shaders with what they need (VK_KHR_acceleration_structure, VK_KHR_ray_query, VK_KHR_deferred_host_operations,
// the device addresses of buffers and arrays of textures indexed in the shaders), for the ray traced reflections (gfx.raytracing).
// the alignment of the scratch memory of the builds of acceleration structures, and the most textures the shading may index
bool ray_query{false};
std::uint32_t scratch_alignment{256};
std::uint32_t max_indexed_textures{0};
// empty when the renderer can run on the device, otherwise one line per missing thing
std::vector<std::string> missing;

View File

@@ -63,7 +63,8 @@ enum call_type : std::uint32_t
call_begin_query,
call_end_query,
call_begin_label,
call_end_label
call_end_label,
call_build_acceleration_structures
};
struct call_header
@@ -165,6 +166,14 @@ struct update_call
VkDeviceSize offset, size;
std::size_t data;
};
// the builds of acceleration structures: the infos, the geometries of all of them one after another and their ranges
struct build_call
{
std::uint32_t count;
std::size_t infos;
std::size_t geometries;
std::size_t ranges;
};
// the copies and the blit: the info with its regions
template <typename Info_> struct copy_call
{
@@ -689,6 +698,36 @@ void command_stream::end_debug_utils_label()
add(call_end_label, 0);
}
void command_stream::build_acceleration_structures(std::uint32_t const Count, VkAccelerationStructureBuildGeometryInfoKHR const *Infos, VkAccelerationStructureBuildRangeInfoKHR const *const *Ranges)
{
if (Count == 0)
{
return;
}
std::vector<VkAccelerationStructureBuildGeometryInfoKHR> infos(Infos, Infos + Count);
std::vector<VkAccelerationStructureGeometryKHR> geometries;
std::vector<VkAccelerationStructureBuildRangeInfoKHR> ranges;
for (std::uint32_t i = 0; i < Count; ++i)
{
auto const &info{Infos[i]};
assert(info.pNext == nullptr);
for (std::uint32_t geometry = 0; geometry < info.geometryCount; ++geometry)
{
geometries.push_back(info.pGeometries != nullptr ? info.pGeometries[geometry] : *info.ppGeometries[geometry]);
ranges.push_back(Ranges[i][geometry]);
}
infos[i].pGeometries = nullptr;
infos[i].ppGeometries = nullptr;
}
auto const call{add(call_build_acceleration_structures, sizeof(build_call))};
auto const infodata{append(infos.data(), sizeof(VkAccelerationStructureBuildGeometryInfoKHR) * infos.size())};
auto const geometrydata{append(geometries.data(), sizeof(VkAccelerationStructureGeometryKHR) * geometries.size())};
auto const rangedata{append(ranges.data(), sizeof(VkAccelerationStructureBuildRangeInfoKHR) * ranges.size())};
payload<build_call>(call) = build_call{Count, infodata, geometrydata, rangedata};
close(call);
work();
}
// the replay
void command_stream::execute(std::size_t const Call, VkCommandBuffer const Buffer, VkRenderingFlags const Flags) const
@@ -956,6 +995,26 @@ void command_stream::execute(std::size_t const Call, VkCommandBuffer const Buffe
vkCmdEndDebugUtilsLabelEXT(Buffer);
}
break;
case call_build_acceleration_structures:
{
auto const &data{payload<build_call>(Call)};
std::vector<VkAccelerationStructureBuildGeometryInfoKHR> infos(at<VkAccelerationStructureBuildGeometryInfoKHR>(data.infos), at<VkAccelerationStructureBuildGeometryInfoKHR>(data.infos) + data.count);
std::vector<VkAccelerationStructureBuildRangeInfoKHR const *> ranges(data.count);
auto const *geometries{data.geometries != no_data ? at<VkAccelerationStructureGeometryKHR>(data.geometries) : nullptr};
auto const *range{data.ranges != no_data ? at<VkAccelerationStructureBuildRangeInfoKHR>(data.ranges) : nullptr};
for (std::uint32_t i = 0; i < data.count; ++i)
{
infos[i].pGeometries = geometries;
ranges[i] = range;
if (geometries != nullptr)
{
geometries += infos[i].geometryCount;
range += infos[i].geometryCount;
}
}
vkCmdBuildAccelerationStructuresKHR(Buffer, data.count, infos.data(), ranges.data());
break;
}
default:
break;
}

View File

@@ -78,6 +78,8 @@ class command_stream
void end_query(VkQueryPool Pool, std::uint32_t Query);
void begin_debug_utils_label(VkDebugUtilsLabelEXT const *Label);
void end_debug_utils_label();
// the builds of acceleration structures (VK_KHR_acceleration_structure, gfx.raytracing), outside of a rendering
void build_acceleration_structures(std::uint32_t Count, VkAccelerationStructureBuildGeometryInfoKHR const *Infos, VkAccelerationStructureBuildRangeInfoKHR const *const *Ranges);
// the stream is made anew
void clear();

View File

@@ -32,6 +32,7 @@ http://mozilla.org/MPL/2.0/.
#include "rendering/vulkan14/vulkan14grass.h"
#include "rendering/vulkan14/vulkan14particles.h"
#include "rendering/vulkan14/vulkan14commands.h"
#include "rendering/vulkan14/vulkan14raytracing.h"
#include "utilities/workerpool.h"
#include "model/material.h"
#include "gl/ubo.h"
@@ -123,6 +124,11 @@ struct viewport_targets
// the temporal anti-aliasing (gfx.antialiasing taa, the main viewport): the two images of the history, written in turn (hdr, the
// lock of the thin things in alpha)
std::array<std::shared_ptr<image>, 2> taa_history;
// the screen space reflections (gfx.ssr, the main viewport): the finished image of the last frame with its mipmaps, which they take
// their colour from, and whether it was cleared once it was made (it's read before the first frame is copied to it)
std::shared_ptr<image> ssr_history;
bool ssr_cleared{false};
bool reflections{false}; // the history was asked for (it can be missing when it couldn't be made)
// the tonemapped image, before the chromatic aberration, and the one which goes to the window
std::shared_ptr<image> tonemapped;
std::shared_ptr<image> output;
@@ -275,7 +281,6 @@ struct cull_resources
{
instance_cull main;
std::array<instance_cull, 3> cascades;
instance_cull reflections;
// the maps of headlights, by the slot of the map, and the scenery parts of the maps of lamps, by their order among those drawn
// in a frame: the passes of a frame don't share the buffers of a culling. made when first needed
std::vector<std::unique_ptr<instance_cull>> headlights;
@@ -361,6 +366,8 @@ struct scene_state
bool grass_tried{false};
// the smoke and the rain or snow of the gpu
gpu_particles particles;
// the ray traced reflections (gfx.raytracing), on a device with ray queries; its set is set RT_SET of the pipelines of the scene
std::unique_ptr<ray_tracing> raytracing;
// the heatmap of the light clusters (gfx.lights.clusterheatmap), made when it's first drawn
std::shared_ptr<scene_program> cluster_heatmap;
bool cluster_heatmap_tried{false};
@@ -369,6 +376,8 @@ struct scene_state
// the format of the levels of the bloom chains: no alpha, no negative values, half the memory of rgba16f, where the device
// draws to it
VkFormat bloom_format{VK_FORMAT_B10G11R11_UFLOAT_PACK32};
// the same for the history of the screen space reflections, where the device blits and filters it
VkFormat history_format{VK_FORMAT_B10G11R11_UFLOAT_PACK32};
// the formats of the passes which don't depend on the viewport
target_formats shadow_formats;
target_formats reflection_formats;

View File

@@ -72,11 +72,6 @@ std::string vulkan14_renderer::cull_info()
text += cull.cascades[cascade].info("gpu cull shadows " + std::to_string(cascade + 1), false);
}
}
if (Global.gfx_gpucull_reflections && cull.reflections.ran())
{
cull.reflections.collect();
text += cull.reflections.info("gpu cull reflections, last face", false);
}
if (Global.gfx_gpucull != 0)
{
// the maps are drawn now and then, the numbers are of the last drawing of each
@@ -177,6 +172,10 @@ std::string vulkan14_renderer::scene_info()
{
text += "cluster lists: " + std::to_string(stats.index_count) + ", overflow " + std::to_string(stats.overflow_count) + " (peak " + std::to_string(stats.overflow_peak) + ")\n";
}
if (scene.raytracing && m_raytracingon)
{
text += scene.raytracing->info() + "\n";
}
return text;
}
@@ -318,6 +317,19 @@ void vulkan14_renderer::draw_debug_ui()
ImGui::SliderInt("ao steps per side", &Global.gfx_postfx_ao_steps, 1, 16);
}
}
ImGui::Separator();
std::string raytracing;
if (m_context->scene.raytracing)
{
ImGui::Checkbox("ray traced reflections where the screen has none (gfx.raytracing)", &m_raytracingon);
ImGui::SliderFloat("range of their rays and of the scenery, m (gfx.raytracing.range)", &Global.gfx_raytracing_range, 10.f, 5000.f, "%.0f", ImGuiSliderFlags_Logarithmic);
raytracing = m_context->scene.raytracing->info();
}
else
{
raytracing = (Global.gfx_raytracing ? "ray traced reflections: the gpu has no ray queries" : "ray traced reflections: off (gfx.raytracing yes in eu07.ini, taken at the start)");
}
m_ssr.draw_debug_ui(raytracing.c_str());
}
ImGui::End();
@@ -338,7 +350,6 @@ void vulkan14_renderer::draw_debug_ui()
if (Global.gfx_gpucull != 0)
{
ImGui::Checkbox("the same for the cascades of the sun shadows (gfx.gpucull.shadows)", &Global.gfx_gpucull_shadows);
ImGui::Checkbox("the same for the environment map, when it's drawn next (gfx.gpucull.reflections)", &Global.gfx_gpucull_reflections);
ImGui::Checkbox("the same for the shadow maps of headlights (gfx.gpucull.shadows.headlights)", &Global.gfx_gpucull_shadows_headlights);
ImGui::Checkbox("the same for the scenery of the shadow maps of lamps, when it's drawn next (gfx.gpucull.shadows.lamps)", &Global.gfx_gpucull_shadows_lamps);
ImGui::Checkbox("the same for the sleepers of the tracks of the view (gfx.gpucull.sleepers)", &Global.gfx_gpucull_sleepers);

View File

@@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "rendering/vulkan14/vulkan14device.h"
#include "rendering/vulkan14/vulkan14instance.h"
#include "utilities/Globals.h"
#include <cstring>
@@ -289,6 +290,32 @@ bool device::create(instance const &Instance, std::string &Reason)
}
}
// the ray traced reflections, asked for in eu07.ini (gfx.raytracing), where the device has what they need
VkPhysicalDeviceAccelerationStructureFeaturesKHR acceleration{};
acceleration.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR;
VkPhysicalDeviceRayQueryFeaturesKHR rayquery{};
rayquery.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR;
m_raytracing = Global.gfx_raytracing && m_capabilities.ray_query;
if (m_raytracing)
{
extensions.push_back(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME);
extensions.push_back(VK_KHR_RAY_QUERY_EXTENSION_NAME);
extensions.push_back(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME);
enabled.v12.bufferDeviceAddress = VK_TRUE;
enabled.v12.runtimeDescriptorArray = VK_TRUE;
enabled.v12.descriptorBindingPartiallyBound = VK_TRUE;
enabled.v12.descriptorBindingVariableDescriptorCount = VK_TRUE;
enabled.v12.shaderSampledImageArrayNonUniformIndexing = VK_TRUE;
acceleration.accelerationStructure = VK_TRUE;
rayquery.rayQuery = VK_TRUE;
acceleration.pNext = &rayquery;
enabled.v14.pNext = &acceleration;
}
else if (Global.gfx_raytracing)
{
log_info("gfx.raytracing: " + m_capabilities.device_name + " has no ray queries (or not what they need), the reflections keep to the screen and the environment map");
}
VkDeviceCreateInfo create{};
create.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
create.pNext = enabled.head();
@@ -316,6 +343,10 @@ bool device::create(instance const &Instance, std::string &Reason)
log_info("using " + m_capabilities.device_name + ", queue families: graphics " + std::to_string(m_families.graphics) + ", compute " + std::to_string(m_families.compute) + (m_families.dedicated_compute ? " (own)" : " (shared)") + ", transfer " +
std::to_string(m_families.transfer) + (m_families.dedicated_transfer ? " (own)" : " (shared)"));
if (m_raytracing)
{
log_info("ray queries on, for the ray traced reflections (gfx.raytracing)");
}
return true;
}

View File

@@ -76,6 +76,12 @@ class device
{
return m_memory_budget;
}
// the ray queries are on with what they need (gfx.raytracing on a device which has them): the device addresses of buffers, the
// acceleration structures, the arrays of textures indexed in the shaders
bool raytracing_enabled() const
{
return m_raytracing;
}
private:
static queue_families find_queue_families(VkInstance Instance, VkPhysicalDevice Physical, VkSurfaceKHR Surface, std::uint32_t &TimestampBits);
@@ -88,6 +94,7 @@ class device
feature_chain m_queried;
bool m_portability_subset{false};
bool m_memory_budget{false};
bool m_raytracing{false};
VkQueue m_graphics_queue{VK_NULL_HANDLE};
VkQueue m_compute_queue{VK_NULL_HANDLE};

View File

@@ -307,8 +307,8 @@ void vulkan14_renderer::init_frame_data()
namespace
{
bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std::uint64_t const Value, std::uint32_t &Formatscount, VkSampleCountFlags const Samplecounts, VkFormat const Bloomformat, vk14::viewport_targets &Targets,
int const Width, int const Height, bool const Srgb, bool const Taa)
bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std::uint64_t const Value, std::uint32_t &Formatscount, VkSampleCountFlags const Samplecounts, VkFormat const Bloomformat, VkFormat const Historyformat,
vk14::viewport_targets &Targets, int const Width, int const Height, bool const Srgb, bool const Taa, bool const Reflections)
{
// as many samples as asked for, as far as the device has them for the colour and the depth
auto samples{static_cast<VkSampleCountFlagBits>(std::clamp(1 << Global.iMultisampling, 1, 8))};
@@ -320,7 +320,7 @@ bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std:
auto const aberration{Global.gfx_postfx_chromaticaberration_enabled};
VkExtent2D const extent{static_cast<std::uint32_t>(std::max(Width, 1)), static_cast<std::uint32_t>(std::max(Height, 1))};
if ((Targets.extent.width == extent.width) && (Targets.extent.height == extent.height) && (Targets.samples == samples) && (Targets.motion == motion) && (Targets.taa == Taa) && (Targets.aberration == aberration) &&
(Targets.srgb == Srgb) && Targets.output)
(Targets.srgb == Srgb) && (Targets.reflections == Reflections) && Targets.output)
{
return true;
}
@@ -336,6 +336,7 @@ bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std:
Targets.taa = Taa;
Targets.aberration = aberration;
Targets.srgb = Srgb;
Targets.reflections = Reflections;
auto make = [&](std::shared_ptr<vk14::image> &Image, VkFormat Format, VkImageUsageFlags Usage, VkSampleCountFlagBits Samples, char const *Name) {
vk14::image_desc desc;
@@ -364,7 +365,9 @@ bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std:
ok = ok && make(Targets.msaa_motion, velocity, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, samples, "msaa velocity");
}
}
ok = ok && make(Targets.colour, hdr, sampledcolour, VK_SAMPLE_COUNT_1_BIT, "scene colour") &&
// the images of the scene the history of the screen space reflections is copied from (blits) are a source of transfers as well
auto const historysource{Reflections ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : 0u};
ok = ok && make(Targets.colour, hdr, sampledcolour | historysource, VK_SAMPLE_COUNT_1_BIT, "scene colour") &&
make(Targets.depth, depth, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_SAMPLE_COUNT_1_BIT, "scene depth");
if (motion)
{
@@ -376,7 +379,23 @@ bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std:
}
if (Taa)
{
ok = ok && make(Targets.taa_history[0], hdr, sampledcolour, VK_SAMPLE_COUNT_1_BIT, "taa history") && make(Targets.taa_history[1], hdr, sampledcolour, VK_SAMPLE_COUNT_1_BIT, "taa history");
ok = ok && make(Targets.taa_history[0], hdr, sampledcolour | historysource, VK_SAMPLE_COUNT_1_BIT, "taa history") &&
make(Targets.taa_history[1], hdr, sampledcolour | historysource, VK_SAMPLE_COUNT_1_BIT, "taa history");
}
if (ok && Reflections)
{
// the history of the screen space reflections, with its mipmaps made by blits; without it they're off
vk14::image_desc desc;
desc.format = Historyformat;
desc.extent = {extent.width, extent.height, 1};
desc.mips = static_cast<std::uint32_t>(std::floor(std::log2(static_cast<float>(std::max(extent.width, extent.height))))) + 1;
desc.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
Targets.ssr_history = std::make_shared<vk14::image>();
if (false == Targets.ssr_history->create(Memory, desc, "reflection history"))
{
ErrorLog("reflection history setup failed, no screen space reflections");
Targets.ssr_history.reset();
}
}
auto const output{Srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM};
if (aberration)
@@ -653,6 +672,11 @@ bool vulkan14_renderer::Render()
clear_sky_images();
ctx.passes.end_pass();
}
// the set of the ray tracing of the frame has a structure to read from its first frame on
if (scene.raytracing)
{
scene.raytracing->begin_frame(ctx.commands, slot, value);
}
// the block of the weather and the scene for the compute passes before the first pass of the scene
m_draw->scene_offset = 0;
m_draw->light_offset = 0;
@@ -693,6 +717,11 @@ bool vulkan14_renderer::Render()
// and so may the tracks the sleeper records were made for, and the models drawn on their own
Clear_Indirect_Records();
scene.glare_visible.clear();
// and the geometry the structures of the ray tracing were built from
if (scene.raytracing)
{
scene.raytracing->clear(ctx.uploads.current_value());
}
}
if ((EditorModeFlag) || (m_nodestateversion != scene::node_state_version))
{
@@ -883,8 +912,10 @@ void vulkan14_renderer::render_scene()
auto const &viewport{m_viewports[i]};
auto const srgb{false == Global.gfx_shadergamma};
auto const *history{scene.targets[i].taa_history[0].get()};
auto const *reflections{scene.targets[i].ssr_history.get()};
if (false == make_targets(ctx.memory, ctx.deletions, ctx.uploads.current_value(), scene.formats_count, ctx.device.caps().limits.framebufferColorSampleCounts & ctx.device.caps().limits.framebufferDepthSampleCounts, scene.bloom_format,
scene.targets[i], viewport.width, viewport.height, srgb, viewport.main && Global.gfx_taa()))
scene.history_format, scene.targets[i], viewport.width, viewport.height, srgb, viewport.main && Global.gfx_taa(),
viewport.main && (false == Global.gfx_skippipeline) && ((Global.gfx_ssr > 0) || (m_raytracingon && scene.raytracing))))
{
continue;
}
@@ -893,6 +924,23 @@ void vulkan14_renderer::render_scene()
// new images of the history, nothing in them yet
m_taa.reset();
}
auto &targets{scene.targets[i]};
if (targets.ssr_history.get() != reflections)
{
m_ssr.reset();
}
if (targets.ssr_history && (false == targets.ssr_cleared))
{
// the pass sets hold the history from now on, it's read (by none of the pixels) before the first frame goes to it
targets.ssr_cleared = true;
auto &image{*targets.ssr_history};
ctx.passes.begin_pass("reflection history", {vk14::use_of(image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_2_CLEAR_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT)});
VkClearColorValue const black{{0.f, 0.f, 0.f, 0.f}};
VkImageSubresourceRange const range{VK_IMAGE_ASPECT_COLOR_BIT, 0, image.desc().mips, 0, 1};
ctx.commands->clear_color_image(image.handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &black, 1, &range);
vk14::barriers(ctx.commands, {vk14::use_of(image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access)});
ctx.passes.end_pass();
}
render_viewport(i);
}
m_current_viewport = (m_viewports.empty() ? nullptr : &m_viewports.front());
@@ -1190,6 +1238,17 @@ void vulkan14_renderer::setup_pass(viewport_config &Viewport, renderpass_config
}
case rendermode::reflections:
{
if (m_rtgather)
{
// the scenery for the ray traced reflections: around the camera of the colour pass in the axes of the world, the draws
// going to the world around the camera; a box of the range of the rays stands for the frustum
auto const range{Global.gfx_raytracing_range};
camera.position() = m_colorpass.pass_camera.position();
camera.projection() = ortho_projection(-range, range, -range, range, -range, range);
frustumtest_proj = ortho_frustumtest_projection(-range, range, -range, range, -range, range);
Config.draw_range = range;
break;
}
// the faces of the map are drawn over a few frames. each is drawn from the place the camera was at
// when the update began, so they fit together along the edges when the camera moves in the meantime
camera.position() = m_environmentupdatelocation;
@@ -1232,6 +1291,15 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
m_renderpass.pass_camera.projection(), glm::dmat3{m_renderpass.pass_camera.modelview()}, m_renderpass.pass_camera.position(), cab, glm::ivec2{vp.width, vp.height});
m_cabfuture = m_taa.cab_future();
}
// the screen space reflections: where the points the rays hit were in the image of the history. the ray traced ones fill in what
// they don't find, or go on their own without them
m_ssrframe = (ssr_wanted(vp) || raytracing_wanted(vp)) && targets.ssr_history;
if (m_ssrframe)
{
auto const *cab{((false == FreeFlyModeFlag) && Global.render_cab && (simulation::Train != nullptr)) ? simulation::Train->Dynamic() : nullptr};
m_ssr.begin_frame(m_renderpass.pass_camera.projection(), glm::dmat3{m_renderpass.pass_camera.modelview()}, m_renderpass.pass_camera.position(), cab, glm::ivec2{vp.width, vp.height},
static_cast<int>(targets.ssr_history->desc().mips));
}
m_colorpass = m_renderpass;
scene_ubs.time = Timer::GetTime();
@@ -1279,6 +1347,10 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
scene_ubs.contact_shadow_params = glm::vec4{0.f};
scene_ubs.contact_shadow_lamps = glm::vec4{0.f};
scene_ubs.shadow_fit = glm::vec4{0.f};
// the reflections: the ground below the horizon of the environment map, and the screen space ones set up for the colour pass of
// the main view only
scene_ubs.env_params = glm::vec4{Global.gfx_ambient_groundalbedo, 0.f, 0.f, 0.f};
gfx::screen_reflections::clear(scene_ubs);
if (vp.main)
{
@@ -1318,6 +1390,12 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
render_headlight_shadows();
ready_lamp_maps();
}
// the scenery around the camera the reflections are traced through, before the passes which trace them
auto const raytracing{raytracing_wanted(vp)};
if (raytracing)
{
render_ray_scene(vp);
}
// the motion blur only when something in the view moves (see opengl46_renderer::Render_pass())
auto const motionblur{Global.gfx_postfx_motionblur_enabled && targets.motion &&
@@ -1393,13 +1471,18 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
// the depth of the whole opaque scene before its colour, which the contact shadows need
m_indirectcolourpending = false;
auto const contactshadows{contact_shadows_wanted(vp)};
if ((Global.gfx_depthprepass_scene || contactshadows) && vp.main && (false == Global.bWireFrame))
auto const reflections{m_ssrframe && (false == Global.bWireFrame)};
if ((Global.gfx_depthprepass_scene || contactshadows || reflections) && vp.main && (false == Global.bWireFrame))
{
render_scene_depth(Index, future);
if (contactshadows)
{
setup_contact_shadows(Index);
}
if (reflections)
{
setup_screen_reflections(Index, taa);
}
}
// opaque parts...
@@ -1474,12 +1557,14 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
}
commands->end_rendering();
end_draws();
if (m_contactshadows)
if (m_contactshadows || m_ssractive)
{
// the depth they read is of this view only
m_contactshadows = false;
m_ssractive = false;
scene_ubs.contact_shadow_params = glm::vec4{0.f};
scene_ubs.contact_shadow_lamps = glm::vec4{0.f};
gfx::screen_reflections::clear(scene_ubs);
}
// the images resolved and drawn are read by the post-processing
m_colorpass.draw_stats = m_renderpass.draw_stats;
@@ -1502,7 +1587,8 @@ void vulkan14_renderer::render_scene_depth(std::size_t const Index, glm::mat4 co
auto &draw{*m_draw};
auto const commands{ctx.commands};
auto const msaa{targets.samples != VK_SAMPLE_COUNT_1_BIT};
auto const contact{contact_shadows_wanted(m_viewports[Index])};
// the depth copied apart from the one the colour pass goes on with, for the contact shadows and the screen space reflections
auto const contact{contact_shadows_wanted(m_viewports[Index]) || m_ssrframe};
commands->end_rendering();
// what the draws go on with
auto const set{draw.set};
@@ -1643,6 +1729,211 @@ void vulkan14_renderer::setup_contact_shadows(std::size_t const Index)
m_contactshadows = true;
}
// the screen space reflections of the main view trace the depth of the scene drawn before the colour and work out the distance of a
// surface from its depth as the contact shadows do: not in the orthographic view of the editor (opengl46_renderer::Ssr_View())
bool vulkan14_renderer::ssr_wanted(viewport_config const &Viewport) const
{
if ((Global.gfx_ssr <= 0) || (false == Viewport.main) || Global.bWireFrame || Global.gfx_skippipeline)
{
return false;
}
auto const &projection{m_renderpass.pass_camera.projection()};
return (projection[2][3] != 0.f) && (projection[2][2] == 0.f) && (projection[3][2] > 0.f);
}
void vulkan14_renderer::setup_screen_reflections(std::size_t const Index, bool const Temporal)
{
auto &targets{m_context->scene.targets[Index]};
if ((false == bool(targets.scene_depth)) || (false == bool(targets.ssr_history)))
{
return;
}
m_ssr.setup(scene_ubs, gfx::screen_reflections::steps(Global.gfx_ssr), raytracing_wanted(m_viewports[Index]), Global.gfx_raytracing_range, Temporal);
upload_scene_block();
m_ssractive = true;
}
// the ray traced reflections, of the main view only (as the screen space ones, which they fill in)
bool vulkan14_renderer::raytracing_wanted(viewport_config const &Viewport) const
{
if ((false == m_raytracingon) || (false == bool(m_context->scene.raytracing)) || (false == Viewport.main) || Global.bWireFrame || Global.gfx_skippipeline)
{
return false;
}
auto const &projection{m_renderpass.pass_camera.projection()};
return (projection[2][3] != 0.f) && (projection[2][2] == 0.f) && (projection[3][2] > 0.f);
}
// the scenery around the camera for the ray traced reflections: the walk of the scene in its reflection mode (the sections, the models
// and the vehicles in their ranges, gfx.reflections.*) around the camera of the colour pass, noting the draws instead of making them,
// built into the acceleration structures of the frame. the vehicle of the cab is left out: from the inside of the cab it would be its
// own shell that the rays hit
void vulkan14_renderer::render_ray_scene(viewport_config &Viewport)
{
auto &ctx{*m_context};
auto &scene{ctx.scene};
m_rtdraws.clear();
m_rtinstances.clear();
auto const skipvehicle{m_shadowskipvehicle};
if ((false == FreeFlyModeFlag) && Global.render_cab && (simulation::Train != nullptr))
{
m_shadowskipvehicle = simulation::Train->Dynamic();
}
auto const colourpass{m_renderpass};
m_rtgather = true;
setup_pass(Viewport, m_renderpass, rendermode::reflections);
setup_matrices();
// the walk binds materials and notes the draws; nothing is recorded
auto &draw{*m_draw};
auto const rendering{draw.rendering};
draw.rendering = false;
draw.blending = false;
Render(simulation::Region);
draw.rendering = rendering;
m_rtgather = false;
m_shadowskipvehicle = skipvehicle;
m_renderpass = colourpass;
setup_matrices();
ctx.passes.begin_pass("ray tracing scene");
scene.raytracing->build(ctx.commands, ctx.slot, ctx.uploads.current_value(), m_framestamp, m_rtdraws, ctx.images);
ctx.passes.end_pass();
}
// a draw of the walk noted for the ray tracing: the chunk with its data on the gpu, the transform, the texture and the colour of the
// material, for each instance of an instanced draw
void vulkan14_renderer::gather_ray_draw(gfx::geometry_handle const &Geometry)
{
auto &draw{*m_draw};
if ((Geometry == null_handle) || (draw.program == nullptr) || (draw.program->vertices != vk14::vertex_kind::scene) || draw.blending || draw.glare)
{
return;
}
if ((Geometry.bank == 0) || (Geometry.bank > m_banks.size()) || (m_banks[Geometry.bank - 1] == nullptr))
{
return;
}
gfx::indirect_source indirect;
if (false == m_geometry.indirect_source(Geometry, indirect))
{
return;
}
auto const &chunk{static_cast<vk14::vulkan14_geometrybank *>(m_banks[Geometry.bank - 1])->last()};
if ((chunk.topology != VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) || (chunk.vertex_address == 0))
{
return;
}
vk14::ray_draw ray;
ray.key = static_cast<std::uint64_t>(Geometry);
ray.source = chunk;
// the diffuse colour of the material as material_diffuse() of the shaders makes it
glm::vec3 const colour{glm::pow(glm::max(glm::vec3{model_ubs.param[0]}, glm::vec3{0.f}), glm::vec3{2.2f})};
auto const value{std::max({model_ubs.param[0].r, model_ubs.param[0].g, model_ubs.param[0].b})};
ray.colour = (value > 0.f ? colour * (model_ubs.param[1].x / value) : glm::vec3{0.f});
ray.glow = model_ubs.emission;
// the diffuse texture of the material, and its alpha test when it has an alpha channel
if (draw.textures[0] != null_handle)
{
auto &texture{m_context->textures->mark_as_used(draw.textures[0])};
if (texture.create() && (m_context->images.find(texture.get_id()) != nullptr))
{
ray.texture = texture.get_id();
ray.cutoff = (texture.get_has_alpha() ? std::max(model_ubs.opacity, 0.f) : 0.f);
}
}
glm::mat4 const modelview{model_ubs.modelview};
if (m_current_instance_count > 0)
{
for (std::size_t instance = 0; instance < m_current_instance_count; ++instance)
{
auto const index{static_cast<std::size_t>(m_current_instance_base) + instance};
if (index >= m_rtinstances.size())
{
break;
}
ray.transform = m_rtinstances[index] * modelview;
m_rtdraws.push_back(ray);
}
}
else
{
ray.transform = modelview;
m_rtdraws.push_back(ray);
}
}
// the finished image of the frame (before the motion blur) copied to the history the reflections of the next frame take their colour
// from, with its mipmaps for the rough surfaces, each level blitted from the one before it
void vulkan14_renderer::store_ssr_history(std::size_t const Index, vk14::image &Source)
{
auto &ctx{*m_context};
auto &targets{ctx.scene.targets[Index]};
if (false == bool(targets.ssr_history))
{
return;
}
auto const commands{ctx.commands};
auto &image{*targets.ssr_history};
auto const mips{image.desc().mips};
auto const width{static_cast<std::int32_t>(image.desc().extent.width)};
auto const height{static_cast<std::int32_t>(image.desc().extent.height)};
ctx.passes.begin_pass("reflections, image of the frame", {vk14::use_of(Source, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_2_BLIT_BIT, VK_ACCESS_2_TRANSFER_READ_BIT),
vk14::use_of(image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_2_BLIT_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT)});
auto blit = [&](VkImage From, std::uint32_t FromLevel, VkImageLayout FromLayout, std::uint32_t ToLevel, glm::ivec2 const &FromSize, glm::ivec2 const &ToSize) {
VkImageBlit2 region{};
region.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2;
region.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, FromLevel, 0, 1};
region.srcOffsets[1] = {FromSize.x, FromSize.y, 1};
region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, ToLevel, 0, 1};
region.dstOffsets[1] = {ToSize.x, ToSize.y, 1};
VkBlitImageInfo2 info{};
info.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2;
info.srcImage = From;
info.srcImageLayout = FromLayout;
info.dstImage = image.handle();
info.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
info.regionCount = 1;
info.pRegions = &region;
info.filter = VK_FILTER_LINEAR;
commands->blit_image2(&info);
};
auto barrier = [&](std::uint32_t Level, VkImageLayout From, VkImageLayout To, VkPipelineStageFlags2 SrcStage, VkAccessFlags2 SrcAccess, VkPipelineStageFlags2 DstStage, VkAccessFlags2 DstAccess) {
VkImageMemoryBarrier2 entry{};
entry.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
entry.srcStageMask = SrcStage;
entry.srcAccessMask = SrcAccess;
entry.dstStageMask = DstStage;
entry.dstAccessMask = DstAccess;
entry.oldLayout = From;
entry.newLayout = To;
entry.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
entry.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
entry.image = image.handle();
entry.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, Level, 1, 0, 1};
VkDependencyInfo dependency{};
dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
dependency.imageMemoryBarrierCount = 1;
dependency.pImageMemoryBarriers = &entry;
commands->pipeline_barrier2(&dependency);
};
glm::ivec2 const sourcesize{static_cast<std::int32_t>(Source.desc().extent.width), static_cast<std::int32_t>(Source.desc().extent.height)};
blit(Source.handle(), 0, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 0, sourcesize, {width, height});
for (std::uint32_t level = 1; level < mips; ++level)
{
barrier(level - 1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_2_BLIT_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_2_BLIT_BIT, VK_ACCESS_2_TRANSFER_READ_BIT);
blit(image.handle(), level - 1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, level, {std::max(width >> (level - 1), 1), std::max(height >> (level - 1), 1)}, {std::max(width >> level, 1), std::max(height >> level, 1)});
}
// the levels read from are in the layout of the sources, the last one in that of the destination: all of them read by the shaders
for (std::uint32_t level = 0; level < mips; ++level)
{
auto const from{(level + 1 < mips) ? VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL};
barrier(level, from, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_2_BLIT_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT, sampled_stage, sampled_access);
}
image.state = vk14::image_state{VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access};
ctx.passes.end_pass();
m_ssr.end_frame();
}
// the rendering of the colour of a viewport: begun with the images cleared, or again after the depth under the cursor was read
void vulkan14_renderer::begin_colour_rendering(std::size_t const Index, bool const Clear)
{
@@ -1815,7 +2106,7 @@ void vulkan14_renderer::render_shadows(viewport_config &Viewport)
vk14::barriers(commands, {vk14::use_of(*scene.shadow_map, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access)});
}
// the environment map, a face of it per frame (opengl46_renderer::Render_reflections())
// the environment map (the sky and the clouds around the camera), a face of it per frame (opengl46_renderer::Render_reflections())
bool vulkan14_renderer::render_reflections(viewport_config &Viewport)
{
auto &ctx{*m_context};
@@ -1957,12 +2248,9 @@ void vulkan14_renderer::render_reflection_face(viewport_config &Viewport)
scene_ubs.projection = OpenGLMatrices.data(GL_PROJECTION);
scene_ubs.inv_view = glm::inverse(glm::mat4{glm::mat3{m_renderpass.pass_camera.modelview()}});
upload_scene_block();
// the sky and the clouds alone: the scenery is reflected by the screen space reflections (gfx.ssr) and the ray tracing, the
// environment map gives what they don't find
render_environment(&simulation::Environment);
// opaque parts...
walk_blending(false);
setup_shadow_matrices();
upload_scene_block();
Render(simulation::Region);
commands->end_rendering();
end_draws();
@@ -2720,6 +3008,12 @@ void vulkan14_renderer::render_post(std::size_t const Index, bool const Motionbl
post_pass("postfx_taa.frag", *targets.taa_history[target], {targets.colour.get(), targets.motion_image.get(), targets.depth.get(), targets.taa_history[1 - target].get()}, constants, "temporal anti-aliasing");
hdr = targets.taa_history[target].get();
}
// the image before the blur is the one the reflections of the next frame take
if (m_ssrframe && m_viewports[Index].main)
{
store_ssr_history(Index, *hdr);
}
m_ssrframe = false;
if (Motionblur && targets.blurred)
{
vk14::draw_constants constants;

View File

@@ -11,6 +11,8 @@ http://mozilla.org/MPL/2.0/.
#include "rendering/vulkan14/vulkan14geometry.h"
#include "rendering/vulkan14/vulkan14texture.h"
#include <atomic>
namespace vk14
{
@@ -20,6 +22,9 @@ namespace
// what opengl reads for the user data of a vertex without any (the attribute isn't given)
gfx::vertex_userdata const no_userdata{glm::vec4{0.f, 0.f, 0.f, 1.f}};
// the numbers the data of the banks get, unique across the banks (a bank made where another one was gets a new one)
std::atomic<std::uint64_t> g_generation{0};
} // namespace
VkPrimitiveTopology topology_of(unsigned int const Type)
@@ -140,6 +145,7 @@ bool vulkan14_geometrybank::build()
}
place.indexcount = static_cast<std::uint32_t>(indices.size()) - place.indexfirst;
place.dirty = false;
place.generation = ++g_generation;
}
m_layoutdirty = false;
m_chunksdirty = false;
@@ -150,9 +156,11 @@ bool vulkan14_geometrybank::build()
auto &memory{*m_resources.memory};
auto &uploads{*m_resources.uploads};
// with the ray tracing the vertices and the indices are built into acceleration structures, and the shading reads them
auto const raytracing{m_resources.raytracing ? (VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR) : 0};
auto const make = [&](std::shared_ptr<buffer> &Buffer, void const *Data, VkDeviceSize const Size, VkBufferUsageFlags const Usage, char const *Name) {
Buffer = std::make_shared<buffer>();
if ((false == Buffer->create(memory, Size, Usage | VK_BUFFER_USAGE_TRANSFER_DST_BIT, memory_kind::device, Name)) || (false == uploads.upload_buffer(Buffer, Data, Size)))
if ((false == Buffer->create(memory, Size, Usage | VK_BUFFER_USAGE_TRANSFER_DST_BIT | raytracing, memory_kind::device, Name)) || (false == uploads.upload_buffer(Buffer, Data, Size)))
{
Buffer.reset();
return false;
@@ -196,6 +204,7 @@ void vulkan14_geometrybank::update()
continue;
}
place.dirty = false;
place.generation = ++g_generation;
auto const &chunk{m_chunks[i]};
gpu_vertices(chunk, vertices, userdata);
vertexparts.emplace_back(place.vertexfirst * sizeof(gfx::basic_vertex), place.vertexcount * sizeof(gfx::basic_vertex));
@@ -245,6 +254,14 @@ bool vulkan14_geometrybank::source(gfx::geometry_handle const &Geometry, chunk_s
Source.vertices = m_vertices->handle();
Source.userdata = m_userdata->handle();
Source.topology = topology_of(m_chunks[index].type);
Source.vertexfirst = place.vertexfirst;
Source.vertexcount = place.vertexcount;
Source.generation = place.generation;
if (m_resources.raytracing)
{
Source.vertex_address = m_vertices->address();
Source.index_address = (m_indices ? m_indices->address() : 0);
}
if (place.indexcount > 0)
{
Source.indices = m_indices->handle();

View File

@@ -36,6 +36,13 @@ struct chunk_source
std::uint32_t first{0}; // first index, or vertex
std::int32_t basevertex{0};
VkPrimitiveTopology topology{VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST};
// for the ray tracing: the vertices of the chunk in the buffer of the bank, the device addresses of the buffers (0 without the ray
// tracing), and the number the data of the chunk on the gpu has: another one when the buffers were made anew or the chunk changed
std::uint32_t vertexfirst{0};
std::uint32_t vertexcount{0};
VkDeviceAddress vertex_address{0};
VkDeviceAddress index_address{0};
std::uint64_t generation{0};
};
// the vulkan primitive of a primitive of opengl (GL_TRIANGLES ...)
@@ -68,6 +75,7 @@ class vulkan14_geometrybank : public gfx::geometry_bank
std::uint32_t indexfirst{0};
std::uint32_t indexcount{0};
bool dirty{true};
std::uint64_t generation{0}; // of its data on the gpu, for the ray tracing (chunk_source::generation)
};
void create_(gfx::geometry_handle const &Geometry) override;

View File

@@ -57,7 +57,7 @@ bool vulkan14_renderer::init_cull()
return false;
}
}
if ((false == cull.reflections.init(environment, device, ctx.pipelines, ctx.shaders, scene.pass_layout, &cull.main)) || (false == cull.hiz.init(device, ctx.pipelines, ctx.shaders, scene.pass_layout)))
if (false == cull.hiz.init(device, ctx.pipelines, ctx.shaders, scene.pass_layout))
{
return false;
}
@@ -90,7 +90,6 @@ void vulkan14_renderer::destroy_cull()
}
culls->clear();
}
cull.reflections.destroy(device);
cull.main.destroy(device);
cull.hiz.destroy(device);
cull.shadowfit.destroy(device);
@@ -140,10 +139,6 @@ void vulkan14_renderer::walk_region_begin()
passcull = &cull.cascades[m_shadowcascade];
m_shadowcasters.clear();
}
else if ((m_renderpass.draw_mode == rendermode::reflections) && Global.gfx_gpucull_reflections && (Global.reflectiontune.fidelity >= 1))
{
passcull = &cull.reflections;
}
}
}
Begin_Pass_Cull(passcull, (passcull == &cull.main));

View File

@@ -35,6 +35,11 @@ bool allocator::create(instance const &Instance, device const &Device, std::stri
{
info.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
}
if (Device.raytracing_enabled())
{
// the acceleration structures are built from the device addresses of the buffers
info.flags |= VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT;
}
// the functions as volk loaded them
VmaVulkanFunctions functions{};
if (!VK14_VERIFY(vmaImportVulkanFunctionsFromVolk(&info, &functions)))
@@ -109,6 +114,7 @@ buffer &buffer::operator=(buffer &&Other) noexcept
std::swap(m_allocation, Other.m_allocation);
std::swap(m_size, Other.m_size);
std::swap(m_mapped, Other.m_mapped);
std::swap(m_address, Other.m_address);
}
return *this;
}
@@ -167,6 +173,22 @@ void buffer::destroy()
m_allocation = VK_NULL_HANDLE;
m_size = 0;
m_mapped = nullptr;
m_address = 0;
}
VkDeviceAddress buffer::address() const
{
if ((m_buffer == VK_NULL_HANDLE) || (m_address != 0))
{
return m_address;
}
VmaAllocatorInfo allocator{};
vmaGetAllocatorInfo(m_allocator, &allocator);
VkBufferDeviceAddressInfo info{};
info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO;
info.buffer = m_buffer;
m_address = vkGetBufferDeviceAddress(allocator.device, &info);
return m_address;
}
void buffer::flush(VkDeviceSize Offset, VkDeviceSize Size) const

View File

@@ -89,6 +89,8 @@ class buffer
{
return m_mapped;
}
// the device address of the buffer, made with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT (the ray tracing)
VkDeviceAddress address() const;
// makes the writes of the cpu seen by the gpu where the memory isn't coherent
void flush(VkDeviceSize Offset, VkDeviceSize Size) const;
// makes the writes of the gpu seen by the cpu where the memory isn't coherent (readback memory may be cached)
@@ -104,6 +106,7 @@ class buffer
VmaAllocation m_allocation{VK_NULL_HANDLE};
VkDeviceSize m_size{0};
std::uint8_t *m_mapped{nullptr};
mutable VkDeviceAddress m_address{0}; // asked for the first time it's needed
};
// the state of an image the barriers start from: its layout and the last stage and access which used it

View File

@@ -0,0 +1,727 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "rendering/vulkan14/vulkan14raytracing.h"
#include "rendering/vulkan14/vulkan14device.h"
#include "rendering/vulkan14/vulkan14commands.h"
#include "rendering/vulkan14/vulkan14texture.h"
#include "rendering/vulkan14/vulkan14bindings.h"
#include <algorithm>
#include <cstring>
namespace vk14
{
namespace
{
// the record of an instance the shading reads (shaders/vk14/rt_reflections.glsl, rt_record)
struct record
{
VkDeviceAddress vertices;
VkDeviceAddress indices;
std::uint32_t texture;
std::uint32_t flags;
float cutoff;
float glow;
glm::vec4 colour;
};
static_assert(sizeof(record) == 48, "the record of an instance of the ray tracing doesn't match the shaders");
std::uint32_t constexpr record_alpha{1};
// the bottom level structures built in a frame at most, and their triangles: the rest waits for the next frames
std::uint32_t constexpr builds_per_frame{96};
std::uint64_t constexpr triangles_per_frame{2000000};
// the instances of the top level structure at most (the records take 24 bits of the custom index)
std::uint32_t constexpr max_instances{1u << 20};
// a bottom level structure not met in this many frames goes, and a texture keeps its index in the array this long
std::uint64_t constexpr bottom_lifetime{1800};
std::uint64_t constexpr texture_lifetime{600};
// the size of the vertices of the banks (gfx::basic_vertex)
VkDeviceSize constexpr vertex_stride{48};
VkDeviceSize grow(VkDeviceSize const Needed, VkDeviceSize const Current)
{
return std::max(Needed, Current + Current / 2);
}
void destroy_structure(VkDevice const Device, VkAccelerationStructureKHR const Structure)
{
if (Structure != VK_NULL_HANDLE)
{
vkDestroyAccelerationStructureKHR(Device, Structure, nullptr);
}
}
// waits for the writes of the builds before the structures are read: by the builds which follow and by the shaders
void build_barrier(command_stream *Commands, VkPipelineStageFlags2 const Source, VkAccessFlags2 const SourceAccess, VkPipelineStageFlags2 const Destination, VkAccessFlags2 const DestinationAccess)
{
VkMemoryBarrier2 barrier{};
barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2;
barrier.srcStageMask = Source;
barrier.srcAccessMask = SourceAccess;
barrier.dstStageMask = Destination;
barrier.dstAccessMask = DestinationAccess;
VkDependencyInfo dependency{};
dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
dependency.memoryBarrierCount = 1;
dependency.pMemoryBarriers = &barrier;
Commands->pipeline_barrier2(&dependency);
}
} // namespace
ray_tracing::~ray_tracing()
{
destroy();
}
bool ray_tracing::init(device const &Device, allocator &Memory, deletion_queue &Deletions, std::uint32_t const Frames, std::string &Reason)
{
using namespace vk14::bindings;
m_device = &Device;
m_memory = &Memory;
m_deletions = &Deletions;
m_scratchalignment = Device.caps().scratch_alignment;
m_texturecount = std::min<std::uint32_t>(Device.caps().max_indexed_textures, 16384);
auto const handle{Device.handle()};
// the structure, the records and the textures, the last binding as many as the set is made with and partly bound
std::array<VkDescriptorSetLayoutBinding, 3> bindings{{{RT_SCENE, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr},
{RT_RECORDS, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr},
{RT_TEXTURES, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, m_texturecount, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}}};
std::array<VkDescriptorBindingFlags, 3> const flags{0, 0, VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT};
VkDescriptorSetLayoutBindingFlagsCreateInfo bindingflags{};
bindingflags.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO;
bindingflags.bindingCount = static_cast<std::uint32_t>(flags.size());
bindingflags.pBindingFlags = flags.data();
VkDescriptorSetLayoutCreateInfo layout{};
layout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layout.pNext = &bindingflags;
layout.bindingCount = static_cast<std::uint32_t>(bindings.size());
layout.pBindings = bindings.data();
if (!VK14_VERIFY(vkCreateDescriptorSetLayout(handle, &layout, nullptr, &m_layout)))
{
m_layout = VK_NULL_HANDLE;
Reason = "no layout of the set of the ray tracing";
return false;
}
name_object(handle, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, handle_value(m_layout), "ray tracing set");
std::array<VkDescriptorPoolSize, 3> const sizes{{{VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, Frames}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, Frames}, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, m_texturecount * Frames}}};
VkDescriptorPoolCreateInfo pool{};
pool.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool.maxSets = Frames;
pool.poolSizeCount = static_cast<std::uint32_t>(sizes.size());
pool.pPoolSizes = sizes.data();
if (!VK14_VERIFY(vkCreateDescriptorPool(handle, &pool, nullptr, &m_pool)))
{
m_pool = VK_NULL_HANDLE;
Reason = "no pool of the sets of the ray tracing";
return false;
}
m_frames.resize(Frames);
std::vector<VkDescriptorSetLayout> const layouts(Frames, m_layout);
std::vector<std::uint32_t> const counts(Frames, m_texturecount);
std::vector<VkDescriptorSet> sets(Frames, VK_NULL_HANDLE);
VkDescriptorSetVariableDescriptorCountAllocateInfo variable{};
variable.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO;
variable.descriptorSetCount = Frames;
variable.pDescriptorCounts = counts.data();
VkDescriptorSetAllocateInfo allocation{};
allocation.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocation.pNext = &variable;
allocation.descriptorPool = m_pool;
allocation.descriptorSetCount = Frames;
allocation.pSetLayouts = layouts.data();
if (!VK14_VERIFY(vkAllocateDescriptorSets(handle, &allocation, sets.data())))
{
Reason = "no sets of the ray tracing";
return false;
}
for (std::uint32_t frame = 0; frame < Frames; ++frame)
{
m_frames[frame].set = sets[frame];
m_frames[frame].written_textures.assign(m_texturecount, {});
}
// the index 0 is the white texture of the records without one
m_textureowners.assign(m_texturecount, 0);
m_textureused.assign(m_texturecount, 0);
m_freetextures.clear();
for (std::uint32_t index = m_texturecount; index > 1; --index)
{
m_freetextures.push_back(index - 1);
}
log_info("ray traced reflections: up to " + std::to_string(m_texturecount) + " textures of the scenery, " + std::to_string(Frames) + " sets");
return true;
}
void ray_tracing::destroy()
{
if (m_device == nullptr)
{
return;
}
auto const handle{m_device->handle()};
for (auto &bottom : m_bottoms)
{
destroy_structure(handle, bottom.second.handle);
}
m_bottoms.clear();
m_bottombytes = 0;
for (auto &frame : m_frames)
{
destroy_structure(handle, frame.top);
}
m_frames.clear();
if (m_pool != VK_NULL_HANDLE)
{
vkDestroyDescriptorPool(handle, m_pool, nullptr);
m_pool = VK_NULL_HANDLE;
}
if (m_layout != VK_NULL_HANDLE)
{
vkDestroyDescriptorSetLayout(handle, m_layout, nullptr);
m_layout = VK_NULL_HANDLE;
}
m_textureindices.clear();
m_device = nullptr;
}
void ray_tracing::clear(std::uint64_t const Value)
{
if (m_device == nullptr)
{
return;
}
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_bottoms.clear();
m_bottombytes = 0;
}
bool ray_tracing::ensure(std::shared_ptr<buffer> &Buffer, VkDeviceSize const Size, VkBufferUsageFlags const Usage, memory_kind const Kind, char const *Name, std::uint64_t const Value)
{
if (Buffer && (Buffer->size() >= Size))
{
return true;
}
auto const current{Buffer ? Buffer->size() : 0};
auto made{std::make_shared<buffer>()};
if (false == made->create(*m_memory, grow(Size, current), Usage, Kind, Name))
{
return false;
}
if (Buffer)
{
m_deletions->push(Value, [old = std::move(Buffer)]() {});
}
Buffer = std::move(made);
return true;
}
VkDeviceAddress ray_tracing::aligned_scratch(frame_data const &Frame) const
{
auto const address{Frame.scratch->address()};
auto const alignment{static_cast<VkDeviceAddress>(m_scratchalignment)};
return (address + alignment - 1) / alignment * alignment;
}
void ray_tracing::begin_frame(command_stream *Commands, std::uint32_t const Slot, std::uint64_t const Value)
{
if ((m_device == nullptr) || (Slot >= m_frames.size()) || (m_frames[Slot].top != VK_NULL_HANDLE) || (m_white == VK_NULL_HANDLE))
{
return;
}
// the shaders statically use the set: it gets an empty structure, a record and the white texture before anything else
auto &frame{m_frames[Slot]};
if (false == ensure(frame.records, sizeof(record), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, memory_kind::upload, "ray tracing records", Value))
{
return;
}
std::memset(frame.records->mapped(), 0, sizeof(record));
frame.records->flush(0, sizeof(record));
if (false == build_top(Commands, frame, 0, Value))
{
return;
}
// the build of the frame later on reuses the structure and the scratch
build_barrier(Commands, VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR,
VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR);
VkWriteDescriptorSetAccelerationStructureKHR structure{};
structure.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR;
structure.accelerationStructureCount = 1;
structure.pAccelerationStructures = &frame.top;
VkDescriptorBufferInfo const records{frame.records->handle(), 0, VK_WHOLE_SIZE};
VkDescriptorImageInfo const white{m_whitesampler, m_white, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL};
std::array<VkWriteDescriptorSet, 3> writes{};
for (auto &write : writes)
{
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.dstSet = frame.set;
write.descriptorCount = 1;
}
writes[0].pNext = &structure;
writes[0].dstBinding = vk14::bindings::RT_SCENE;
writes[0].descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
writes[1].dstBinding = vk14::bindings::RT_RECORDS;
writes[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
writes[1].pBufferInfo = &records;
writes[2].dstBinding = vk14::bindings::RT_TEXTURES;
writes[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writes[2].pImageInfo = &white;
vkUpdateDescriptorSets(m_device->handle(), static_cast<std::uint32_t>(writes.size()), writes.data(), 0, nullptr);
frame.written_top = frame.top;
frame.written_records = frame.records->handle();
frame.written_textures[0] = {m_white, m_whitesampler, {}};
}
bool ray_tracing::build_top(command_stream *Commands, frame_data &Frame, std::uint32_t const Count, std::uint64_t const Value)
{
auto const handle{m_device->handle()};
// the instances: room for the count, kept from frame to frame
auto const capacity{std::max<std::uint32_t>(Count, 1)};
if (false == ensure(Frame.instances, sizeof(VkAccelerationStructureInstanceKHR) * capacity, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR,
memory_kind::upload, "ray tracing instances", Value))
{
return false;
}
VkAccelerationStructureGeometryKHR geometry{};
geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;
geometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR;
geometry.geometry.instances.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR;
geometry.geometry.instances.arrayOfPointers = VK_FALSE;
geometry.geometry.instances.data.deviceAddress = Frame.instances->address();
VkAccelerationStructureBuildGeometryInfoKHR info{};
info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR;
info.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;
info.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
info.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;
info.geometryCount = 1;
info.pGeometries = &geometry;
// the structure is made for more instances than the frame has, so it lasts a while as the count changes
auto count{Count};
if ((Frame.top == VK_NULL_HANDLE) || (Count > Frame.top_capacity))
{
auto const room{std::max<std::uint32_t>(static_cast<std::uint32_t>(grow(Count, Frame.top_capacity)), 64)};
VkAccelerationStructureBuildSizesInfoKHR sizes{};
sizes.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR;
vkGetAccelerationStructureBuildSizesKHR(handle, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &info, &room, &sizes);
auto storage{std::make_shared<buffer>()};
VkAccelerationStructureKHR made{VK_NULL_HANDLE};
auto ok{storage->create(*m_memory, sizes.accelerationStructureSize, VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, memory_kind::device,
"ray tracing top level")};
if (ok)
{
VkAccelerationStructureCreateInfoKHR create{};
create.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR;
create.buffer = storage->handle();
create.size = sizes.accelerationStructureSize;
create.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;
ok = VK14_VERIFY(vkCreateAccelerationStructureKHR(handle, &create, nullptr, &made));
}
if (ok)
{
name_object(handle, VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR, handle_value(made), "ray tracing top level");
if (Frame.top != VK_NULL_HANDLE)
{
m_deletions->push(Value, [handle, structure = Frame.top, oldstorage = Frame.storage]() { destroy_structure(handle, structure); });
}
Frame.top = made;
Frame.storage = std::move(storage);
Frame.top_capacity = room;
Frame.top_scratch = sizes.buildScratchSize + m_scratchalignment;
}
else
{
// no memory for a bigger one: the old one (the set holds it) with as many instances as it has room for
if (Frame.top == VK_NULL_HANDLE)
{
return false;
}
count = Frame.top_capacity;
}
}
if (false == ensure(Frame.scratch, Frame.top_scratch, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, memory_kind::device, "ray tracing scratch", Value))
{
return false;
}
info.dstAccelerationStructure = Frame.top;
info.scratchData.deviceAddress = aligned_scratch(Frame);
VkAccelerationStructureBuildRangeInfoKHR range{};
range.primitiveCount = count;
VkAccelerationStructureBuildRangeInfoKHR const *ranges{&range};
Commands->build_acceleration_structures(1, &info, &ranges);
return true;
}
std::uint32_t ray_tracing::texture_index(std::uint64_t const Id, std::uint64_t const Frame)
{
if (Id == 0)
{
return 0;
}
auto const lookup{m_textureindices.find(Id)};
if (lookup != m_textureindices.end())
{
m_textureused[lookup->second] = Frame;
return lookup->second;
}
if (m_freetextures.empty())
{
// the indices not used for a while are taken back (the sets write them anew before they're read again)
for (std::uint32_t index = 1; index < m_texturecount; ++index)
{
if ((m_textureowners[index] != 0) && (m_textureused[index] + texture_lifetime < Frame))
{
m_textureindices.erase(m_textureowners[index]);
m_textureowners[index] = 0;
m_freetextures.push_back(index);
}
}
if (m_freetextures.empty())
{
return 0;
}
}
auto const index{m_freetextures.back()};
m_freetextures.pop_back();
m_textureindices.emplace(Id, index);
m_textureowners[index] = Id;
m_textureused[index] = Frame;
return index;
}
void ray_tracing::build(command_stream *Commands, std::uint32_t const Slot, std::uint64_t const Value, std::uint64_t const Frame, std::vector<ray_draw> const &Draws, image_table const &Images)
{
if ((m_device == nullptr) || (Slot >= m_frames.size()))
{
return;
}
auto const handle{m_device->handle()};
auto &frame{m_frames[Slot]};
// the bottom level structures the draws need: the ones not there yet or out of date are built, as many as a frame lets
struct pending_build
{
VkAccelerationStructureGeometryKHR geometry;
VkAccelerationStructureBuildRangeInfoKHR range;
VkAccelerationStructureBuildGeometryInfoKHR info;
VkDeviceSize scratch;
};
std::vector<pending_build> builds;
std::uint64_t triangles{0};
std::uint32_t waiting{0};
VkDeviceSize scratchsize{0};
auto const scratchalignment{static_cast<VkDeviceSize>(m_scratchalignment)};
for (auto const &draw : Draws)
{
auto lookup{m_bottoms.find(draw.key)};
if ((lookup != m_bottoms.end()) && (lookup->second.generation == draw.source.generation))
{
lookup->second.used = Frame;
continue;
}
if ((builds.size() >= builds_per_frame) || (triangles >= triangles_per_frame))
{
++waiting;
continue;
}
auto const &source{draw.source};
auto const indexed{source.indices != VK_NULL_HANDLE};
auto const primitives{source.count / 3};
if ((primitives == 0) || (source.vertex_address == 0) || (indexed && (source.index_address == 0)) || (source.vertexcount == 0))
{
continue;
}
pending_build build{};
build.geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;
build.geometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
auto &triangles_data{build.geometry.geometry.triangles};
triangles_data.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR;
triangles_data.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT;
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);
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;
build.info.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
build.info.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;
build.info.geometryCount = 1;
VkAccelerationStructureBuildSizesInfoKHR sizes{};
sizes.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR;
build.info.pGeometries = &build.geometry;
vkGetAccelerationStructureBuildSizesKHR(handle, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &build.info, &primitives, &sizes);
build.info.pGeometries = nullptr;
auto storage{std::make_shared<buffer>()};
if (false == storage->create(*m_memory, sizes.accelerationStructureSize, VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, memory_kind::device, "ray tracing bottom level"))
{
continue;
}
VkAccelerationStructureCreateInfoKHR create{};
create.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR;
create.buffer = storage->handle();
create.size = sizes.accelerationStructureSize;
create.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
VkAccelerationStructureKHR structure{VK_NULL_HANDLE};
if (!VK14_VERIFY(vkCreateAccelerationStructureKHR(handle, &create, nullptr, &structure)))
{
continue;
}
if (lookup != m_bottoms.end())
{
// 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); });
}
VkAccelerationStructureDeviceAddressInfoKHR addressinfo{};
addressinfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR;
addressinfo.accelerationStructure = structure;
auto &entry{m_bottoms[draw.key]};
entry.storage = std::move(storage);
entry.handle = structure;
entry.address = vkGetAccelerationStructureDeviceAddressKHR(handle, &addressinfo);
entry.generation = source.generation;
entry.used = Frame;
entry.bytes = sizes.accelerationStructureSize;
m_bottombytes += entry.bytes;
build.info.dstAccelerationStructure = structure;
build.scratch = scratchsize;
scratchsize += (sizes.buildScratchSize + scratchalignment - 1) / scratchalignment * scratchalignment;
builds.push_back(build);
triangles += primitives;
}
// the vertices and the indices copied to the banks in this frame (the uploads go to the queue before the frame) are read by the builds
if (false == builds.empty())
{
build_barrier(Commands, VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_ACCESS_2_SHADER_READ_BIT);
if (false == ensure(frame.scratch, scratchsize + scratchalignment, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, memory_kind::device, "ray tracing scratch", Value))
{
// no memory for the builds: the structures made for them go, they're tried again
for (auto const &build : builds)
{
for (auto it = m_bottoms.begin(); it != m_bottoms.end(); ++it)
{
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_bottoms.erase(it);
break;
}
}
}
builds.clear();
}
else
{
auto const scratchbase{aligned_scratch(frame)};
std::vector<VkAccelerationStructureBuildGeometryInfoKHR> infos;
std::vector<VkAccelerationStructureBuildRangeInfoKHR const *> ranges;
infos.reserve(builds.size());
ranges.reserve(builds.size());
for (auto &build : builds)
{
build.info.pGeometries = &build.geometry;
build.info.scratchData.deviceAddress = scratchbase + build.scratch;
infos.push_back(build.info);
ranges.push_back(&build.range);
}
Commands->build_acceleration_structures(static_cast<std::uint32_t>(infos.size()), infos.data(), ranges.data());
// the top level structure reads them, and its build uses the scratch memory again
build_barrier(Commands, VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR);
}
}
// the records and the instances of the draws with their structures
std::size_t instancecount{0};
for (auto const &draw : Draws)
{
auto const lookup{m_bottoms.find(draw.key)};
if ((lookup != m_bottoms.end()) && (lookup->second.generation == draw.source.generation))
{
++instancecount;
}
}
instancecount = std::min<std::size_t>(instancecount, max_instances);
if ((false == ensure(frame.records, sizeof(record) * std::max<std::size_t>(instancecount, 1), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, memory_kind::upload, "ray tracing records", Value)) ||
(false == ensure(frame.instances, sizeof(VkAccelerationStructureInstanceKHR) * std::max<std::size_t>(instancecount, 1),
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, memory_kind::upload, "ray tracing instances", Value)))
{
instancecount = 0;
}
auto *records{reinterpret_cast<record *>(frame.records ? frame.records->mapped() : nullptr)};
auto *instances{reinterpret_cast<VkAccelerationStructureInstanceKHR *>(frame.instances ? frame.instances->mapped() : nullptr)};
std::vector<std::uint32_t> usedtextures;
std::uint32_t written{0};
for (auto const &draw : Draws)
{
if ((written >= instancecount) || (records == nullptr) || (instances == nullptr))
{
break;
}
auto const lookup{m_bottoms.find(draw.key)};
if ((lookup == m_bottoms.end()) || (lookup->second.generation != draw.source.generation))
{
continue;
}
auto const &source{draw.source};
auto const texture{texture_index(draw.texture, Frame)};
if (texture != 0)
{
usedtextures.push_back(texture);
}
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.texture = texture;
auto const alpha{(texture != 0) && (draw.cutoff > 0.f)};
entry.flags = (alpha ? record_alpha : 0);
entry.cutoff = draw.cutoff;
entry.glow = draw.glow;
entry.colour = glm::vec4{draw.colour, 1.f};
auto &instance{instances[written]};
// the rows of the transform, which glm keeps by the columns
for (int row = 0; row < 3; ++row)
{
for (int column = 0; column < 4; ++column)
{
instance.transform.matrix[row][column] = draw.transform[column][row];
}
}
instance.instanceCustomIndex = written;
instance.mask = 0xFF;
instance.instanceShaderBindingTableRecordOffset = 0;
// the models have faces of one side, seen from both in the reflections; an alpha tested texture leaves holes in its faces
instance.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR | (alpha ? VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_KHR : VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR);
instance.accelerationStructureReference = lookup->second.address;
++written;
}
if (written > 0)
{
frame.records->flush(0, sizeof(record) * written);
frame.instances->flush(0, sizeof(VkAccelerationStructureInstanceKHR) * written);
}
if (build_top(Commands, frame, written, Value))
{
build_barrier(Commands, VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR);
}
// the set of the frame: what changed in it
std::vector<VkWriteDescriptorSet> writes;
std::vector<VkDescriptorImageInfo> images;
images.reserve(usedtextures.size());
VkWriteDescriptorSetAccelerationStructureKHR structure{};
structure.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR;
structure.accelerationStructureCount = 1;
structure.pAccelerationStructures = &frame.top;
VkDescriptorBufferInfo const recordsinfo{frame.records ? frame.records->handle() : VK_NULL_HANDLE, 0, VK_WHOLE_SIZE};
if ((frame.top != VK_NULL_HANDLE) && (frame.top != frame.written_top))
{
VkWriteDescriptorSet write{};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.pNext = &structure;
write.dstSet = frame.set;
write.dstBinding = vk14::bindings::RT_SCENE;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
writes.push_back(write);
frame.written_top = frame.top;
}
if (frame.records && (frame.records->handle() != frame.written_records))
{
VkWriteDescriptorSet write{};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.dstSet = frame.set;
write.dstBinding = vk14::bindings::RT_RECORDS;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
write.pBufferInfo = &recordsinfo;
writes.push_back(write);
frame.written_records = frame.records->handle();
}
std::sort(usedtextures.begin(), usedtextures.end());
usedtextures.erase(std::unique(usedtextures.begin(), usedtextures.end()), usedtextures.end());
for (auto const index : usedtextures)
{
// the texture as its material samples it; a texture which isn't a plain 2d image (or is gone) reads as white
auto view{m_white};
auto sampler{m_whitesampler};
std::shared_ptr<image> source;
if (auto const *entry{Images.find(m_textureowners[index])})
{
if (entry->target && (entry->target->desc().view_type == VK_IMAGE_VIEW_TYPE_2D) && (entry->sampler != VK_NULL_HANDLE))
{
view = entry->target->view();
sampler = entry->sampler;
source = entry->target;
}
}
auto &cached{frame.written_textures[index]};
if ((cached.view == view) && (cached.sampler == sampler) && (cached.source.lock() == source))
{
continue;
}
cached = {view, sampler, source};
images.push_back({sampler, view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL});
VkWriteDescriptorSet write{};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.dstSet = frame.set;
write.dstBinding = vk14::bindings::RT_TEXTURES;
write.dstArrayElement = index;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write.pImageInfo = &images.back();
writes.push_back(write);
}
if (false == writes.empty())
{
vkUpdateDescriptorSets(handle, static_cast<std::uint32_t>(writes.size()), writes.data(), 0, nullptr);
}
// the structures not met for a while go
for (auto it = m_bottoms.begin(); it != m_bottoms.end();)
{
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); });
it = m_bottoms.erase(it);
}
else
{
++it;
}
}
m_lastinstances = written;
m_lastrecords = written;
m_lastbuilt = static_cast<std::uint32_t>(builds.size());
m_lastwaiting = waiting;
m_lasttextures = static_cast<std::uint32_t>(usedtextures.size());
}
std::string ray_tracing::info() const
{
return "ray traced reflections: " + std::to_string(m_lastinstances) + " instances, " + std::to_string(m_bottoms.size()) + " chunks (" + memory_size_string(m_bottombytes) + "), built " + std::to_string(m_lastbuilt) +
", waiting " + std::to_string(m_lastwaiting) + ", textures " + std::to_string(m_lasttextures);
}
} // namespace vk14

View File

@@ -0,0 +1,157 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
// the ray traced reflections of the vulkan renderer (gfx.raytracing, on a gpu with ray queries): what the screen space reflections
// don't find is traced through the scenery around the camera (shaders/vk14/rt_reflections.glsl).
//
// the renderer walks the scene for it once a frame (the walk in its reflection mode, gathering the draws instead of making them) and
// hands the draws over: each chunk of geometry gets a bottom level acceleration structure of its own the first time it's met (a few
// of them a frame, the rest wait for the next ones; made anew when its data changes, let go when it isn't met for a while), each draw
// an instance of the top level structure of the frame with a record the shading reads (where its vertices are, the texture and the
// colour of its material). each frame in flight has its own top level structure, buffers and set of the pipelines of the scene (set
// RT_SET), written while the frame is made: the gpu is done with the frame which used them before
#pragma once
#include "rendering/vulkan14/vulkan14memory.h"
#include "rendering/vulkan14/vulkan14geometry.h"
#include <memory>
#include <unordered_map>
#include <vector>
namespace vk14
{
class device;
class command_stream;
class image_table;
// a draw of the walk for the ray tracing: what an instance of the top level structure is made of
struct ray_draw
{
glm::mat4 transform{1.f}; // the vertices of the chunk to the world around the camera (the axes of the world, the camera at the origin)
std::uint64_t key{0}; // the chunk, gfx::geometry_handle
chunk_source source; // where its data is
std::uint64_t texture{0}; // the id of the diffuse texture in the image table, 0: none
glm::vec3 colour{1.f}; // the diffuse colour of the material, linear
float cutoff{0.f}; // the alpha test of the texture, 0: none
float glow{0.f}; // the light of its own
};
class ray_tracing
{
public:
ray_tracing() = default;
ray_tracing(ray_tracing const &) = delete;
ray_tracing &operator=(ray_tracing const &) = delete;
~ray_tracing();
// Frames: the frames in flight
bool init(device const &Device, allocator &Memory, deletion_queue &Deletions, std::uint32_t Frames, std::string &Reason);
// the texture of the records without one (index 0 of the array), before the first frame
void set_white(VkImageView View, VkSampler Sampler)
{
m_white = View;
m_whitesampler = Sampler;
}
void destroy();
bool ready() const
{
return m_layout != VK_NULL_HANDLE;
}
// the layout of the set of the pipelines of the scene
VkDescriptorSetLayout layout() const
{
return m_layout;
}
// the frame Slot begins (its frame before is done on the gpu): the set of the slot gets an empty structure the first time, so the
// shaders always have one to read. Value: of the timeline, when the gpu is done with the frame
void begin_frame(command_stream *Commands, std::uint32_t Slot, std::uint64_t Value);
// the scenery of the frame: the bottom level structures of the draws which aren't there yet (as many as a frame lets), the top
// level structure with the instances of the draws, their records and textures written to the set of the frame. outside of a
// rendering, before the passes which trace the rays
void build(command_stream *Commands, std::uint32_t Slot, std::uint64_t Value, std::uint64_t Frame, std::vector<ray_draw> const &Draws, image_table const &Images);
// the set of the frame
VkDescriptorSet set(std::uint32_t Slot) const
{
return (Slot < m_frames.size() ? m_frames[Slot].set : VK_NULL_HANDLE);
}
// a scenery is being loaded, or the geometry is gone: the bottom level structures go
void clear(std::uint64_t Value);
// for the debug panel: the structures, their memory, the instances of the last frame
std::string info() const;
private:
struct bottom
{
std::shared_ptr<buffer> storage;
VkAccelerationStructureKHR handle{VK_NULL_HANDLE};
VkDeviceAddress address{0};
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};
};
struct frame_data
{
VkDescriptorSet set{VK_NULL_HANDLE};
// the top level structure, its memory and the instances it's built from, the records of the shading, the scratch of the builds
std::shared_ptr<buffer> storage;
VkAccelerationStructureKHR top{VK_NULL_HANDLE};
std::uint32_t top_capacity{0}; // instances
VkDeviceSize top_scratch{0}; // what a build of the structure needs of the scratch, with the alignment
std::shared_ptr<buffer> instances;
std::shared_ptr<buffer> records;
std::shared_ptr<buffer> scratch;
// what the set holds: the structure, the records, and the textures by their index
VkAccelerationStructureKHR written_top{VK_NULL_HANDLE};
VkBuffer written_records{VK_NULL_HANDLE};
struct written_texture
{
VkImageView view{VK_NULL_HANDLE};
VkSampler sampler{VK_NULL_HANDLE};
std::weak_ptr<image> source; // a view made after this one is gone can get its handle
};
std::vector<written_texture> written_textures;
};
// a buffer of the frame at least Size bytes, grown when it isn't (the old one stays when the new one can't be made)
bool ensure(std::shared_ptr<buffer> &Buffer, VkDeviceSize Size, VkBufferUsageFlags Usage, memory_kind Kind, char const *Name, std::uint64_t Value);
// the top level structure of the frame for Count instances, recorded to Commands; false: no memory
bool build_top(command_stream *Commands, frame_data &Frame, std::uint32_t Count, std::uint64_t Value);
// the index of a texture in the array of the set, 0: none (white, or no room)
std::uint32_t texture_index(std::uint64_t Id, std::uint64_t Frame);
VkDeviceAddress aligned_scratch(frame_data const &Frame) const;
device const *m_device{nullptr};
allocator *m_memory{nullptr};
deletion_queue *m_deletions{nullptr};
VkDescriptorSetLayout m_layout{VK_NULL_HANDLE};
VkDescriptorPool m_pool{VK_NULL_HANDLE};
VkImageView m_white{VK_NULL_HANDLE};
VkSampler m_whitesampler{VK_NULL_HANDLE};
std::uint32_t m_texturecount{0};
std::uint32_t m_scratchalignment{256};
std::vector<frame_data> m_frames;
std::unordered_map<std::uint64_t, bottom> m_bottoms; // by the chunk
VkDeviceSize m_bottombytes{0};
// the indices of the textures in the array: by the id of the image, the image of each index and the frame it was last used in
std::unordered_map<std::uint64_t, std::uint32_t> m_textureindices;
std::vector<std::uint64_t> m_textureowners;
std::vector<std::uint64_t> m_textureused;
std::vector<std::uint32_t> m_freetextures;
// what the last frame had, for the panel
std::uint32_t m_lastinstances{0};
std::uint32_t m_lastrecords{0};
std::uint32_t m_lastbuilt{0};
std::uint32_t m_lastwaiting{0};
std::uint32_t m_lasttextures{0};
};
} // namespace vk14

View File

@@ -343,7 +343,7 @@ bool vulkan14_renderer::context::create_core(std::uint32_t const Frames, std::st
Reason = "no ring for the data of the frames";
return false;
}
if ((false == shaders.init(vk14::binding_preamble(), Reason)) || (false == pipelines.init(device, Reason)) || (false == passes.create(device, Frames, Reason)))
if ((false == shaders.init(vk14::binding_preamble(device.raytracing_enabled()), Reason)) || (false == pipelines.init(device, Reason)) || (false == passes.create(device, Frames, Reason)))
{
return false;
}
@@ -355,6 +355,7 @@ bool vulkan14_renderer::context::create_core(std::uint32_t const Frames, std::st
resources.images = &images;
resources.thread = std::this_thread::get_id();
resources.pending = &pending;
resources.raytracing = device.raytracing_enabled();
textures = std::make_unique<texture_bank>([this]() -> texture_source * { return new vk14::vulkan14_texture(resources); });
return imgui.create(resources, shaders, pipelines, ring, Reason);
}

View File

@@ -29,6 +29,7 @@ http://mozilla.org/MPL/2.0/.
#include "rendering/common/shadowfit.h"
#include "rendering/common/gpuparticles.h"
#include "rendering/common/temporalaa.h"
#include "rendering/common/screenreflections.h"
#include "rendering/particles.h"
#include "model/material.h"
#include "vehicle/Camera.h"
@@ -46,6 +47,7 @@ struct draw_constants;
enum class blend_mode;
class instance_cull;
struct image_use;
struct ray_draw;
} // namespace vk14
class vulkan14_renderer : public gfx_renderer, public gfx::scene_walk
@@ -271,6 +273,16 @@ class vulkan14_renderer : public gfx_renderer, public gfx::scene_walk
void render_shadows(viewport_config &Viewport);
bool render_reflections(viewport_config &Viewport);
void render_reflection_face(viewport_config &Viewport);
// the ray traced reflections (gfx.raytracing, vulkan14raytracing.h): whether the colour pass of the viewport gets them, and the
// scenery around the camera gathered by the walk in its reflection mode (the draws noted instead of made) and built into the
// acceleration structures of the frame; a draw noted, with the state of the walk
bool raytracing_wanted(viewport_config const &Viewport) const;
void render_ray_scene(viewport_config &Viewport);
void gather_ray_draw(gfx::geometry_handle const &Geometry);
bool m_rtgather{false};
bool m_raytracingon{true}; // switched off in the debug window
std::vector<vk14::ray_draw> m_rtdraws;
std::vector<glm::mat4> m_rtinstances; // the modelviews of the instanced draws noted (walk_instances())
void render_picking(rendermode const Mode);
// the sky dome and the sun and the moon
void render_environment(world_environment *Environment);
@@ -302,6 +314,15 @@ class vulkan14_renderer : public gfx_renderer, public gfx::scene_walk
bool contact_shadows_wanted(viewport_config const &Viewport) const;
void setup_contact_shadows(std::size_t const Viewport);
bool m_contactshadows{false};
// the screen space reflections (gfx.ssr) of the main viewport: whether its colour pass gets them (they need the depth of the
// scene drawn before the colour), the parameters of the pass set for them, the finished image of the frame (before the motion
// blur) copied to the history with its mipmaps; the matrices and the state, begun for the frame and set up for its colour pass
bool ssr_wanted(viewport_config const &Viewport) const;
void setup_screen_reflections(std::size_t const Viewport, bool const Temporal);
void store_ssr_history(std::size_t const Viewport, vk14::image &Source);
gfx::screen_reflections m_ssr;
bool m_ssrframe{false};
bool m_ssractive{false};
// the materials
material_handle invalid_material() const
{

View File

@@ -190,6 +190,8 @@ std::array<std::array<VkImageView, vk14::bindings::PASS_BINDING_COUNT>, 3> pass_
views[HEADLIGHT_SHADOW_TEX] = (headlights != nullptr ? headlights->view() : scene.headlight_shadow_empty->view());
// the depth of the scene of the main view drawn before its colour, which the contact shadows read
views[SCENE_DEPTH_TEX] = (colour && (false == scene.targets.empty()) && scene.targets.front().scene_depth ? scene.targets.front().scene_depth->view() : scene.depth_empty->view());
// the image of the last frame of the main view, which the screen space reflections take their colour from
views[SSR_HISTORY_TEX] = (colour && (false == scene.targets.empty()) && scene.targets.front().ssr_history ? scene.targets.front().ssr_history->view() : scene.white->view());
}
return result;
}
@@ -303,7 +305,20 @@ bool vulkan14_renderer::init_scene(std::string &Reason)
}
}
VkPushConstantRange const graphicsconstants{VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, PUSH_CONSTANTS_SIZE};
scene.scene_layout = vk14::make_pipeline_layout(device, {scene.pass_layout, scene.material_set.layout}, {graphicsconstants}, "scene");
// the ray traced reflections: the scenery around the camera, set RT_SET of the scene, which the shaders of the scene read when they
// were made with REFLECTION_RAYS (vk14::binding_preamble())
std::vector<VkDescriptorSetLayout> scenesets{scene.pass_layout, scene.material_set.layout};
if (ctx.device.raytracing_enabled())
{
scene.raytracing = std::make_unique<vk14::ray_tracing>();
static_assert(RT_SET == 2, "the set of the ray tracing follows the pass set and the material set");
if (false == scene.raytracing->init(ctx.device, ctx.memory, ctx.deletions, frames, Reason))
{
return false;
}
scenesets.push_back(scene.raytracing->layout());
}
scene.scene_layout = vk14::make_pipeline_layout(device, scenesets, {graphicsconstants}, "scene");
if ((scene.scene_layout == VK_NULL_HANDLE) || (false == scene.material_set.create_template(device, scene.scene_layout, "material textures")))
{
Reason = "no layout of the pipelines of the scene";
@@ -386,6 +401,10 @@ bool vulkan14_renderer::init_scene(std::string &Reason)
}
std::uint32_t const white{0xffffffff};
ctx.uploads.upload(scene.white, &white, sizeof(white), {{0, {1, 1, 1}}}, false);
if (scene.raytracing)
{
scene.raytracing->set_white(scene.white->view(), ctx.sampling.repeat_aniso);
}
}
{
vk14::image_desc desc;
@@ -518,6 +537,13 @@ bool vulkan14_renderer::init_scene(std::string &Reason)
{
scene.bloom_format = VK_FORMAT_R16G16B16A16_SFLOAT;
}
// the history of the screen space reflections is copied and filtered by blits
vkGetPhysicalDeviceFormatProperties(ctx.device.physical(), scene.history_format, &properties);
auto const blits{VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT};
if ((properties.optimalTilingFeatures & blits) != blits)
{
scene.history_format = VK_FORMAT_R16G16B16A16_SFLOAT;
}
}
// the formats of the passes which don't depend on the viewport
scene.shadow_formats.depth = VK_FORMAT_D32_SFLOAT;
@@ -737,6 +763,11 @@ void vulkan14_renderer::destroy_scene()
return;
}
destroy_cull();
if (scene.raytracing)
{
scene.raytracing->destroy();
scene.raytracing.reset();
}
scene.lamps.destroy();
scene.terrain.reset();
scene.grass.destroy();
@@ -939,6 +970,7 @@ void vulkan14_renderer::write_pass_sets(vk14::instance_cull *Cull)
write(handle, LOCAL_SHADOW_LAMPS_TEX, nullptr, image(ctx.sampling.shadow, views[LOCAL_SHADOW_LAMPS_TEX]));
write(handle, HEADLIGHT_SHADOW_TEX, nullptr, image(ctx.sampling.shadow, views[HEADLIGHT_SHADOW_TEX]));
write(handle, SCENE_DEPTH_TEX, nullptr, image(ctx.sampling.clamp_nearest, views[SCENE_DEPTH_TEX]));
write(handle, SSR_HISTORY_TEX, nullptr, image(ctx.sampling.clamp_linear, views[SSR_HISTORY_TEX]));
}
vkUpdateDescriptorSets(device, static_cast<std::uint32_t>(writes.size()), writes.data(), 0, nullptr);
}
@@ -1003,6 +1035,19 @@ void vulkan14_renderer::bind_pass_set()
info.pDynamicOffsets = offsets.data();
draw.commands->bind_descriptor_sets2(&info);
draw.set_dirty = false;
// the set of the ray tracing of the frame, which the shaders of the scene made with REFLECTION_RAYS read
if (scene.raytracing)
{
VkDescriptorSet const rtset{scene.raytracing->set(m_context->slot)};
VkBindDescriptorSetsInfo rt{};
rt.sType = VK_STRUCTURE_TYPE_BIND_DESCRIPTOR_SETS_INFO;
rt.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
rt.layout = scene.scene_layout;
rt.firstSet = vk14::bindings::RT_SET;
rt.descriptorSetCount = 1;
rt.pDescriptorSets = &rtset;
draw.commands->bind_descriptor_sets2(&rt);
}
}
// the pass set of the colour of the main view bound for the compute kernels (their layouts share set 0 and the push constants),
@@ -1575,6 +1620,13 @@ std::uint32_t vulkan14_renderer::push_draw_block()
std::uint32_t vulkan14_renderer::walk_instances(glm::mat4 const *Modelviews, std::size_t const Count)
{
if (m_rtgather)
{
// noted for the ray tracing: the base is the index of the first of them among those noted
auto const base{static_cast<std::uint32_t>(m_rtinstances.size())};
m_rtinstances.insert(m_rtinstances.end(), Modelviews, Modelviews + Count);
return base;
}
auto &scene{m_context->scene};
auto &instances{scene.instances};
auto const capacity{static_cast<std::size_t>(instances.window / instances.stride) - 1};
@@ -1618,6 +1670,11 @@ void vulkan14_renderer::open_instance_window()
void vulkan14_renderer::walk_draw(gfx::geometry_handle const &Geometry)
{
model_ubs.set_modelview(OpenGLMatrices.data(GL_MODELVIEW));
if (m_rtgather)
{
gather_ray_draw(Geometry);
return;
}
if (m_indirectrecording)
{
// the draw is made later, from the gpu culling, with this state
@@ -1630,6 +1687,14 @@ void vulkan14_renderer::walk_draw(gfx::geometry_handle const &Geometry)
void vulkan14_renderer::walk_draw(std::vector<gfx::geometrybank_handle>::iterator First, std::vector<gfx::geometrybank_handle>::iterator Last)
{
model_ubs.set_modelview(OpenGLMatrices.data(GL_MODELVIEW));
if (m_rtgather)
{
for (; First != Last; ++First)
{
gather_ray_draw(*First);
}
return;
}
// the chunks drawn together share the block of the draw
auto newblock{true};
for (; First != Last; ++First)

View File

@@ -181,6 +181,12 @@ bool shader_library::init(std::string const &Preamble, std::string &Reason)
m_cachedir = user_cache_dir("shaders");
m_binaries = Global.gfx_shader_binaries;
if (m_binaries && (Preamble.find("#define REFLECTION_RAYS") != std::string::npos))
{
// the binaries are made without the ray traced reflections
log_info("shaders: the ray traced reflections are on (gfx.raytracing), the SPIR-V binaries aren't used");
m_binaries = false;
}
if (m_binaries)
{
// the binaries are compiled with the constants written in the files, the sources get those of the renderer
@@ -752,9 +758,18 @@ void set_draw_state(command_stream *Commands, VkExtent2D Extent)
namespace vk14
{
std::string binding_preamble()
std::string binding_preamble(bool const Raytracing)
{
std::string preamble{"#define MASZYNA_CONSTANTS 1\n"};
if (Raytracing)
{
// the extensions go before anything else of the source; shaders/vk14/rt_reflections.glsl says what they're for
preamble += "#extension GL_EXT_ray_query : require\n"
"#extension GL_EXT_buffer_reference2 : require\n"
"#extension GL_EXT_buffer_reference_uvec2 : require\n"
"#extension GL_EXT_nonuniform_qualifier : require\n"
"#define REFLECTION_RAYS 1\n";
}
auto const define = [&preamble](char const *Name, auto const Value, char const *Suffix = "") { preamble += std::string("#define ") + Name + " " + std::to_string(Value) + Suffix + "\n"; };
using namespace vk14::bindings;
define("UI_SET", UI_SET);
@@ -786,6 +801,7 @@ std::string binding_preamble()
define("LOCAL_SHADOW_LAMPS_TEX", LOCAL_SHADOW_LAMPS_TEX);
define("HEADLIGHT_SHADOW_TEX", HEADLIGHT_SHADOW_TEX);
define("SCENE_DEPTH_TEX", SCENE_DEPTH_TEX);
define("SSR_HISTORY_TEX", SSR_HISTORY_TEX);
define("PASS_BINDING_COUNT", PASS_BINDING_COUNT);
define("MATERIAL_TEXTURE_FIRST", MATERIAL_TEXTURE_FIRST);
define("CL_CLUSTER_UBO", CL_CLUSTER_UBO);
@@ -799,6 +815,10 @@ std::string binding_preamble()
define("POST_TEXTURES", POST_TEXTURES);
define("POST_EXPOSURE_SSBO", POST_EXPOSURE_SSBO);
define("COMPUTE_SET", COMPUTE_SET);
define("RT_SET", RT_SET);
define("RT_SCENE", RT_SCENE);
define("RT_RECORDS", RT_RECORDS);
define("RT_TEXTURES", RT_TEXTURES);
define("PUSH_CONSTANTS_SIZE", PUSH_CONSTANTS_SIZE);
define("SPEC_OUTPUT_GAMMA", SPEC_OUTPUT_GAMMA);
define("SPEC_SHADOWMAP_ENABLED", SPEC_SHADOWMAP_ENABLED);

View File

@@ -139,6 +139,8 @@ struct resources
// the thread of the renderer: the content given by other threads waits for it (update_from_memory())
std::thread::id thread;
pending_textures *pending{nullptr};
// the ray tracing is on (gfx.raytracing): the geometry is built into acceleration structures and read by its device address
bool raytracing{false};
};
// the vulkan format of the data of a texture and how the data has to be changed for it