/* 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 "rendering/vulkan14/vulkan14compute.h" #include "rendering/vulkan14/vulkan14cull.h" #include "rendering/vulkan14/vulkan14shadows.h" #include "rendering/vulkan14/vulkan14cabprobe.h" #include "rendering/vulkan14/vulkan14skyocclusion.h" #include "rendering/vulkan14/vulkan14terrain.h" #include "rendering/vulkan14/vulkan14grass.h" #include "rendering/vulkan14/vulkan14particles.h" #include "rendering/vulkan14/vulkan14water.h" #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" #include #include #include #include #include #include #include #include #include #include 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 star, // direction with the magnitude and colour with the phase of the twinkling in one stream, the stars particle, // position, colour and texture coordinates in one stream, the smoke position_uv, // position and texture coordinates in two streams, the precipitation particle_instance // three vec4 of gl::particle_draw for each instance, the particles of the gpu }; // 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 vertex; std::shared_ptr 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 fragment_single; // the vertex shader, and the definitions both stages were made with std::string vertex_name; std::string defines; // the variant of the multi draws of the instances drawn from the gpu culling (MODEL_DRAWS), made when it's first needed std::shared_ptr 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 pipelines; std::unordered_set 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 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 struct target_formats { std::array 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 views drawn at once (multiview), 0: none std::uint32_t view_mask{0}; // 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 or the temporal anti-aliasing bool taa{false}; bool aberration{false}; // with more than one sample: the images drawn to, resolved to the ones below std::shared_ptr msaa_colour; std::shared_ptr msaa_motion; std::shared_ptr 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 colour; std::shared_ptr motion_image; std::shared_ptr depth; // the size of the images after the upscaling of dlss (the window), the same as extent without it; dlss is on for the viewport VkExtent2D output_extent{0, 0}; bool dlss{false}; // dlss (gfx.antialiasing dlss): the velocities it takes (those of the sky made from the motion of the camera), the exposure of the // tonemapping in a texel, and its output, the size of the window (hdr, a storage image) std::shared_ptr dlss_motion; std::shared_ptr dlss_exposure; std::shared_ptr dlss_output; // the colour after the motion blur (hdr) std::shared_ptr blurred; // the temporal anti-aliasing (gfx.antialiasing taa, the main viewport): the two images of the history, written in turn (hdr, the // lock of the thin things in alpha) std::array, 2> taa_history; // the screen space reflections (gfx.ssr, the main viewport): the finished image of the last frame with its mipmaps, which they take // their colour from, and whether it was cleared once it was made (it's read before the first frame is copied to it) std::shared_ptr ssr_history; bool ssr_cleared{false}; bool reflections{false}; // the history was asked for (it can be missing when it couldn't be made) // the tonemapped image, before the chromatic aberration, and the one which goes to the window std::shared_ptr tonemapped; std::shared_ptr 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 cursor_depth; // the depth of the opaque scene drawn before its colour, which the contact shadows read (the stage P3), and the formats of the // pass drawing it (the depth alone) std::shared_ptr scene_depth; target_formats depth_formats; // the ambient occlusion (gfx.postfx.ao, the stage P3), half the size: the raw one and the smoothed one; made when it's on std::shared_ptr ssao; std::shared_ptr ssao_blur; // the formats of the passes drawn over the colour of the scene after it (the ambient occlusion): the colour alone target_formats post_formats; // the bloom chain (gfx.postfx.bloom, the stage P1): the levels down, half the size each, and up, one fewer std::vector> bloom_down; std::vector> bloom_up; std::vector bloom_sizes; target_formats formats; target_formats output_formats; bool srgb{true}; // the output encodes the colour (gfx.shadergamma no) }; // the images of the frame generation of dlss (gfx.framegeneration) for the main window, made for its size and its format: the frame // with the user interface (the backbuffer) and without it (the hudless), the frames generated, the size of the window; the depth // and the velocities turned over (postfx_fg_flip.frag), the size of the scene. all of them storage images (ngx takes them so) struct generation_targets { VkExtent2D extent{0, 0}; VkExtent2D render{0, 0}; VkFormat window_format{VK_FORMAT_UNDEFINED}; // unorm (a storage image can't be srgb), the bits of the format of the window where it can be, which go to it by copies; another // one goes by blits VkFormat format{VK_FORMAT_UNDEFINED}; bool blit{false}; std::shared_ptr backbuffer; std::shared_ptr hudless; std::shared_ptr depth; std::shared_ptr motion; std::array, 3> outputs; // the format of the window can't take the frames (said in the log once) VkFormat unusable{VK_FORMAT_UNDEFINED}; }; // 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}; }; // 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}; // the counters of the cullings read back, for the debug panel (gl::cluster_grid::cull_stats): entries of the index list in // use, lights dropped from full clusters, the most of them seen readback_ring counters_readback; struct { std::uint32_t index_count{0}; std::uint32_t overflow_count{0}; std::uint32_t overflow_peak{0}; bool valid{false}; } stats; }; // 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 data; std::uint64_t value{0}; // of the timeline bool pending{false}; glm::ivec2 position{0}; }; // the light of the sky on the gpu (the stage P1, opengl46_sky): the buffers the shading reads (the directional ambient light, // the parameters of the atmosphere), the look-up tables of the atmosphere with the kernels making them and the result read // back, the environment map prefiltered with the GGX lobe, the stars struct sky_state { buffer sky; // gl::sky_data buffer atmosphere; // gl::atmosphere_ubs buffer result; // gl::atmosphere_result, written by atmosphere_ambient.comp readback_ring result_readback; std::shared_ptr transmittance; std::shared_ptr multiscattering; std::shared_ptr skyview; bool tables_ready{false}; // the tables were made once (they are black before) compute_kernel transmittance_kernel; compute_kernel multiscattering_kernel; compute_kernel skyview_kernel; compute_kernel ambient_kernel; // the prefiltered environment map, a cube view of each of its levels for the prefilter to write std::shared_ptr ggx; std::vector ggx_levels; bool ggx_ready{false}; compute_kernel prefilter_kernel; // what the buffers were last given gl::sky_data uploaded{}; bool sky_uploaded{false}; // the dome drawn with the atmosphere, the stars of the catalogue std::shared_ptr atmosphere_dome; std::shared_ptr stars; std::shared_ptr star_vertices; std::uint32_t star_count{0}; }; // the automatic exposure (the stage P1, opengl46_exposure): the histogram and the exposure on the gpu (gl::exposure_data), the // kernels, the values read back for the debug window struct exposure_state { buffer data; compute_kernel histogram; compute_kernel average; readback_ring readback; float values[4]{1.f, 1.f, 1.f, 0.f}; bool cleared{false}; }; // the weather on the gpu (the stage P2, opengl46_weather): the noises of the clouds, the map of their shadows, the clouds of the // main view and their history, the panorama of the clouds for the other views, the fog volume of the main view and its // history, with the kernels making them, and the program laying the clouds and the fog over the sky struct weather_resources { std::shared_ptr shape_noise; std::shared_ptr detail_noise; std::shared_ptr coverage_noise; bool noise_made{false}; std::shared_ptr shadow_map; int shadow_size{0}; // the clouds of the main view, at a part of its resolution glm::ivec2 view_size{0}; std::shared_ptr clouds_color; std::shared_ptr clouds_depth; std::array, 2> clouds_history; int clouds_current{0}; // the history holding the latest result std::shared_ptr panorama; glm::ivec2 panorama_size{0}; // what the panorama is marched with as its depth, which it doesn't write std::shared_ptr depth_dummy; // the fog volume: the scattering of the cells and its history, the fog integrated along the view glm::ivec3 fog_grid{0}; std::array, 2> fog_scattering; int fog_current{0}; std::shared_ptr fog_integrated; compute_kernel noise; compute_kernel march; compute_kernel temporal; compute_kernel shadow; compute_kernel horizon; compute_kernel inject; compute_kernel integrate; std::shared_ptr sky; }; // the gpu culling of the instances (the stage P5, Global.gfx_gpucull): the cullings of the passes, which share the records and // the kernels with the one of the main view, and the depth pyramid of its occlusion test struct cull_resources { instance_cull main; std::array cascades; // the maps of headlights, by the slot of the map, and the scenery parts of the maps of lamps, by their order among those drawn // in a frame: the passes of a frame don't share the buffers of a culling. made when first needed std::vector> headlights; std::vector> lamps; hiz_pyramid hiz; bool ready{false}; // the cascades of the sun shadows fitted to the depth of the main view (gfx.shadow.sdsm), made when they're first needed shadow_fitter shadowfit; bool shadowfit_tried{false}; }; // 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 pass_sets{}; bool pass_sets_stale{true}; // the pass sets were made anew this many times (the ones of the cullings follow them) std::uint64_t pass_generation{0}; // 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 white; // a texture of a material which isn't there std::shared_ptr shadow_empty; std::shared_ptr shadow_map; std::vector shadow_layers; // a view of each cascade std::uint32_t shadow_size{0}; std::shared_ptr env_empty; std::shared_ptr env_map; std::shared_ptr env_depth; std::array env_faces{}; std::shared_ptr pick_colour; std::shared_ptr pick_depth; // the stand-ins of the images of the stages P the pass sets hold until there are real ones: a volume (the fog volume, the noises // of the clouds), the cube shadow maps of the lamps, the shadow maps of the headlights, the depth of the scene std::shared_ptr volume_empty; std::shared_ptr cube_shadow_empty; std::shared_ptr headlight_shadow_empty; std::shared_ptr depth_empty; // the colours of the fog towards the horizon (the stage P2), a block of the pass sets buffer weather_horizon; VkImageView headlights_view{VK_NULL_HANDLE}; // the one the pass sets were made with VkSampler headlights_sampler{VK_NULL_HANDLE}; // the views of the images of the bindings of each pass set it was made with (vulkan14scene.cpp, pass_set_images()) std::array, 3> pass_images{}; cluster_state clusters; // the ambient occlusion: the noise of the older ssao, the kernels of the ground truth one, the program applying it std::shared_ptr ssao_noise; compute_kernel gtao_main; compute_kernel gtao_denoise; std::shared_ptr ao_apply; sky_state sky; exposure_state exposure; weather_resources weather; cull_resources cull; // the shadow maps of the lamps and the headlights (the stage P4), and the programs drawing the cube maps local_shadow_maps lamps; std::shared_ptr cube_shadow, cube_alpha_shadow; target_formats cube_formats; // the probe of the occupied cab (the stage A4): its maps, the programs and the targets drawing it, the kernel of the spherical // harmonics of the light it reflects and their storage (CAB_PROBE_SSBO), and the stand-in of its maps cab_probe_maps cabprobe; std::shared_ptr cabprobe_program, cabprobe_alpha_program; target_formats cabprobe_formats; compute_kernel cabprobe_sh; buffer cabprobe_bounce; std::shared_ptr cabprobe_empty; // the occlusion of the sky by the scenery around the camera (the track T): its maps, and their stand-ins while there are none sky_occlusion_maps skyocclusion; std::shared_ptr skyocclusion_heights_empty, skyocclusion_empty; // the materials of the heightmap terrain (the stage P6), made with the bank of the textures std::unique_ptr terrain; // the grass of the heightmap terrain, and its program, made when it's first drawn grass_field grass; std::shared_ptr grass_program; bool grass_tried{false}; // the smoke and the rain or snow of the gpu gpu_particles particles; // the water on the windscreens of the cab of the train; its storage is held by the pass sets (WATER_SSBO) windscreen_water water; // the ray traced reflections (gfx.raytracing), on a device with ray queries; its set is set RT_SET of the pipelines of the scene std::unique_ptr raytracing; // the heatmap of the light clusters (gfx.lights.clusterheatmap), made when it's first drawn std::shared_ptr cluster_heatmap; bool cluster_heatmap_tried{false}; // the targets of the viewports, [0] the main window std::vector targets; // the frame generation of dlss for the main window generation_targets generation; // the format of the levels of the bloom chains: no alpha, no negative values, half the memory of rgba16f, where the device // draws to it VkFormat bloom_format{VK_FORMAT_B10G11R11_UFLOAT_PACK32}; // the same for the history of the screen space reflections, where the device blits and filters it VkFormat history_format{VK_FORMAT_B10G11R11_UFLOAT_PACK32}; // the formats of the passes which don't depend on the viewport target_formats shadow_formats; target_formats reflection_formats; 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> materials; std::shared_ptr shadow, alpha_shadow, pick, pick_surface, line, billboard, freespot, celestial, skydome, smoke, precipitation; // the depth prepass of the scene (the stage P3): without and with the alpha test, and which of them a program of a material takes std::shared_ptr prepass, prepass_alpha; std::unordered_map prepass_classes; // the post-processing, by the fragment shader std::unordered_map> post_fragments; std::shared_ptr 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 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 occlusion_pools; std::vector> occlusion_submodels; std::unordered_map 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}; // the calls made while the frame is made, recorded into the command buffers once it's done (M1): the uploads into theirs, // the commands chunk by chunk by the workers vk14::command_stream upload_stream; vk14::command_stream commands; // a command pool for each worker, with the command buffers it made struct worker_commands { VkCommandPool pool{VK_NULL_HANDLE}; std::vector buffers; std::size_t used{0}; }; std::vector workers; // the command buffers of the chunks of the frame, in their order std::vector 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 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; // dlss of the main viewport (gfx.antialiasing dlss), with the bias of the mipmaps of the textures of the scene it wants vk14::dlss dlss; float lod_bias{0.f}; // reflex (gfx.reflex, VK_NV_low_latency2): the timeline the sleep of the driver signals, its last value VkSemaphore reflex{VK_NULL_HANDLE}; std::uint64_t reflexvalue{0}; // the presents of the main window with the frames generated by dlss (the render thread): when the last frame was handed over, // the time between two (a moving average) which the frames of one are spread over, how long the last one took to be submitted std::chrono::steady_clock::time_point present_last{}; double present_interval{0.0}; // seconds, 0: not known yet double submit_time{0.0}; // seconds // the textures of the files; made after the objects above and gone before them std::unique_ptr 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 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 warm_records; std::unordered_set warm_lines; std::atomic warm_made{0}; std::atomic pipelines_late{0}; // [0]: the main window, then the windows of the extra viewports std::vector> windows; std::vector 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 std::vector acquired; bool devicelost{false}; // the frame being recorded: its index among the frames in flight, its commands std::uint32_t slot{0}; vk14::command_stream *commands{nullptr}; // the threads recording the command buffers of the frames: a pool of its own, the one of the application is for the simulation worker_pool workers; // the cpu time the command buffers of the last frame took to record, in milliseconds; its calls and chunks std::atomic record_time{0.0}; std::atomic record_calls{0}; std::atomic record_chunks{0}; std::atomic record_threads{0}; // what's left of a frame made by the main thread: recording its command buffers, submitting them, presenting its windows struct render_job { 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 waits; std::vector signals; // the windows presented once the frame is submitted (the render thread) std::vector windows; // the id of the frame for its present and for the markers of reflex std::uint64_t present_id{0}; // when the main thread handed it over to the render thread std::chrono::steady_clock::time_point handed{}; }; // the render thread and the job it's given std::thread render_thread; std::mutex render_mutex; std::condition_variable render_wake; std::condition_variable render_idle; render_job render_pending; bool render_queued{false}; // render_pending holds a job not done yet bool render_submitted{false}; // its frame is submitted, its windows may still be presented bool render_stop{false}; char const *render_failure{nullptr}; // how long the main thread waited for the render thread at the start of the last frame, in milliseconds double render_wait{0.0}; ~context() { destroy(); } bool create_frames(std::uint32_t const Count, std::string &Reason); // 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); // the part of it before the presents: its command buffers recorded and submitted bool submit(render_job &Job, char const *&Failure); // the windows of the frame submitted last presented, the frames generated for the main window before it (spread over the time // of a frame when there's the render thread to wait); Id: the id of the present of the main window (0: none); false: the device or // a surface is lost bool present(std::vector const &Windows, std::uint64_t Id); // the render thread (gfx.vulkan.renderthread, M4): the frames are recorded, submitted and presented by it while the main // thread goes on with the simulation of the next one. the main thread waits for it to be done before it begins a frame and // before anything else touches the queue, the swapchains or the device as a whole void start_render_thread(); void stop_render_thread(); bool render_threaded() const { return render_thread.joinable(); } // the job given to the render thread void hand_over(render_job &&Job); // waits until the render thread is done with the jobs given to it; where it failed, if it did char const *drain(); // waits until the render thread submitted the frame given to it (its presents may go on): the frames in flight and the streams // are the main thread's again, the queue and the swapchains aren't until drain() 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 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; // the value completed() read last: once the device is lost the timeline can't be read mutable std::atomic lastcompleted{0}; // 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 { vk14::command_stream *commands{nullptr}; 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}; std::uint32_t weather_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 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 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 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 used_banks; std::vector used_textures; // the occlusion queries of the glare of the lamps in the frame: the submodels, by the index of their query std::vector 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}; // the depth of the scene before its colour (the stage P3): the walk draws with the programs of the depth prepass, and what they // don't draw is left out (depthskip, set by walk_material()) bool depthpass{false}; bool depthskip{true}; // the probe of the cab (the stage A4): the walk draws with the programs of the probe, what they don't draw is left out alike bool probepass{false}; // the 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 struct rendering_resume { std::array colours{}; VkRenderingAttachmentInfo depth{}; bool has_depth{false}; VkRenderingInfo info{}; std::vector uses; bool valid{false}; } resume; };