Files
WickedEngine/WickedEngine/wiGraphicsDevice_Vulkan.h
T
Turánszki János 53e79ff3ed some changes to try
2026-01-28 08:43:33 +01:00

874 lines
36 KiB
C++

#pragma once
#include "CommonInclude.h"
#include "wiPlatform.h"
#if defined(PLATFORM_WINDOWS_DESKTOP) || defined(PLATFORM_LINUX)
#define WICKEDENGINE_BUILD_VULKAN
#endif // PLATFORM_WINDOWS_DESKTOP || PLATFORM_LINUX
#ifdef WICKEDENGINE_BUILD_VULKAN
#include "wiGraphicsDevice.h"
#include "wiUnorderedMap.h"
#include "wiVector.h"
#include "wiSpinLock.h"
#include "wiBacklog.h"
#include "wiHelper.h"
#ifdef _WIN32
#define VK_USE_PLATFORM_WIN32_KHR
#endif // _WIN32
#define VK_NO_PROTOTYPES
#include "Utility/vulkan/vulkan.h"
#include "Utility/volk.h"
#include "Utility/vk_mem_alloc.h"
#include "Utility/vk_enum_string_helper.h"
#include <deque>
#include <atomic>
#include <mutex>
#include <algorithm>
#define vulkan_assert(cond, fname) { wilog_assert(cond, "Vulkan error: %s failed with %s (%s:%d)", fname, string_VkResult(res), relative_path(__FILE__), __LINE__); }
#define vulkan_check(call) [&]() { VkResult res = call; vulkan_assert((res >= VK_SUCCESS), extract_function_name(#call).c_str()); return res; }()
namespace wi::graphics
{
class GraphicsDevice_Vulkan final : public GraphicsDevice
{
friend struct CommandQueue;
protected:
VkInstance instance = VK_NULL_HANDLE;
VkDebugUtilsMessengerEXT debugUtilsMessenger = VK_NULL_HANDLE;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE;
wi::vector<VkQueueFamilyProperties2> queueFamilies;
wi::vector<VkQueueFamilyVideoPropertiesKHR> queueFamiliesVideo;
uint32_t graphicsFamily = VK_QUEUE_FAMILY_IGNORED;
uint32_t computeFamily = VK_QUEUE_FAMILY_IGNORED;
uint32_t copyFamily = VK_QUEUE_FAMILY_IGNORED;
uint32_t videoFamily = VK_QUEUE_FAMILY_IGNORED;
uint32_t initFamily = VK_QUEUE_FAMILY_IGNORED;
uint32_t sparseFamily = VK_QUEUE_FAMILY_IGNORED;
wi::vector<uint32_t> families;
VkQueue graphicsQueue = VK_NULL_HANDLE;
VkQueue computeQueue = VK_NULL_HANDLE;
VkQueue copyQueue = VK_NULL_HANDLE;
VkQueue videoQueue = VK_NULL_HANDLE;
VkQueue initQueue = VK_NULL_HANDLE;
VkQueue sparseQueue = VK_NULL_HANDLE;
bool debugUtils = false;
VkPhysicalDeviceProperties2 properties2 = {};
VkPhysicalDeviceVulkan11Properties properties_1_1 = {};
VkPhysicalDeviceVulkan12Properties properties_1_2 = {};
VkPhysicalDeviceVulkan13Properties properties_1_3 = {};
VkPhysicalDeviceSamplerFilterMinmaxProperties sampler_minmax_properties = {};
VkPhysicalDeviceAccelerationStructurePropertiesKHR acceleration_structure_properties = {};
VkPhysicalDeviceRayTracingPipelinePropertiesKHR raytracing_properties = {};
VkPhysicalDeviceFragmentShadingRatePropertiesKHR fragment_shading_rate_properties = {};
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = {};
VkPhysicalDeviceMemoryProperties2 memory_properties_2 = {};
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve_properties = {};
VkPhysicalDeviceConservativeRasterizationPropertiesEXT conservative_raster_properties = {};
VkPhysicalDeviceFeatures2 features2 = {};
VkPhysicalDeviceVulkan11Features features_1_1 = {};
VkPhysicalDeviceVulkan12Features features_1_2 = {};
VkPhysicalDeviceVulkan13Features features_1_3 = {};
VkPhysicalDeviceAccelerationStructureFeaturesKHR acceleration_structure_features = {};
VkPhysicalDeviceRayTracingPipelineFeaturesKHR raytracing_features = {};
VkPhysicalDeviceRayQueryFeaturesKHR raytracing_query_features = {};
VkPhysicalDeviceFragmentShadingRateFeaturesKHR fragment_shading_rate_features = {};
VkPhysicalDeviceConditionalRenderingFeaturesEXT conditional_rendering_features = {};
VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT mutable_descriptor_features = {};
VkPhysicalDeviceDepthClipEnableFeaturesEXT depth_clip_enable_features = {};
VkPhysicalDeviceImageViewMinLodFeaturesEXT image_view_min_lod_features = {};
VkPhysicalDeviceMeshShaderFeaturesEXT mesh_shader_features = {};
struct VideoCapability
{
VkVideoProfileInfoKHR profile = {};
VkVideoDecodeCapabilitiesKHR decode_capabilities = {};
VkVideoCapabilitiesKHR video_capabilities = {};
};
VkVideoDecodeH264ProfileInfoKHR decode_h264_profile = {};
VkVideoDecodeH264CapabilitiesKHR decode_h264_capabilities = {};
VideoCapability video_capability_h264 = {};
VkVideoDecodeH265ProfileInfoKHR decode_h265_profile = {};
VkVideoDecodeH265CapabilitiesKHR decode_h265_capabilities = {};
VideoCapability video_capability_h265 = {};
wi::vector<VkDynamicState> pso_dynamicStates;
VkPipelineDynamicStateCreateInfo dynamicStateInfo = {};
VkPipelineDynamicStateCreateInfo dynamicStateInfo_MeshShader = {};
VkBuffer nullBuffer = VK_NULL_HANDLE;
VmaAllocation nullBufferAllocation = VK_NULL_HANDLE;
VkSampler nullSampler = VK_NULL_HANDLE;
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
enum DESCRIPTOR_SET
{
DESCRIPTOR_SET_BINDINGS,
DESCRIPTOR_SET_BINDLESS_SAMPLER,
DESCRIPTOR_SET_BINDLESS_RESOURCE,
DESCRIPTOR_SET_COUNT,
};
VkDescriptorSetLayout descriptor_set_layouts[DESCRIPTOR_SET_COUNT] = {};
uint32_t dynamic_cbv_count = ROOT_CBV_COUNT;
wi::vector<VkDescriptorPoolSize> binding_layout_allocations;
struct CommandQueue
{
VkQueue queue = VK_NULL_HANDLE;
VkSemaphore frame_semaphores[BUFFERCOUNT][QUEUE_COUNT] = {};
wi::vector<SwapChain> swapchain_updates;
wi::vector<VkSemaphoreSubmitInfo> submit_waitSemaphoreInfos;
wi::vector<VkSemaphoreSubmitInfo> submit_signalSemaphoreInfos;
wi::vector<VkCommandBufferSubmitInfo> submit_cmds;
wi::vector<VkSemaphore> swapchainWaitSemaphores;
wi::vector<VkSwapchainKHR> swapchains;
wi::vector<uint32_t> swapchainImageIndices;
bool sparse_binding_supported = false;
wi::allocator::shared_ptr<std::mutex> locker;
void clear();
void signal(VkSemaphore semaphore);
void wait(VkSemaphore semaphore);
void submit(GraphicsDevice_Vulkan* device, VkFence fence);
} queues[QUEUE_COUNT];
CommandQueue queue_init;
CommandQueue queue_sparse;
struct CopyAllocator
{
GraphicsDevice_Vulkan* device = nullptr;
std::mutex locker;
struct CopyCMD
{
VkCommandPool transferCommandPool = VK_NULL_HANDLE;
VkCommandBuffer transferCommandBuffer = VK_NULL_HANDLE;
VkFence fence = VK_NULL_HANDLE;
GPUBuffer uploadbuffer;
constexpr bool IsValid() const { return transferCommandBuffer != VK_NULL_HANDLE; }
};
wi::vector<CopyCMD> freelist;
void init(GraphicsDevice_Vulkan* device);
void destroy();
CopyCMD allocate(uint64_t staging_size);
void submit(CopyCMD cmd);
};
mutable CopyAllocator copyAllocator;
// Resource init transition helper:
mutable std::mutex transitionLocker;
mutable wi::vector<VkImageMemoryBarrier2> init_transitions;
struct TransitionHandler
{
VkCommandPool commandPool = VK_NULL_HANDLE;
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
VkSemaphore semaphores[QUEUE_COUNT - 1] = {}; // for each queue except graphics
};
TransitionHandler transition_handlers[BUFFERCOUNT];
inline TransitionHandler& GetTransitionHandler() { return transition_handlers[GetBufferIndex()]; }
VkFence frame_fence[BUFFERCOUNT][QUEUE_COUNT] = {};
struct DescriptorBinder
{
DescriptorBindingTable table;
GraphicsDevice_Vulkan* device = nullptr;
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
enum DIRTY_FLAGS
{
DIRTY_NONE = 0,
DIRTY_DESCRIPTOR = 1 << 1,
DIRTY_OFFSET = 1 << 2,
DIRTY_ALL = ~0,
};
uint32_t dirty = DIRTY_NONE;
void init(GraphicsDevice_Vulkan* device)
{
this->device = device;
}
void reset()
{
table = {};
dirty = DIRTY_ALL;
descriptorSet = VK_NULL_HANDLE;
}
void flush(bool graphics, CommandList cmd);
};
struct DescriptorBinderPool
{
static constexpr uint32_t pool_size = 256;
GraphicsDevice_Vulkan* device = nullptr;
wi::vector<VkDescriptorPool> pools;
wi::vector<VkDescriptorSet> free_sets;
VkDescriptorSetLayout layouts[pool_size] = {};
bool needs_reset = true;
void init(GraphicsDevice_Vulkan* device)
{
this->device = device;
for (auto& x : layouts)
{
x = device->descriptor_set_layouts[DESCRIPTOR_SET_BINDINGS];
}
}
VkDescriptorSet allocate()
{
if (needs_reset)
{
// Cannot simply recycle descriptor sets because then views that are set on them will be still "referenced" and invalid to free
// So we will reset pools and realloc all sets
free_sets.clear();
for (auto& x : pools)
{
vulkan_check(vkResetDescriptorPool(device->device, x, 0));
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = x;
allocInfo.descriptorSetCount = pool_size;
allocInfo.pSetLayouts = layouts;
const size_t count = free_sets.size();
free_sets.resize(count + pool_size);
vulkan_check(vkAllocateDescriptorSets(device->device, &allocInfo, free_sets.data() + count));
}
needs_reset = false;
}
if (free_sets.empty())
{
VkDescriptorPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = (uint32_t)device->binding_layout_allocations.size();
poolInfo.pPoolSizes = device->binding_layout_allocations.data();
poolInfo.maxSets = pool_size;
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
vulkan_check(vkCreateDescriptorPool(device->device, &poolInfo, nullptr, &descriptorPool));
pools.push_back(descriptorPool);
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = descriptorPool;
allocInfo.descriptorSetCount = pool_size;
allocInfo.pSetLayouts = layouts;
free_sets.resize(pool_size);
vulkan_check(vkAllocateDescriptorSets(device->device, &allocInfo, free_sets.data()));
}
VkDescriptorSet descriptor_set = free_sets.back();
free_sets.pop_back();
return descriptor_set;
}
void destroy()
{
device->allocationhandler->destroylocker.lock();
for (auto& x : pools)
{
device->allocationhandler->destroyer_descriptorPools.push_back(std::make_pair(x, device->allocationhandler->framecount));
}
device->allocationhandler->destroylocker.unlock();
pools.clear();
}
void reset()
{
// Don't really reset here, but defer reset until first use
// The first use will more likely be executed on worker thread instead of resetting new commandlists on main thread
needs_reset = true;
}
};
wi::vector<VkSemaphore> semaphore_pool;
std::mutex semaphore_pool_locker;
VkSemaphore new_semaphore()
{
std::scoped_lock lck(semaphore_pool_locker);
if (semaphore_pool.empty())
{
VkSemaphore& sema = semaphore_pool.emplace_back();
VkSemaphoreCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
vulkan_check(vkCreateSemaphore(device, &info, nullptr, &sema));
set_semaphore_name(sema, "DependencySemaphore");
}
VkSemaphore semaphore = semaphore_pool.back();
semaphore_pool.pop_back();
return semaphore;
}
void free_semaphore(VkSemaphore semaphore)
{
std::scoped_lock lck(semaphore_pool_locker);
semaphore_pool.push_back(semaphore);
}
struct CommandList_Vulkan
{
VkCommandPool commandPools[BUFFERCOUNT][QUEUE_COUNT] = {};
VkCommandBuffer commandBuffers[BUFFERCOUNT][QUEUE_COUNT] = {};
uint32_t buffer_index = 0;
QUEUE_TYPE queue = {};
uint32_t id = 0;
wi::vector<VkSemaphore> waits;
wi::vector<VkSemaphore> signals;
DescriptorBinder binder;
DescriptorBinderPool binder_pools[BUFFERCOUNT];
GPULinearAllocator frame_allocators[BUFFERCOUNT];
wi::vector<std::pair<PipelineHash, VkPipeline>> pipelines_worker;
PipelineHash prev_pipeline_hash = {};
const PipelineState* active_pso = {};
const Shader* active_cs = {};
const RaytracingPipelineState* active_rt = {};
ShadingRate prev_shadingrate = {};
uint32_t prev_stencilref = 0;
wi::vector<SwapChain> prev_swapchains;
bool dirty_pso = {};
wi::vector<VkMemoryBarrier2> frame_memoryBarriers;
wi::vector<VkImageMemoryBarrier2> frame_imageBarriers;
wi::vector<VkBufferMemoryBarrier2> frame_bufferBarriers;
wi::vector<VkAccelerationStructureGeometryKHR> accelerationstructure_build_geometries;
wi::vector<VkAccelerationStructureBuildRangeInfoKHR> accelerationstructure_build_ranges;
RenderPassInfo renderpass_info;
wi::vector<VkImageMemoryBarrier2> renderpass_barriers_begin;
wi::vector<VkImageMemoryBarrier2> renderpass_barriers_end;
void reset(uint32_t bufferindex)
{
buffer_index = bufferindex;
waits.clear();
signals.clear();
binder_pools[buffer_index].reset();
binder.reset();
frame_allocators[buffer_index].reset();
prev_pipeline_hash = {};
active_pso = nullptr;
active_cs = nullptr;
active_rt = nullptr;
dirty_pso = false;
prev_shadingrate = ShadingRate::RATE_INVALID;
prev_stencilref = 0;
prev_swapchains.clear();
renderpass_info = {};
renderpass_barriers_begin.clear();
renderpass_barriers_end.clear();
}
constexpr VkCommandPool GetCommandPool() const
{
return commandPools[buffer_index][queue];
}
constexpr VkCommandBuffer GetCommandBuffer() const
{
return commandBuffers[buffer_index][queue];
}
};
wi::allocator::BlockAllocator<CommandList_Vulkan, 64> cmd_allocator;
wi::vector<CommandList_Vulkan*> commandlists;
uint32_t cmd_count = 0;
wi::SpinLock cmd_locker;
constexpr CommandList_Vulkan& GetCommandList(CommandList cmd) const
{
assert(cmd.IsValid());
return *(CommandList_Vulkan*)cmd.internal_state;
}
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
wi::unordered_map<PipelineHash, VkPipeline> pipelines_global;
void pso_validate(CommandList cmd);
void predraw(CommandList cmd);
void predispatch(CommandList cmd);
VkSampler immutable_samplers[10];
void set_fence_name(VkFence fence, const char* name);
void set_semaphore_name(VkSemaphore semaphore, const char* name);
public:
GraphicsDevice_Vulkan(wi::platform::window_type window, ValidationMode validationMode = ValidationMode::Disabled, GPUPreference preference = GPUPreference::Discrete);
~GraphicsDevice_Vulkan() override;
bool CreateSwapChain(const SwapChainDesc* desc, wi::platform::window_type window, SwapChain* swapchain) const override;
bool CreateBuffer2(const GPUBufferDesc* desc, const std::function<void(void*)>& init_callback, GPUBuffer* buffer, const GPUResource* alias = nullptr, uint64_t alias_offset = 0ull) const override;
bool CreateTexture(const TextureDesc* desc, const SubresourceData* initial_data, Texture* texture, const GPUResource* alias = nullptr, uint64_t alias_offset = 0ull) const override;
bool CreateShader(ShaderStage stage, const void* shadercode, size_t shadercode_size, Shader* shader) const override;
bool CreateSampler(const SamplerDesc* desc, Sampler* sampler) const override;
bool CreateQueryHeap(const GPUQueryHeapDesc* desc, GPUQueryHeap* queryheap) const override;
bool CreatePipelineState(const PipelineStateDesc* desc, PipelineState* pso, const RenderPassInfo* renderpass_info = nullptr) const override;
bool CreateRaytracingAccelerationStructure(const RaytracingAccelerationStructureDesc* desc, RaytracingAccelerationStructure* bvh) const override;
bool CreateRaytracingPipelineState(const RaytracingPipelineStateDesc* desc, RaytracingPipelineState* rtpso) const override;
bool CreateVideoDecoder(const VideoDesc* desc, VideoDecoder* video_decoder) const override;
int CreateSubresource(Texture* texture, SubresourceType type, uint32_t firstSlice, uint32_t sliceCount, uint32_t firstMip, uint32_t mipCount, const Format* format_change = nullptr, const ImageAspect* aspect = nullptr, const Swizzle* swizzle = nullptr, float min_lod_clamp = 0) const override;
int CreateSubresource(GPUBuffer* buffer, SubresourceType type, uint64_t offset, uint64_t size = ~0, const Format* format_change = nullptr, const uint32_t* structuredbuffer_stride_change = nullptr) const override;
void DeleteSubresources(GPUResource* resource) override;
int GetDescriptorIndex(const GPUResource* resource, SubresourceType type, int subresource = -1) const override;
int GetDescriptorIndex(const Sampler* sampler) const override;
void WriteShadingRateValue(ShadingRate rate, void* dest) const override;
void WriteTopLevelAccelerationStructureInstance(const RaytracingAccelerationStructureDesc::TopLevel::Instance* instance, void* dest) const override;
void WriteShaderIdentifier(const RaytracingPipelineState* rtpso, uint32_t group_index, void* dest) const override;
void SetName(GPUResource* pResource, const char* name) const override;
void SetName(Shader* shader, const char* name) const override;
CommandList BeginCommandList(QUEUE_TYPE queue = QUEUE_GRAPHICS) override;
void SubmitCommandLists() override;
void WaitForGPU() const override;
void ClearPipelineStateCache() override;
size_t GetActivePipelineCount() const override { return pipelines_global.size(); }
ShaderFormat GetShaderFormat() const override { return ShaderFormat::SPIRV; }
Texture GetBackBuffer(const SwapChain* swapchain) const override;
ColorSpace GetSwapChainColorSpace(const SwapChain* swapchain) const override;
bool IsSwapChainSupportsHDR(const SwapChain* swapchain) const override;
uint32_t GetMinOffsetAlignment(const GPUBufferDesc* desc) const override
{
uint32_t alignment = 1u;
if (has_flag(desc->bind_flags, BindFlag::CONSTANT_BUFFER))
{
alignment = std::max(alignment, (uint32_t)properties2.properties.limits.minUniformBufferOffsetAlignment);
}
if (has_flag(desc->misc_flags, ResourceMiscFlag::BUFFER_RAW) || has_flag(desc->misc_flags, ResourceMiscFlag::BUFFER_STRUCTURED))
{
alignment = std::max(alignment, (uint32_t)properties2.properties.limits.minStorageBufferOffsetAlignment);
}
if (desc->format != Format::UNKNOWN || has_flag(desc->misc_flags, ResourceMiscFlag::TYPED_FORMAT_CASTING))
{
alignment = std::max(alignment, (uint32_t)properties2.properties.limits.minTexelBufferOffsetAlignment);
}
if (has_flag(desc->misc_flags, ResourceMiscFlag::ALIASING_BUFFER) || has_flag(desc->misc_flags, ResourceMiscFlag::ALIASING_TEXTURE_NON_RT_DS) || has_flag(desc->misc_flags, ResourceMiscFlag::ALIASING_TEXTURE_RT_DS))
{
alignment = std::max(alignment, uint32_t(64 * 1024)); // 64KB safety to match DX12, because cannot use vkGetBufferMemoryRequirements here
}
return alignment;
}
MemoryUsage GetMemoryUsage() const override
{
MemoryUsage retval;
VmaBudget budgets[VK_MAX_MEMORY_HEAPS] = {};
vmaGetHeapBudgets(allocationhandler->allocator, budgets);
for (uint32_t i = 0; i < memory_properties_2.memoryProperties.memoryHeapCount; ++i)
{
if (memory_properties_2.memoryProperties.memoryHeaps[i].flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
{
retval.budget += budgets[i].budget;
retval.usage += budgets[i].usage;
}
}
return retval;
}
uint32_t GetMaxViewportCount() const override { return properties2.properties.limits.maxViewports; };
void SparseUpdate(QUEUE_TYPE queue, const SparseUpdateCommand* commands, uint32_t command_count) override;
const char* GetTag() const override { return "[Vulkan]"; }
///////////////Thread-sensitive////////////////////////
void WaitCommandList(CommandList cmd, CommandList wait_for) override;
void RenderPassBegin(const SwapChain* swapchain, CommandList cmd) override;
void RenderPassBegin(const RenderPassImage* images, uint32_t image_count, CommandList cmd, RenderPassFlags flags = RenderPassFlags::NONE) override;
void RenderPassEnd(CommandList cmd) override;
void BindScissorRects(uint32_t numRects, const Rect* rects, CommandList cmd) override;
void BindViewports(uint32_t NumViewports, const Viewport *pViewports, CommandList cmd) override;
void BindResource(const GPUResource* resource, uint32_t slot, CommandList cmd, int subresource = -1) override;
void BindResources(const GPUResource *const* resources, uint32_t slot, uint32_t count, CommandList cmd) override;
void BindUAV(const GPUResource* resource, uint32_t slot, CommandList cmd, int subresource = -1) override;
void BindUAVs(const GPUResource *const* resources, uint32_t slot, uint32_t count, CommandList cmd) override;
void BindSampler(const Sampler* sampler, uint32_t slot, CommandList cmd) override;
void BindConstantBuffer(const GPUBuffer* buffer, uint32_t slot, CommandList cmd, uint64_t offset = 0ull) override;
void BindVertexBuffers(const GPUBuffer *const* vertexBuffers, uint32_t slot, uint32_t count, const uint32_t* strides, const uint64_t* offsets, CommandList cmd) override;
void BindIndexBuffer(const GPUBuffer* indexBuffer, const IndexBufferFormat format, uint64_t offset, CommandList cmd) override;
void BindStencilRef(uint32_t value, CommandList cmd) override;
void BindBlendFactor(float r, float g, float b, float a, CommandList cmd) override;
void BindShadingRate(ShadingRate rate, CommandList cmd) override;
void BindPipelineState(const PipelineState* pso, CommandList cmd) override;
void BindComputeShader(const Shader* cs, CommandList cmd) override;
void BindDepthBounds(float min_bounds, float max_bounds, CommandList cmd) override;
void Draw(uint32_t vertexCount, uint32_t startVertexLocation, CommandList cmd) override;
void DrawIndexed(uint32_t indexCount, uint32_t startIndexLocation, int32_t baseVertexLocation, CommandList cmd) override;
void DrawInstanced(uint32_t vertexCount, uint32_t instanceCount, uint32_t startVertexLocation, uint32_t startInstanceLocation, CommandList cmd) override;
void DrawIndexedInstanced(uint32_t indexCount, uint32_t instanceCount, uint32_t startIndexLocation, int32_t baseVertexLocation, uint32_t startInstanceLocation, CommandList cmd) override;
void DrawInstancedIndirect(const GPUBuffer* args, uint64_t args_offset, CommandList cmd) override;
void DrawIndexedInstancedIndirect(const GPUBuffer* args, uint64_t args_offset, CommandList cmd) override;
void DrawInstancedIndirectCount(const GPUBuffer* args, uint64_t args_offset, const GPUBuffer* count, uint64_t count_offset, uint32_t max_count, CommandList cmd) override;
void DrawIndexedInstancedIndirectCount(const GPUBuffer* args, uint64_t args_offset, const GPUBuffer* count, uint64_t count_offset, uint32_t max_count, CommandList cmd) override;
void Dispatch(uint32_t threadGroupCountX, uint32_t threadGroupCountY, uint32_t threadGroupCountZ, CommandList cmd) override;
void DispatchIndirect(const GPUBuffer* args, uint64_t args_offset, CommandList cmd) override;
void DispatchMesh(uint32_t threadGroupCountX, uint32_t threadGroupCountY, uint32_t threadGroupCountZ, CommandList cmd) override;
void DispatchMeshIndirect(const GPUBuffer* args, uint64_t args_offset, CommandList cmd) override;
void DispatchMeshIndirectCount(const GPUBuffer* args, uint64_t args_offset, const GPUBuffer* count, uint64_t count_offset, uint32_t max_count, CommandList cmd) override;
void CopyResource(const GPUResource* pDst, const GPUResource* pSrc, CommandList cmd) override;
void CopyBuffer(const GPUBuffer* pDst, uint64_t dst_offset, const GPUBuffer* pSrc, uint64_t src_offset, uint64_t size, CommandList cmd) override;
void CopyTexture(const Texture* dst, uint32_t dstX, uint32_t dstY, uint32_t dstZ, uint32_t dstMip, uint32_t dstSlice, const Texture* src, uint32_t srcMip, uint32_t srcSlice, CommandList cmd, const Box* srcbox, ImageAspect dst_aspect, ImageAspect src_aspect) override;
void QueryBegin(const GPUQueryHeap* heap, uint32_t index, CommandList cmd) override;
void QueryEnd(const GPUQueryHeap* heap, uint32_t index, CommandList cmd) override;
void QueryResolve(const GPUQueryHeap* heap, uint32_t index, uint32_t count, const GPUBuffer* dest, uint64_t dest_offset, CommandList cmd) override;
void QueryReset(const GPUQueryHeap* heap, uint32_t index, uint32_t count, CommandList cmd) override;
void Barrier(const GPUBarrier* barriers, uint32_t numBarriers, CommandList cmd) override;
void BuildRaytracingAccelerationStructure(const RaytracingAccelerationStructure* dst, CommandList cmd, const RaytracingAccelerationStructure* src = nullptr) override;
void BindRaytracingPipelineState(const RaytracingPipelineState* rtpso, CommandList cmd) override;
void DispatchRays(const DispatchRaysDesc* desc, CommandList cmd) override;
void PushConstants(const void* data, uint32_t size, CommandList cmd, uint32_t offset = 0) override;
void PredicationBegin(const GPUBuffer* buffer, uint64_t offset, PredicationOp op, CommandList cmd) override;
void PredicationEnd(CommandList cmd) override;
void ClearUAV(const GPUResource* resource, uint32_t value, CommandList cmd) override;
void VideoDecode(const VideoDecoder* video_decoder, const VideoDecodeOperation* op, CommandList cmd) override;
void EventBegin(const char* name, CommandList cmd) override;
void EventEnd(CommandList cmd) override;
void SetMarker(const char* name, CommandList cmd) override;
RenderPassInfo GetRenderPassInfo(CommandList cmd) override
{
return GetCommandList(cmd).renderpass_info;
}
GPULinearAllocator& GetFrameAllocator(CommandList cmd) override
{
return GetCommandList(cmd).frame_allocators[GetBufferIndex()];
}
VkDevice GetDevice();
VkImage GetTextureInternalResource(const Texture* texture);
VkPhysicalDevice GetPhysicalDevice();
VkInstance GetInstance();
VkQueue GetGraphicsCommandQueue();
uint32_t GetGraphicsFamilyIndex();
struct AllocationHandler
{
VmaAllocator allocator = VK_NULL_HANDLE;
VmaAllocator externalAllocator = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE;
VkInstance instance;
uint64_t framecount = 0;
std::mutex destroylocker;
struct BindlessDescriptorHeap
{
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
wi::vector<int> freelist;
std::mutex locker;
void init(GraphicsDevice_Vulkan* device, VkDescriptorType type, uint32_t descriptorCount)
{
descriptorCount = std::min(descriptorCount, 500000u);
VkDescriptorPoolSize poolSize = {};
poolSize.type = type;
poolSize.descriptorCount = descriptorCount;
VkDescriptorPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = 1;
poolInfo.pPoolSizes = &poolSize;
poolInfo.maxSets = 1;
poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT;
vulkan_check(vkCreateDescriptorPool(device->device, &poolInfo, nullptr, &descriptorPool));
VkDescriptorSetLayoutBinding binding = {};
binding.descriptorType = type;
binding.binding = 0;
binding.descriptorCount = descriptorCount;
binding.stageFlags = VK_SHADER_STAGE_ALL;
binding.pImmutableSamplers = nullptr;
VkDescriptorSetLayoutCreateInfo layoutInfo = {};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.bindingCount = 1;
layoutInfo.pBindings = &binding;
layoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT;
const VkDescriptorBindingFlags bindingFlags =
VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT |
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT |
VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT |
VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT;
VkDescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsInfo = {};
bindingFlagsInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO;
bindingFlagsInfo.bindingCount = 1;
bindingFlagsInfo.pBindingFlags = &bindingFlags;
layoutInfo.pNext = &bindingFlagsInfo;
const VkDescriptorType mutable_types[] = {
VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR // last one!
};
VkMutableDescriptorTypeListEXT list = {};
list.pDescriptorTypes = mutable_types;
list.descriptorTypeCount = arraysize(mutable_types);
if (!device->CheckCapability(GraphicsDeviceCapability::RAYTRACING))
{
// If raytracing not supported, remove this option from mutable descriptor type:
list.descriptorTypeCount--;
}
VkMutableDescriptorTypeCreateInfoEXT mutable_info = {};
mutable_info.sType = VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT;
mutable_info.pMutableDescriptorTypeLists = &list;
mutable_info.mutableDescriptorTypeListCount = 1;
if (type == VK_DESCRIPTOR_TYPE_MUTABLE_EXT)
{
bindingFlagsInfo.pNext = &mutable_info;
}
vulkan_check(vkCreateDescriptorSetLayout(device->device, &layoutInfo, nullptr, &descriptorSetLayout));
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = descriptorPool;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &descriptorSetLayout;
vulkan_check(vkAllocateDescriptorSets(device->device, &allocInfo, &descriptorSet));
for (int i = 0; i < (int)descriptorCount; ++i)
{
freelist.push_back((int)descriptorCount - i - 1);
}
// Descriptor safety feature:
// We init null descriptors for bindless index = 0 for access safety
// Because shader compiler sometimes incorrectly loads descriptor outside of safety branch
// Note: these are never freed, this is intentional
if (type == VK_DESCRIPTOR_TYPE_MUTABLE_EXT)
{
int index = allocate();
wilog_assert(index == 0, "Descriptor safety feature error: descriptor index must be 0!");
VkWriteDescriptorSet write = {};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
write.dstBinding = 0;
write.dstArrayElement = index;
write.descriptorCount = 1;
write.dstSet = descriptorSet;
VkDescriptorBufferInfo buffer_info = {};
buffer_info.buffer = device->nullBuffer;
buffer_info.range = VK_WHOLE_SIZE;
write.pBufferInfo = &buffer_info;
vkUpdateDescriptorSets(device->device, 1, &write, 0, nullptr);
}
else if (type == VK_DESCRIPTOR_TYPE_SAMPLER)
{
int index = allocate();
wilog_assert(index == 0, "Descriptor safety feature error: descriptor index must be 0!");
VkWriteDescriptorSet write = {};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.descriptorType = type;
write.dstBinding = 0;
write.dstArrayElement = index;
write.descriptorCount = 1;
write.dstSet = descriptorSet;
VkDescriptorImageInfo image_info = {};
image_info.sampler = device->nullSampler;
write.pImageInfo = &image_info;
vkUpdateDescriptorSets(device->device, 1, &write, 0, nullptr);
}
}
void destroy(VkDevice device)
{
if (descriptorSetLayout != VK_NULL_HANDLE)
{
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
descriptorSetLayout = VK_NULL_HANDLE;
}
if (descriptorPool != VK_NULL_HANDLE)
{
vkDestroyDescriptorPool(device, descriptorPool, nullptr);
descriptorPool = VK_NULL_HANDLE;
}
}
int allocate()
{
locker.lock();
if (!freelist.empty())
{
int index = freelist.back();
freelist.pop_back();
locker.unlock();
return index;
}
locker.unlock();
return -1;
}
void free(int index)
{
if (index >= 0)
{
locker.lock();
freelist.push_back(index);
locker.unlock();
}
}
};
BindlessDescriptorHeap bindless_resources;
BindlessDescriptorHeap bindless_samplers;
std::deque<std::pair<VmaAllocation, uint64_t>> destroyer_allocations;
std::deque<std::pair<std::pair<VkImage, VmaAllocation>, uint64_t>> destroyer_images;
std::deque<std::pair<VkImageView, uint64_t>> destroyer_imageviews;
std::deque<std::pair<std::pair<VkBuffer, VmaAllocation>, uint64_t>> destroyer_buffers;
std::deque<std::pair<VkBufferView, uint64_t>> destroyer_bufferviews;
std::deque<std::pair<VkAccelerationStructureKHR, uint64_t>> destroyer_bvhs;
std::deque<std::pair<VkSampler, uint64_t>> destroyer_samplers;
std::deque<std::pair<VkDescriptorPool, uint64_t>> destroyer_descriptorPools;
std::deque<std::pair<VkDescriptorSetLayout, uint64_t>> destroyer_descriptorSetLayouts;
std::deque<std::pair<VkDescriptorUpdateTemplate, uint64_t>> destroyer_descriptorUpdateTemplates;
std::deque<std::pair<VkShaderModule, uint64_t>> destroyer_shadermodules;
std::deque<std::pair<VkPipelineLayout, uint64_t>> destroyer_pipelineLayouts;
std::deque<std::pair<VkPipeline, uint64_t>> destroyer_pipelines;
std::deque<std::pair<VkQueryPool, uint64_t>> destroyer_querypools;
std::deque<std::pair<VkSwapchainKHR, uint64_t>> destroyer_swapchains;
std::deque<std::pair<VkSurfaceKHR, uint64_t>> destroyer_surfaces;
std::deque<std::pair<VkSemaphore, uint64_t>> destroyer_semaphores;
std::deque<std::pair<VkVideoSessionKHR, uint64_t>> destroyer_video_sessions;
std::deque<std::pair<VkVideoSessionParametersKHR, uint64_t>> destroyer_video_session_parameters;
std::deque<std::pair<int, uint64_t>> destroyer_bindless_resources;
std::deque<std::pair<int, uint64_t>> destroyer_bindless_samplers;
~AllocationHandler()
{
bindless_resources.destroy(device);
bindless_samplers.destroy(device);
Update(~0ull, 0); // destroy all remaining
vmaDestroyAllocator(allocator);
vmaDestroyAllocator(externalAllocator);
vkDestroyDevice(device, nullptr);
vkDestroyInstance(instance, nullptr);
}
// Deferred destroy of resources that the GPU is already finished with:
void Update(uint64_t FRAMECOUNT, uint32_t BUFFERCOUNT)
{
const auto destroy = [&](auto&& queue, auto&& handler) {
while (!queue.empty()) {
if (queue.front().second + BUFFERCOUNT < FRAMECOUNT)
{
auto item = queue.front();
queue.pop_front();
handler(item.first);
}
else
{
break;
}
}
};
framecount = FRAMECOUNT;
std::scoped_lock lck(destroylocker);
destroy(destroyer_allocations, [&](auto& item) {
vmaFreeMemory(allocator, item);
});
destroy(destroyer_imageviews, [&](auto& item) {
vkDestroyImageView(device, item, nullptr);
});
destroy(destroyer_images, [&](auto& item) {
vmaDestroyImage(allocator, item.first, item.second);
});
destroy(destroyer_bufferviews, [&](auto& item) {
vkDestroyBufferView(device, item, nullptr);
});
destroy(destroyer_buffers, [&](auto& item) {
vmaDestroyBuffer(allocator, item.first, item.second);
});
destroy(destroyer_bvhs, [&](auto& item) {
vkDestroyAccelerationStructureKHR(device, item, nullptr);
});
destroy(destroyer_samplers, [&](auto& item) {
vkDestroySampler(device, item, nullptr);
});
destroy(destroyer_descriptorPools, [&](auto& item) {
vkDestroyDescriptorPool(device, item, nullptr);
});
destroy(destroyer_descriptorSetLayouts, [&](auto& item) {
vkDestroyDescriptorSetLayout(device, item, nullptr);
});
destroy(destroyer_descriptorUpdateTemplates, [&](auto& item) {
vkDestroyDescriptorUpdateTemplate(device, item, nullptr);
});
destroy(destroyer_shadermodules, [&](auto& item) {
vkDestroyShaderModule(device, item, nullptr);
});
destroy(destroyer_pipelineLayouts, [&](auto& item) {
vkDestroyPipelineLayout(device, item, nullptr);
});
destroy(destroyer_pipelines, [&](auto& item) {
vkDestroyPipeline(device, item, nullptr);
});
destroy(destroyer_querypools, [&](auto& item) {
vkDestroyQueryPool(device, item, nullptr);
});
destroy(destroyer_swapchains, [&](auto& item) {
vkDestroySwapchainKHR(device, item, nullptr);
});
destroy(destroyer_surfaces, [&](auto& item) {
vkDestroySurfaceKHR(instance, item, nullptr);
});
destroy(destroyer_semaphores, [&](auto& item) {
vkDestroySemaphore(device, item, nullptr);
});
destroy(destroyer_video_sessions, [&](auto& item) {
vkDestroyVideoSessionKHR(device, item, nullptr);
});
destroy(destroyer_video_session_parameters, [&](auto& item) {
vkDestroyVideoSessionParametersKHR(device, item, nullptr);
});
destroy(destroyer_bindless_resources, [&](auto& item) {
bindless_resources.free(item);
});
destroy(destroyer_bindless_samplers, [&](auto& item) {
bindless_samplers.free(item);
});
}
};
wi::allocator::shared_ptr<AllocationHandler> allocationhandler;
};
}
#endif // WICKEDENGINE_BUILD_VULKAN