17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-10 06:25:31 +01:00

Take the ambient occlusion into the lighting and add a probe of the cab

This commit is contained in:
2026-10-27 13:23:52 +01:00
parent ac0b4e5f15
commit 9dde5b0e5e
43 changed files with 2402 additions and 45 deletions

View File

@@ -116,6 +116,70 @@ void vulkan14_renderer::destroy_ao()
scene.ssao_noise.reset();
}
// the ambient occlusion goes into the light of the surfaces of the colour pass of the main view (gfx.postfx.ao.lighting,
// opengl46_renderer::Ao_Lighting_Wanted()): the ground truth one, made from the depth of the whole scene drawn before the colour,
// which needs a perspective projection with the far plane at infinity (the distance of a surface worked out from its depth)
bool vulkan14_renderer::ao_lighting_wanted(viewport_config const &Viewport) const
{
if ((Global.gfx_postfx_ao != 2) || (false == Global.gfx_postfx_ao_lighting) || (false == Viewport.main) || Global.bWireFrame || Global.gfx_skippipeline)
{
return false;
}
auto const &targets{m_context->scene.targets};
if (targets.empty() || (false == bool(targets.front().ssao)) || (false == bool(targets.front().ssao_blur)))
{
return false;
}
auto const &projection{scene_ubs.projection};
return (projection[2][3] != 0.f) && (projection[2][2] == 0.f) && (projection[3][2] > 0.f);
}
// the ground truth ambient occlusion of the depth of the scene drawn before the colour (render_scene_depth()), for the shading of the
// colour pass, left in the layout it reads it in. outside a rendering
bool vulkan14_renderer::compute_ao_lighting(std::size_t const Index)
{
auto &ctx{*m_context};
auto &scene{ctx.scene};
auto &targets{scene.targets[Index]};
auto const commands{ctx.commands};
if ((false == bool(targets.ssao)) || (false == bool(targets.ssao_blur)) || (false == bool(targets.scene_depth)))
{
return false;
}
ctx.passes.begin_pass("ambient occlusion");
// the projection the depth was drawn with
auto const &projection{m_colorpass.pass_camera.projection()};
auto const inverseprojection{glm::inverse(projection)};
auto const width{targets.ssao->desc().extent.width};
auto const height{targets.ssao->desc().extent.height};
auto const groups_x{(width + GROUP_SIZE - 1) / GROUP_SIZE};
auto const groups_y{(height + GROUP_SIZE - 1) / GROUP_SIZE};
// horizon search
vk14::barriers(commands, {vk14::use_of(*targets.scene_depth, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access),
vk14::use_of(*targets.ssao, VK_IMAGE_LAYOUT_GENERAL, compute_stage, VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT)});
gtao_constants constants{};
constants.inv_projection = inverseprojection;
constants.params = glm::vec2{Global.gfx_postfx_ao_radius, projection[0][0] * 0.5f * static_cast<float>(width)};
constants.clipw_row = glm::vec4{projection[0][3], projection[1][3], projection[2][3], projection[3][3]};
constants.quality = glm::ivec2{Global.gfx_postfx_ao_slices, Global.gfx_postfx_ao_steps};
auto const depthimage{vk14::image_descriptor(ctx.sampling.clamp_nearest, targets.scene_depth->view(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)};
scene.gtao_main.dispatch(commands, {depthimage, vk14::storage_image(targets.ssao->view())}, constants, groups_x, groups_y);
// smoothing
vk14::barriers(commands, {vk14::use_of(*targets.ssao, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access),
vk14::use_of(*targets.ssao_blur, VK_IMAGE_LAYOUT_GENERAL, compute_stage, VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT)});
scene.gtao_denoise.dispatch(commands, {depthimage, vk14::image_descriptor(ctx.sampling.clamp_nearest, targets.ssao->view(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL), vk14::storage_image(targets.ssao_blur->view())},
inverseprojection, groups_x, groups_y);
vk14::barriers(commands, {vk14::use_of(*targets.ssao_blur, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access)});
return true;
}
// the block of the scene of the colour pass set up to read the occlusion (light_common.glsl, ambient_occlusion())
void vulkan14_renderer::setup_ao_lighting()
{
scene_ubs.ao_params = glm::vec4{1.f, Global.gfx_postfx_ao_strength, 0.f, 0.f};
upload_scene_block();
}
// the ambient occlusion of the viewport from the depth of its scene (resolved), multiplied into its colour
// (opengl46_renderer::Render_ambient_occlusion()). Mode: Global.gfx_postfx_ao. outside a rendering
void vulkan14_renderer::render_ao(std::size_t const Index, int const Mode)

View File

@@ -73,7 +73,14 @@ constexpr std::uint32_t SCENE_DEPTH_TEX = 24;
constexpr std::uint32_t SSR_HISTORY_TEX = 25;
// the water on the windscreens (gfx::windscreen_water): the drops, their grids and the wetted paths the glasses read
constexpr std::uint32_t WATER_SSBO = 26;
constexpr std::uint32_t PASS_BINDING_COUNT = 27;
// the ambient occlusion of the main view in the light of the surfaces (gfx.postfx.ao.lighting, the stage A3), half its size
constexpr std::uint32_t SCENE_AO_TEX = 27;
// the probe of the occupied cab (gfx::cab_probe, the stage A4): the cube maps of the light of its lamps the cab reflects and of its
// albedo, and the light it lights the cab with again (gl::cab_probe_data)
constexpr std::uint32_t CAB_PROBE_TEX = 28;
constexpr std::uint32_t CAB_PROBE_ALBEDO_TEX = 29;
constexpr std::uint32_t CAB_PROBE_SSBO = 30;
constexpr std::uint32_t PASS_BINDING_COUNT = 31;
// 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

View File

@@ -0,0 +1,405 @@
/*
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 probe of the occupied cab in the vulkan renderer (the stage A4 of the plan; opengl46_cab_probe and
// opengl46_renderer::Render_cab_probe()): the maps of vk14::cab_probe_maps, the cab drawn to them by the colour mode of the shared walk
// of the scene with the programs of the probe (the six faces at once by multiview), their mipmaps and the spherical harmonics of
// the light the cab reflects (cabprobe_sh.comp)
#include "stdafx.h"
#include "rendering/vulkan14/vulkan14context.h"
#include "rendering/vulkan14/vulkan14commands.h"
#include "simulation/simulation.h"
#include "utilities/Globals.h"
#include "vehicle/DynObj.h"
#include "vehicle/Train.h"
namespace
{
VkPipelineStageFlags2 const colour_stage{VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT};
VkAccessFlags2 const colour_access{VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT};
VkPipelineStageFlags2 const depth_stage{VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT};
VkAccessFlags2 const depth_access{VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT};
VkPipelineStageFlags2 const sampled_stage{VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT};
VkAccessFlags2 const sampled_access{VK_ACCESS_2_SHADER_SAMPLED_READ_BIT};
// the formats of the maps: the light reflected (hdr) and the albedo
VkFormat const LIGHT_FORMAT{VK_FORMAT_R16G16B16A16_SFLOAT};
VkFormat const ALBEDO_FORMAT{VK_FORMAT_R8G8B8A8_UNORM};
// the mipmaps of a cube map whose first level was drawn: each level from the one before it, all of them left in the layout the
// shading reads them in (the environment map makes its own the same way, render_reflections())
void make_cube_mipmaps(vk14::command_stream *Commands, vk14::image &Image)
{
auto const mips{Image.desc().mips};
auto const size{static_cast<std::int32_t>(Image.desc().extent.width)};
auto barrier = [&](std::uint32_t Level, std::uint32_t Count, 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, Count, 0, 6};
VkDependencyInfo dependency{};
dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
dependency.imageMemoryBarrierCount = 1;
dependency.pImageMemoryBarriers = &entry;
Commands->pipeline_barrier2(&dependency);
};
// the first level was drawn, the others are undefined
barrier(0, 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colour_stage, VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_2_BLIT_BIT, VK_ACCESS_2_TRANSFER_READ_BIT);
if (mips > 1)
{
barrier(1, mips - 1, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_2_NONE, VK_ACCESS_2_NONE, VK_PIPELINE_STAGE_2_BLIT_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);
}
for (std::uint32_t level = 1; level < mips; ++level)
{
VkImageBlit2 blit{};
blit.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2;
blit.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, level - 1, 0, 6};
blit.srcOffsets[1] = {std::max(size >> (level - 1), 1), std::max(size >> (level - 1), 1), 1};
blit.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, level, 0, 6};
blit.dstOffsets[1] = {std::max(size >> level, 1), std::max(size >> level, 1), 1};
VkBlitImageInfo2 info{};
info.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2;
info.srcImage = Image.handle();
info.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
info.dstImage = Image.handle();
info.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
info.regionCount = 1;
info.pRegions = &blit;
info.filter = VK_FILTER_LINEAR;
Commands->blit_image2(&info);
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);
}
barrier(0, mips, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_2_BLIT_BIT, VK_ACCESS_2_TRANSFER_READ_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};
}
VkRenderingAttachmentInfo cleared_attachment(VkImageView const View, VkImageLayout const Layout, VkClearValue const &Value)
{
VkRenderingAttachmentInfo info{};
info.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
info.imageView = View;
info.imageLayout = Layout;
info.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
info.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
info.clearValue = Value;
return info;
}
} // namespace
namespace vk14
{
cab_probe_maps::~cab_probe_maps()
{
destroy();
}
void cab_probe_maps::init(VkDevice const Device, allocator &Memory, deletion_queue &Deletions)
{
m_device = Device;
m_memory = &Memory;
m_deletions = &Deletions;
}
void cab_probe_maps::destroy()
{
// the device is idle: the maps go at once
m_deletions = nullptr;
let_go();
}
bool cab_probe_maps::gpu_allocate(int const Size)
{
if ((m_memory == nullptr) || (Size <= 0))
{
return false;
}
auto const size{static_cast<std::uint32_t>(Size)};
// the maps: cube maps with their mipmaps, read by the shading, drawn to through a view of the six layers of the first level
auto make_map = [&](std::shared_ptr<image> &Map, VkImageView &View, VkFormat const Format, char const *Name) {
image_desc desc;
desc.format = Format;
desc.extent = {size, size, 1};
desc.layers = 6;
desc.mips = static_cast<std::uint32_t>(std::floor(std::log2(static_cast<float>(size)))) + 1;
desc.view_type = VK_IMAGE_VIEW_TYPE_CUBE;
desc.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
desc.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
Map = std::make_shared<image>();
if (false == Map->create(*m_memory, desc, Name))
{
Map.reset();
return false;
}
VkImageViewCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.image = Map->handle();
info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
info.format = Format;
info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6};
if (vkCreateImageView(m_device, &info, nullptr, &View) != VK_SUCCESS)
{
View = VK_NULL_HANDLE;
return false;
}
return true;
};
if ((false == make_map(m_light, m_lightview, LIGHT_FORMAT, "cab probe light")) || (false == make_map(m_albedo, m_albedoview, ALBEDO_FORMAT, "cab probe albedo")))
{
let_go();
return false;
}
image_desc depth;
depth.format = VK_FORMAT_D32_SFLOAT;
depth.extent = {size, size, 1};
depth.layers = 6;
depth.view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
depth.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
m_depth = std::make_shared<image>();
if (false == m_depth->create(*m_memory, depth, "cab probe depth"))
{
let_go();
return false;
}
m_target = draw_target{m_light.get(), m_albedo.get(), m_depth.get(), m_lightview, m_albedoview, size};
m_drawn = false;
return true;
}
void cab_probe_maps::gpu_release()
{
let_go();
}
void cab_probe_maps::let_go()
{
m_target = draw_target{};
m_drawn = false;
std::vector<VkImageView> views;
for (auto *view : {&m_lightview, &m_albedoview})
{
if (*view != VK_NULL_HANDLE)
{
views.push_back(*view);
}
*view = VK_NULL_HANDLE;
}
if (m_deletions != nullptr)
{
// the frames in flight may still read them
m_deletions->push(m_value, [device = m_device, views = std::move(views), light = std::move(m_light), albedo = std::move(m_albedo), depth = std::move(m_depth)]() {
for (auto *view : views)
{
vkDestroyImageView(device, view, nullptr);
}
});
}
else
{
for (auto *view : views)
{
vkDestroyImageView(m_device, view, nullptr);
}
}
m_light.reset();
m_albedo.reset();
m_depth.reset();
}
} // namespace vk14
bool vulkan14_renderer::init_cab_probe(std::string &Reason)
{
auto &ctx{*m_context};
auto &scene{ctx.scene};
auto const device{ctx.device.handle()};
try
{
auto program = [&](char const *Fragment) {
auto result{std::make_shared<vk14::scene_program>()};
result->vertex = ctx.shaders.get("cabprobe.vert");
result->fragment = ctx.shaders.get(Fragment);
result->fragment_name = Fragment;
result->vertex_name = "cabprobe.vert";
result->vertices = vk14::vertex_kind::scene;
result->name = std::string("cabprobe.vert + ") + Fragment;
return result;
};
scene.cabprobe_program = program("cabprobe.frag");
scene.cabprobe_alpha_program = program("cabprobe_alpha.frag");
if (false == scene.cabprobe_sh.create(device, ctx.pipelines, ctx.shaders.get("cabprobe_sh.comp"), scene.pass_layout, {}, "cab probe spherical harmonics"))
{
Reason = "no compute pipeline of the cab probe";
return false;
}
}
catch (gfx::shader_exception const &e)
{
Reason = std::string("invalid shader: ") + e.what();
return false;
}
// the light reflected and the albedo, the six faces drawn at once
scene.cabprobe_formats.colour = {LIGHT_FORMAT, ALBEDO_FORMAT};
scene.cabprobe_formats.colours = 2;
scene.cabprobe_formats.depth = VK_FORMAT_D32_SFLOAT;
scene.cabprobe_formats.view_mask = 0x3f;
scene.cabprobe_formats.id = ++scene.formats_count;
// the light the cab lights itself with again, cleared with the stand-ins in the first frame
if (false == scene.cabprobe_bounce.create(ctx.memory, sizeof(gl::cab_probe_data), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, vk14::memory_kind::device, "cab probe bounce"))
{
Reason = "no buffer of the cab probe";
return false;
}
// the stand-in of the maps: 1x1 faces of nothing, cleared with the other stand-ins in the first frame
vk14::image_desc desc;
desc.format = VK_FORMAT_R8G8B8A8_UNORM;
desc.view_type = VK_IMAGE_VIEW_TYPE_CUBE;
desc.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
desc.layers = 6;
desc.extent = {1, 1, 1};
desc.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
scene.cabprobe_empty = std::make_shared<vk14::image>();
if (false == scene.cabprobe_empty->create(ctx.memory, desc, "empty cab probe"))
{
scene.cabprobe_empty.reset();
Reason = "no stand-in of the cab probe";
return false;
}
scene.cabprobe.init(device, ctx.memory, ctx.deletions);
return true;
}
void vulkan14_renderer::destroy_cab_probe()
{
auto &ctx{*m_context};
auto &scene{ctx.scene};
scene.cabprobe.destroy();
scene.cabprobe_sh.destroy(ctx.device.handle());
scene.cabprobe_program.reset();
scene.cabprobe_alpha_program.reset();
scene.cabprobe_bounce.destroy();
scene.cabprobe_empty.reset();
}
TDynamicObject *vulkan14_renderer::cab_probe_vehicle() const
{
// the conditions of Render_cab()
if (FreeFlyModeFlag || (false == Global.render_cab) || (simulation::Train == nullptr))
{
return nullptr;
}
auto *vehicle{simulation::Train->Dynamic()};
if ((vehicle == nullptr) || (false == vehicle->bDisplayCab) || (vehicle->mdKabina == nullptr) || (vehicle->mdKabina == vehicle->mdModel))
{
return nullptr;
}
return vehicle;
}
void vulkan14_renderer::render_cab_probe()
{
auto &ctx{*m_context};
auto &scene{ctx.scene};
auto &probe{scene.cabprobe};
auto const commands{ctx.commands};
auto *vehicle{cab_probe_vehicle()};
glm::vec3 center{0.f};
glm::vec3 extent{0.f};
if ((vehicle != nullptr) && (false == m_locallights.cab_bounds(vehicle->mdKabina, m_geometry, center, extent)))
{
vehicle = nullptr;
}
// the cab lamp lights the probe when it's a light of the frame
auto const *lamp{(vehicle != nullptr) && (m_locallights.cab_lamp_vehicle() == vehicle) ? m_locallights.cab_lamp() : nullptr};
if (false == probe.update_wanted(vehicle, (vehicle != nullptr ? vehicle->mdKabina : nullptr), center, extent, lamp, m_framestamp))
{
return;
}
auto const &target{probe.target()};
if ((target.light == nullptr) || (target.albedo == nullptr) || (target.depth == nullptr) || (false == bool(scene.cabprobe_program)))
{
return;
}
ctx.passes.begin_pass("cab probe");
// the lamp with its shadow map, when the map holds the shadows of the lamp where it is
auto const shadows{(lamp != nullptr) && scene.lamps.ready(vehicle) && (scene.lamps.lamp() == m_locallights.cab_lamp_local())};
probe.setup_drawing(scene_ubs, lamp, m_locallights.cab_lamp_local(), shadows, scene.lamps.weights());
// the cab drawn by the colour mode with the programs of the probe. the camera is put at the vehicle, which Render_cab() places the cab
// relative to, and the modelview takes the cab model to the space of the probe
m_renderpass.draw_mode = rendermode::color;
m_renderpass.pass_camera.position() = vehicle->GetPosition();
m_renderpass.draw_stats = {};
OpenGLMatrices.mode(GL_MODELVIEW);
OpenGLMatrices.load_matrix(glm::mat4{probe.model_to_probe() * glm::inverse(vehicle->mMatrix)});
probe.begin();
std::vector<vk14::image_use> const uses{vk14::use_of(*target.light, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, colour_stage, colour_access),
vk14::use_of(*target.albedo, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, colour_stage, colour_access),
vk14::use_of(*target.depth, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, depth_stage, depth_access)};
vk14::barriers(commands, uses);
VkClearValue nothing{};
nothing.color = {{0.f, 0.f, 0.f, 0.f}};
VkClearValue farthest{};
farthest.depthStencil = {0.f, 0};
std::array<VkRenderingAttachmentInfo, 2> colours{cleared_attachment(target.light_view, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, nothing),
cleared_attachment(target.albedo_view, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, nothing)};
auto depth{cleared_attachment(target.depth->view(), VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, farthest)};
VkRenderingInfo rendering{};
rendering.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
rendering.renderArea = {{0, 0}, {target.size, target.size}};
rendering.layerCount = 1;
rendering.viewMask = scene.cabprobe_formats.view_mask;
rendering.colorAttachmentCount = static_cast<std::uint32_t>(colours.size());
rendering.pColorAttachments = colours.data();
rendering.pDepthAttachment = &depth;
commands->begin_rendering(&rendering);
remember_rendering(rendering, uses);
// one sided walls face the probe from either side
start_draws(scene.cabprobe_formats, static_cast<int>(target.size), static_cast<int>(target.size), vk14::scene_state::set_plain, false);
m_draw->cullface = false;
walk_blending(false);
upload_scene_block();
upload_light_header();
m_draw->probepass = true;
m_draw->depthskip = true;
Render_cab(vehicle, 0.0f, false);
m_draw->probepass = false;
m_draw->depthskip = true;
if (m_draw->rendering)
{
commands->end_rendering();
}
end_draws();
// the mipmaps, and the light the cab lights itself with again from a small level of the light reflected
make_cube_mipmaps(commands, *target.light);
make_cube_mipmaps(commands, *target.albedo);
struct
{
glm::ivec2 level;
} constants{glm::ivec2{probe.sh_level(), probe.sh_size()}};
scene.cabprobe_sh.dispatch(commands,
{vk14::image_descriptor(ctx.sampling.clamp_linear, target.light->view(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL),
vk14::buffer_descriptor(scene.cabprobe_bounce.handle(), 0, sizeof(gl::cab_probe_data))},
constants, 1);
vk14::memory_barrier(commands, VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT, VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, VK_ACCESS_2_SHADER_STORAGE_READ_BIT);
probe.drawn();
probe.end(static_cast<std::uint32_t>(m_renderpass.draw_stats.drawcalls));
m_renderpass = m_colorpass;
ctx.passes.end_pass();
}

View File

@@ -0,0 +1,93 @@
/*
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 probe of the occupied cab in the vulkan renderer (the stage A4 of the plan, opengl46_cab_probe): the maps of gfx::cab_probe,
// which tells when they're drawn and what the shading reads. the six faces are drawn at once by multiview (cabprobe.vert reads the
// face from gl_ViewIndex) instead of the geometry shader of the opengl renderer; the renderer draws to the target the class keeps
// (target()), makes the mipmaps and the spherical harmonics (vulkan14cabprobe.cpp)
#pragma once
#include "rendering/common/cabprobe.h"
#include "rendering/vulkan14/vulkan14memory.h"
#include <memory>
namespace vk14
{
class cab_probe_maps : public gfx::cab_probe
{
public:
// what the drawing draws to: the views of the six layers of the first level of the maps and of the depth
struct draw_target
{
image *light{nullptr};
image *albedo{nullptr};
image *depth{nullptr};
VkImageView light_view{VK_NULL_HANDLE};
VkImageView albedo_view{VK_NULL_HANDLE};
std::uint32_t size{0};
};
cab_probe_maps() = default;
~cab_probe_maps() override;
// Device, Memory, Deletions: what the maps are made and let go with; called once
void init(VkDevice Device, allocator &Memory, deletion_queue &Deletions);
// the maps go (the device is about to)
void destroy();
// the value of the timeline the gpu reaches when it's done with the frame being recorded, before the drawing
void begin_frame(std::uint64_t const Value)
{
m_value = Value;
}
draw_target const &target() const
{
return m_target;
}
// the maps, once drawn (in the layout the shading reads them in), for the pass sets; null: the stand-in is read
image *light_map() const
{
return m_drawn ? m_light.get() : nullptr;
}
image *albedo_map() const
{
return m_drawn ? m_albedo.get() : nullptr;
}
// the maps were drawn and are in the layout the shading reads them in
void drawn()
{
m_drawn = true;
}
protected:
bool gpu_allocate(int const Size) override;
void gpu_release() override;
// the drawing is recorded by the renderer
void gpu_begin() override {}
void gpu_end() override {}
private:
void let_go();
VkDevice m_device{VK_NULL_HANDLE};
allocator *m_memory{nullptr};
deletion_queue *m_deletions{nullptr};
std::uint64_t m_value{0};
std::shared_ptr<image> m_light;
std::shared_ptr<image> m_albedo;
std::shared_ptr<image> m_depth;
VkImageView m_lightview{VK_NULL_HANDLE};
VkImageView m_albedoview{VK_NULL_HANDLE};
draw_target m_target;
bool m_drawn{false};
};
} // namespace vk14

View File

@@ -28,6 +28,7 @@ http://mozilla.org/MPL/2.0/.
#include "rendering/vulkan14/vulkan14compute.h"
#include "rendering/vulkan14/vulkan14cull.h"
#include "rendering/vulkan14/vulkan14shadows.h"
#include "rendering/vulkan14/vulkan14cabprobe.h"
#include "rendering/vulkan14/vulkan14terrain.h"
#include "rendering/vulkan14/vulkan14grass.h"
#include "rendering/vulkan14/vulkan14particles.h"
@@ -390,6 +391,14 @@ struct scene_state
local_shadow_maps lamps;
std::shared_ptr<scene_program> cube_shadow, cube_alpha_shadow;
target_formats cube_formats;
// the probe of the occupied cab (the stage A4): its maps, the programs and the targets drawing it, the kernel of the spherical
// harmonics of the light it reflects and their storage (CAB_PROBE_SSBO), and the stand-in of its maps
cab_probe_maps cabprobe;
std::shared_ptr<scene_program> cabprobe_program, cabprobe_alpha_program;
target_formats cabprobe_formats;
compute_kernel cabprobe_sh;
buffer cabprobe_bounce;
std::shared_ptr<image> cabprobe_empty;
// the materials of the heightmap terrain (the stage P6), made with the bank of the textures
std::unique_ptr<terrain_materials> terrain;
// the grass of the heightmap terrain, and its program, made when it's first drawn
@@ -643,6 +652,8 @@ struct vulkan14_renderer::draw_state
// don't draw is left out (depthskip, set by walk_material())
bool depthpass{false};
bool depthskip{true};
// the probe of the cab (the stage A4): the walk draws with the programs of the probe, what they don't draw is left out alike
bool probepass{false};
// the pass sets of a culling, the instance buffer its output, for the indirect draws of its instances; null: the pass sets
VkDescriptorSet const *cull_sets{nullptr};
// the rendering under way, begun again where it was after it was ended for the compute passes of the culling

View File

@@ -164,6 +164,10 @@ std::string vulkan14_renderer::scene_info()
text += " contact shadows: " + std::string{Global.gfx_contactshadows == 3 ? "high" : Global.gfx_contactshadows == 2 ? "medium" : "low"} + ", rays " +
to_string(Global.gfx_contactshadows_length, 2) + " m within " + to_string(Global.gfx_contactshadows_range, 0) + " m\n";
}
if (Global.gfx_cabprobe > 0)
{
text += " " + scene.cabprobe.info() + "\n";
}
text += cascades + line(" submodels:", colour.submodels, shadowstats.submodels) + line(" paths: ", colour.paths, shadowstats.paths) +
line(" shapes: ", colour.shapes, shadowstats.shapes) + " traction: " + to_string(colour.traction, 7) + "\n" + " lines: " + to_string(colour.lines, 7) + "\n" +
"particles: " + to_string(colour.particles, 7) + "\nlights: " + to_string(static_cast<int>(m_lights_lit), 7) + " (" + std::to_string(m_lights_dropped) + " over capacity), " +
@@ -223,6 +227,8 @@ void vulkan14_renderer::draw_debug_ui()
ImGui::Separator();
scene.lamps.draw_debug_ui();
ImGui::Separator();
scene.cabprobe.draw_debug_ui();
ImGui::Separator();
char const *contactlevels[]{"no", "low (the sun)", "medium (the sun and 2 lamps)", "high (the sun and 4 lamps)"};
ImGui::Combo("contact shadows (gfx.contactshadows)", &Global.gfx_contactshadows, contactlevels, IM_ARRAYSIZE(contactlevels));
ImGui::SliderFloat("length of their rays, m (gfx.contactshadows.length)", &Global.gfx_contactshadows_length, 0.05f, 5.f, "%.2f", ImGuiSliderFlags_Logarithmic);
@@ -323,6 +329,7 @@ void vulkan14_renderer::draw_debug_ui()
ImGui::SliderFloat("ao bounce albedo", &Global.gfx_postfx_ao_bounce, 0.f, 1.f);
ImGui::SliderInt("ao slices", &Global.gfx_postfx_ao_slices, 1, 8);
ImGui::SliderInt("ao steps per side", &Global.gfx_postfx_ao_steps, 1, 16);
ImGui::Checkbox("ao in the ambient light, not over the image (gfx.postfx.ao.lighting)", &Global.gfx_postfx_ao_lighting);
}
}
ImGui::Separator();

View File

@@ -609,6 +609,7 @@ bool vulkan14_renderer::Render()
scene.cull.main.begin_frame(slot, value, ctx.completed());
}
scene.lamps.begin_frame(ctx.commands, value);
scene.cabprobe.begin_frame(value);
if (scene.terrain)
{
// textures of new heightmap terrain layers go to the arrays the terrain shader reads them from
@@ -679,7 +680,8 @@ bool vulkan14_renderer::Render()
if (false == scene.images_ready)
{
scene.images_ready = true;
std::vector<vk14::image *> const standins{scene.shadow_empty.get(), scene.env_empty.get(), scene.volume_empty.get(), scene.cube_shadow_empty.get(), scene.headlight_shadow_empty.get(), scene.depth_empty.get()};
std::vector<vk14::image *> const standins{scene.shadow_empty.get(), scene.env_empty.get(), scene.volume_empty.get(), scene.cube_shadow_empty.get(),
scene.headlight_shadow_empty.get(), scene.depth_empty.get(), scene.cabprobe_empty.get()};
std::vector<vk14::image_use> clears;
for (auto *image : standins)
{
@@ -715,6 +717,8 @@ bool vulkan14_renderer::Render()
vk14::barriers(ctx.commands, uses);
// no light is assigned to any cluster until the culling pass runs
ctx.commands->fill_buffer(scene.clusters.ranges.handle(), 0, VK_WHOLE_SIZE, 0);
// nor does the cab light itself until its probe is drawn
ctx.commands->fill_buffer(scene.cabprobe_bounce.handle(), 0, VK_WHOLE_SIZE, 0);
VkMemoryBarrier2 barrier{};
barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2;
barrier.srcStageMask = VK_PIPELINE_STAGE_2_CLEAR_BIT;
@@ -1500,6 +1504,8 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
render_lamp_shadows();
render_headlight_shadows();
ready_lamp_maps();
// the probe of the cab, which reads the map of the cab lamp
render_cab_probe();
}
// the scenery around the camera the reflections are traced through, before the passes which trace them
auto const raytracing{raytracing_wanted(vp)};
@@ -1557,6 +1563,11 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
upload_weather_block();
}
// the ambient occlusion the shading may read (SCENE_AO_TEX) is in the layout it's read in from the first frame on
if (targets.ssao_blur && (targets.ssao_blur->state.layout == VK_IMAGE_LAYOUT_UNDEFINED))
{
vk14::barriers(commands, {vk14::use_of(*targets.ssao_blur, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access)});
}
// the colour of the scene: drawn to the images with the samples, resolved to the single ones
auto const uses{colour_target_uses(targets)};
ctx.passes.begin_pass("sky", uses);
@@ -1579,11 +1590,25 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
setup_shadow_matrices();
upload_scene_block();
// the probe of the cab, which the parts of the cab drawn in the colour pass of the main view read
if (vp.main)
{
scene.cabprobe.setup_scene(scene_ubs, cab_probe_vehicle(), m_colorpass.pass_camera.position(), glm::mat3{m_colorpass.pass_camera.modelview()});
m_cabprobelit = (scene_ubs.cab_probe_box.w > 0.f);
if (m_cabprobelit)
{
upload_scene_block();
}
}
// the depth of the whole opaque scene before its colour, which the contact shadows need
m_indirectcolourpending = false;
m_aolighting = false;
auto const contactshadows{contact_shadows_wanted(vp)};
auto const reflections{m_ssrframe && (false == Global.bWireFrame)};
if ((Global.gfx_depthprepass_scene || contactshadows || reflections) && vp.main && (false == Global.bWireFrame))
// and the ambient occlusion in the light of the surfaces
auto const aolighting{ao_lighting_wanted(vp)};
if ((Global.gfx_depthprepass_scene || contactshadows || reflections || aolighting) && vp.main && (false == Global.bWireFrame))
{
render_scene_depth(Index, future);
if (contactshadows)
@@ -1594,6 +1619,10 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
{
setup_screen_reflections(Index, taa);
}
if (m_aolighting)
{
setup_ao_lighting();
}
}
// opaque parts...
@@ -1668,14 +1697,17 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
}
commands->end_rendering();
end_draws();
if (m_contactshadows || m_ssractive)
if (m_contactshadows || m_ssractive || m_aolighting || m_cabprobelit)
{
// the depth they read is of this view only
// the depth they read is of this view only, and so is the probe of the cab
m_contactshadows = false;
m_ssractive = false;
m_cabprobelit = false;
scene_ubs.contact_shadow_params = glm::vec4{0.f};
scene_ubs.contact_shadow_lamps = glm::vec4{0.f};
scene_ubs.ao_params = glm::vec4{0.f};
gfx::screen_reflections::clear(scene_ubs);
gfx::cab_probe::clear_scene(scene_ubs);
}
// the images resolved and drawn are read by the post-processing
m_colorpass.draw_stats = m_renderpass.draw_stats;
@@ -1698,8 +1730,10 @@ 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};
// 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};
// the depth copied apart from the one the colour pass goes on with, for the contact shadows, the screen space reflections and the
// ambient occlusion in the light of the surfaces
auto const aolighting{ao_lighting_wanted(m_viewports[Index])};
auto const contact{contact_shadows_wanted(m_viewports[Index]) || m_ssrframe || aolighting};
commands->end_rendering();
// what the draws go on with
auto const set{draw.set};
@@ -1794,6 +1828,11 @@ void vulkan14_renderer::render_scene_depth(std::size_t const Index, glm::mat4 co
copy.pRegions = &region;
commands->copy_image2(&copy);
vk14::barriers(commands, {vk14::use_of(*targets.scene_depth, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access)});
// the ambient occlusion of the depth, between the renderings (compute)
if (aolighting)
{
m_aolighting = compute_ao_lighting(Index);
}
}
// the colour pass goes on where the sky left it
vk14::barriers(commands, colour_target_uses(targets));
@@ -3478,8 +3517,9 @@ void vulkan14_renderer::render_post(std::size_t const Index, bool const Motionbl
auto const commands{ctx.commands};
// the occlusion goes into the image before the blur, which smears both alike. the targets are there only when it was on as they
// were made
auto const aomode{(targets.ssao != nullptr) ? Global.gfx_postfx_ao : 0};
// were made. in the light of the surfaces it's in the image already
auto const aomode{((targets.ssao != nullptr) && (false == m_aolighting)) ? Global.gfx_postfx_ao : 0};
m_aolighting = false;
if (aomode != 0)
{
ctx.passes.begin_pass("ambient occlusion");

View File

@@ -389,6 +389,19 @@ class vulkan14_renderer : public gfx_renderer, public gfx::scene_walk
bool init_ao(std::string &Reason);
void destroy_ao();
void render_ao(std::size_t const Viewport, int const Mode);
// the ambient occlusion in the light of the surfaces (gfx.postfx.ao.lighting, the stage A3): whether the colour pass of the
// viewport takes it, the occlusion worked out from the depth of the scene drawn before the colour (outside a rendering; false: it
// couldn't be), and the block of the scene set up for the colour pass to read it
bool ao_lighting_wanted(viewport_config const &Viewport) const;
bool compute_ao_lighting(std::size_t const Viewport);
void setup_ao_lighting();
bool m_aolighting{false};
// the probe of the occupied cab (vulkan14cabprobe.cpp, the stage A4; opengl46_renderer::Render_cab_probe()): the resources, the
// occupied vehicle whose cab is drawn (nullptr: none), the drawing of the probe when it needs it in this frame
bool init_cab_probe(std::string &Reason);
void destroy_cab_probe();
TDynamicObject *cab_probe_vehicle() const;
void render_cab_probe();
// the particles of the gpu: what the colour pass under way draws them with, and the depth of the scene drawn so far copied for
// the soft particles (false: they aren't soft)
gfx::gpu_particles::view particles_view() const;

View File

@@ -151,6 +151,7 @@ VkDescriptorType pass_binding_type(std::uint32_t const Binding)
case CLUSTER_INDEX_SSBO:
case SKY_SSBO:
case WATER_SSBO:
case CAB_PROBE_SSBO:
return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
case ATMOSPHERE_UBO:
case WEATHER_HORIZON_UBO:
@@ -193,6 +194,14 @@ std::array<std::array<VkImageView, vk14::bindings::PASS_BINDING_COUNT>, 3> pass_
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());
// the ambient occlusion of the main view the shading takes into the ambient light (the stage A3)
views[SCENE_AO_TEX] = (colour && (false == scene.targets.empty()) && scene.targets.front().ssao_blur ? scene.targets.front().ssao_blur->view() : scene.white->view());
// the probe of the cab, once it was drawn (the stage A4)
auto const *probelight{scene.cabprobe.light_map()};
auto const *probealbedo{scene.cabprobe.albedo_map()};
auto const probeempty{scene.cabprobe_empty ? scene.cabprobe_empty->view() : scene.env_empty->view()};
views[CAB_PROBE_TEX] = (colour && (probelight != nullptr) ? probelight->view() : probeempty);
views[CAB_PROBE_ALBEDO_TEX] = (colour && (probealbedo != nullptr) ? probealbedo->view() : probeempty);
}
return result;
}
@@ -650,7 +659,7 @@ bool vulkan14_renderer::init_scene(std::string &Reason)
}
// the light of the sky, the exposure, the weather
if ((false == init_sky(Reason)) || (false == init_weather(Reason)) || (false == init_ao(Reason)))
if ((false == init_sky(Reason)) || (false == init_weather(Reason)) || (false == init_ao(Reason)) || (false == init_cab_probe(Reason)))
{
return false;
}
@@ -822,6 +831,7 @@ void vulkan14_renderer::destroy_scene()
destroy_sky();
destroy_weather();
destroy_ao();
destroy_cab_probe();
for (auto *pool : scene.occlusion_pools)
{
vkDestroyQueryPool(device, pool, nullptr);
@@ -968,6 +978,7 @@ void vulkan14_renderer::write_pass_sets(vk14::instance_cull *Cull)
VkDescriptorBufferInfo const weatherblock{ctx.ring.handle(), 0, offsetof(gl::weather_ubs, fog_horizon)};
VkDescriptorBufferInfo const horizonblock{scene.weather_horizon.handle(), 0, sizeof(glm::vec4) * gl::FOG_HORIZON_SAMPLES};
VkDescriptorBufferInfo const water{scene.water.storage(), 0, VK_WHOLE_SIZE};
VkDescriptorBufferInfo const cabprobe{scene.cabprobe_bounce.handle(), 0, sizeof(gl::cab_probe_data)};
VkDescriptorImageInfo const headlightsinfo{scene.headlights_sampler, scene.headlights_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL};
std::vector<VkWriteDescriptorSet> writes;
std::vector<std::unique_ptr<VkDescriptorImageInfo>> images;
@@ -1017,6 +1028,10 @@ void vulkan14_renderer::write_pass_sets(vk14::instance_cull *Cull)
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]));
write(handle, SCENE_AO_TEX, nullptr, image(ctx.sampling.clamp_nearest, views[SCENE_AO_TEX]));
write(handle, CAB_PROBE_TEX, nullptr, image(ctx.sampling.clamp_linear, views[CAB_PROBE_TEX]));
write(handle, CAB_PROBE_ALBEDO_TEX, nullptr, image(ctx.sampling.clamp_linear, views[CAB_PROBE_ALBEDO_TEX]));
write(handle, CAB_PROBE_SSBO, &cabprobe, nullptr);
}
vkUpdateDescriptorSets(device, static_cast<std::uint32_t>(writes.size()), writes.data(), 0, nullptr);
}
@@ -1403,7 +1418,7 @@ void vulkan14_renderer::walk_material(material_handle const Material, TSubModel
// materials which declare texture variants provide a different texture set per model instance
auto const &materialtextures{material.texture_variants.empty() ? material.textures : material.GetTextures(TSubModel::VariantId(Submodel))};
auto &draw{*m_draw};
if (draw.depthpass)
if (draw.depthpass || draw.probepass)
{
// the depth of the whole scene: the programs of the depth prepass. what they can't draw (blended, materials of other programs)
// is left to the colour pass
@@ -1418,6 +1433,27 @@ void vulkan14_renderer::walk_material(material_handle const Material, TSubModel
return;
}
auto &scene{m_context->scene};
if (draw.probepass)
{
// the probe of the cab: the programs of the probe, of the same materials, the albedo from the diffuse texture (the white
// stand-in without it), alpha tested as the depth prepass tests it
if (prepassclass == 2)
{
if (materialtextures[0] == null_handle)
{
return;
}
auto &texture{m_context->textures->mark_as_used(materialtextures[0])};
if ((false == texture.create()) || (m_context->images.find(texture.get_id()) == nullptr))
{
return;
}
}
walk_texture(0, materialtextures[0]);
draw.program = (prepassclass == 2 ? scene.cabprobe_alpha_program.get() : scene.cabprobe_program.get());
draw.depthskip = (draw.program == nullptr);
return;
}
if (prepassclass == 2)
{
// a texture which isn't there (yet) would leave the one bound before to the alpha test
@@ -1769,7 +1805,7 @@ void vulkan14_renderer::draw_chunk(gfx::geometry_handle const &Geometry, bool co
}
auto const &chunk{static_cast<vk14::vulkan14_geometrybank *>(m_banks[Geometry.bank - 1])->last()};
if (draw.depthpass && (draw.depthskip || (topology_class(chunk.topology) != VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)))
if ((draw.depthpass || draw.probepass) && (draw.depthskip || (topology_class(chunk.topology) != VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)))
{
// the depth of the scene: what the programs of the prepass don't draw, and no points or lines
return;

View File

@@ -803,6 +803,10 @@ std::string binding_preamble(bool const Raytracing)
define("SCENE_DEPTH_TEX", SCENE_DEPTH_TEX);
define("SSR_HISTORY_TEX", SSR_HISTORY_TEX);
define("WATER_SSBO", WATER_SSBO);
define("SCENE_AO_TEX", SCENE_AO_TEX);
define("CAB_PROBE_TEX", CAB_PROBE_TEX);
define("CAB_PROBE_ALBEDO_TEX", CAB_PROBE_ALBEDO_TEX);
define("CAB_PROBE_SSBO", CAB_PROBE_SSBO);
define("PASS_BINDING_COUNT", PASS_BINDING_COUNT);
define("MATERIAL_TEXTURE_FIRST", MATERIAL_TEXTURE_FIRST);
define("CL_CLUSTER_UBO", CL_CLUSTER_UBO);
@@ -873,6 +877,8 @@ std::string binding_preamble(bool const Raytracing)
define("WATER_TRAIL", gl::WATER_TRAIL);
define("WATER_MAP", gl::WATER_MAP);
define("WATER_GROUP_SIZE", gl::WATER_GROUP_SIZE);
define("CAB_PROBE_SH_SIZE", static_cast<unsigned int>(gl::CAB_PROBE_SH_SIZE));
define("CAB_PROBE_SH_GROUP_SIZE", static_cast<unsigned int>(gl::CAB_PROBE_SH_GROUP_SIZE));
return preamble;
}