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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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 = ®ion;
|
||||
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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
727
rendering/vulkan14/vulkan14raytracing.cpp
Normal file
727
rendering/vulkan14/vulkan14raytracing.cpp
Normal 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
|
||||
157
rendering/vulkan14/vulkan14raytracing.h
Normal file
157
rendering/vulkan14/vulkan14raytracing.h
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user