mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-08 23:07:38 +01:00
Vulkan: scene rendering (D1–D7) on a scene walk shared with GL (N6a, N6b)
This commit is contained in:
@@ -12,9 +12,9 @@ http://mozilla.org/MPL/2.0/.
|
||||
// same values for the SPIR-V binaries, which glslc compiles without the renderer (the renderer checks they are the same).
|
||||
//
|
||||
// the descriptor sets (decision K6 of the plan): set 0 holds the resources of a pass, made when the renderer starts and bound
|
||||
// with a dynamic offset in the ring of the frame; set 1 is pushed with the draws (vkCmdPushDescriptorSet2): the textures of
|
||||
// the material and what changes between the draws. a layout can have only one set pushed, so the passes without a set of
|
||||
// their own (the user interface, the compute passes) push set 0
|
||||
// with dynamic offsets in the ring of the frame; set 1 is pushed with the draws (vkCmdPushDescriptorSet2): the textures of
|
||||
// the material. a layout can have only one set pushed, so the passes without a set of their own (the user interface, the
|
||||
// compute passes, the post-processing) push set 0
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -28,27 +28,63 @@ namespace vk14::bindings
|
||||
constexpr std::uint32_t UI_SET = 0;
|
||||
constexpr std::uint32_t UI_TEXTURE = 0;
|
||||
|
||||
// the sets of the passes of the scene (from the stage D1 of the plan)
|
||||
// the sets of the passes of the scene
|
||||
constexpr std::uint32_t PASS_SET = 0;
|
||||
constexpr std::uint32_t MATERIAL_SET = 1;
|
||||
// set 0 of the scene: the block of the scene (gl::scene_ubs, dynamic offset in the ring of the frame)
|
||||
constexpr std::uint32_t SCENE_UBO = 0;
|
||||
// the blocks of the draws of the frame (gl::model_ubs each), the one of a draw picked by the push constant draw
|
||||
constexpr std::uint32_t DRAW_SSBO = 1;
|
||||
// the modelview matrices of the instances of the frame, element 0 the identity (draws which aren't instanced)
|
||||
constexpr std::uint32_t INSTANCE_SSBO = 2;
|
||||
// the light buffer of the pass (gl::light_data, dynamic offset)
|
||||
constexpr std::uint32_t LIGHT_SSBO = 3;
|
||||
// the light clusters: the grid of the pass (gl::cluster_ubs, dynamic offset), the lists of the lights of the clusters
|
||||
constexpr std::uint32_t CLUSTER_UBO = 4;
|
||||
constexpr std::uint32_t CLUSTER_RANGE_SSBO = 5;
|
||||
constexpr std::uint32_t CLUSTER_INDEX_SSBO = 6;
|
||||
// the cascades of the sun shadows (depth compared), the environment map, the beams of the headlights
|
||||
constexpr std::uint32_t SHADOW_TEX = 7;
|
||||
constexpr std::uint32_t ENV_TEX = 8;
|
||||
constexpr std::uint32_t HEADLIGHT_TEX = 9;
|
||||
constexpr std::uint32_t PASS_BINDING_COUNT = 10;
|
||||
// the textures of a material take the bindings of their units (#pragma texture(name, unit, ...)), 0 .. MAX_TEXTURES - 1
|
||||
constexpr std::uint32_t MATERIAL_TEXTURE_FIRST = 0;
|
||||
|
||||
// the compute passes of the light clusters, set 0 pushed
|
||||
constexpr std::uint32_t CL_CLUSTER_UBO = 0;
|
||||
constexpr std::uint32_t CL_AABB_SSBO = 1;
|
||||
constexpr std::uint32_t CL_RANGE_SSBO = 2;
|
||||
constexpr std::uint32_t CL_INDEX_SSBO = 3;
|
||||
constexpr std::uint32_t CL_COUNTER_SSBO = 4;
|
||||
constexpr std::uint32_t CL_LIGHT_SSBO = 5;
|
||||
|
||||
// the post-processing and the copies of whole images: set 0 pushed, the images it reads from binding 0 on
|
||||
constexpr std::uint32_t POST_SET = 0;
|
||||
constexpr std::uint32_t POST_TEXTURE_FIRST = 0;
|
||||
|
||||
// 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;
|
||||
|
||||
// the specialization constants of the shaders
|
||||
// 1: the colour written to the window is encoded by the shader (a window without an srgb format, gfx.shadergamma yes)
|
||||
constexpr std::uint32_t SPEC_OUTPUT_GAMMA = 0;
|
||||
// the settings of the game (gfx.* in eu07.ini) the shaders of the scene depend on, as in shaders/ogl46/settings.glsl
|
||||
constexpr std::uint32_t SPEC_SHADOWMAP_ENABLED = 1;
|
||||
constexpr std::uint32_t SPEC_ENVMAP_ENABLED = 2;
|
||||
constexpr std::uint32_t SPEC_MOTIONBLUR_ENABLED = 3;
|
||||
constexpr std::uint32_t SPEC_POSTFX_ENABLED = 4;
|
||||
constexpr std::uint32_t SPEC_EXTRAEFFECTS_ENABLED = 5;
|
||||
constexpr std::uint32_t SPEC_COUNT = 6;
|
||||
|
||||
} // namespace vk14::bindings
|
||||
|
||||
namespace vk14
|
||||
{
|
||||
|
||||
// the bindings above as #define lines, with MASZYNA_CONSTANTS, which tells shaders/vk14/common.glsl not to write its own
|
||||
inline std::string binding_preamble()
|
||||
{
|
||||
auto const define = [](char const *Name, std::uint32_t const Value) { return std::string("#define ") + Name + " " + std::to_string(Value) + "\n"; };
|
||||
return define("MASZYNA_CONSTANTS", 1) + define("UI_SET", bindings::UI_SET) + define("UI_TEXTURE", bindings::UI_TEXTURE) + define("PASS_SET", bindings::PASS_SET) + define("MATERIAL_SET", bindings::MATERIAL_SET) +
|
||||
define("MATERIAL_TEXTURE_FIRST", bindings::MATERIAL_TEXTURE_FIRST) + define("SPEC_OUTPUT_GAMMA", bindings::SPEC_OUTPUT_GAMMA);
|
||||
}
|
||||
// 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();
|
||||
|
||||
} // namespace vk14
|
||||
|
||||
322
rendering/vulkan14/vulkan14context.h
Normal file
322
rendering/vulkan14/vulkan14context.h
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
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 objects of the vulkan api of the vulkan renderer (vulkan14_renderer::context), for the files of the renderer only:
|
||||
// the core (vulkan14renderer.cpp) and the scene (vulkan14scene.cpp)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rendering/vulkan14/vulkan14renderer.h"
|
||||
#include "rendering/vulkan14/vulkan14common.h"
|
||||
#include "rendering/vulkan14/vulkan14instance.h"
|
||||
#include "rendering/vulkan14/vulkan14device.h"
|
||||
#include "rendering/vulkan14/vulkan14swapchain.h"
|
||||
#include "rendering/vulkan14/vulkan14memory.h"
|
||||
#include "rendering/vulkan14/vulkan14shaders.h"
|
||||
#include "rendering/vulkan14/vulkan14descriptors.h"
|
||||
#include "rendering/vulkan14/vulkan14passes.h"
|
||||
#include "rendering/vulkan14/vulkan14texture.h"
|
||||
#include "rendering/vulkan14/vulkan14imgui.h"
|
||||
#include "rendering/vulkan14/vulkan14bindings.h"
|
||||
#include "rendering/vulkan14/vulkan14geometry.h"
|
||||
#include "model/material.h"
|
||||
#include "gl/ubo.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace vk14
|
||||
{
|
||||
|
||||
// how the vertices of a program of the scene come
|
||||
enum class vertex_kind : std::uint8_t
|
||||
{
|
||||
none, // made by the vertex shader (the celestial bodies, the full screen quads)
|
||||
scene, // gfx::basic_vertex and the user data, the geometry banks
|
||||
colour, // position and colour, the sky dome
|
||||
particle, // position, colour and texture coordinates in one stream, the smoke
|
||||
position_uv // position and texture coordinates in two streams, the precipitation
|
||||
};
|
||||
|
||||
// the push constants of the pipelines of the scene and of the post-processing (shaders/vk14/common.glsl, draw_constants_block)
|
||||
struct draw_constants
|
||||
{
|
||||
std::uint32_t draw{0};
|
||||
std::uint32_t flags{0};
|
||||
std::uint32_t pad0{0};
|
||||
std::uint32_t pad1{0};
|
||||
glm::vec4 data[6]{};
|
||||
};
|
||||
static_assert(sizeof(draw_constants) == bindings::PUSH_CONSTANTS_SIZE, "the push constants don't match the shaders");
|
||||
|
||||
// a program of the scene: the shaders and the pipelines made of them for the targets and the states they were drawn with
|
||||
struct scene_program : public gfx::shader_program
|
||||
{
|
||||
std::shared_ptr<shader_code const> vertex;
|
||||
std::shared_ptr<shader_code const> fragment;
|
||||
// the fragment shader of the passes without the velocity target (NO_MOTION_OUTPUT), made when it's first needed
|
||||
std::string fragment_name;
|
||||
std::shared_ptr<shader_code const> fragment_single;
|
||||
vertex_kind vertices{vertex_kind::scene};
|
||||
std::unordered_map<std::uint64_t, VkPipeline> pipelines;
|
||||
};
|
||||
|
||||
// what a pass of the scene draws to; the pipelines are made for these
|
||||
struct target_formats
|
||||
{
|
||||
std::array<VkFormat, 2> colour{VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED};
|
||||
std::uint32_t colours{0};
|
||||
VkFormat depth{VK_FORMAT_UNDEFINED};
|
||||
VkSampleCountFlagBits samples{VK_SAMPLE_COUNT_1_BIT};
|
||||
// the number the pipelines of the targets are known by
|
||||
std::uint32_t id{0};
|
||||
};
|
||||
|
||||
// the images a viewport draws its scene to, made for its size and the settings
|
||||
struct viewport_targets
|
||||
{
|
||||
VkExtent2D extent{0, 0};
|
||||
VkSampleCountFlagBits samples{VK_SAMPLE_COUNT_1_BIT};
|
||||
bool motion{false}; // the velocity is drawn, for the motion blur
|
||||
bool aberration{false};
|
||||
// with more than one sample: the images drawn to, resolved to the ones below
|
||||
std::shared_ptr<image> msaa_colour;
|
||||
std::shared_ptr<image> msaa_motion;
|
||||
std::shared_ptr<image> msaa_depth;
|
||||
// the colour of the scene (hdr), the velocity and the depth: the resolved ones, or the ones drawn to with one sample
|
||||
std::shared_ptr<image> colour;
|
||||
std::shared_ptr<image> motion_image;
|
||||
std::shared_ptr<image> depth;
|
||||
// the colour after the motion blur (hdr)
|
||||
std::shared_ptr<image> blurred;
|
||||
// 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;
|
||||
// the depth under the cursor of the editor: one sample of the depth of the ground resolved to it, and the buffer it's read back with
|
||||
std::shared_ptr<image> cursor_depth;
|
||||
target_formats formats;
|
||||
target_formats output_formats;
|
||||
bool srgb{true}; // the output encodes the colour (gfx.shadergamma no)
|
||||
};
|
||||
|
||||
// the ring of a frame some data of the draws goes through, the descriptor of which reads a window of it: the window starts again
|
||||
// further on when it's full, and the pass set is bound again with the new offset
|
||||
struct window_ring
|
||||
{
|
||||
frame_ring ring;
|
||||
VkDeviceSize window{0}; // the size of the window, bytes
|
||||
VkDeviceSize stride{0};
|
||||
VkDeviceSize base{0}; // where the current window starts
|
||||
bool open{false};
|
||||
};
|
||||
|
||||
// the light clusters (gl::cluster_grid), of the colour pass of each viewport in turn
|
||||
struct cluster_state
|
||||
{
|
||||
buffer aabbs;
|
||||
buffer ranges;
|
||||
buffer indices;
|
||||
buffer counters;
|
||||
gl::cluster_ubs params{};
|
||||
bool stale{true}; // the boxes have to be made anew
|
||||
VkDeviceSize params_offset{0}; // in the ring of the frame, for the pass under way
|
||||
std::uint32_t lights_total{0};
|
||||
};
|
||||
|
||||
// a picking or a reading of a value of the depth, waiting for the gpu to be done with the frame it was made in
|
||||
struct readback
|
||||
{
|
||||
std::shared_ptr<buffer> data;
|
||||
std::uint64_t value{0}; // of the timeline
|
||||
bool pending{false};
|
||||
glm::ivec2 position{0};
|
||||
};
|
||||
|
||||
// what the scene is drawn with
|
||||
struct scene_state
|
||||
{
|
||||
// the layouts: of the pass set (set 0), the pushed set of the materials (set 1), of the pipelines of the scene, the
|
||||
// post-processing and the compute passes of the clusters
|
||||
VkDescriptorSetLayout pass_layout{VK_NULL_HANDLE};
|
||||
push_set material_set;
|
||||
VkPipelineLayout scene_layout{VK_NULL_HANDLE};
|
||||
push_set post_set;
|
||||
VkPipelineLayout post_layout{VK_NULL_HANDLE};
|
||||
push_set cluster_set;
|
||||
VkPipelineLayout cluster_layout{VK_NULL_HANDLE};
|
||||
// the pass sets: made with the pool when the images they read are made anew
|
||||
VkDescriptorPool pass_pool{VK_NULL_HANDLE};
|
||||
enum pass_set_kind
|
||||
{
|
||||
set_colour, // the shadow map and the environment map
|
||||
set_reflections, // the shadow map and the empty environment map
|
||||
set_plain, // the empty maps (shadows, picking)
|
||||
set_count
|
||||
};
|
||||
std::array<VkDescriptorSet, set_count> pass_sets{};
|
||||
bool pass_sets_stale{true};
|
||||
// the rings of the draws: the blocks of the models (gl::model_ubs) and the modelview matrices of the instances
|
||||
window_ring draws;
|
||||
window_ring instances;
|
||||
// the images every pass may read: the stand-ins, the shadow maps, the environment map
|
||||
std::shared_ptr<image> white; // a texture of a material which isn't there
|
||||
std::shared_ptr<image> shadow_empty;
|
||||
std::shared_ptr<image> shadow_map;
|
||||
std::vector<VkImageView> shadow_layers; // a view of each cascade
|
||||
std::uint32_t shadow_size{0};
|
||||
std::shared_ptr<image> env_empty;
|
||||
std::shared_ptr<image> env_map;
|
||||
std::shared_ptr<image> env_depth;
|
||||
std::array<VkImageView, 6> env_faces{};
|
||||
std::shared_ptr<image> pick_colour;
|
||||
std::shared_ptr<image> pick_depth;
|
||||
VkImageView headlights_view{VK_NULL_HANDLE}; // the one the pass sets were made with
|
||||
cluster_state clusters;
|
||||
// the targets of the viewports, [0] the main window
|
||||
std::vector<viewport_targets> targets;
|
||||
// the formats of the passes which don't depend on the viewport
|
||||
target_formats shadow_formats;
|
||||
target_formats reflection_formats;
|
||||
target_formats pick_formats;
|
||||
std::uint32_t formats_count{0};
|
||||
// the programs: of the materials (by the name Fetch_Shader() is given), and the ones of the renderer
|
||||
std::unordered_map<std::string, std::shared_ptr<scene_program>> materials;
|
||||
std::shared_ptr<scene_program> shadow, alpha_shadow, pick, pick_surface, line, billboard, freespot, celestial, skydome, smoke, precipitation;
|
||||
// the post-processing, by the fragment shader
|
||||
std::unordered_map<std::string, std::shared_ptr<shader_code const>> post_fragments;
|
||||
std::shared_ptr<shader_code const> quad;
|
||||
VkPipeline cluster_build{VK_NULL_HANDLE};
|
||||
VkPipeline cluster_cull{VK_NULL_HANDLE};
|
||||
// the reading of the colour under the cursor of the picking passes and of the depth under the cursor of the editor
|
||||
readback pick_controls;
|
||||
readback pick_scenery;
|
||||
readback cursor;
|
||||
// the screenshot asked for, copied with the frame
|
||||
std::shared_ptr<buffer> screenshot;
|
||||
std::uint64_t screenshot_value{0};
|
||||
VkExtent2D screenshot_extent{0, 0};
|
||||
bool screenshot_pending{false};
|
||||
bool screenshot_wanted{false};
|
||||
// the occlusion queries of the points of light of the lamps, a pool for each frame in flight with the submodels they were made
|
||||
// for; the glare of a lamp is drawn where its point was seen in the frame the gpu was done with last
|
||||
std::vector<VkQueryPool> occlusion_pools;
|
||||
std::vector<std::vector<TSubModel const *>> occlusion_submodels;
|
||||
std::unordered_map<TSubModel const *, bool> glare_visible;
|
||||
static constexpr std::uint32_t occlusion_queries{1024};
|
||||
// the images of the scene were put in their layouts (the stand-ins cleared)
|
||||
bool images_ready{false};
|
||||
};
|
||||
|
||||
} // namespace vk14
|
||||
|
||||
// the objects of the vulkan api. destroyed in the reverse order: the swapchains and the frames, then the device, then the instance
|
||||
struct vulkan14_renderer::context
|
||||
{
|
||||
struct frame
|
||||
{
|
||||
VkCommandPool pool{VK_NULL_HANDLE};
|
||||
// the uploads of the frame go to the queue first, then the commands of the frame
|
||||
VkCommandBuffer uploads{VK_NULL_HANDLE};
|
||||
VkCommandBuffer commands{VK_NULL_HANDLE};
|
||||
// the value of the timeline semaphore the gpu reaches when it's done with the frame; 0: never submitted
|
||||
std::uint64_t done{0};
|
||||
// the number of the frame, for the report of a lost device
|
||||
std::uint64_t number{0};
|
||||
};
|
||||
|
||||
vk14::instance instance;
|
||||
vk14::device device;
|
||||
vk14::allocator memory;
|
||||
vk14::deletion_queue deletions;
|
||||
vk14::uploader uploads;
|
||||
vk14::image_table images;
|
||||
vk14::samplers sampling;
|
||||
vk14::frame_ring ring;
|
||||
vk14::shader_library shaders;
|
||||
vk14::pipeline_library pipelines;
|
||||
vk14::pass_list passes;
|
||||
vk14::pending_textures pending;
|
||||
vk14::resources resources;
|
||||
vk14::imgui_backend imgui;
|
||||
vk14::scene_state scene;
|
||||
// the textures of the files; made after the objects above and gone before them
|
||||
std::unique_ptr<texture_bank> textures;
|
||||
// [0]: the main window, then the windows of the extra viewports
|
||||
std::vector<std::unique_ptr<vk14::swapchain>> windows;
|
||||
std::vector<frame> frames;
|
||||
// one semaphore counts the frames done by the gpu; the binary ones are only between the swapchains and the queue
|
||||
VkSemaphore timeline{VK_NULL_HANDLE};
|
||||
std::uint64_t timelinevalue{0};
|
||||
// frames recorded since the start
|
||||
std::uint64_t framecount{0};
|
||||
// the windows with an image acquired in the frame being made
|
||||
std::vector<vk14::swapchain *> acquired;
|
||||
bool devicelost{false};
|
||||
// the frame being recorded: its index among the frames in flight, its commands
|
||||
std::uint32_t slot{0};
|
||||
VkCommandBuffer commands{VK_NULL_HANDLE};
|
||||
|
||||
~context()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
bool create_frames(std::uint32_t const Count, std::string &Reason);
|
||||
// the parts of the renderer made once the device is there
|
||||
bool create_core(std::uint32_t const Frames, std::string &Reason);
|
||||
// the value of the timeline the gpu reached, 0 when it can't be read
|
||||
std::uint64_t completed() const;
|
||||
// waits until the gpu reached the value of the timeline; false: the device is lost
|
||||
bool wait(std::uint64_t const Value) const;
|
||||
void destroy();
|
||||
};
|
||||
|
||||
// the state of the draws of the walk being recorded into a pass of the scene
|
||||
struct vulkan14_renderer::draw_state
|
||||
{
|
||||
VkCommandBuffer commands{VK_NULL_HANDLE};
|
||||
vk14::target_formats const *targets{nullptr};
|
||||
VkExtent2D extent{0, 0};
|
||||
vk14::scene_state::pass_set_kind set{vk14::scene_state::set_plain};
|
||||
bool rendering{false}; // inside a rendering of a pass of the scene
|
||||
bool shadows{false}; // a pass of the sun shadows: the back faces are drawn
|
||||
// the offsets of the pass set in the rings of the frame
|
||||
std::uint32_t scene_offset{0};
|
||||
std::uint32_t light_offset{0};
|
||||
std::uint32_t cluster_offset{0};
|
||||
bool set_dirty{true};
|
||||
// the program and the textures for the next draw (walk_material(), walk_program(), walk_texture())
|
||||
vk14::scene_program *program{nullptr};
|
||||
std::array<texture_handle, gl::MAX_TEXTURES> textures{};
|
||||
bool textures_dirty{true};
|
||||
// the block of the draw last pushed, for the chunks drawn together
|
||||
std::uint32_t block{0};
|
||||
// what is bound
|
||||
VkPipeline pipeline{VK_NULL_HANDLE};
|
||||
VkBuffer vertices{VK_NULL_HANDLE};
|
||||
VkBuffer userdata{VK_NULL_HANDLE};
|
||||
VkBuffer indices{VK_NULL_HANDLE};
|
||||
VkPrimitiveTopology topology{VK_PRIMITIVE_TOPOLOGY_MAX_ENUM};
|
||||
VkCullModeFlags cull{VK_CULL_MODE_FLAG_BITS_MAX_ENUM};
|
||||
int depthtest{-1};
|
||||
int depthwrite{-1};
|
||||
std::uint32_t pushed_draw{~0u};
|
||||
// the state of the walk (setup_drawing() and the glare of the opengl renderer)
|
||||
bool blending{false};
|
||||
bool cullface{true};
|
||||
bool glare{false};
|
||||
bool depthmask_off{false}; // the editor overlay: no depth written whatever the blending
|
||||
// the occlusion queries of the glare of the lamps in the frame: the submodels, by the index of their query
|
||||
std::vector<TSubModel const *> queries;
|
||||
std::uint32_t query{~0u}; // the query under way
|
||||
// the draws made in the pass, for the debug panel
|
||||
std::uint32_t draws{0};
|
||||
};
|
||||
@@ -37,9 +37,10 @@ bool samplers::create(device const &Device, float Anisotropy, std::string &Reaso
|
||||
}
|
||||
if (Compare)
|
||||
{
|
||||
// reverse z: the fragment is lit where it's nearer the light than the map, its depth greater
|
||||
// as GL_LEQUAL of the opengl renderer: 1 (in the shadow) where the reference isn't nearer the light than the map (reverse
|
||||
// z: its depth isn't greater), the shaders take the result as the amount of the shadow
|
||||
info.compareEnable = VK_TRUE;
|
||||
info.compareOp = VK_COMPARE_OP_GREATER_OR_EQUAL;
|
||||
info.compareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
|
||||
info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
|
||||
}
|
||||
if (!VK14_VERIFY(vkCreateSampler(handle, &info, nullptr, &Sampler)))
|
||||
@@ -53,7 +54,7 @@ bool samplers::create(device const &Device, float Anisotropy, std::string &Reaso
|
||||
};
|
||||
return make(repeat_aniso, VK_FILTER_LINEAR, VK_SAMPLER_ADDRESS_MODE_REPEAT, true, Anisotropy, false, "repeat aniso") && make(clamp_aniso, VK_FILTER_LINEAR, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, true, Anisotropy, false, "clamp aniso") &&
|
||||
make(clamp_linear, VK_FILTER_LINEAR, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, true, 1.f, false, "clamp linear") && make(clamp_nearest, VK_FILTER_NEAREST, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, false, 1.f, false, "clamp nearest") &&
|
||||
make(shadow, VK_FILTER_LINEAR, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, false, 1.f, true, "shadow");
|
||||
make(shadow, VK_FILTER_LINEAR, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, false, 1.f, true, "shadow");
|
||||
}
|
||||
|
||||
void samplers::destroy(VkDevice Device)
|
||||
|
||||
2839
rendering/vulkan14/vulkan14frame.cpp
Normal file
2839
rendering/vulkan14/vulkan14frame.cpp
Normal file
File diff suppressed because it is too large
Load Diff
280
rendering/vulkan14/vulkan14geometry.cpp
Normal file
280
rendering/vulkan14/vulkan14geometry.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
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/vulkan14geometry.h"
|
||||
#include "rendering/vulkan14/vulkan14texture.h"
|
||||
|
||||
namespace vk14
|
||||
{
|
||||
|
||||
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}};
|
||||
|
||||
} // namespace
|
||||
|
||||
VkPrimitiveTopology topology_of(unsigned int const Type)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case GL_POINTS:
|
||||
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
||||
case GL_LINES:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
||||
case GL_LINE_STRIP:
|
||||
case GL_LINE_LOOP:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
||||
case GL_TRIANGLE_STRIP:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
||||
case GL_TRIANGLE_FAN:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
|
||||
case GL_TRIANGLES:
|
||||
default:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
}
|
||||
}
|
||||
|
||||
vulkan14_geometrybank::~vulkan14_geometrybank()
|
||||
{
|
||||
release_();
|
||||
}
|
||||
|
||||
void vulkan14_geometrybank::create_(gfx::geometry_handle const &Geometry)
|
||||
{
|
||||
// a new chunk: the buffers are made anew with it
|
||||
m_layoutdirty = true;
|
||||
}
|
||||
|
||||
void vulkan14_geometrybank::replace_(gfx::geometry_handle const &Geometry)
|
||||
{
|
||||
if (m_layoutdirty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto const index{Geometry.chunk - 1};
|
||||
if (index >= m_places.size())
|
||||
{
|
||||
m_layoutdirty = true;
|
||||
return;
|
||||
}
|
||||
auto const &chunk{gfx::geometry_bank::chunk(Geometry)};
|
||||
auto &place{m_places[index]};
|
||||
auto const loop{chunk.type == GL_LINE_LOOP};
|
||||
auto const vertexcount{static_cast<std::uint32_t>(chunk.vertices.size()) + ((loop && chunk.indices.empty() && (false == chunk.vertices.empty())) ? 1u : 0u)};
|
||||
auto const indexcount{static_cast<std::uint32_t>(chunk.indices.size()) + ((loop && (false == chunk.indices.empty())) ? 1u : 0u)};
|
||||
if ((vertexcount != place.vertexcount) || (indexcount != place.indexcount))
|
||||
{
|
||||
// it doesn't fit in its place any more
|
||||
m_layoutdirty = true;
|
||||
return;
|
||||
}
|
||||
place.dirty = true;
|
||||
m_chunksdirty = true;
|
||||
}
|
||||
|
||||
void vulkan14_geometrybank::release_()
|
||||
{
|
||||
auto *deletions{m_resources.deletions};
|
||||
if ((deletions != nullptr) && (m_resources.uploads != nullptr) && (m_vertices || m_userdata || m_indices))
|
||||
{
|
||||
deletions->push(m_resources.uploads->current_value(), [vertices = std::move(m_vertices), userdata = std::move(m_userdata), indices = std::move(m_indices)]() {});
|
||||
}
|
||||
m_vertices.reset();
|
||||
m_userdata.reset();
|
||||
m_indices.reset();
|
||||
m_places.clear();
|
||||
m_bytes = 0;
|
||||
m_layoutdirty = true;
|
||||
m_chunksdirty = false;
|
||||
}
|
||||
|
||||
void vulkan14_geometrybank::gpu_vertices(geometry_chunk const &Chunk, std::vector<gfx::basic_vertex> &Vertices, std::vector<gfx::vertex_userdata> &Userdata) const
|
||||
{
|
||||
Vertices.insert(Vertices.end(), Chunk.vertices.begin(), Chunk.vertices.end());
|
||||
if (Chunk.userdata.size() >= Chunk.vertices.size())
|
||||
{
|
||||
Userdata.insert(Userdata.end(), Chunk.userdata.begin(), Chunk.userdata.begin() + Chunk.vertices.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
Userdata.insert(Userdata.end(), Chunk.userdata.begin(), Chunk.userdata.end());
|
||||
Userdata.resize(Vertices.size(), no_userdata);
|
||||
}
|
||||
if ((Chunk.type == GL_LINE_LOOP) && Chunk.indices.empty() && (false == Chunk.vertices.empty()))
|
||||
{
|
||||
// back to the first vertex
|
||||
Vertices.push_back(Chunk.vertices.front());
|
||||
Userdata.push_back(Chunk.userdata.empty() ? no_userdata : Chunk.userdata.front());
|
||||
}
|
||||
}
|
||||
|
||||
bool vulkan14_geometrybank::build()
|
||||
{
|
||||
release_();
|
||||
|
||||
std::vector<gfx::basic_vertex> vertices;
|
||||
std::vector<gfx::vertex_userdata> userdata;
|
||||
std::vector<gfx::basic_index> indices;
|
||||
m_places.resize(m_chunks.size());
|
||||
for (std::size_t i = 0; i < m_chunks.size(); ++i)
|
||||
{
|
||||
auto const &chunk{m_chunks[i]};
|
||||
auto &place{m_places[i]};
|
||||
place.vertexfirst = static_cast<std::uint32_t>(vertices.size());
|
||||
place.indexfirst = static_cast<std::uint32_t>(indices.size());
|
||||
gpu_vertices(chunk, vertices, userdata);
|
||||
place.vertexcount = static_cast<std::uint32_t>(vertices.size()) - place.vertexfirst;
|
||||
indices.insert(indices.end(), chunk.indices.begin(), chunk.indices.end());
|
||||
if ((chunk.type == GL_LINE_LOOP) && (false == chunk.indices.empty()))
|
||||
{
|
||||
indices.push_back(chunk.indices.front());
|
||||
}
|
||||
place.indexcount = static_cast<std::uint32_t>(indices.size()) - place.indexfirst;
|
||||
place.dirty = false;
|
||||
}
|
||||
m_layoutdirty = false;
|
||||
m_chunksdirty = false;
|
||||
if (vertices.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto &memory{*m_resources.memory};
|
||||
auto &uploads{*m_resources.uploads};
|
||||
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)))
|
||||
{
|
||||
Buffer.reset();
|
||||
return false;
|
||||
}
|
||||
m_bytes += Size;
|
||||
return true;
|
||||
};
|
||||
auto ok{make(m_vertices, vertices.data(), vertices.size() * sizeof(gfx::basic_vertex), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, "geometry vertices") &&
|
||||
make(m_userdata, userdata.data(), userdata.size() * sizeof(gfx::vertex_userdata), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, "geometry user data")};
|
||||
if (ok && (false == indices.empty()))
|
||||
{
|
||||
ok = make(m_indices, indices.data(), indices.size() * sizeof(gfx::basic_index), VK_BUFFER_USAGE_INDEX_BUFFER_BIT, "geometry indices");
|
||||
}
|
||||
if (false == ok)
|
||||
{
|
||||
log_error("no memory for the geometry of a bank of " + std::to_string(vertices.size()) + " vertices");
|
||||
release_();
|
||||
// tried again when it's drawn the next time
|
||||
m_layoutdirty = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void vulkan14_geometrybank::update()
|
||||
{
|
||||
m_chunksdirty = false;
|
||||
if ((false == bool(m_vertices)) || (false == bool(m_userdata)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::vector<gfx::basic_vertex> vertices;
|
||||
std::vector<gfx::vertex_userdata> userdata;
|
||||
std::vector<gfx::basic_index> indices;
|
||||
std::vector<std::pair<VkDeviceSize, VkDeviceSize>> vertexparts, userdataparts, indexparts;
|
||||
for (std::size_t i = 0; i < m_places.size(); ++i)
|
||||
{
|
||||
auto &place{m_places[i]};
|
||||
if (false == place.dirty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
place.dirty = false;
|
||||
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));
|
||||
userdataparts.emplace_back(place.vertexfirst * sizeof(gfx::vertex_userdata), place.vertexcount * sizeof(gfx::vertex_userdata));
|
||||
if ((place.indexcount > 0) && m_indices)
|
||||
{
|
||||
indices.insert(indices.end(), chunk.indices.begin(), chunk.indices.end());
|
||||
if (chunk.type == GL_LINE_LOOP)
|
||||
{
|
||||
indices.push_back(chunk.indices.front());
|
||||
}
|
||||
indexparts.emplace_back(place.indexfirst * sizeof(gfx::basic_index), place.indexcount * sizeof(gfx::basic_index));
|
||||
}
|
||||
}
|
||||
auto &uploads{*m_resources.uploads};
|
||||
if (false == vertices.empty())
|
||||
{
|
||||
uploads.upload_buffer(m_vertices, vertices.data(), vertices.size() * sizeof(gfx::basic_vertex), vertexparts);
|
||||
uploads.upload_buffer(m_userdata, userdata.data(), userdata.size() * sizeof(gfx::vertex_userdata), userdataparts);
|
||||
}
|
||||
if (false == indices.empty())
|
||||
{
|
||||
uploads.upload_buffer(m_indices, indices.data(), indices.size() * sizeof(gfx::basic_index), indexparts);
|
||||
}
|
||||
}
|
||||
|
||||
bool vulkan14_geometrybank::source(gfx::geometry_handle const &Geometry, chunk_source &Source)
|
||||
{
|
||||
if ((Geometry.chunk == 0) || (Geometry.chunk > m_chunks.size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_layoutdirty)
|
||||
{
|
||||
build();
|
||||
}
|
||||
else if (m_chunksdirty)
|
||||
{
|
||||
update();
|
||||
}
|
||||
auto const index{Geometry.chunk - 1};
|
||||
if ((false == bool(m_vertices)) || (index >= m_places.size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto const &place{m_places[index]};
|
||||
Source.vertices = m_vertices->handle();
|
||||
Source.userdata = m_userdata->handle();
|
||||
Source.topology = topology_of(m_chunks[index].type);
|
||||
if (place.indexcount > 0)
|
||||
{
|
||||
Source.indices = m_indices->handle();
|
||||
Source.count = place.indexcount;
|
||||
Source.first = place.indexfirst;
|
||||
Source.basevertex = static_cast<std::int32_t>(place.vertexfirst);
|
||||
}
|
||||
else
|
||||
{
|
||||
Source.indices = VK_NULL_HANDLE;
|
||||
Source.count = place.vertexcount;
|
||||
Source.first = place.vertexfirst;
|
||||
Source.basevertex = 0;
|
||||
}
|
||||
return Source.count > 0;
|
||||
}
|
||||
|
||||
bool vulkan14_geometrybank::indirect_source_(gfx::geometry_handle const &Geometry, gfx::indirect_source &Source)
|
||||
{
|
||||
m_last = chunk_source{};
|
||||
if (false == source(Geometry, m_last))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Source.count = m_last.count;
|
||||
Source.first = m_last.first;
|
||||
Source.basevertex = m_last.basevertex;
|
||||
Source.indexed = (m_last.indices != VK_NULL_HANDLE);
|
||||
Source.type = m_chunks[Geometry.chunk - 1].type;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace vk14
|
||||
99
rendering/vulkan14/vulkan14geometry.h
Normal file
99
rendering/vulkan14/vulkan14geometry.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
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 geometry banks of the vulkan renderer (stage D1 of the plan): the chunks of a bank in three device buffers of the bank
|
||||
// (vertices, their user data, 32 bit indices), made the first time the bank is drawn after its chunks changed. a chunk replaced
|
||||
// with the same size is copied in place, otherwise the buffers are made anew and the old ones go once the gpu is done with them.
|
||||
// the data stays in memory as well (gfx::geometry_bank), the simulation reads it back. the primitives are the values of opengl
|
||||
// the program gives them with; a line loop is drawn as a strip which goes back to its first vertex
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rendering/geometrybank.h"
|
||||
#include "rendering/vulkan14/vulkan14common.h"
|
||||
#include "rendering/vulkan14/vulkan14memory.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace vk14
|
||||
{
|
||||
|
||||
struct resources;
|
||||
|
||||
// where the data of a chunk is in the buffers of its bank
|
||||
struct chunk_source
|
||||
{
|
||||
VkBuffer vertices{VK_NULL_HANDLE};
|
||||
VkBuffer userdata{VK_NULL_HANDLE};
|
||||
VkBuffer indices{VK_NULL_HANDLE}; // null for a chunk without indices
|
||||
std::uint32_t count{0}; // vertices, or indices
|
||||
std::uint32_t first{0}; // first index, or vertex
|
||||
std::int32_t basevertex{0};
|
||||
VkPrimitiveTopology topology{VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST};
|
||||
};
|
||||
|
||||
// the vulkan primitive of a primitive of opengl (GL_TRIANGLES ...)
|
||||
VkPrimitiveTopology topology_of(unsigned int Type);
|
||||
|
||||
class vulkan14_geometrybank : public gfx::geometry_bank
|
||||
{
|
||||
public:
|
||||
explicit vulkan14_geometrybank(resources const &Resources) : m_resources(Resources) {}
|
||||
~vulkan14_geometrybank() override;
|
||||
|
||||
// the chunk with its data on the gpu: uploaded now when it changed since the last time. false: nothing to draw.
|
||||
// gfx::geometrybank_manager::indirect_source() calls it too (which marks the bank as used), and keeps its answer in last()
|
||||
bool source(gfx::geometry_handle const &Geometry, chunk_source &Source);
|
||||
chunk_source const &last() const
|
||||
{
|
||||
return m_last;
|
||||
}
|
||||
// the bytes on the gpu, for the debug panel
|
||||
VkDeviceSize gpu_bytes() const
|
||||
{
|
||||
return m_bytes;
|
||||
}
|
||||
|
||||
private:
|
||||
struct chunk_place
|
||||
{
|
||||
std::uint32_t vertexfirst{0};
|
||||
std::uint32_t vertexcount{0}; // on the gpu: one more than the chunk for a line loop
|
||||
std::uint32_t indexfirst{0};
|
||||
std::uint32_t indexcount{0};
|
||||
bool dirty{true};
|
||||
};
|
||||
|
||||
void create_(gfx::geometry_handle const &Geometry) override;
|
||||
void replace_(gfx::geometry_handle const &Geometry) override;
|
||||
std::size_t draw_(gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
bool indirect_source_(gfx::geometry_handle const &Geometry, gfx::indirect_source &Source) override;
|
||||
void release_() override;
|
||||
// the buffers made anew for all the chunks
|
||||
bool build();
|
||||
// the chunks changed in place copied to the buffers
|
||||
void update();
|
||||
// the vertices of a chunk as they go to the gpu
|
||||
void gpu_vertices(geometry_chunk const &Chunk, std::vector<gfx::basic_vertex> &Vertices, std::vector<gfx::vertex_userdata> &Userdata) const;
|
||||
|
||||
resources const &m_resources;
|
||||
std::shared_ptr<buffer> m_vertices;
|
||||
std::shared_ptr<buffer> m_userdata;
|
||||
std::shared_ptr<buffer> m_indices;
|
||||
std::vector<chunk_place> m_places;
|
||||
bool m_layoutdirty{true}; // the buffers have to be made anew
|
||||
bool m_chunksdirty{false}; // some chunks have to be copied
|
||||
VkDeviceSize m_bytes{0};
|
||||
chunk_source m_last;
|
||||
};
|
||||
|
||||
} // namespace vk14
|
||||
@@ -177,6 +177,14 @@ void buffer::flush(VkDeviceSize Offset, VkDeviceSize Size) const
|
||||
}
|
||||
}
|
||||
|
||||
void buffer::invalidate(VkDeviceSize Offset, VkDeviceSize Size) const
|
||||
{
|
||||
if ((m_allocation != VK_NULL_HANDLE) && (Size > 0))
|
||||
{
|
||||
vmaInvalidateAllocation(m_allocator, m_allocation, Offset, Size);
|
||||
}
|
||||
}
|
||||
|
||||
// image
|
||||
|
||||
image::~image()
|
||||
@@ -334,12 +342,12 @@ void deletion_queue::flush()
|
||||
|
||||
// frame ring
|
||||
|
||||
bool frame_ring::create(allocator const &Allocator, VkDeviceSize SizePerFrame, std::uint32_t Frames, VkBufferUsageFlags Usage, std::string const &Name)
|
||||
bool frame_ring::create(allocator const &Allocator, VkDeviceSize SizePerFrame, std::uint32_t Frames, VkBufferUsageFlags Usage, std::string const &Name, VkDeviceSize Tail)
|
||||
{
|
||||
m_sizeperframe = SizePerFrame;
|
||||
m_base = 0;
|
||||
m_used = 0;
|
||||
return m_buffer.create(Allocator, SizePerFrame * Frames, Usage, memory_kind::upload, Name) && (m_buffer.mapped() != nullptr);
|
||||
return m_buffer.create(Allocator, SizePerFrame * Frames + Tail, Usage, memory_kind::upload, Name) && (m_buffer.mapped() != nullptr);
|
||||
}
|
||||
|
||||
void frame_ring::destroy()
|
||||
|
||||
@@ -91,6 +91,8 @@ class buffer
|
||||
}
|
||||
// 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)
|
||||
void invalidate(VkDeviceSize Offset, VkDeviceSize Size) const;
|
||||
explicit operator bool() const
|
||||
{
|
||||
return m_buffer != VK_NULL_HANDLE;
|
||||
@@ -214,7 +216,8 @@ struct ring_slice
|
||||
class frame_ring
|
||||
{
|
||||
public:
|
||||
bool create(allocator const &Allocator, VkDeviceSize SizePerFrame, std::uint32_t Frames, VkBufferUsageFlags Usage, std::string const &Name);
|
||||
// Tail: bytes past the part of the last frame, so a descriptor of a fixed range can be given an offset anywhere in a part
|
||||
bool create(allocator const &Allocator, VkDeviceSize SizePerFrame, std::uint32_t Frames, VkBufferUsageFlags Usage, std::string const &Name, VkDeviceSize Tail = 0);
|
||||
void destroy();
|
||||
// the part of the frame Frame is used from its start again
|
||||
void begin_frame(std::uint32_t Frame);
|
||||
@@ -226,10 +229,20 @@ class frame_ring
|
||||
{
|
||||
return m_used;
|
||||
}
|
||||
// the offset in the buffer the next allocation aligned to Alignment would get
|
||||
VkDeviceSize next_offset(VkDeviceSize Alignment) const
|
||||
{
|
||||
auto const alignment{std::max<VkDeviceSize>(Alignment, 4)};
|
||||
return m_base + (m_used + alignment - 1) / alignment * alignment;
|
||||
}
|
||||
VkDeviceSize capacity() const
|
||||
{
|
||||
return m_sizeperframe;
|
||||
}
|
||||
VkBuffer handle() const
|
||||
{
|
||||
return m_buffer.handle();
|
||||
}
|
||||
|
||||
private:
|
||||
buffer m_buffer;
|
||||
|
||||
@@ -8,18 +8,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "rendering/vulkan14/vulkan14renderer.h"
|
||||
#include "rendering/vulkan14/vulkan14common.h"
|
||||
#include "rendering/vulkan14/vulkan14instance.h"
|
||||
#include "rendering/vulkan14/vulkan14device.h"
|
||||
#include "rendering/vulkan14/vulkan14swapchain.h"
|
||||
#include "rendering/vulkan14/vulkan14memory.h"
|
||||
#include "rendering/vulkan14/vulkan14shaders.h"
|
||||
#include "rendering/vulkan14/vulkan14descriptors.h"
|
||||
#include "rendering/vulkan14/vulkan14passes.h"
|
||||
#include "rendering/vulkan14/vulkan14texture.h"
|
||||
#include "rendering/vulkan14/vulkan14imgui.h"
|
||||
#include "rendering/vulkan14/vulkan14bindings.h"
|
||||
#include "rendering/vulkan14/vulkan14context.h"
|
||||
#include "rendering/nullrenderer.h"
|
||||
#include "application/application.h"
|
||||
#include "simulation/simulation.h"
|
||||
@@ -37,8 +26,11 @@ namespace
|
||||
// background of the opengl renderer (srgb). before that the windows are black, as with the opengl renderer
|
||||
glm::vec3 const clear_colour{51.f / 255.f, 102.f / 255.f, 85.f / 255.f};
|
||||
|
||||
// the ring each frame in flight streams its data through (the user interface for now)
|
||||
VkDeviceSize const ring_size{8 * 1024 * 1024};
|
||||
// the ring each frame in flight streams its data through: the user interface, the blocks of the scene and of the light clusters,
|
||||
// the light buffers of the passes, the vertices of the particles and the sky; the descriptors read the light buffers with the range
|
||||
// of the whole gl::light_data, which the tail past the last frame leaves room for
|
||||
VkDeviceSize const ring_size{16 * 1024 * 1024};
|
||||
VkDeviceSize const ring_tail{(sizeof(gl::light_data) + 65535) / 65536 * 65536};
|
||||
|
||||
float srgb_to_linear(float const Value)
|
||||
{
|
||||
@@ -47,192 +39,157 @@ float srgb_to_linear(float const Value)
|
||||
|
||||
} // namespace
|
||||
|
||||
// the objects of the vulkan api. destroyed in the reverse order: the swapchains and the frames, then the device, then the instance
|
||||
struct vulkan14_renderer::context
|
||||
bool vulkan14_renderer::context::create_frames(std::uint32_t const Count, std::string &Reason)
|
||||
{
|
||||
struct frame
|
||||
{
|
||||
VkCommandPool pool{VK_NULL_HANDLE};
|
||||
// the uploads of the frame go to the queue first, then the commands of the frame
|
||||
VkCommandBuffer uploads{VK_NULL_HANDLE};
|
||||
VkCommandBuffer commands{VK_NULL_HANDLE};
|
||||
// the value of the timeline semaphore the gpu reaches when it's done with the frame; 0: never submitted
|
||||
std::uint64_t done{0};
|
||||
// the number of the frame, for the report of a lost device
|
||||
std::uint64_t number{0};
|
||||
};
|
||||
auto const handle{device.handle()};
|
||||
|
||||
vk14::instance instance;
|
||||
vk14::device device;
|
||||
vk14::allocator memory;
|
||||
vk14::deletion_queue deletions;
|
||||
vk14::uploader uploads;
|
||||
vk14::image_table images;
|
||||
vk14::samplers sampling;
|
||||
vk14::frame_ring ring;
|
||||
vk14::shader_library shaders;
|
||||
vk14::pipeline_library pipelines;
|
||||
vk14::pass_list passes;
|
||||
vk14::pending_textures pending;
|
||||
vk14::resources resources;
|
||||
vk14::imgui_backend imgui;
|
||||
// the textures of the files; made after the objects above and gone before them
|
||||
std::unique_ptr<texture_bank> textures;
|
||||
// [0]: the main window, then the windows of the extra viewports
|
||||
std::vector<std::unique_ptr<vk14::swapchain>> windows;
|
||||
std::vector<frame> frames;
|
||||
// one semaphore counts the frames done by the gpu; the binary ones are only between the swapchains and the queue
|
||||
VkSemaphore timeline{VK_NULL_HANDLE};
|
||||
std::uint64_t timelinevalue{0};
|
||||
// frames recorded since the start
|
||||
std::uint64_t framecount{0};
|
||||
// the windows with an image acquired in the frame being made
|
||||
std::vector<vk14::swapchain *> acquired;
|
||||
bool devicelost{false};
|
||||
|
||||
~context()
|
||||
VkSemaphoreTypeCreateInfo timelineinfo{};
|
||||
timelineinfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO;
|
||||
timelineinfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;
|
||||
timelineinfo.initialValue = 0;
|
||||
VkSemaphoreCreateInfo semaphoreinfo{};
|
||||
semaphoreinfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
semaphoreinfo.pNext = &timelineinfo;
|
||||
if (!VK14_VERIFY(vkCreateSemaphore(handle, &semaphoreinfo, nullptr, &timeline)))
|
||||
{
|
||||
destroy();
|
||||
Reason = "no timeline semaphore";
|
||||
return false;
|
||||
}
|
||||
instance.set_object_name(handle, VK_OBJECT_TYPE_SEMAPHORE, reinterpret_cast<std::uint64_t>(timeline), "frame timeline");
|
||||
|
||||
bool create_frames(std::uint32_t const Count, std::string &Reason)
|
||||
frames.resize(Count);
|
||||
for (std::uint32_t i = 0; i < Count; ++i)
|
||||
{
|
||||
auto const handle{device.handle()};
|
||||
|
||||
VkSemaphoreTypeCreateInfo timelineinfo{};
|
||||
timelineinfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO;
|
||||
timelineinfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;
|
||||
timelineinfo.initialValue = 0;
|
||||
VkSemaphoreCreateInfo semaphoreinfo{};
|
||||
semaphoreinfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
semaphoreinfo.pNext = &timelineinfo;
|
||||
if (!VK14_VERIFY(vkCreateSemaphore(handle, &semaphoreinfo, nullptr, &timeline)))
|
||||
VkCommandPoolCreateInfo poolinfo{};
|
||||
poolinfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
poolinfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
||||
poolinfo.queueFamilyIndex = device.families().graphics;
|
||||
if (!VK14_VERIFY(vkCreateCommandPool(handle, &poolinfo, nullptr, &frames[i].pool)))
|
||||
{
|
||||
Reason = "no timeline semaphore";
|
||||
Reason = "no command pool";
|
||||
return false;
|
||||
}
|
||||
instance.set_object_name(handle, VK_OBJECT_TYPE_SEMAPHORE, reinterpret_cast<std::uint64_t>(timeline), "frame timeline");
|
||||
|
||||
frames.resize(Count);
|
||||
for (std::uint32_t i = 0; i < Count; ++i)
|
||||
VkCommandBuffer buffers[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE};
|
||||
VkCommandBufferAllocateInfo bufferinfo{};
|
||||
bufferinfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
bufferinfo.commandPool = frames[i].pool;
|
||||
bufferinfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
bufferinfo.commandBufferCount = 2;
|
||||
if (!VK14_VERIFY(vkAllocateCommandBuffers(handle, &bufferinfo, buffers)))
|
||||
{
|
||||
VkCommandPoolCreateInfo poolinfo{};
|
||||
poolinfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
poolinfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
||||
poolinfo.queueFamilyIndex = device.families().graphics;
|
||||
if (!VK14_VERIFY(vkCreateCommandPool(handle, &poolinfo, nullptr, &frames[i].pool)))
|
||||
{
|
||||
Reason = "no command pool";
|
||||
return false;
|
||||
}
|
||||
VkCommandBuffer buffers[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE};
|
||||
VkCommandBufferAllocateInfo bufferinfo{};
|
||||
bufferinfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
bufferinfo.commandPool = frames[i].pool;
|
||||
bufferinfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
bufferinfo.commandBufferCount = 2;
|
||||
if (!VK14_VERIFY(vkAllocateCommandBuffers(handle, &bufferinfo, buffers)))
|
||||
{
|
||||
Reason = "no command buffer";
|
||||
return false;
|
||||
}
|
||||
frames[i].uploads = buffers[0];
|
||||
frames[i].commands = buffers[1];
|
||||
instance.set_object_name(handle, VK_OBJECT_TYPE_COMMAND_BUFFER, reinterpret_cast<std::uint64_t>(frames[i].uploads), "uploads " + std::to_string(i));
|
||||
instance.set_object_name(handle, VK_OBJECT_TYPE_COMMAND_BUFFER, reinterpret_cast<std::uint64_t>(frames[i].commands), "frame " + std::to_string(i));
|
||||
Reason = "no command buffer";
|
||||
return false;
|
||||
}
|
||||
frames[i].uploads = buffers[0];
|
||||
frames[i].commands = buffers[1];
|
||||
instance.set_object_name(handle, VK_OBJECT_TYPE_COMMAND_BUFFER, reinterpret_cast<std::uint64_t>(frames[i].uploads), "uploads " + std::to_string(i));
|
||||
instance.set_object_name(handle, VK_OBJECT_TYPE_COMMAND_BUFFER, reinterpret_cast<std::uint64_t>(frames[i].commands), "frame " + std::to_string(i));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// the parts of the renderer made once the device is there
|
||||
bool vulkan14_renderer::context::create_core(std::uint32_t const Frames, std::string &Reason)
|
||||
{
|
||||
if (false == memory.create(instance, device, Reason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uploads.init(memory, deletions, device.physical());
|
||||
if (false == sampling.create(device, static_cast<float>(Global.AnisotropicFiltering), Reason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto const &limits{device.caps().limits};
|
||||
(void)limits;
|
||||
if (false == ring.create(memory, ring_size, Frames, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, "frame ring", ring_tail))
|
||||
{
|
||||
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)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
resources.gpu = &device;
|
||||
resources.memory = &memory;
|
||||
resources.uploads = &uploads;
|
||||
resources.deletions = &deletions;
|
||||
resources.sampling = &sampling;
|
||||
resources.images = &images;
|
||||
resources.thread = std::this_thread::get_id();
|
||||
resources.pending = &pending;
|
||||
textures = std::make_unique<texture_bank>([this]() -> texture_source * { return new vk14::vulkan14_texture(resources); });
|
||||
return imgui.create(resources, shaders, pipelines, ring, Reason);
|
||||
}
|
||||
|
||||
// the value of the timeline the gpu reached, 0 when it can't be read
|
||||
std::uint64_t vulkan14_renderer::context::completed() const
|
||||
{
|
||||
std::uint64_t value{0};
|
||||
if ((timeline == VK_NULL_HANDLE) || (vkGetSemaphoreCounterValue(device.handle(), timeline, &value) != VK_SUCCESS))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool vulkan14_renderer::context::wait(std::uint64_t const Value) const
|
||||
{
|
||||
if ((Value == 0) || (completed() >= Value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
VkSemaphoreWaitInfo wait{};
|
||||
wait.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO;
|
||||
wait.semaphoreCount = 1;
|
||||
wait.pSemaphores = &timeline;
|
||||
wait.pValues = &Value;
|
||||
return VK14_VERIFY(vkWaitSemaphores(device.handle(), &wait, UINT64_MAX));
|
||||
}
|
||||
|
||||
// the parts of the renderer made once the device is there
|
||||
bool create_core(std::uint32_t const Frames, std::string &Reason)
|
||||
void vulkan14_renderer::context::destroy()
|
||||
{
|
||||
auto const handle{device.handle()};
|
||||
if (handle != VK_NULL_HANDLE)
|
||||
{
|
||||
if (false == memory.create(instance, device, Reason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uploads.init(memory, deletions, device.physical());
|
||||
if (false == sampling.create(device, static_cast<float>(Global.AnisotropicFiltering), Reason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto const &limits{device.caps().limits};
|
||||
(void)limits;
|
||||
if (false == ring.create(memory, ring_size, Frames, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, "frame ring"))
|
||||
{
|
||||
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)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
resources.gpu = &device;
|
||||
resources.memory = &memory;
|
||||
resources.uploads = &uploads;
|
||||
resources.deletions = &deletions;
|
||||
resources.sampling = &sampling;
|
||||
resources.images = &images;
|
||||
resources.thread = std::this_thread::get_id();
|
||||
resources.pending = &pending;
|
||||
textures = std::make_unique<texture_bank>([this]() -> texture_source * { return new vk14::vulkan14_texture(resources); });
|
||||
return imgui.create(resources, shaders, pipelines, ring, Reason);
|
||||
// the end of the renderer, not a frame: everything on the gpu has to be done
|
||||
vkDeviceWaitIdle(handle);
|
||||
}
|
||||
|
||||
// the value of the timeline the gpu reached, 0 when it can't be read
|
||||
std::uint64_t completed() const
|
||||
acquired.clear();
|
||||
windows.clear();
|
||||
imgui.destroy();
|
||||
// the textures give their images to the deletion queue, which goes now that the gpu is idle
|
||||
textures.reset();
|
||||
uploads.end_frame();
|
||||
uploads = vk14::uploader{};
|
||||
images.clear();
|
||||
deletions.flush();
|
||||
pipelines.shutdown();
|
||||
shaders.shutdown();
|
||||
passes.destroy();
|
||||
ring.destroy();
|
||||
if (handle != VK_NULL_HANDLE)
|
||||
{
|
||||
std::uint64_t value{0};
|
||||
if ((timeline == VK_NULL_HANDLE) || (vkGetSemaphoreCounterValue(device.handle(), timeline, &value) != VK_SUCCESS))
|
||||
sampling.destroy(handle);
|
||||
for (auto &frame : frames)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
auto const handle{device.handle()};
|
||||
if (handle != VK_NULL_HANDLE)
|
||||
{
|
||||
// the end of the renderer, not a frame: everything on the gpu has to be done
|
||||
vkDeviceWaitIdle(handle);
|
||||
}
|
||||
acquired.clear();
|
||||
windows.clear();
|
||||
imgui.destroy();
|
||||
// the textures give their images to the deletion queue, which goes now that the gpu is idle
|
||||
textures.reset();
|
||||
uploads.end_frame();
|
||||
uploads = vk14::uploader{};
|
||||
images.clear();
|
||||
deletions.flush();
|
||||
pipelines.shutdown();
|
||||
shaders.shutdown();
|
||||
passes.destroy();
|
||||
ring.destroy();
|
||||
if (handle != VK_NULL_HANDLE)
|
||||
{
|
||||
sampling.destroy(handle);
|
||||
for (auto &frame : frames)
|
||||
if (frame.pool != VK_NULL_HANDLE)
|
||||
{
|
||||
if (frame.pool != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyCommandPool(handle, frame.pool, nullptr);
|
||||
}
|
||||
}
|
||||
if (timeline != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroySemaphore(handle, timeline, nullptr);
|
||||
vkDestroyCommandPool(handle, frame.pool, nullptr);
|
||||
}
|
||||
}
|
||||
memory.destroy();
|
||||
frames.clear();
|
||||
timeline = VK_NULL_HANDLE;
|
||||
device.destroy();
|
||||
instance.destroy();
|
||||
if (timeline != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroySemaphore(handle, timeline, nullptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
memory.destroy();
|
||||
frames.clear();
|
||||
timeline = VK_NULL_HANDLE;
|
||||
device.destroy();
|
||||
instance.destroy();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -320,10 +277,20 @@ bool vulkan14_renderer::Init(GLFWwindow *Window)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false == init_scene(reason))
|
||||
{
|
||||
vk14::log_error("the renderer can't start: " + reason);
|
||||
destroy_scene();
|
||||
m_context.reset();
|
||||
return false;
|
||||
}
|
||||
init_frame_data();
|
||||
|
||||
vk14::log_info(std::to_string(framesinflight) + " frames in flight");
|
||||
// until the stages of the plan bring them, these parts of the simulator aren't there with vulkan
|
||||
vk14::log_info("not available with vulkan yet: the scenery and the vehicles (D1-D7), picking, mouse position in the scene and screenshots (D6), the map panel, "
|
||||
"python screens in their own windows (N5), panels in their own windows (R5b), VR (X4)");
|
||||
vk14::log_info("not available with vulkan yet: the atmosphere, the sky ambient light, automatic exposure and bloom, stars (P1), the weather fog and clouds (P2), "
|
||||
"ambient occlusion and contact shadows (P3), shadows of lamps and headlights (P4), the culling on the gpu (P5), the heightmap terrain, grass and the particles "
|
||||
"of the gpu (P6), the map panel, python screens in their own windows (N5), panels in their own windows (R5b), VR (X4)");
|
||||
WriteLog("Gfx Renderer: setup complete");
|
||||
update_info();
|
||||
return true;
|
||||
@@ -331,38 +298,64 @@ bool vulkan14_renderer::Init(GLFWwindow *Window)
|
||||
|
||||
bool vulkan14_renderer::AddViewport(global_settings::extraviewport_config const &conf)
|
||||
{
|
||||
if (!m_context)
|
||||
if (!m_context || m_viewports.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
viewport_config *viewport{nullptr};
|
||||
if (conf.monitor == "MAIN")
|
||||
{
|
||||
// the projection of the main window is changed only, which the drawing of the scenery takes care of (D6)
|
||||
vk14::log_info("extra viewport on the main window: drawn from the stage D6 of the plan");
|
||||
return true;
|
||||
// the main window drawn with the projection of the viewport
|
||||
viewport = &m_viewports.front();
|
||||
}
|
||||
|
||||
// a window of its own, without an opengl context
|
||||
auto *window{Application.window(-1, true, conf.width, conf.height, Application.find_monitor(conf.monitor), true, false)};
|
||||
if (window == nullptr)
|
||||
else
|
||||
{
|
||||
vk14::log_error("the window of the extra viewport on " + conf.monitor + " couldn't be made");
|
||||
return false;
|
||||
// a window of its own, without an opengl context, with a swapchain of its own
|
||||
auto *window{Application.window(-1, true, conf.width, conf.height, Application.find_monitor(conf.monitor), true, false)};
|
||||
if (window == nullptr)
|
||||
{
|
||||
vk14::log_error("the window of the extra viewport on " + conf.monitor + " couldn't be made");
|
||||
return false;
|
||||
}
|
||||
auto &ctx{*m_context};
|
||||
auto swapchain{std::make_unique<vk14::swapchain>()};
|
||||
std::string reason;
|
||||
if (false == swapchain->create(ctx.instance, ctx.device, window, "viewport " + conf.monitor, static_cast<std::uint32_t>(ctx.frames.size()), reason))
|
||||
{
|
||||
vk14::log_error(reason);
|
||||
return false;
|
||||
}
|
||||
ctx.windows.emplace_back(std::move(swapchain));
|
||||
m_viewports.emplace_back();
|
||||
viewport = &m_viewports.back();
|
||||
viewport->window = ctx.windows.size() - 1;
|
||||
}
|
||||
auto &ctx{*m_context};
|
||||
auto viewport{std::make_unique<vk14::swapchain>()};
|
||||
std::string reason;
|
||||
if (false == viewport->create(ctx.instance, ctx.device, window, "viewport " + conf.monitor, static_cast<std::uint32_t>(ctx.frames.size()), reason))
|
||||
{
|
||||
vk14::log_error(reason);
|
||||
return false;
|
||||
}
|
||||
ctx.windows.emplace_back(std::move(viewport));
|
||||
viewport->width = conf.width;
|
||||
viewport->height = conf.height;
|
||||
viewport->projection = conf.projection;
|
||||
viewport->proj_type = viewport_config::custom;
|
||||
viewport->draw_range = conf.draw_range;
|
||||
vk14::log_info("viewport " + conf.monitor + ": " + std::to_string(conf.width) + "x" + std::to_string(conf.height));
|
||||
return true;
|
||||
}
|
||||
|
||||
void vulkan14_renderer::Shutdown()
|
||||
{
|
||||
if (m_context && (m_context->device.handle() != VK_NULL_HANDLE))
|
||||
{
|
||||
// the end of the renderer, not a frame: everything on the gpu has to be done
|
||||
vkDeviceWaitIdle(m_context->device.handle());
|
||||
}
|
||||
// the geometry on the gpu goes with the context; the data of the banks stays for the simulation
|
||||
for (auto *bank : m_banks)
|
||||
{
|
||||
if (bank != nullptr)
|
||||
{
|
||||
bank->release();
|
||||
}
|
||||
}
|
||||
destroy_scene();
|
||||
m_draw.reset();
|
||||
m_context.reset();
|
||||
}
|
||||
|
||||
@@ -387,184 +380,6 @@ void vulkan14_renderer::report_device_lost(char const *Where)
|
||||
crashreport_add_info("vulkan_device_lost", report);
|
||||
}
|
||||
|
||||
bool vulkan14_renderer::Render()
|
||||
{
|
||||
if (!m_context || m_context->devicelost)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto &ctx{*m_context};
|
||||
auto const device{ctx.device.handle()};
|
||||
auto const slot{static_cast<std::uint32_t>(ctx.framecount % ctx.frames.size())};
|
||||
auto &frame{ctx.frames[slot]};
|
||||
|
||||
Timer::subsystem.gfx_total.start();
|
||||
|
||||
// the gpu has to be done with the frame which used the slot before
|
||||
if (frame.done > 0)
|
||||
{
|
||||
VkSemaphoreWaitInfo wait{};
|
||||
wait.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO;
|
||||
wait.semaphoreCount = 1;
|
||||
wait.pSemaphores = &ctx.timeline;
|
||||
wait.pValues = &frame.done;
|
||||
if (!VK14_VERIFY(vkWaitSemaphores(device, &wait, UINT64_MAX)))
|
||||
{
|
||||
report_device_lost("waiting for a frame");
|
||||
return false;
|
||||
}
|
||||
ctx.passes.collect(slot);
|
||||
}
|
||||
// what the frames done used last goes
|
||||
ctx.deletions.collect(std::max(ctx.completed(), frame.done));
|
||||
VK14_VERIFY(vkResetCommandPool(device, frame.pool, 0));
|
||||
ctx.ring.begin_frame(slot);
|
||||
|
||||
VkCommandBufferBeginInfo begin{};
|
||||
begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||
VK14_VERIFY(vkBeginCommandBuffer(frame.uploads, &begin));
|
||||
VK14_VERIFY(vkBeginCommandBuffer(frame.commands, &begin));
|
||||
auto const value{ctx.timelinevalue + 1};
|
||||
ctx.uploads.begin_frame(frame.uploads, value);
|
||||
// the content other threads gave the textures (python)
|
||||
{
|
||||
std::vector<vk14::vulkan14_texture *> pending;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(ctx.pending.mutex);
|
||||
pending.swap(ctx.pending.textures);
|
||||
}
|
||||
for (auto *texture : pending)
|
||||
{
|
||||
texture->apply_pending();
|
||||
}
|
||||
}
|
||||
|
||||
ctx.acquired.clear();
|
||||
for (auto &window : ctx.windows)
|
||||
{
|
||||
switch (window->acquire(slot))
|
||||
{
|
||||
case vk14::swapchain::acquire_result::ready:
|
||||
ctx.acquired.push_back(window.get());
|
||||
break;
|
||||
case vk14::swapchain::acquire_result::skipped:
|
||||
break;
|
||||
default:
|
||||
report_device_lost("acquiring an image");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// the user interface: imgui makes its frame, the images it asks for are uploaded in this frame
|
||||
Timer::subsystem.gfx_gui.start();
|
||||
Application.render_ui();
|
||||
Timer::subsystem.gfx_gui.stop();
|
||||
|
||||
frame.number = ctx.framecount;
|
||||
ctx.passes.begin_frame(frame.commands, slot, ctx.framecount);
|
||||
auto const background{simulation::is_ready ? clear_colour : glm::vec3{0.f}};
|
||||
bool uidrawn{false};
|
||||
for (auto *window : ctx.acquired)
|
||||
{
|
||||
auto const colour{window->srgb() ? glm::vec3{srgb_to_linear(background.r), srgb_to_linear(background.g), srgb_to_linear(background.b)} : background};
|
||||
if (window != ctx.windows.front().get())
|
||||
{
|
||||
// the extra viewports: their colour only, until the scenery is drawn into them (D6)
|
||||
window->record_clear(frame.commands, glm::vec4{colour, 1.f});
|
||||
continue;
|
||||
}
|
||||
// the main window: cleared, the user interface over it, shown
|
||||
ctx.passes.begin_pass("ui", {window->use(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT)});
|
||||
VkRenderingAttachmentInfo attachment{};
|
||||
attachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
|
||||
attachment.imageView = window->view();
|
||||
attachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachment.clearValue.color = {{colour.r, colour.g, colour.b, 1.f}};
|
||||
VkRenderingInfo rendering{};
|
||||
rendering.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
||||
rendering.renderArea = {{0, 0}, window->extent()};
|
||||
rendering.layerCount = 1;
|
||||
rendering.colorAttachmentCount = 1;
|
||||
rendering.pColorAttachments = &attachment;
|
||||
vkCmdBeginRendering(frame.commands, &rendering);
|
||||
ctx.imgui.record(frame.commands, window->extent(), window->format(), window->srgb());
|
||||
vkCmdEndRendering(frame.commands);
|
||||
ctx.passes.end_pass();
|
||||
vk14::barriers(frame.commands, {window->use(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_PIPELINE_STAGE_2_NONE, VK_ACCESS_2_NONE)});
|
||||
uidrawn = true;
|
||||
}
|
||||
if (false == uidrawn)
|
||||
{
|
||||
// the main window is minimized: the frame of imgui has nowhere to go
|
||||
ctx.imgui.skip();
|
||||
}
|
||||
ctx.passes.end_frame();
|
||||
auto const uploaded{ctx.uploads.end_frame()};
|
||||
VK14_VERIFY(vkEndCommandBuffer(frame.uploads));
|
||||
VK14_VERIFY(vkEndCommandBuffer(frame.commands));
|
||||
ctx.ring.flush();
|
||||
|
||||
std::vector<VkSemaphoreSubmitInfo> waits;
|
||||
std::vector<VkSemaphoreSubmitInfo> signals;
|
||||
for (auto *window : ctx.acquired)
|
||||
{
|
||||
VkSemaphoreSubmitInfo wait{};
|
||||
wait.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO;
|
||||
wait.semaphore = window->acquire_semaphore(slot);
|
||||
wait.stageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
waits.push_back(wait);
|
||||
|
||||
VkSemaphoreSubmitInfo signal{};
|
||||
signal.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO;
|
||||
signal.semaphore = window->present_semaphore();
|
||||
signal.stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
|
||||
signals.push_back(signal);
|
||||
}
|
||||
VkSemaphoreSubmitInfo timelinesignal{};
|
||||
timelinesignal.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO;
|
||||
timelinesignal.semaphore = ctx.timeline;
|
||||
timelinesignal.value = value;
|
||||
timelinesignal.stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
|
||||
signals.push_back(timelinesignal);
|
||||
|
||||
// the uploads first, in the same submit: their barriers make them done before the frame samples the images
|
||||
std::vector<VkCommandBufferSubmitInfo> commands;
|
||||
if (uploaded)
|
||||
{
|
||||
VkCommandBufferSubmitInfo info{};
|
||||
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO;
|
||||
info.commandBuffer = frame.uploads;
|
||||
commands.push_back(info);
|
||||
}
|
||||
{
|
||||
VkCommandBufferSubmitInfo info{};
|
||||
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO;
|
||||
info.commandBuffer = frame.commands;
|
||||
commands.push_back(info);
|
||||
}
|
||||
|
||||
VkSubmitInfo2 submit{};
|
||||
submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2;
|
||||
submit.waitSemaphoreInfoCount = static_cast<std::uint32_t>(waits.size());
|
||||
submit.pWaitSemaphoreInfos = waits.data();
|
||||
submit.commandBufferInfoCount = static_cast<std::uint32_t>(commands.size());
|
||||
submit.pCommandBufferInfos = commands.data();
|
||||
submit.signalSemaphoreInfoCount = static_cast<std::uint32_t>(signals.size());
|
||||
submit.pSignalSemaphoreInfos = signals.data();
|
||||
if (!VK14_VERIFY(vkQueueSubmit2(ctx.device.graphics_queue(), 1, &submit, VK_NULL_HANDLE)))
|
||||
{
|
||||
report_device_lost("submitting a frame");
|
||||
return false;
|
||||
}
|
||||
frame.done = ctx.timelinevalue = value;
|
||||
|
||||
Timer::subsystem.gfx_total.stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
void vulkan14_renderer::SwapBuffers()
|
||||
{
|
||||
if (!m_context)
|
||||
@@ -616,143 +431,71 @@ float vulkan14_renderer::Framerate()
|
||||
return m_framerate;
|
||||
}
|
||||
|
||||
// geometry: kept in memory for the simulation, nothing on the gpu until D1
|
||||
|
||||
gfx::geometrybank_handle vulkan14_renderer::Create_Bank()
|
||||
{
|
||||
return m_geometry.register_bank(std::make_unique<null_geometrybank>());
|
||||
}
|
||||
|
||||
gfx::geometry_handle vulkan14_renderer::Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type)
|
||||
{
|
||||
// NOTE: we expect indexed geometry to come with calculated tangents
|
||||
return m_geometry.create_chunk(Indices, Vertices, Userdata, Geometry, Type);
|
||||
}
|
||||
|
||||
gfx::geometry_handle vulkan14_renderer::Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type)
|
||||
{
|
||||
gfx::calculate_tangents(Vertices, gfx::index_array(), Type);
|
||||
return m_geometry.create_chunk(Vertices, Userdata, Geometry, Type);
|
||||
}
|
||||
|
||||
bool vulkan14_renderer::Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset)
|
||||
{
|
||||
gfx::calculate_tangents(Vertices, gfx::index_array(), Type);
|
||||
return m_geometry.replace(Vertices, Userdata, Geometry, Offset);
|
||||
}
|
||||
|
||||
bool vulkan14_renderer::Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type)
|
||||
{
|
||||
gfx::calculate_tangents(Vertices, gfx::index_array(), Type);
|
||||
return m_geometry.append(Vertices, Userdata, Geometry);
|
||||
}
|
||||
|
||||
gfx::index_array const &vulkan14_renderer::Indices(gfx::geometry_handle const &Geometry) const
|
||||
{
|
||||
return m_geometry.indices(Geometry);
|
||||
}
|
||||
|
||||
gfx::vertex_array const &vulkan14_renderer::Vertices(gfx::geometry_handle const &Geometry) const
|
||||
{
|
||||
return m_geometry.vertices(Geometry);
|
||||
}
|
||||
|
||||
gfx::userdata_array const &vulkan14_renderer::UserData(gfx::geometry_handle const &Geometry) const
|
||||
{
|
||||
return m_geometry.userdata(Geometry);
|
||||
}
|
||||
|
||||
// materials and their shaders: none until D1
|
||||
|
||||
material_handle vulkan14_renderer::Fetch_Material(std::string const &Filename, bool const Loadnow)
|
||||
{
|
||||
return null_handle;
|
||||
}
|
||||
|
||||
void vulkan14_renderer::Bind_Material(material_handle const Material, TSubModel const *sm, lighting_data const *lighting) {}
|
||||
|
||||
IMaterial const *vulkan14_renderer::Material(material_handle const Material) const
|
||||
{
|
||||
return IMaterial::null_material();
|
||||
}
|
||||
|
||||
std::shared_ptr<gfx::shader_program> vulkan14_renderer::Fetch_Shader(std::string const &name)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// textures: the files read and decoded as for the opengl renderer, made into images when they are first used (create(), e.g. by the
|
||||
// launcher); the materials of the scene take them from D1
|
||||
|
||||
texture_handle vulkan14_renderer::Fetch_Texture(std::string const &Filename, bool const Loadnow, GLint format_hint)
|
||||
{
|
||||
if (!m_context || !m_context->textures)
|
||||
{
|
||||
return null_handle;
|
||||
}
|
||||
return m_context->textures->create(Filename, Loadnow, format_hint);
|
||||
}
|
||||
|
||||
// the shaders take the textures by the pushed descriptors (D1), there's nothing to bind
|
||||
void vulkan14_renderer::Bind_Texture(texture_handle const Texture) {}
|
||||
|
||||
void vulkan14_renderer::Bind_Texture(std::size_t const Unit, texture_handle const Texture) {}
|
||||
|
||||
ITexture &vulkan14_renderer::Texture(texture_handle const Texture)
|
||||
{
|
||||
if ((Texture == null_handle) || !m_context || !m_context->textures)
|
||||
{
|
||||
return *ITexture::null_texture();
|
||||
}
|
||||
return m_context->textures->mark_as_used(Texture);
|
||||
}
|
||||
|
||||
ITexture const &vulkan14_renderer::Texture(texture_handle const Texture) const
|
||||
{
|
||||
if ((Texture == null_handle) || !m_context || !m_context->textures)
|
||||
{
|
||||
return *ITexture::null_texture();
|
||||
}
|
||||
return m_context->textures->texture(Texture);
|
||||
}
|
||||
|
||||
// picking: D6
|
||||
|
||||
void vulkan14_renderer::Pick_Control_Callback(std::function<void(TSubModel const *, glm::vec2 const)> Callback) {}
|
||||
|
||||
void vulkan14_renderer::Pick_Node_Callback(std::function<void(scene::basic_node *)> Callback) {}
|
||||
|
||||
TSubModel const *vulkan14_renderer::Pick_Control() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
scene::basic_node const *vulkan14_renderer::Pick_Node() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
glm::dvec3 vulkan14_renderer::Mouse_Position() const
|
||||
{
|
||||
return glm::dvec3(0.0);
|
||||
}
|
||||
|
||||
// maintenance
|
||||
|
||||
void vulkan14_renderer::Update(double const Deltatime)
|
||||
{
|
||||
if (m_context && m_context->textures && (true == Global.ResourceSweep) && (true == simulation::is_ready))
|
||||
{
|
||||
// textures nothing used for a while give their images back
|
||||
m_context->textures->update();
|
||||
}
|
||||
Update_Pick_Control();
|
||||
Update_Pick_Node();
|
||||
update_screenshot();
|
||||
|
||||
m_updateaccumulator += Deltatime;
|
||||
if (m_updateaccumulator < 1.0)
|
||||
{
|
||||
// too early for any work
|
||||
return;
|
||||
}
|
||||
m_updateaccumulator = 0.0;
|
||||
m_framerate = 1000.f / Timer::subsystem.mainloop_total.average();
|
||||
|
||||
// adjust draw ranges etc, based on recent performance (opengl46_renderer::Update())
|
||||
if (Global.targetfps != 0.0f)
|
||||
{
|
||||
float fps_diff = Global.targetfps - m_framerate;
|
||||
if (fps_diff > 0.0f)
|
||||
Global.fDistanceFactor = std::max(0.5f, Global.fDistanceFactor - 0.05f);
|
||||
else
|
||||
Global.fDistanceFactor = std::min(Global.gfx_distance_factor_max, Global.fDistanceFactor + 0.05f);
|
||||
}
|
||||
// legacy framerate parameters
|
||||
else if (Global.fFpsAverage != 0.f)
|
||||
{
|
||||
auto const fps_diff = m_framerate - Global.fFpsAverage;
|
||||
if (fps_diff < -Global.fFpsDeviation)
|
||||
{
|
||||
Global.fDistanceFactor = std::min(std::max(1.0f, Global.fDistanceFactor - 0.05f), Global.gfx_distance_factor_max);
|
||||
}
|
||||
else if (fps_diff > Global.fFpsDeviation)
|
||||
{
|
||||
Global.fDistanceFactor = std::min(std::min(3.0f, Global.fDistanceFactor + 0.05f), Global.gfx_distance_factor_max);
|
||||
}
|
||||
}
|
||||
|
||||
if (Global.UpdateMaterials)
|
||||
{
|
||||
// update resources if there was environmental change
|
||||
simulation_state simulationstate{Global.Weather, Global.Season};
|
||||
std::swap(m_simulationstate, simulationstate);
|
||||
if ((m_simulationstate.season != simulationstate.season) && (false == simulationstate.season.empty()))
|
||||
{
|
||||
m_materials.on_season_change();
|
||||
}
|
||||
if ((m_simulationstate.weather != simulationstate.weather) && (false == simulationstate.weather.empty()))
|
||||
{
|
||||
m_materials.on_weather_change();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_context && m_context->textures && (true == Global.ResourceSweep) && (true == simulation::is_ready))
|
||||
{
|
||||
// garbage collection
|
||||
m_geometry.update();
|
||||
m_context->textures->update();
|
||||
}
|
||||
if ((Global.gfx_texture_releasedistance > 0.f) && (true == simulation::is_ready))
|
||||
{
|
||||
update_texture_release();
|
||||
}
|
||||
update_info();
|
||||
}
|
||||
|
||||
@@ -781,15 +524,6 @@ void vulkan14_renderer::update_info()
|
||||
}
|
||||
}
|
||||
|
||||
void vulkan14_renderer::Update_Pick_Control() {}
|
||||
|
||||
void vulkan14_renderer::Update_Pick_Node() {}
|
||||
|
||||
glm::dvec3 vulkan14_renderer::Update_Mouse_Position()
|
||||
{
|
||||
return glm::dvec3(0.0);
|
||||
}
|
||||
|
||||
bool vulkan14_renderer::Debug_Ui_State(std::optional<bool> State)
|
||||
{
|
||||
if (State)
|
||||
@@ -813,8 +547,3 @@ imgui_renderer *vulkan14_renderer::GetImguiRenderer()
|
||||
{
|
||||
return (m_context ? &m_context->imgui : nullptr);
|
||||
}
|
||||
|
||||
void vulkan14_renderer::MakeScreenshot()
|
||||
{
|
||||
vk14::log_warning("screenshots aren't made by the vulkan renderer yet (D6)");
|
||||
}
|
||||
|
||||
@@ -7,11 +7,12 @@ obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// the vulkan 1.4 renderer, picked with gfxrenderer vulkan. so far the core of the renderer (the track R of the plan,
|
||||
// claude/vulkan14-renderer-plan.md): the gpu, the windows with their swapchains, the frames in flight, the memory and the
|
||||
// uploads, the shaders and the pipelines, the pushed descriptors, the passes with their barriers and gpu times, the textures
|
||||
// and the user interface (imgui) with the launcher. the scenery and the materials come in the later stages of the plan; until
|
||||
// then those methods keep the data the simulation reads back and draw nothing.
|
||||
// the vulkan 1.4 renderer, picked with gfxrenderer vulkan (claude/vulkan14-renderer-plan.md): the gpu, the windows with their
|
||||
// swapchains, the frames in flight, the memory and the uploads, the shaders and the pipelines, the pushed descriptors, the passes
|
||||
// with their barriers and gpu times, the textures and the user interface (imgui) with the launcher (the track R of the plan), and
|
||||
// the scene (the track D): the geometry, the materials, the passes of the colour of the scene with the sun shadows, the
|
||||
// environment map and the post-processing, the picking, the screenshots, the extra viewports and the light clusters. the scene
|
||||
// is walked by the walk shared with the opengl renderer (gfx::scene_walk), whose draws this renderer records.
|
||||
//
|
||||
// the vulkan headers stay in the files of rendering/vulkan14: the objects of the api live in vulkan14_renderer::context
|
||||
|
||||
@@ -19,8 +20,24 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include "rendering/renderer.h"
|
||||
#include "rendering/geometrybank.h"
|
||||
#include "rendering/light.h"
|
||||
#include "rendering/lightarray.h"
|
||||
#include "rendering/common/scenewalk.h"
|
||||
#include "rendering/common/locallights.h"
|
||||
#include "rendering/particles.h"
|
||||
#include "model/material.h"
|
||||
#include "vehicle/Camera.h"
|
||||
#include "gl/ubo.h"
|
||||
|
||||
class vulkan14_renderer : public gfx_renderer
|
||||
class world_environment;
|
||||
namespace vk14
|
||||
{
|
||||
struct scene_program;
|
||||
struct target_formats;
|
||||
enum class blend_mode;
|
||||
} // namespace vk14
|
||||
|
||||
class vulkan14_renderer : public gfx_renderer, public gfx::scene_walk
|
||||
{
|
||||
public:
|
||||
vulkan14_renderer();
|
||||
@@ -38,16 +55,23 @@ class vulkan14_renderer : public gfx_renderer
|
||||
gfx::geometry_handle Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override;
|
||||
gfx::geometry_handle Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override;
|
||||
bool Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset = 0) override;
|
||||
bool Replace_Indexed(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry) override;
|
||||
bool Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) override;
|
||||
gfx::index_array const &Indices(gfx::geometry_handle const &Geometry) const override;
|
||||
gfx::vertex_array const &Vertices(gfx::geometry_handle const &Geometry) const override;
|
||||
gfx::userdata_array const &UserData(gfx::geometry_handle const &Geometry) const override;
|
||||
int Primitive(gfx::geometry_handle const &Geometry) const override;
|
||||
// materials
|
||||
material_handle Fetch_Material(std::string const &Filename, bool const Loadnow = true) override;
|
||||
material_handle Matte_Material(material_handle const Material) override;
|
||||
material_handle Tinted_Material(material_handle const Material, glm::vec3 const &Colour) override;
|
||||
material_handle Blend_Material(material_handle const Material, std::array<material_handle, 3> const &Others) override;
|
||||
bool Blendable_Material(material_handle const Material) override;
|
||||
void Bind_Material(material_handle const Material, TSubModel const *sm = nullptr, lighting_data const *lighting = nullptr) override;
|
||||
IMaterial const *Material(material_handle const Material) const override;
|
||||
// shaders
|
||||
std::shared_ptr<gfx::shader_program> Fetch_Shader(std::string const &name) override;
|
||||
std::string Shader_Name(std::shared_ptr<gfx::shader_program> const &Program) const override;
|
||||
// textures
|
||||
texture_handle Fetch_Texture(std::string const &Filename, bool const Loadnow = true, GLint format_hint = GL_SRGB_ALPHA) override;
|
||||
void Bind_Texture(texture_handle const Texture) override;
|
||||
@@ -60,6 +84,10 @@ class vulkan14_renderer : public gfx_renderer
|
||||
TSubModel const *Pick_Control() const override;
|
||||
scene::basic_node const *Pick_Node() const override;
|
||||
glm::dvec3 Mouse_Position() const override;
|
||||
bool Mouse_Hit() const override;
|
||||
glm::mat4 Camera_View_Matrix() const override;
|
||||
glm::mat4 Camera_Projection_Matrix() const override;
|
||||
glm::dvec3 Camera_Position() const override;
|
||||
// maintenance
|
||||
void Update(double const Deltatime) override;
|
||||
void Update_Pick_Control() override;
|
||||
@@ -72,27 +100,203 @@ class vulkan14_renderer : public gfx_renderer
|
||||
// ui
|
||||
imgui_renderer *GetImguiRenderer() override;
|
||||
void MakeScreenshot() override;
|
||||
bool Render_Preview(TAnimModel *Instance, int const Size, int const Margin, bool const Shadows, std::vector<std::uint8_t> &Image) override;
|
||||
|
||||
static std::unique_ptr<gfx_renderer> create_func();
|
||||
// how the application makes the windows of the renderer, and whether it can run on the machine
|
||||
static gfx_backend_desc backend_desc();
|
||||
|
||||
private:
|
||||
// the objects of the vulkan api (vulkan14renderer.cpp)
|
||||
// the objects of the vulkan api (vulkan14context.h)
|
||||
struct context;
|
||||
|
||||
void update_info();
|
||||
// the device was lost: what was on the gpu goes to the log
|
||||
void report_device_lost(char const *Where);
|
||||
// a viewport the scene is drawn to: the main window, an extra viewport (a window of its own or the main window drawn with
|
||||
// another projection)
|
||||
struct viewport_config
|
||||
{
|
||||
int width{0};
|
||||
int height{0};
|
||||
float draw_range{1.f};
|
||||
bool main{false};
|
||||
bool shadow{false}; // its view is taken into the sun shadows
|
||||
std::size_t window{0}; // the swapchain it goes to (vulkan14_renderer::context::windows)
|
||||
enum vp_type
|
||||
{
|
||||
normal,
|
||||
custom
|
||||
} proj_type{normal};
|
||||
viewport_proj_config projection{};
|
||||
};
|
||||
// the sun of the frame, with the factor of the place drawn (tunnels)
|
||||
struct sun_light : public basic_light
|
||||
{
|
||||
float factor{1.f};
|
||||
};
|
||||
|
||||
using gfx::scene_walk::Render;
|
||||
using gfx::scene_walk::Render_Alpha;
|
||||
|
||||
// the draws of the shared walk of the scene, recorded
|
||||
void walk_material(material_handle const Material, TSubModel const *Submodel = nullptr, lighting_data const *Lighting = nullptr) override;
|
||||
void walk_shadow_material(material_handle const Material) override;
|
||||
opengl_material const &walk_material_data(material_handle const Material) const override;
|
||||
opengl_material const &walk_material_data(TSubModel const *Submodel) const override;
|
||||
void walk_program(walk_shader const Program) override;
|
||||
void walk_texture(std::size_t const Unit, texture_handle const Texture) override;
|
||||
void walk_blending(bool const Alpha) override;
|
||||
void walk_sunlight(float const Factor = 1.f) override;
|
||||
void walk_environment(TEnvironmentType const Environment = e_flat) override;
|
||||
void walk_ambient_save() override;
|
||||
void walk_ambient_add(glm::vec3 const &Addition) override;
|
||||
void walk_ambient_restore() override;
|
||||
glm::vec4 walk_sun_ambient() const override;
|
||||
void walk_draw(gfx::geometry_handle const &Geometry) override;
|
||||
void walk_draw(std::vector<gfx::geometrybank_handle>::iterator First, std::vector<gfx::geometrybank_handle>::iterator Last) override;
|
||||
std::uint32_t walk_instances(glm::mat4 const *Modelviews, std::size_t const Count) override;
|
||||
void walk_cull_face(bool const Enabled) override;
|
||||
void walk_glare(bool const Enabled) override;
|
||||
void walk_glare_draw(TSubModel *Submodel, gfx::geometry_handle const &Geometry) override;
|
||||
void walk_light_query(TSubModel *Submodel, bool const Begin) override;
|
||||
void walk_editor_cursor() override;
|
||||
|
||||
// the scene (vulkan14scene.cpp)
|
||||
bool init_scene(std::string &Reason);
|
||||
// what the renderer prepares for the frames once it's there: the viewport of the main window, the common textures and geometry
|
||||
void init_frame_data();
|
||||
void destroy_scene();
|
||||
// the scene of the frame: the passes of the viewports
|
||||
void render_scene();
|
||||
void setup_pass(viewport_config &Viewport, renderpass_config &Config, rendermode const Mode, float const Znear = 0.f, float const Zfar = 1.f, bool const Ignoredebug = false);
|
||||
void setup_matrices();
|
||||
// the colour of a viewport, with the passes it needs first: the sun shadows, the environment map, the light clusters
|
||||
void render_viewport(std::size_t const Viewport);
|
||||
void render_shadows(viewport_config &Viewport);
|
||||
bool render_reflections(viewport_config &Viewport);
|
||||
void render_reflection_face(viewport_config &Viewport);
|
||||
void render_picking(rendermode const Mode);
|
||||
// the sky dome and the sun and the moon
|
||||
void render_environment(world_environment *Environment);
|
||||
void render_particles();
|
||||
void render_precipitation();
|
||||
void render_editor_overlay();
|
||||
// the post-processing of a viewport: the motion blur, the tonemapping, the chromatic aberration
|
||||
void render_post(std::size_t const Viewport, bool const Motionblur);
|
||||
// the output of a viewport to its window, with the user interface over it for the main window
|
||||
void present_viewport(std::size_t const Viewport);
|
||||
// the light buffer of the pass: the sun, the ambient light, the fog, the headlights, the lamps (the clustered lights)
|
||||
void update_lights(light_array &Lights, bool const Headlights = true, bool const Clustered = false);
|
||||
void write_local_light(gl::light_element &Light, gfx::local_lights::light const &Source, glm::dvec3 const &Camera, glm::mat4 const &View);
|
||||
void headlight_projector(light_array::light_record const &Light, glm::dvec3 &Position, glm::mat3 &Rotation, glm::mat4 &Projection, float &Near, float &Fieldofview);
|
||||
// the light buffer and its header written for the draws which follow
|
||||
void upload_light_header();
|
||||
void upload_lights();
|
||||
void upload_scene_block();
|
||||
void setup_sunlight_intensity(float const Factor = 1.f);
|
||||
void setup_shadow_color(glm::vec4 const &Shadowcolor);
|
||||
void setup_shadow_matrices();
|
||||
// the light clusters of the pass: the grid and the lists of the lights
|
||||
void update_clusters(glm::ivec2 const &Targetsize);
|
||||
// the materials
|
||||
material_handle invalid_material() const
|
||||
{
|
||||
return m_invalid_material;
|
||||
}
|
||||
// the draws: the pipeline of the program for the state of the walk, made when it's first needed
|
||||
void draw_chunk(gfx::geometry_handle const &Geometry, bool const Newblock);
|
||||
// a pass of the scene recorded with the walk: its targets, its size, its pass set (vk14::scene_state::pass_sets)
|
||||
void start_draws(vk14::target_formats const &Targets, int const Width, int const Height, int const Set, bool const Shadows);
|
||||
void end_draws();
|
||||
// the rendering of the colour of a viewport, begun with the images cleared or again where it was
|
||||
void begin_colour_rendering(std::size_t const Viewport, bool const Clear);
|
||||
void push_constants(void const *Constants);
|
||||
// a pipeline of the post-processing (a VkPipeline), nullptr when the shader isn't there. Encode: the colour is encoded by the
|
||||
// shader (OUTPUT_GAMMA), -1: as gfx.shadergamma says
|
||||
void *post_pipeline(char const *Fragment, int const Format, int const Encode = -1);
|
||||
// the screenshot copied with a frame the gpu is done with, saved
|
||||
void update_screenshot();
|
||||
void bind_pass_set();
|
||||
bool bind_scene_pipeline(vk14::scene_program &Program, vk14::target_formats const &Targets, vk14::blend_mode const Blend, int const Topology);
|
||||
void apply_draw_state();
|
||||
void push_textures();
|
||||
std::uint32_t push_draw_block();
|
||||
void open_instance_window();
|
||||
void update_texture_release();
|
||||
|
||||
std::unique_ptr<context> m_context;
|
||||
// the data of the geometry, which the simulation reads back; nothing on the gpu yet
|
||||
// the data of the geometry, which the simulation reads back, and the banks with it on the gpu
|
||||
gfx::geometrybank_manager m_geometry;
|
||||
std::vector<gfx::geometry_bank *> m_banks;
|
||||
material_manager m_materials;
|
||||
material_handle m_invalid_material{null_handle};
|
||||
std::vector<viewport_config> m_viewports;
|
||||
viewport_config *m_current_viewport{nullptr};
|
||||
renderpass_config m_colorpass; // of the latest colour pass
|
||||
std::array<renderpass_config, 3> m_shadowpass; // of the latest cascades of the sun shadows
|
||||
sun_light m_sunlight;
|
||||
glm::vec3 m_fogcolor{0.f};
|
||||
glm::vec4 m_shadowcolor{colors::shadow};
|
||||
gl::scene_ubs scene_ubs{};
|
||||
gl::light_data light_data{};
|
||||
std::uint32_t m_lightcount{1}; // lights written to light_data
|
||||
// the copies of the light buffer in the ring of the frame since the lights were last written, by the header and the sun
|
||||
std::vector<std::pair<std::array<std::uint8_t, offsetof(gl::light_data, lights) + sizeof(gl::light_element)>, std::uint32_t>> m_lightheaders;
|
||||
packed_vec3 m_cabambient{0.f};
|
||||
gfx::local_lights m_locallights;
|
||||
std::uint32_t m_lights_lit{0};
|
||||
std::uint32_t m_lights_dropped{0};
|
||||
// the reflections: the face drawn, and the place and the time of the last update
|
||||
int m_environmentcubetextureface{0};
|
||||
int m_environmentupdateface{6};
|
||||
bool m_environmentready{false};
|
||||
double m_environmentupdatetime{0.0};
|
||||
glm::dvec3 m_environmentupdatelocation{0.0};
|
||||
bool m_vehiclesmoved{true};
|
||||
texture_handle m_suntexture{-1};
|
||||
texture_handle m_moontexture{-1};
|
||||
texture_handle m_smoketexture{-1};
|
||||
texture_handle m_headlightstexture{-1};
|
||||
texture_handle m_precipitationtexture{-1};
|
||||
float m_precipitationovercast{-1.f};
|
||||
double m_precipitationrotation{0.0};
|
||||
// the editor overlay
|
||||
gfx::geometrybank_handle m_editor_overlay_bank{};
|
||||
std::vector<gfx::geometry_handle> m_editor_overlay_geometry;
|
||||
unsigned int m_editor_overlay_revision{0};
|
||||
unsigned int m_nodestateversion{0};
|
||||
// picking
|
||||
std::vector<std::function<void(TSubModel const *, glm::vec2)>> m_control_pick_requests;
|
||||
std::vector<std::function<void(scene::basic_node *)>> m_node_pick_requests;
|
||||
TSubModel const *m_pickcontrolitem{nullptr};
|
||||
scene::basic_node *m_picksceneryitem{nullptr};
|
||||
bool m_pickcontrolswanted{false};
|
||||
bool m_picknodewanted{false};
|
||||
glm::ivec2 m_pickposition{0};
|
||||
glm::vec3 m_worldmousecoordinates{0.f};
|
||||
bool m_mousehit{false};
|
||||
// the state of the draws of the walk being recorded
|
||||
struct draw_state;
|
||||
std::unique_ptr<draw_state> m_draw;
|
||||
// textures used by vehicle models, for the release of far textures
|
||||
std::unordered_map<TModel3d const *, std::vector<texture_handle>> m_modeltextures;
|
||||
std::vector<texture_handle> const &Model_Textures(TModel3d *Model);
|
||||
|
||||
// the season and the weather the materials were last given
|
||||
struct simulation_state
|
||||
{
|
||||
std::string weather;
|
||||
std::string season;
|
||||
} m_simulationstate;
|
||||
|
||||
float m_framerate{0.f};
|
||||
double m_updateaccumulator{0.0};
|
||||
bool m_debug_ui_active{false};
|
||||
std::string m_infotimes;
|
||||
std::string m_infostats;
|
||||
debug_stats m_laststats;
|
||||
|
||||
void update_info();
|
||||
// the device was lost: what was on the gpu goes to the log
|
||||
void report_device_lost(char const *Where);
|
||||
|
||||
static bool renderer_register;
|
||||
};
|
||||
|
||||
1405
rendering/vulkan14/vulkan14scene.cpp
Normal file
1405
rendering/vulkan14/vulkan14scene.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,8 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "stdafx.h"
|
||||
#include "rendering/vulkan14/vulkan14shaders.h"
|
||||
#include "rendering/vulkan14/vulkan14device.h"
|
||||
#include "rendering/vulkan14/vulkan14bindings.h"
|
||||
#include "gl/ubo.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/Logs.h"
|
||||
#include "utilities/utilities.h"
|
||||
@@ -562,6 +564,24 @@ VkPipeline pipeline_library::get(pipeline_desc const &Desc)
|
||||
attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
attachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
break;
|
||||
case blend_mode::scene_alpha:
|
||||
attachment.blendEnable = VK_TRUE;
|
||||
attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
attachment.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
attachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
break;
|
||||
case blend_mode::scene_additive:
|
||||
attachment.blendEnable = VK_TRUE;
|
||||
attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
break;
|
||||
case blend_mode::additive:
|
||||
attachment.blendEnable = VK_TRUE;
|
||||
attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
@@ -703,3 +723,60 @@ void set_draw_state(VkCommandBuffer Commands, VkExtent2D Extent)
|
||||
}
|
||||
|
||||
} // namespace vk14
|
||||
|
||||
namespace vk14
|
||||
{
|
||||
|
||||
std::string binding_preamble()
|
||||
{
|
||||
std::string preamble{"#define MASZYNA_CONSTANTS 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);
|
||||
define("UI_TEXTURE", UI_TEXTURE);
|
||||
define("PASS_SET", PASS_SET);
|
||||
define("MATERIAL_SET", MATERIAL_SET);
|
||||
define("SCENE_UBO", SCENE_UBO);
|
||||
define("DRAW_SSBO", DRAW_SSBO);
|
||||
define("INSTANCE_SSBO", INSTANCE_SSBO);
|
||||
define("LIGHT_SSBO", LIGHT_SSBO);
|
||||
define("CLUSTER_UBO", CLUSTER_UBO);
|
||||
define("CLUSTER_RANGE_SSBO", CLUSTER_RANGE_SSBO);
|
||||
define("CLUSTER_INDEX_SSBO", CLUSTER_INDEX_SSBO);
|
||||
define("SHADOW_TEX", SHADOW_TEX);
|
||||
define("ENV_TEX", ENV_TEX);
|
||||
define("HEADLIGHT_TEX", HEADLIGHT_TEX);
|
||||
define("PASS_BINDING_COUNT", PASS_BINDING_COUNT);
|
||||
define("MATERIAL_TEXTURE_FIRST", MATERIAL_TEXTURE_FIRST);
|
||||
define("CL_CLUSTER_UBO", CL_CLUSTER_UBO);
|
||||
define("CL_AABB_SSBO", CL_AABB_SSBO);
|
||||
define("CL_RANGE_SSBO", CL_RANGE_SSBO);
|
||||
define("CL_INDEX_SSBO", CL_INDEX_SSBO);
|
||||
define("CL_COUNTER_SSBO", CL_COUNTER_SSBO);
|
||||
define("CL_LIGHT_SSBO", CL_LIGHT_SSBO);
|
||||
define("POST_SET", POST_SET);
|
||||
define("POST_TEXTURE_FIRST", POST_TEXTURE_FIRST);
|
||||
define("PUSH_CONSTANTS_SIZE", PUSH_CONSTANTS_SIZE);
|
||||
define("SPEC_OUTPUT_GAMMA", SPEC_OUTPUT_GAMMA);
|
||||
define("SPEC_SHADOWMAP_ENABLED", SPEC_SHADOWMAP_ENABLED);
|
||||
define("SPEC_ENVMAP_ENABLED", SPEC_ENVMAP_ENABLED);
|
||||
define("SPEC_MOTIONBLUR_ENABLED", SPEC_MOTIONBLUR_ENABLED);
|
||||
define("SPEC_POSTFX_ENABLED", SPEC_POSTFX_ENABLED);
|
||||
define("SPEC_EXTRAEFFECTS_ENABLED", SPEC_EXTRAEFFECTS_ENABLED);
|
||||
define("SPEC_COUNT", SPEC_COUNT);
|
||||
// the constants of the game the blocks of the shaders are sized by, the same as for the opengl renderer (gl/ubo.h)
|
||||
define("MAX_LIGHTS", static_cast<unsigned int>(gl::MAX_LIGHTS), "U");
|
||||
define("MAX_CASCADES", static_cast<unsigned int>(gl::MAX_CASCADES), "U");
|
||||
define("MAX_PARAMS", static_cast<unsigned int>(gl::MAX_PARAMS), "U");
|
||||
define("MAX_HEADLIGHT_SHADOWS", static_cast<unsigned int>(gl::MAX_HEADLIGHT_SHADOWS), "U");
|
||||
define("CLUSTER_GRID_X", gl::CLUSTER_GRID_X);
|
||||
define("CLUSTER_GRID_Y", gl::CLUSTER_GRID_Y);
|
||||
define("CLUSTER_GRID_Z", gl::CLUSTER_GRID_Z);
|
||||
define("CLUSTER_COUNT", gl::CLUSTER_COUNT);
|
||||
define("MAX_LIGHTS_PER_CLUSTER", gl::MAX_LIGHTS_PER_CLUSTER);
|
||||
define("CLUSTER_BUILD_GROUP_SIZE", gl::CLUSTER_BUILD_GROUP_SIZE);
|
||||
define("CLUSTER_CULL_GROUP_SIZE", gl::CLUSTER_CULL_GROUP_SIZE);
|
||||
return preamble;
|
||||
}
|
||||
|
||||
} // namespace vk14
|
||||
|
||||
@@ -76,7 +76,10 @@ enum class blend_mode
|
||||
none,
|
||||
alpha, // the colour over what is there by its alpha, the alpha kept for what's behind (the user interface)
|
||||
premultiplied,
|
||||
additive
|
||||
additive,
|
||||
// the blending of the scene, as glBlendFunc() of the opengl renderer gives it to the colour and the alpha alike
|
||||
scene_alpha, // GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA
|
||||
scene_additive // GL_SRC_ALPHA, GL_ONE (the glare of the lamps)
|
||||
};
|
||||
|
||||
struct vertex_layout
|
||||
|
||||
@@ -57,10 +57,59 @@ void uploader::begin_frame(VkCommandBuffer Commands, std::uint64_t Value)
|
||||
|
||||
bool uploader::end_frame()
|
||||
{
|
||||
if (m_buffercopies && (m_commands != VK_NULL_HANDLE))
|
||||
{
|
||||
// the draws of the frame read what was copied
|
||||
VkMemoryBarrier2 barrier{};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2;
|
||||
barrier.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT;
|
||||
barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
|
||||
barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT;
|
||||
VkDependencyInfo dependency{};
|
||||
dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
|
||||
dependency.memoryBarrierCount = 1;
|
||||
dependency.pMemoryBarriers = &barrier;
|
||||
vkCmdPipelineBarrier2(m_commands, &dependency);
|
||||
}
|
||||
m_buffercopies = false;
|
||||
m_commands = VK_NULL_HANDLE;
|
||||
return m_recorded;
|
||||
}
|
||||
|
||||
bool uploader::upload_buffer(std::shared_ptr<buffer> const &Target, void const *Data, VkDeviceSize Size, std::vector<std::pair<VkDeviceSize, VkDeviceSize>> const &Parts)
|
||||
{
|
||||
if ((Target == nullptr) || (Size == 0))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
job upload;
|
||||
upload.buffertarget = Target;
|
||||
upload.parts = Parts;
|
||||
if (upload.parts.empty())
|
||||
{
|
||||
upload.parts.emplace_back(0, Size);
|
||||
}
|
||||
upload.staging = std::make_shared<buffer>();
|
||||
if ((false == upload.staging->create(*m_allocator, Size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, memory_kind::upload)) || (upload.staging->mapped() == nullptr))
|
||||
{
|
||||
log_error("no staging buffer of " + memory_size_string(Size) + " for an upload");
|
||||
return false;
|
||||
}
|
||||
std::memcpy(upload.staging->mapped(), Data, static_cast<std::size_t>(Size));
|
||||
upload.staging->flush(0, Size);
|
||||
m_uploaded += Size;
|
||||
if (m_commands != VK_NULL_HANDLE)
|
||||
{
|
||||
record(upload);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pending.emplace_back(std::move(upload));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uploader::upload(std::shared_ptr<image> const &Image, void const *Data, VkDeviceSize Size, std::vector<level> const &Levels, bool const Mipmaps)
|
||||
{
|
||||
job upload;
|
||||
@@ -89,6 +138,47 @@ bool uploader::upload(std::shared_ptr<image> const &Image, void const *Data, VkD
|
||||
|
||||
void uploader::record(job &Job)
|
||||
{
|
||||
if (Job.buffertarget != nullptr)
|
||||
{
|
||||
if (false == m_buffercopies)
|
||||
{
|
||||
// the frames sent before may still read the buffers the copies write to
|
||||
VkMemoryBarrier2 barrier{};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2;
|
||||
barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
|
||||
barrier.srcAccessMask = VK_ACCESS_2_NONE;
|
||||
barrier.dstStageMask = VK_PIPELINE_STAGE_2_COPY_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
|
||||
VkDependencyInfo dependency{};
|
||||
dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
|
||||
dependency.memoryBarrierCount = 1;
|
||||
dependency.pMemoryBarriers = &barrier;
|
||||
vkCmdPipelineBarrier2(m_commands, &dependency);
|
||||
m_buffercopies = true;
|
||||
}
|
||||
std::vector<VkBufferCopy2> regions;
|
||||
VkDeviceSize source{0};
|
||||
for (auto const &part : Job.parts)
|
||||
{
|
||||
VkBufferCopy2 region{};
|
||||
region.sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2;
|
||||
region.srcOffset = source;
|
||||
region.dstOffset = part.first;
|
||||
region.size = part.second;
|
||||
regions.push_back(region);
|
||||
source += part.second;
|
||||
}
|
||||
VkCopyBufferInfo2 copy{};
|
||||
copy.sType = VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2;
|
||||
copy.srcBuffer = Job.staging->handle();
|
||||
copy.dstBuffer = Job.buffertarget->handle();
|
||||
copy.regionCount = static_cast<std::uint32_t>(regions.size());
|
||||
copy.pRegions = regions.data();
|
||||
vkCmdCopyBuffer2(m_commands, ©);
|
||||
m_recorded = true;
|
||||
m_deletions->push(m_value, [staging = Job.staging, target = Job.buffertarget]() {});
|
||||
return;
|
||||
}
|
||||
auto &target{*Job.target};
|
||||
auto const commands{m_commands};
|
||||
auto const aspect{target.aspect()};
|
||||
|
||||
@@ -54,6 +54,10 @@ class uploader
|
||||
// copies Data into Image (the levels given, the others made by blits of the first one when Mipmaps), the image left ready to be
|
||||
// sampled. Data is copied away. false: no staging memory, nothing will be copied
|
||||
bool upload(std::shared_ptr<image> const &Image, void const *Data, VkDeviceSize Size, std::vector<level> const &Levels, bool const Mipmaps);
|
||||
// copies Data into Target, done before the draws of the frame read it (and after the frames sent before did). Data holds the
|
||||
// pieces of Parts (offset in Target, size) one after another; all of Data goes to the start of Target when there are none.
|
||||
// false: no staging memory
|
||||
bool upload_buffer(std::shared_ptr<buffer> const &Target, void const *Data, VkDeviceSize Size, std::vector<std::pair<VkDeviceSize, VkDeviceSize>> const &Parts = {});
|
||||
// the bytes uploaded since the start, for the debug panel
|
||||
std::uint64_t uploaded() const
|
||||
{
|
||||
@@ -69,6 +73,9 @@ class uploader
|
||||
std::shared_ptr<buffer> staging;
|
||||
std::vector<level> levels;
|
||||
bool mipmaps{false};
|
||||
// a copy to a buffer instead: the pieces copied
|
||||
std::shared_ptr<buffer> buffertarget;
|
||||
std::vector<std::pair<VkDeviceSize, VkDeviceSize>> parts;
|
||||
};
|
||||
void record(job &Job);
|
||||
|
||||
@@ -78,6 +85,8 @@ class uploader
|
||||
VkCommandBuffer m_commands{VK_NULL_HANDLE};
|
||||
std::uint64_t m_value{0};
|
||||
bool m_recorded{false};
|
||||
// buffers were copied to in the frame: the barriers before the first copy and after the last one
|
||||
bool m_buffercopies{false};
|
||||
std::vector<job> m_pending;
|
||||
std::uint64_t m_uploaded{0};
|
||||
mutable std::unordered_map<VkFormat, bool> m_blittable;
|
||||
@@ -153,9 +162,10 @@ struct vulkan14_texture : public texture_source
|
||||
|
||||
bool create(bool const Static = false) override;
|
||||
void release() override;
|
||||
// as the opengl textures give it: one not made yet has no id of 0, which the materials take for a texture which is missing
|
||||
size_t get_id() const override
|
||||
{
|
||||
return static_cast<size_t>(m_id);
|
||||
return (m_made ? static_cast<size_t>(m_id) : std::numeric_limits<size_t>::max());
|
||||
}
|
||||
void update_from_memory(size_t width, size_t height, const uint8_t *data) override;
|
||||
bool resident() const override
|
||||
|
||||
Reference in New Issue
Block a user