mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-26 15:54:53 +01:00
255 lines
7.4 KiB
C++
255 lines
7.4 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 memory of the vulkan renderer: the allocator (VMA), buffers and images which give their memory back when they go, the
|
|
// ring each frame in flight streams its data through, the uploads of data to the gpu through staging buffers, and the queue
|
|
// of objects deleted once the gpu is done with the frames which used them (by the value of the timeline semaphore)
|
|
|
|
#pragma once
|
|
|
|
#include "rendering/vulkan14/vulkan14common.h"
|
|
|
|
#include <deque>
|
|
#include <functional>
|
|
#include <vk_mem_alloc.h>
|
|
|
|
namespace vk14
|
|
{
|
|
|
|
class instance;
|
|
class device;
|
|
|
|
// the allocator of the device memory
|
|
class allocator
|
|
{
|
|
public:
|
|
allocator() = default;
|
|
allocator(allocator const &) = delete;
|
|
allocator &operator=(allocator const &) = delete;
|
|
~allocator();
|
|
|
|
bool create(instance const &Instance, device const &Device, std::string &Reason);
|
|
void destroy();
|
|
|
|
VmaAllocator handle() const
|
|
{
|
|
return m_allocator;
|
|
}
|
|
VkDevice device_handle() const
|
|
{
|
|
return m_device;
|
|
}
|
|
// device memory used and the budget of the heaps which are local to the device, bytes
|
|
void budget(VkDeviceSize &Usage, VkDeviceSize &Budget) const;
|
|
|
|
private:
|
|
VmaAllocator m_allocator{VK_NULL_HANDLE};
|
|
VkDevice m_device{VK_NULL_HANDLE};
|
|
std::uint32_t m_heapcount{0};
|
|
std::vector<bool> m_deviceheaps;
|
|
};
|
|
|
|
// where the memory of a buffer is
|
|
enum class memory_kind
|
|
{
|
|
device, // device local, filled by copies
|
|
upload, // seen by the cpu, written in order (staging, streamed data); device local where the device has such memory
|
|
readback // seen by the cpu, cached, for the data read back from the gpu
|
|
};
|
|
|
|
class buffer
|
|
{
|
|
public:
|
|
buffer() = default;
|
|
buffer(buffer const &) = delete;
|
|
buffer &operator=(buffer const &) = delete;
|
|
buffer(buffer &&Other) noexcept;
|
|
buffer &operator=(buffer &&Other) noexcept;
|
|
~buffer();
|
|
|
|
bool create(allocator const &Allocator, VkDeviceSize Size, VkBufferUsageFlags Usage, memory_kind Kind, std::string const &Name = {});
|
|
void destroy();
|
|
|
|
VkBuffer handle() const
|
|
{
|
|
return m_buffer;
|
|
}
|
|
VkDeviceSize size() const
|
|
{
|
|
return m_size;
|
|
}
|
|
// the memory as the cpu sees it, null for memory the cpu doesn't see. the upload and readback memory stays mapped
|
|
std::uint8_t *mapped() const
|
|
{
|
|
return m_mapped;
|
|
}
|
|
// 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;
|
|
}
|
|
|
|
private:
|
|
VmaAllocator m_allocator{VK_NULL_HANDLE};
|
|
VkBuffer m_buffer{VK_NULL_HANDLE};
|
|
VmaAllocation m_allocation{VK_NULL_HANDLE};
|
|
VkDeviceSize m_size{0};
|
|
std::uint8_t *m_mapped{nullptr};
|
|
};
|
|
|
|
// the state of an image the barriers start from: its layout and the last stage and access which used it
|
|
struct image_state
|
|
{
|
|
VkImageLayout layout{VK_IMAGE_LAYOUT_UNDEFINED};
|
|
VkPipelineStageFlags2 stage{VK_PIPELINE_STAGE_2_NONE};
|
|
VkAccessFlags2 access{VK_ACCESS_2_NONE};
|
|
};
|
|
|
|
struct image_desc
|
|
{
|
|
VkImageType type{VK_IMAGE_TYPE_2D};
|
|
VkImageViewType view_type{VK_IMAGE_VIEW_TYPE_2D};
|
|
VkFormat format{VK_FORMAT_R8G8B8A8_UNORM};
|
|
VkExtent3D extent{1, 1, 1};
|
|
std::uint32_t mips{1};
|
|
std::uint32_t layers{1};
|
|
VkSampleCountFlagBits samples{VK_SAMPLE_COUNT_1_BIT};
|
|
VkImageUsageFlags usage{VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT};
|
|
VkImageCreateFlags flags{0};
|
|
// the channels the default view reads, e.g. a single channel texture seen as rrr1
|
|
VkComponentMapping components{VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
|
|
};
|
|
|
|
class image
|
|
{
|
|
public:
|
|
image() = default;
|
|
image(image const &) = delete;
|
|
image &operator=(image const &) = delete;
|
|
~image();
|
|
|
|
// a device local image with a view of all its levels and layers
|
|
bool create(allocator const &Allocator, image_desc const &Desc, std::string const &Name = {});
|
|
void destroy();
|
|
|
|
VkImage handle() const
|
|
{
|
|
return m_image;
|
|
}
|
|
VkImageView view() const
|
|
{
|
|
return m_view;
|
|
}
|
|
image_desc const &desc() const
|
|
{
|
|
return m_desc;
|
|
}
|
|
VkImageAspectFlags aspect() const;
|
|
VkDeviceSize bytes() const
|
|
{
|
|
return m_bytes;
|
|
}
|
|
// the state the next barrier starts from (the pass list and the uploads keep it)
|
|
image_state state{};
|
|
explicit operator bool() const
|
|
{
|
|
return m_image != VK_NULL_HANDLE;
|
|
}
|
|
|
|
private:
|
|
VmaAllocator m_allocator{VK_NULL_HANDLE};
|
|
VkDevice m_device{VK_NULL_HANDLE};
|
|
VkImage m_image{VK_NULL_HANDLE};
|
|
VkImageView m_view{VK_NULL_HANDLE};
|
|
VmaAllocation m_allocation{VK_NULL_HANDLE};
|
|
VkDeviceSize m_bytes{0};
|
|
image_desc m_desc;
|
|
};
|
|
|
|
// a barrier of an image from its state to the given one, the state of the image updated. nothing when the image already is
|
|
// in the layout and was only read by the same stage
|
|
void transition(command_stream *Commands, image &Image, VkImageLayout Layout, VkPipelineStageFlags2 Stage, VkAccessFlags2 Access);
|
|
|
|
// objects destroyed once the gpu is done with the frame which used them last
|
|
class deletion_queue
|
|
{
|
|
public:
|
|
// Value: the value of the timeline semaphore the gpu reaches when it's done with the object
|
|
void push(std::uint64_t Value, std::function<void()> &&Deleter);
|
|
// runs the deleters of the values the gpu reached
|
|
void collect(std::uint64_t Completed);
|
|
// runs all of them (the device is idle)
|
|
void flush();
|
|
std::size_t size() const
|
|
{
|
|
return m_entries.size();
|
|
}
|
|
|
|
private:
|
|
std::deque<std::pair<std::uint64_t, std::function<void()>>> m_entries;
|
|
};
|
|
|
|
// a part of the ring of a frame: where the data goes on the cpu, and where the gpu reads it
|
|
struct ring_slice
|
|
{
|
|
std::uint8_t *data{nullptr};
|
|
VkBuffer buffer{VK_NULL_HANDLE};
|
|
VkDeviceSize offset{0};
|
|
VkDeviceSize size{0};
|
|
explicit operator bool() const
|
|
{
|
|
return data != nullptr;
|
|
}
|
|
};
|
|
|
|
// the data streamed by the frames: one mapped buffer split into a part for each frame in flight, the part of a frame taken
|
|
// from the start again once the gpu is done with the frame which used it before (the counterpart of gl::frame_stream)
|
|
class frame_ring
|
|
{
|
|
public:
|
|
// 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);
|
|
// Size bytes aligned to Alignment; empty when the part of the frame is full
|
|
ring_slice allocate(VkDeviceSize Size, VkDeviceSize Alignment);
|
|
// makes the writes of the frame seen by the gpu, where the memory isn't coherent
|
|
void flush() const;
|
|
VkDeviceSize used() const
|
|
{
|
|
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;
|
|
VkDeviceSize m_sizeperframe{0};
|
|
VkDeviceSize m_base{0};
|
|
VkDeviceSize m_used{0};
|
|
};
|
|
|
|
} // namespace vk14
|