17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-26 15:54:53 +01:00
Files
maszyna/rendering/vulkan14/vulkan14sky.cpp

483 lines
21 KiB
C++

/*
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 light of the sky of the vulkan renderer (the stage P1 of the plan, opengl46_sky and opengl46_exposure of the opengl
// renderer): the look-up tables of the physical atmosphere made by compute kernels, the light of the frame worked out from
// them and the directional part of the ambient light (gfx::sky_light, shared with the opengl renderer), the environment map
// prefiltered with the GGX lobe, the stars of the catalogue; the automatic exposure and the bloom of the post-processing
#include "stdafx.h"
#include "rendering/vulkan14/vulkan14context.h"
#include "rendering/vulkan14/vulkan14commands.h"
#include "rendering/common/exposure.h"
#include "simulation/simulation.h"
#include "simulation/simulationenvironment.h"
#include "utilities/Globals.h"
#include "utilities/Timer.h"
namespace
{
VkPipelineStageFlags2 const shader_stages{VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_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};
VkPipelineStageFlags2 const compute_stage{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT};
VkAccessFlags2 const storage_write{VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT};
std::uint32_t groups(std::uint32_t const Size, std::uint32_t const Group)
{
return (Size + Group - 1) / Group;
}
// a view of one level of a cube image as a cube, for a kernel writing the level
VkImageView level_view(VkDevice Device, vk14::image const &Image, std::uint32_t const Level, std::string const &Name)
{
VkImageViewCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.image = Image.handle();
info.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
info.format = Image.desc().format;
info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, Level, 1, 0, 6};
VkImageView view{VK_NULL_HANDLE};
if (!VK14_VERIFY(vkCreateImageView(Device, &info, nullptr, &view)))
{
return VK_NULL_HANDLE;
}
vk14::name_object(Device, VK_OBJECT_TYPE_IMAGE_VIEW, vk14::handle_value(view), Name);
return view;
}
// the push constants of the kernels, laid out as the blocks of the shaders (std430)
struct prefilter_constants
{
float roughness;
std::int32_t target_size;
float source_size;
std::int32_t sample_count;
};
struct histogram_constants
{
glm::vec2 log_range;
};
struct average_constants
{
glm::vec2 log_range;
glm::vec2 pad;
glm::vec4 adaptation;
glm::vec2 exposure_range;
};
} // namespace
bool vulkan14_renderer::init_sky(std::string &Reason)
{
auto &ctx{*m_context};
auto &scene{ctx.scene};
auto &sky{scene.sky};
auto &exposure{scene.exposure};
auto const device{ctx.device.handle()};
auto const frames{static_cast<std::uint32_t>(ctx.frames.size())};
// the buffers the shading reads
if ((false == sky.sky.create(ctx.memory, sizeof(gl::sky_data), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, vk14::memory_kind::device, "sky ambient")) ||
(false == sky.atmosphere.create(ctx.memory, sizeof(gl::atmosphere_ubs), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, vk14::memory_kind::device, "atmosphere")) ||
(false == sky.result.create(ctx.memory, sizeof(gl::atmosphere_result), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, vk14::memory_kind::device, "atmosphere result")) ||
(false == sky.result_readback.create(ctx.memory, sizeof(gl::atmosphere_result), frames, "atmosphere result readback")) ||
(false == exposure.data.create(ctx.memory, sizeof(gl::exposure_data), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, vk14::memory_kind::device, "exposure")) ||
(false == exposure.readback.create(ctx.memory, 4 * sizeof(float), frames, "exposure readback")))
{
Reason = "no buffers of the light of the sky";
return false;
}
// the tables of the atmosphere, black until they are first made
auto make_image = [&](std::shared_ptr<vk14::image> &Image, vk14::image_desc const &Desc, char const *Name) {
Image = std::make_shared<vk14::image>();
if (false == Image->create(ctx.memory, Desc, Name))
{
Image.reset();
Reason = std::string("no image for ") + Name;
return false;
}
return true;
};
{
vk14::image_desc desc;
desc.format = VK_FORMAT_R16G16B16A16_SFLOAT;
desc.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
desc.extent = {gl::ATMOSPHERE_TRANSMITTANCE_WIDTH, gl::ATMOSPHERE_TRANSMITTANCE_HEIGHT, 1};
if (false == make_image(sky.transmittance, desc, "atmosphere transmittance"))
{
return false;
}
desc.extent = {gl::ATMOSPHERE_MULTISCATTERING_SIZE, gl::ATMOSPHERE_MULTISCATTERING_SIZE, 1};
if (false == make_image(sky.multiscattering, desc, "atmosphere multiple scattering"))
{
return false;
}
desc.extent = {gl::ATMOSPHERE_SKYVIEW_WIDTH, gl::ATMOSPHERE_SKYVIEW_HEIGHT, 1};
if (false == make_image(sky.skyview, desc, "atmosphere sky-view"))
{
return false;
}
}
// the environment map prefiltered with the GGX lobe, made with the environment map
if (Global.gfx_envmap_enabled)
{
vk14::image_desc desc;
desc.format = VK_FORMAT_R16G16B16A16_SFLOAT;
desc.view_type = VK_IMAGE_VIEW_TYPE_CUBE;
desc.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
desc.layers = 6;
desc.extent = {static_cast<std::uint32_t>(gl::ENVMAP_GGX_SIZE), static_cast<std::uint32_t>(gl::ENVMAP_GGX_SIZE), 1};
desc.mips = static_cast<std::uint32_t>(gl::ENVMAP_GGX_LEVELS);
desc.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
if (false == make_image(sky.ggx, desc, "environment map ggx"))
{
return false;
}
for (std::uint32_t level = 0; level < desc.mips; ++level)
{
sky.ggx_levels.push_back(level_view(device, *sky.ggx, level, "environment map ggx " + std::to_string(level)));
}
}
// the stars of the catalogue, in the equatorial frame
{
auto const stars{gfx::weather_state::star_vertices()};
sky.star_count = static_cast<std::uint32_t>(stars.size() / 2);
if (sky.star_count > 0)
{
auto vertices{std::make_shared<vk14::buffer>()};
auto const size{static_cast<VkDeviceSize>(stars.size() * sizeof(glm::vec4))};
if ((false == vertices->create(ctx.memory, size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, vk14::memory_kind::device, "stars")) || (false == ctx.uploads.upload_buffer(vertices, stars.data(), size)))
{
Reason = "no buffer of the stars";
return false;
}
sky.star_vertices = vertices;
}
}
// the kernels and the programs
try
{
auto kernel = [&](vk14::compute_kernel &Kernel, char const *Shader, char const *Name) {
if (false == Kernel.create(device, ctx.pipelines, ctx.shaders.get(Shader), scene.pass_layout, {}, Name))
{
Reason = std::string("no compute pipeline of ") + Name;
return false;
}
return true;
};
if ((false == kernel(sky.transmittance_kernel, "atmosphere_transmittance.comp", "atmosphere transmittance")) || (false == kernel(sky.multiscattering_kernel, "atmosphere_multiscattering.comp", "atmosphere multiple scattering")) ||
(false == kernel(sky.skyview_kernel, "atmosphere_skyview.comp", "atmosphere sky-view")) || (false == kernel(sky.ambient_kernel, "atmosphere_ambient.comp", "atmosphere ambient")) ||
(false == kernel(sky.prefilter_kernel, "envmap_prefilter.comp", "environment map ggx")) || (false == kernel(exposure.histogram, "exposure_histogram.comp", "exposure histogram")) ||
(false == kernel(exposure.average, "exposure_average.comp", "exposure average")))
{
return false;
}
auto program = [&](char const *Vertex, char const *Fragment, vk14::vertex_kind const Kind) {
auto result{std::make_shared<vk14::scene_program>()};
result->vertex = ctx.shaders.get(Vertex);
result->fragment = ctx.shaders.get(Fragment);
result->fragment_name = Fragment;
result->vertices = Kind;
result->name = std::string(Vertex) + " + " + Fragment;
return result;
};
sky.atmosphere_dome = program("vbocolor.vert", "sky_atmosphere.frag", vk14::vertex_kind::colour);
sky.stars = program("stars.vert", "stars.frag", vk14::vertex_kind::star);
for (auto const *name : {"postfx_bloom_down.frag", "postfx_bloom_up.frag"})
{
scene.post_fragments.emplace(name, ctx.shaders.get(name));
}
}
catch (gfx::shader_exception const &e)
{
Reason = std::string("invalid shader: ") + e.what();
return false;
}
return true;
}
void vulkan14_renderer::destroy_sky()
{
auto &ctx{*m_context};
auto &scene{ctx.scene};
auto &sky{scene.sky};
auto &exposure{scene.exposure};
auto const device{ctx.device.handle()};
for (auto *kernel : {&sky.transmittance_kernel, &sky.multiscattering_kernel, &sky.skyview_kernel, &sky.ambient_kernel, &sky.prefilter_kernel, &exposure.histogram, &exposure.average})
{
kernel->destroy(device);
}
for (auto *view : sky.ggx_levels)
{
if (view != VK_NULL_HANDLE)
{
vkDestroyImageView(device, view, nullptr);
}
}
sky.ggx_levels.clear();
sky.ggx.reset();
sky.transmittance.reset();
sky.multiscattering.reset();
sky.skyview.reset();
sky.sky.destroy();
sky.atmosphere.destroy();
sky.result.destroy();
sky.result_readback.destroy();
sky.atmosphere_dome.reset();
sky.stars.reset();
sky.star_vertices.reset();
exposure.data.destroy();
exposure.readback.destroy();
}
// the images of the sky put in their layouts the first time: black, ready to be read
void vulkan14_renderer::clear_sky_images()
{
auto &ctx{*m_context};
auto &scene{ctx.scene};
auto &sky{scene.sky};
auto const commands{ctx.commands};
std::vector<vk14::image_use> uses;
for (auto *image : {sky.transmittance.get(), sky.multiscattering.get(), sky.skyview.get(), sky.ggx.get()})
{
if (image != nullptr)
{
uses.push_back(vk14::use_of(*image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_2_CLEAR_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT));
}
}
vk14::barriers(commands, uses);
VkClearColorValue const black{{0.f, 0.f, 0.f, 0.f}};
for (auto *image : {sky.transmittance.get(), sky.multiscattering.get(), sky.skyview.get(), sky.ggx.get()})
{
if (image != nullptr)
{
VkImageSubresourceRange const range{VK_IMAGE_ASPECT_COLOR_BIT, 0, image->desc().mips, 0, image->desc().layers};
commands->clear_color_image(image->handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &black, 1, &range);
}
}
uses.clear();
for (auto *image : {sky.transmittance.get(), sky.multiscattering.get(), sky.skyview.get(), sky.ggx.get()})
{
if (image != nullptr)
{
uses.push_back(vk14::use_of(*image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access));
}
}
vk14::barriers(commands, uses);
// the exposure starts from the key, not adapted yet
gl::exposure_data data{};
data.adapted_luminance = Global.gfx_postfx_autoexposure_key;
data.exposure = 1.f;
data.average_luminance = Global.gfx_postfx_autoexposure_key;
data.valid = 0;
vk14::update_buffer(commands, scene.exposure.data, &data, sizeof(data), 0);
// the atmosphere valid from the start, and no directional ambient light
vk14::update_buffer(commands, sky.atmosphere, &m_skylight.atmosphere(), sizeof(gl::atmosphere_ubs), 0);
vk14::update_buffer(commands, sky.sky, &sky.uploaded, sizeof(gl::sky_data), 0);
sky.sky_uploaded = true;
}
// the light of the sky of the frame (opengl46_sky::update()): the tables of the atmosphere made where their input changed, the
// result of an earlier frame read back, the sunlight, the ambient light and the fog colour of the frame worked out from it and
// the directional ambient light written for the shading. before the passes of the frame
void vulkan14_renderer::update_sky()
{
auto &ctx{*m_context};
auto &scene{ctx.scene};
auto &sky{scene.sky};
auto const commands{ctx.commands};
auto const &sun{simulation::Environment.sun()};
auto const sundirection{sun.getDirection()};
m_skylight.begin_update();
if (Global.gfx_atmosphere)
{
bool medium{false};
if (m_skylight.atmosphere_changed(glm::normalize(sundirection), medium))
{
ctx.passes.begin_pass("atmosphere");
vk14::update_buffer(commands, sky.atmosphere, &m_skylight.atmosphere(), sizeof(gl::atmosphere_ubs), 0);
bind_compute_set(sky.transmittance_kernel);
auto const sampled = [&](vk14::image &Image) { return vk14::image_descriptor(ctx.sampling.clamp_linear, Image.view(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); };
if (medium)
{
vk14::barriers(commands, {vk14::use_of(*sky.transmittance, VK_IMAGE_LAYOUT_GENERAL, compute_stage, storage_write)});
sky.transmittance_kernel.run(commands, {vk14::storage_image(sky.transmittance->view())}, groups(gl::ATMOSPHERE_TRANSMITTANCE_WIDTH, 8), groups(gl::ATMOSPHERE_TRANSMITTANCE_HEIGHT, 8));
vk14::barriers(commands, {vk14::use_of(*sky.transmittance, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access),
vk14::use_of(*sky.multiscattering, VK_IMAGE_LAYOUT_GENERAL, compute_stage, storage_write)});
sky.multiscattering_kernel.run(commands, {sampled(*sky.transmittance), vk14::storage_image(sky.multiscattering->view())}, gl::ATMOSPHERE_MULTISCATTERING_SIZE, gl::ATMOSPHERE_MULTISCATTERING_SIZE);
vk14::barriers(commands, {vk14::use_of(*sky.multiscattering, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access)});
}
vk14::barriers(commands, {vk14::use_of(*sky.skyview, VK_IMAGE_LAYOUT_GENERAL, compute_stage, storage_write)});
sky.skyview_kernel.run(commands, {sampled(*sky.transmittance), sampled(*sky.multiscattering), vk14::storage_image(sky.skyview->view())}, groups(gl::ATMOSPHERE_SKYVIEW_WIDTH, 8), groups(gl::ATMOSPHERE_SKYVIEW_HEIGHT, 8));
vk14::barriers(commands, {vk14::use_of(*sky.skyview, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access)});
vk14::memory_barrier(commands, VK_PIPELINE_STAGE_2_COPY_BIT, VK_ACCESS_2_TRANSFER_READ_BIT, compute_stage, storage_write);
sky.ambient_kernel.run(commands, {sampled(*sky.transmittance), sampled(*sky.skyview), vk14::buffer_descriptor(sky.result.handle(), 0, sizeof(gl::atmosphere_result))}, 1);
sky.result_readback.push(commands, ctx.slot, ctx.timelinevalue + 1, sky.result.handle(), 0, sizeof(gl::atmosphere_result));
ctx.passes.end_pass();
}
gl::atmosphere_result result;
if (sky.result_readback.pop(ctx.completed(), &result, sizeof(result)))
{
m_skylight.set_result(result);
}
}
m_skylight.end_update(m_sunlight, m_fogcolor, simulation::Environment.skydome(), sundirection, sun.getAngle());
// the directional part of the ambient light for the shading
auto const &skydata{m_skylight.sky_data()};
if ((false == sky.sky_uploaded) || (0 != std::memcmp(&skydata, &sky.uploaded, sizeof(skydata))))
{
sky.uploaded = skydata;
vk14::update_buffer(commands, sky.sky, &sky.uploaded, sizeof(gl::sky_data), 0);
sky.sky_uploaded = true;
}
}
// the levels of the GGX map prefiltered from the environment map with its mipmaps (opengl46_sky::prefilter())
void vulkan14_renderer::prefilter_environment()
{
auto &ctx{*m_context};
auto &scene{ctx.scene};
auto &sky{scene.sky};
if ((false == bool(sky.ggx)) || (false == bool(scene.env_map)))
{
return;
}
auto const commands{ctx.commands};
ctx.passes.begin_pass("envmap ggx", {vk14::use_of(*scene.env_map, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, compute_stage, sampled_access),
vk14::use_of(*sky.ggx, VK_IMAGE_LAYOUT_GENERAL, compute_stage, storage_write)});
for (std::size_t level = 0; level < gl::ENVMAP_GGX_LEVELS; ++level)
{
auto const size{static_cast<std::int32_t>(gl::ENVMAP_GGX_SIZE >> level)};
prefilter_constants constants;
constants.roughness = gl::ENVMAP_GGX_ROUGHNESS_MIN + (1.f - gl::ENVMAP_GGX_ROUGHNESS_MIN) * static_cast<float>(level) / static_cast<float>(gl::ENVMAP_GGX_LEVELS - 1);
constants.target_size = size;
constants.source_size = static_cast<float>(gl::ENVMAP_SIZE);
constants.sample_count = 64;
auto const count{groups(static_cast<std::uint32_t>(size), static_cast<std::uint32_t>(gl::ENVMAP_GGX_GROUP_SIZE))};
sky.prefilter_kernel.dispatch(commands, {vk14::storage_image(sky.ggx_levels[level]), vk14::image_descriptor(ctx.sampling.clamp_linear, scene.env_map->view(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)}, constants, count, count, 6);
}
vk14::barriers(commands, {vk14::use_of(*sky.ggx, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access), vk14::use_of(*scene.env_map, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access)});
ctx.passes.end_pass();
sky.ggx_ready = true;
}
// the stars, in the sky pass (opengl46_weather::draw_stars()). View: the rotation of the camera of the pass
void vulkan14_renderer::render_stars(glm::mat4 const &View)
{
auto &ctx{*m_context};
auto &scene{ctx.scene};
auto &sky{scene.sky};
auto &draw{*m_draw};
if ((sky.star_count == 0) || (false == bool(sky.star_vertices)) || (false == bool(sky.stars)))
{
return;
}
auto const &sun{simulation::Environment.sun()};
glm::mat4 toworld, toview;
glm::vec4 params;
if (false == m_weatherstate.stars(sun.getAngle(), sun.getSiderealTime(), sun.getLatitude(), View, toworld, toview, params))
{
return;
}
if (false == bind_scene_pipeline(*sky.stars, *draw.targets, vk14::blend_mode::additive, VK_PRIMITIVE_TOPOLOGY_POINT_LIST))
{
return;
}
apply_draw_state();
model_ubs.param[0] = params;
draw.block = push_draw_block();
if (draw.block == ~0u)
{
return;
}
if (draw.set_dirty)
{
bind_pass_set();
}
if (draw.textures_dirty)
{
push_textures();
}
vk14::draw_constants constants;
constants.draw = draw.block;
for (int column = 0; column < 3; ++column)
{
constants.data[column] = toworld[column];
constants.data[3 + column] = toview[column];
}
push_constants(&constants);
VkDeviceSize const offset{0};
auto const buffer{sky.star_vertices->handle()};
draw.commands->bind_vertex_buffers(0, 1, &buffer, &offset);
draw.commands->draw(sky.star_count, 1, 0, 0);
draw.vertices = VK_NULL_HANDLE;
draw.userdata = VK_NULL_HANDLE;
}
// the luminance of the image measured and the exposure adapted to it (opengl46_exposure::measure()), outside a rendering
void vulkan14_renderer::measure_exposure(vk14::image &Image)
{
auto &ctx{*m_context};
auto &exposure{ctx.scene.exposure};
auto const commands{ctx.commands};
float data[4];
if (exposure.readback.pop(ctx.completed(), data, sizeof(data)))
{
std::copy(std::begin(data), std::end(data), std::begin(exposure.values));
}
vk14::barriers(commands, {vk14::use_of(Image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, compute_stage, sampled_access)});
// the reads of the tonemapping of the frame before
vk14::memory_barrier(commands, VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_COPY_BIT, VK_ACCESS_2_SHADER_STORAGE_READ_BIT | VK_ACCESS_2_TRANSFER_READ_BIT, compute_stage, VK_ACCESS_2_SHADER_STORAGE_READ_BIT | storage_write);
auto const buffer{vk14::buffer_descriptor(exposure.data.handle(), 0, sizeof(gl::exposure_data))};
histogram_constants histogram{gfx::exposure_settings::histogram_range()};
auto const groupsize{2 * gl::EXPOSURE_HISTOGRAM_GROUP_SIZE};
exposure.histogram.dispatch(commands, {vk14::image_descriptor(ctx.sampling.clamp_nearest, Image.view(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL), buffer}, histogram, groups(Image.desc().extent.width, groupsize),
groups(Image.desc().extent.height, groupsize));
vk14::memory_barrier(commands, compute_stage, storage_write, compute_stage, VK_ACCESS_2_SHADER_STORAGE_READ_BIT | storage_write);
average_constants average{};
average.log_range = gfx::exposure_settings::average_range();
average.adaptation = gfx::exposure_settings::adaptation(Timer::GetDeltaRenderTime());
average.exposure_range = gfx::exposure_settings::exposure_range();
exposure.average.dispatch(commands, {buffer}, average, 1);
// read by the tonemapping, and copied for the debug window
vk14::memory_barrier(commands, compute_stage, storage_write, VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_COPY_BIT, VK_ACCESS_2_SHADER_STORAGE_READ_BIT | VK_ACCESS_2_TRANSFER_READ_BIT);
exposure.readback.push(commands, ctx.slot, ctx.timelinevalue + 1, exposure.data.handle(), offsetof(gl::exposure_data, adapted_luminance), 4 * sizeof(float));
}
// the image taken down and up the bloom chain of the viewport (opengl46_exposure::bloom()); the result, which the tonemapping
// blends in, or null without the chain
vk14::image *vulkan14_renderer::render_bloom(std::size_t const Index, vk14::image &Image)
{
auto &ctx{*m_context};
auto &targets{ctx.scene.targets[Index]};
if (targets.bloom_sizes.empty())
{
return nullptr;
}
// down, the first step from the image itself
for (std::size_t level = 0; level < targets.bloom_sizes.size(); ++level)
{
vk14::draw_constants constants;
constants.data[0] = gfx::exposure_settings::bloom_params(level);
post_pass("postfx_bloom_down.frag", *targets.bloom_down[level], {level == 0 ? &Image : targets.bloom_down[level - 1].get()}, constants);
}
// up, from the lowest level
for (std::size_t level = targets.bloom_up.size(); level-- > 0;)
{
auto *lower{(level + 1 < targets.bloom_up.size()) ? targets.bloom_up[level + 1].get() : targets.bloom_down[level + 1].get()};
post_pass("postfx_bloom_up.frag", *targets.bloom_up[level], {lower, targets.bloom_down[level].get()}, vk14::draw_constants{});
}
return targets.bloom_up.empty() ? targets.bloom_down.front().get() : targets.bloom_up.front().get();
}