mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-29 15:07:29 +01:00
252 lines
12 KiB
C++
252 lines
12 KiB
C++
/*
|
|
This Source Code Form is subject to the
|
|
terms of the Mozilla Public License, v.
|
|
2.0. If a copy of the MPL was not
|
|
distributed with this file, You can
|
|
obtain one at
|
|
http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
// the commands of a frame kept to be recorded into command buffers later (the stage M1 of the plan): the renderer makes the calls
|
|
// of vkCmd* on a stream, which keeps them with their data in the order they were made. the stream is cut into chunks as it's
|
|
// made, every so many draws: a chunk is recorded into a command buffer of its own, by a worker thread, and the command buffers go
|
|
// to the queue in one submit in the order of the chunks. a chunk which starts inside a rendering begins it again resumed
|
|
// (VK_RENDERING_RESUMING_BIT, the one before it ended it suspended) and sets again the state the draws before it left: the
|
|
// pipelines, the descriptor sets, the pushed descriptors and constants, the vertex and index buffers and the dynamic state, as
|
|
// the last calls of each kind made them. where the cuts are doesn't depend on the number of the threads, so the gpu gets the
|
|
// same commands with any of them
|
|
|
|
#pragma once
|
|
|
|
#include "rendering/vulkan14/vulkan14common.h"
|
|
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <vector>
|
|
|
|
namespace vk14
|
|
{
|
|
|
|
class command_stream
|
|
{
|
|
public:
|
|
// the draws, dispatches and copies a chunk holds at least before it's cut (inside a rendering, between two draws)
|
|
static constexpr std::uint32_t chunk_work{256};
|
|
|
|
// the calls, with the arguments vkCmd* takes (the command buffer left out); the data they point to is copied. chains of pNext
|
|
// aren't kept: none of the calls is made with one
|
|
void begin_rendering(VkRenderingInfo const *Info);
|
|
void end_rendering();
|
|
void pipeline_barrier2(VkDependencyInfo const *Dependency);
|
|
void bind_pipeline(VkPipelineBindPoint BindPoint, VkPipeline Pipeline);
|
|
void bind_descriptor_sets2(VkBindDescriptorSetsInfo const *Info);
|
|
// Size: the bytes of the data of the template; BindPoint: the one the template was made for
|
|
void push_descriptor_set_with_template2(VkPushDescriptorSetWithTemplateInfo const *Info, std::size_t Size, VkPipelineBindPoint BindPoint);
|
|
void push_constants2(VkPushConstantsInfo const *Info);
|
|
void bind_vertex_buffers(std::uint32_t First, std::uint32_t Count, VkBuffer const *Buffers, VkDeviceSize const *Offsets);
|
|
void bind_index_buffer(VkBuffer Buffer, VkDeviceSize Offset, VkIndexType Type);
|
|
void draw(std::uint32_t Vertices, std::uint32_t Instances, std::uint32_t FirstVertex, std::uint32_t FirstInstance);
|
|
void draw_indexed(std::uint32_t Indices, std::uint32_t Instances, std::uint32_t FirstIndex, std::int32_t VertexOffset, std::uint32_t FirstInstance);
|
|
void draw_indirect(VkBuffer Buffer, VkDeviceSize Offset, std::uint32_t Count, std::uint32_t Stride);
|
|
void draw_indexed_indirect(VkBuffer Buffer, VkDeviceSize Offset, std::uint32_t Count, std::uint32_t Stride);
|
|
void dispatch(std::uint32_t X, std::uint32_t Y, std::uint32_t Z);
|
|
void dispatch_indirect(VkBuffer Buffer, VkDeviceSize Offset);
|
|
void set_viewport(std::uint32_t First, std::uint32_t Count, VkViewport const *Viewports);
|
|
void set_scissor(std::uint32_t First, std::uint32_t Count, VkRect2D const *Scissors);
|
|
void set_cull_mode(VkCullModeFlags Mode);
|
|
void set_front_face(VkFrontFace Face);
|
|
void set_primitive_topology(VkPrimitiveTopology Topology);
|
|
void set_depth_test_enable(VkBool32 Enable);
|
|
void set_depth_write_enable(VkBool32 Enable);
|
|
void set_depth_compare_op(VkCompareOp Op);
|
|
void set_depth_bias_enable(VkBool32 Enable);
|
|
void set_depth_bias(float Constant, float Clamp, float Slope);
|
|
void set_primitive_restart_enable(VkBool32 Enable);
|
|
void set_stencil_test_enable(VkBool32 Enable);
|
|
void fill_buffer(VkBuffer Buffer, VkDeviceSize Offset, VkDeviceSize Size, std::uint32_t Data);
|
|
void update_buffer(VkBuffer Buffer, VkDeviceSize Offset, VkDeviceSize Size, void const *Data);
|
|
void copy_buffer2(VkCopyBufferInfo2 const *Info);
|
|
void copy_image2(VkCopyImageInfo2 const *Info);
|
|
void copy_buffer_to_image2(VkCopyBufferToImageInfo2 const *Info);
|
|
void copy_image_to_buffer2(VkCopyImageToBufferInfo2 const *Info);
|
|
void blit_image2(VkBlitImageInfo2 const *Info);
|
|
void clear_color_image(VkImage Image, VkImageLayout Layout, VkClearColorValue const *Color, std::uint32_t Count, VkImageSubresourceRange const *Ranges);
|
|
void clear_depth_stencil_image(VkImage Image, VkImageLayout Layout, VkClearDepthStencilValue const *Value, std::uint32_t Count, VkImageSubresourceRange const *Ranges);
|
|
void write_timestamp2(VkPipelineStageFlags2 Stage, VkQueryPool Pool, std::uint32_t Query);
|
|
void reset_query_pool(VkQueryPool Pool, std::uint32_t First, std::uint32_t Count);
|
|
void begin_query(VkQueryPool Pool, std::uint32_t Query, VkQueryControlFlags Flags);
|
|
void end_query(VkQueryPool Pool, std::uint32_t Query);
|
|
void begin_debug_utils_label(VkDebugUtilsLabelEXT const *Label);
|
|
void end_debug_utils_label();
|
|
// a checkpoint of VK_NV_device_diagnostic_checkpoints, with the number of the marker (vk14::pass_list)
|
|
void set_checkpoint(std::uint64_t Marker);
|
|
// the builds of acceleration structures (VK_KHR_acceleration_structure, gfx.raytracing), outside of a rendering
|
|
void build_acceleration_structures(std::uint32_t Count, VkAccelerationStructureBuildGeometryInfoKHR const *Infos, VkAccelerationStructureBuildRangeInfoKHR const *const *Ranges);
|
|
// a call of a library recording its own commands (the evaluation of dlss), outside of a rendering: Function gets the command
|
|
// buffer of the replay and Data, which has to live until the replay is done. what it binds is left as it leaves it, so the
|
|
// calls after it bind what they use again
|
|
using external_function = void (*)(VkCommandBuffer Buffer, void *Data);
|
|
void external(external_function Function, void *Data);
|
|
|
|
// the calls of Other go to the gpu here, where this stream is now (the stage M2: the passes recorded by other threads at the same
|
|
// time): the calls made after this one follow them, with the state they leave (the pipelines, the sets, the constants, the buffers,
|
|
// the dynamic state), as if they were made in this stream. outside of a rendering and of a query; Other is a stream without
|
|
// streams of its own spliced into it, finished before this one is, and kept until this one is replayed
|
|
void splice(command_stream const &Other);
|
|
|
|
// the stream goes to a queue of the compute family (the async compute of M6): the stages and the accesses of its barriers which such
|
|
// a queue doesn't have are left out. the work of the graphics queue they wait for is done before it through the semaphores
|
|
void set_compute_queue(bool Compute)
|
|
{
|
|
m_compute = Compute;
|
|
}
|
|
// the frame goes to the queue in two submits from here on, the calls after this one in the second, which waits for the work of the
|
|
// compute queue (M6). outside of a rendering and of a query; the first one made counts
|
|
void split();
|
|
// the first chunk of the second submit (chunks() when nothing was split), valid after finish()
|
|
std::size_t split_chunk() const
|
|
{
|
|
return m_splitchunk;
|
|
}
|
|
|
|
// the stream is made anew
|
|
void clear();
|
|
bool empty() const;
|
|
// the stream is ended: no calls after it
|
|
void finish();
|
|
// the chunks, valid after finish(); those of the streams spliced into it, in their places
|
|
std::size_t chunks() const
|
|
{
|
|
return m_pieces.empty() ? m_chunks.size() : m_order.size();
|
|
}
|
|
// the calls recorded since the start, with those of the streams spliced into it
|
|
std::size_t calls() const;
|
|
// the chunk recorded into Buffer (begun already, ended by the caller)
|
|
void replay(std::size_t Chunk, VkCommandBuffer Buffer) const;
|
|
// all of it into one command buffer
|
|
void replay(VkCommandBuffer Buffer) const;
|
|
|
|
private:
|
|
// a call of a stream, the stream itself or one spliced into it
|
|
using call_ref = std::pair<command_stream const *, std::size_t>;
|
|
// a part of the stream as the gpu gets it: chunks of the stream itself or of a stream spliced into it, the state the calls before
|
|
// it left (the calls they made last of each kind, in the order they were made) and the state the part leaves with them
|
|
struct piece
|
|
{
|
|
command_stream const *stream{nullptr};
|
|
std::size_t first{0}; // chunks of the stream
|
|
std::size_t end{0};
|
|
std::vector<call_ref> inherited;
|
|
// the calls of the part setting the state last, kept when the part is of the stream itself (splice())
|
|
std::vector<std::size_t> state;
|
|
std::vector<std::size_t> pushes;
|
|
};
|
|
// the calls setting the state, of all the parts so far: a slot each (state_slot), and the pushes of the constants of each range
|
|
// and stages; the part of each, for their order
|
|
struct state_ref
|
|
{
|
|
command_stream const *stream{nullptr};
|
|
std::size_t call{0};
|
|
std::size_t piece{0};
|
|
};
|
|
struct merged_state
|
|
{
|
|
std::vector<state_ref> slots;
|
|
std::vector<state_ref> pushes;
|
|
};
|
|
// the state the calls of the part leave behind, over the state the parts before it left
|
|
void leave_state(std::size_t Piece, merged_state &State) const;
|
|
// the calls of the state a chunk sets again; the dynamic states have a slot each
|
|
enum state_slot : std::uint32_t
|
|
{
|
|
pipeline_graphics,
|
|
pipeline_compute,
|
|
viewport,
|
|
scissor,
|
|
cull_mode,
|
|
front_face,
|
|
topology,
|
|
depth_test,
|
|
depth_write,
|
|
depth_compare,
|
|
depth_bias_enable,
|
|
depth_bias,
|
|
primitive_restart,
|
|
stencil_test,
|
|
index_buffer,
|
|
sets_graphics, // set_slots of each
|
|
sets_compute = sets_graphics + 8,
|
|
pushed_graphics = sets_compute + 8,
|
|
pushed_compute = pushed_graphics + 8,
|
|
vertex_buffers = pushed_compute + 8, // vertex_slots
|
|
slot_count = vertex_buffers + 8
|
|
};
|
|
static constexpr std::size_t no_call{~static_cast<std::size_t>(0)};
|
|
static constexpr std::uint32_t set_slots{8};
|
|
static constexpr std::uint32_t vertex_slots{8};
|
|
struct chunk
|
|
{
|
|
std::size_t first{0}; // offset of its first call in the data
|
|
std::size_t end{0}; // past its last one
|
|
// the begin of the rendering the chunk starts inside of, resumed, and the calls of the state set again, in their order
|
|
std::size_t resumed{no_call};
|
|
std::vector<std::size_t> state;
|
|
// the begin of the rendering the chunk ends inside of, suspended
|
|
std::size_t suspended{no_call};
|
|
};
|
|
|
|
// room for a call of Type with Size bytes of its own data after its header, aligned; the offset of the call
|
|
std::size_t add(std::uint32_t Type, std::size_t Size);
|
|
template <typename Type_> Type_ &payload(std::size_t Call)
|
|
{
|
|
return *reinterpret_cast<Type_ *>(m_data.data() + Call + header_size);
|
|
}
|
|
template <typename Type_> Type_ const &payload(std::size_t Call) const
|
|
{
|
|
return *reinterpret_cast<Type_ const *>(m_data.data() + Call + header_size);
|
|
}
|
|
template <typename Type_> Type_ const *at(std::size_t Offset) const
|
|
{
|
|
return reinterpret_cast<Type_ const *>(m_data.data() + Offset);
|
|
}
|
|
// copies Bytes of Data after the call made last (aligned), returns their offset in the data
|
|
std::size_t append(void const *Data, std::size_t Bytes);
|
|
// the call made last ends here
|
|
void close(std::size_t Call);
|
|
void remember(std::uint32_t Slot, std::size_t Call)
|
|
{
|
|
m_state[Slot] = Call;
|
|
}
|
|
// a draw, dispatch or copy was added: the chunk is cut after it when it has enough of them
|
|
void work();
|
|
void cut();
|
|
// Flags: added to those of a begin of a rendering
|
|
void execute(std::size_t Call, VkCommandBuffer Buffer, VkRenderingFlags Flags = 0) const;
|
|
// a chunk of this stream alone, and the calls between two offsets
|
|
void replay_chunk(std::size_t Chunk, VkCommandBuffer Buffer) const;
|
|
void replay_calls(std::size_t First, std::size_t End, VkCommandBuffer Buffer) const;
|
|
|
|
static constexpr std::size_t header_size{8};
|
|
std::vector<std::uint8_t> m_data;
|
|
std::vector<chunk> m_chunks;
|
|
std::array<std::size_t, slot_count> m_state{};
|
|
// the pushes of the constants: the last one of each range and stages, in their order
|
|
std::vector<std::size_t> m_pushes;
|
|
std::size_t m_rendering{no_call}; // the begin of the rendering under way
|
|
std::uint32_t m_queries{0}; // queries active
|
|
std::uint32_t m_work{0}; // of the chunk under way
|
|
std::size_t m_calls{0};
|
|
bool m_finished{false};
|
|
// the parts of the stream with the streams spliced into it, empty without them; the first chunk of the part of the stream itself
|
|
// under way; the chunks in the order of the parts (the part, the chunk of its stream)
|
|
std::vector<piece> m_pieces;
|
|
std::size_t m_piecefirst{0};
|
|
std::vector<std::pair<std::size_t, std::size_t>> m_order;
|
|
bool m_compute{false};
|
|
// the chunk of the stream itself the split begins, and the first chunk of the second submit
|
|
std::size_t m_split{no_call};
|
|
std::size_t m_splitchunk{0};
|
|
};
|
|
|
|
} // namespace vk14
|