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

Walk the shadow passes and the culled model groups on worker threads in the Vulkan renderer

This commit is contained in:
2026-10-27 20:56:11 +01:00
parent c44b243698
commit 1447dcf075
55 changed files with 4114 additions and 427 deletions

View File

@@ -37,6 +37,7 @@ http://mozilla.org/MPL/2.0/.
#include "rendering/vulkan14/vulkan14commands.h"
#include "rendering/vulkan14/vulkan14raytracing.h"
#include "rendering/vulkan14/vulkan14dlss.h"
#include "rendering/vulkan14/vulkan14tasks.h"
#include "utilities/workerpool.h"
#include "model/material.h"
#include "gl/ubo.h"
@@ -49,6 +50,7 @@ http://mozilla.org/MPL/2.0/.
#include <mutex>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace vk14
@@ -92,7 +94,36 @@ struct scene_program : public gfx::shader_program
std::shared_ptr<scene_program> multidraw;
bool multidraw_tried{false};
vertex_kind vertices{vertex_kind::scene};
// the pipelines made, by the key of bind_scene_pipeline(), and the ones the background threads are making (M5). guarded by the
// mutex, as are the variants above made when they're first needed: the walks of the passes and the background threads ask for
// them at once
std::mutex mutex;
std::unordered_map<std::uint64_t, VkPipeline> pipelines;
std::unordered_set<std::uint64_t> pending;
};
// a pipeline of the scene as the list of the pipelines made in a session keeps it, to be made ahead at the start of the next one
// (gfx.vulkan.asyncresources, the stage M5): what bind_scene_pipeline() makes it of, by the names of the shaders
struct scene_pipeline_record
{
// the shaders, by the names and the variants shader_library::get() takes
std::string vertex;
std::string vertex_defines;
std::string fragment;
std::string fragment_defines;
vertex_kind vertices{vertex_kind::scene};
VkPrimitiveTopology topology{VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST}; // the class
std::uint32_t blending{0};
std::array<VkFormat, 2> colour{VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED};
std::uint32_t colours{0};
VkFormat depth{VK_FORMAT_UNDEFINED};
std::uint32_t samples{1};
std::uint32_t view_mask{0};
std::uint32_t settings{0};
std::string name;
// one line of the list, and the record of it; false: not a line of the list
std::string line() const;
bool parse(std::string const &Line);
};
// what a pass of the scene draws to; the pipelines are made for these
@@ -188,13 +219,18 @@ struct generation_targets
// 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
// the threads of the walkers (the stage M2): as many as the passes they're given in a frame use, within gfx.threads
std::size_t walk_threads();
// the parts of the rings of the draws and of the instances a renderer (the main one, a walker) takes for its slices (vk14::ring_cache)
constexpr VkDeviceSize draw_ring_part{64 * 1024};
constexpr VkDeviceSize instance_ring_part{128 * 1024};
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
@@ -487,6 +523,11 @@ struct vulkan14_renderer::context
std::vector<worker_commands> workers;
// the command buffers of the chunks of the frame, in their order
std::vector<VkCommandBuffer> chunks;
// the passes of the frame for the compute queue (the async compute of M6, the sky and the weather), recorded into a command
// buffer of the compute family
vk14::command_stream compute_stream;
VkCommandPool compute_pool{VK_NULL_HANDLE};
VkCommandBuffer compute_buffer{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
@@ -521,12 +562,36 @@ struct vulkan14_renderer::context
double submit_time{0.0}; // seconds
// the textures of the files; made after the objects above and gone before them
std::unique_ptr<texture_bank> textures;
// the background threads (the stage M5): the pipelines and the textures made when they're first needed, the pipelines of the last
// session made ahead. stopped before anything they use goes
vk14::background_tasks background;
// the threads of the walkers (the stage M2): the passes of the frame walked at the same time as the main thread walks its own
vk14::background_tasks walks;
// the threads of the helpers of the walks (the stage M3): the groups of a culling walked by several of them, the walk waiting
vk14::background_tasks models;
// the groups they walked since the frame began
std::atomic<std::size_t> helped{0};
// the pass sets of the scene and those of the cullings made anew (the main thread) and those of the cullings written (any walker)
std::mutex passsets_mutex;
// the pipelines of the scene made in the session, written to the list at the end (cache/vulkan14/pipelines.txt), and how many of the
// list of the last session were made ahead and how many weren't made in time for the draw which needed them
std::mutex warm_mutex;
std::vector<vk14::scene_pipeline_record> warm_records;
std::unordered_set<std::string> warm_lines;
std::atomic<std::size_t> warm_made{0};
std::atomic<std::size_t> pipelines_late{0};
// [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};
// the async compute (gfx.vulkan.asynccompute, M6), on a device with a compute family of its own: the passes of the sky and the
// weather go to its queue, which signals the frames done by it on a timeline of its own. its work of a frame waits for the
// graphics queue to be done with the frame before (what it writes is read there), the part of the frame after the first pass
// which reads what it makes waits for it
bool async_compute{false};
VkSemaphore compute_timeline{VK_NULL_HANDLE};
// frames recorded since the start
std::uint64_t framecount{0};
// the windows with an image acquired in the frame being made
@@ -547,6 +612,9 @@ struct vulkan14_renderer::context
{
frame *target{nullptr};
bool uploaded{false};
// the value of the timeline of the frame, and whether part of it goes to the compute queue
std::uint64_t value{0};
bool async{false};
std::vector<VkSemaphoreSubmitInfo> waits;
std::vector<VkSemaphoreSubmitInfo> signals;
// the windows presented once the frame is submitted (the render thread)
@@ -575,8 +643,9 @@ struct vulkan14_renderer::context
}
bool create_frames(std::uint32_t const Count, std::string &Reason);
// the streams of Frame recorded into its command buffers; false: no command buffer for them
bool record(frame &Frame, bool Uploads);
// the streams of Frame recorded into its command buffers (Compute: the one of the compute queue too); false: no command buffer for
// them
bool record(frame &Frame, bool Uploads, bool Compute);
// the job done; false: Failure says where it failed
bool execute(render_job &Job, char const *&Failure);
@@ -604,6 +673,18 @@ struct vulkan14_renderer::context
void wait_submitted();
// the parts of the renderer made once the device is there
bool create_core(std::uint32_t const Frames, std::string &Reason);
// the pipelines of the scene (vulkan14scene.cpp, vulkan14_renderer::bind_scene_pipeline()): the fragment shader of a program for
// targets with or without the velocity (Compile: made when it isn't there yet, otherwise null then); the pipeline made now, found
// made already, or made by the background threads (Key: of the program's map); the ones of the list of the last session made
// ahead, the list of this one written
std::shared_ptr<vk14::shader_code const> scene_fragment(vk14::scene_program &Program, bool Single, bool Compile);
VkPipeline make_scene_pipeline(vk14::scene_program &Program, vk14::target_formats const &Targets, vk14::blend_mode Blend, VkPrimitiveTopology Primitiveclass, std::uint32_t Settings, bool Single);
bool find_scene_pipeline(vk14::scene_program &Program, vk14::target_formats const &Targets, vk14::blend_mode Blend, VkPrimitiveTopology Primitiveclass, std::uint32_t Settings, bool Single,
VkPipeline &Pipeline);
void queue_scene_pipeline(vk14::scene_program &Program, vk14::target_formats const &Targets, vk14::blend_mode Blend, VkPrimitiveTopology Primitiveclass, std::uint32_t Settings, bool Single,
std::uint64_t Key);
void warm_scene_pipelines();
void save_scene_pipelines();
// 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
@@ -647,6 +728,32 @@ struct vulkan14_renderer::draw_state
bool cullface{true};
bool glare{false};
bool depthmask_off{false}; // the editor overlay: no depth written whatever the blending
// the pipelines and the textures a draw needs first may be made by the background threads, the draw left out till then (the walks
// of the scene, gfx.vulkan.asyncresources); the draws outside of them (the cab, the sky, the particles) wait for what they need
bool async{false};
// the pipeline of the draw before, which the next one takes without asking the program when it's the same
vk14::scene_program const *lastprogram{nullptr};
std::uint64_t lastkey{0};
VkPipeline lastpipeline{VK_NULL_HANDLE};
// the windows of the rings of the draws and of the instances the draws read (where the current one starts), and the parts of the
// rings the renderer places its slices in (vk14::ring_cache; each walker has its own, M2)
struct ring_window
{
VkDeviceSize base{0};
bool open{false};
};
ring_window draws_window;
ring_window instances_window;
vk14::ring_cache draws_cache;
vk14::ring_cache instances_cache;
// the map of a lamp or of headlights the pass draws to (begin_local_shadow_rendering())
vk14::local_shadow_maps::draw_target local_target;
// the pass sets of the scene a walker binds: those of the frame when it was given its pass (the main renderer binds them as they are)
std::array<VkDescriptorSet, vk14::scene_state::set_count> pass_sets{};
// what a walker's draws used, marked as used by the main renderer once the walks are done (the marks aren't made by several
// threads): the banks of the geometry and the textures
std::vector<std::uint32_t> used_banks;
std::vector<texture_handle> used_textures;
// 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
@@ -658,6 +765,9 @@ struct vulkan14_renderer::draw_state
bool depthskip{true};
// the probe of the cab (the stage A4): the walk draws with the programs of the probe, what they don't draw is left out alike
bool probepass{false};
// the draws recorded by the walk of the groups of a culling are made (draw_indirect_colour()): they take the model blocks recorded
// with them, the state of their submodels isn't read (the passes walked by other threads set it up for theirs, walk_sync)
bool replaying{false};
// the pass sets of a culling, the instance buffer its output, for the indirect draws of its instances; null: the pass sets
VkDescriptorSet const *cull_sets{nullptr};
// the rendering under way, begun again where it was after it was ended for the compute passes of the culling