Browse Source
[vulkan] Initial implementation on Bindless Buffer/Descriptors (#4251)
[vulkan] Initial implementation on Bindless Buffer/Descriptors (#4251)
Adds a VK_EXT_descriptor_buffer, VK_KHR_device_buffer_address path alongside push descriptors and descriptor sets. The ring is chunked and sized from the device limits, so drivers with a small sampler range (QCOM reports 128 KiB) get more, chunks per frame instead of losing the feature. A pipeline uses one path or the other, never both, since some drivers do not support bufferless push descriptors. The chunk binding is only re-emitted when it changes, and a draw that repeats the previous payload reuses its allocation instead of rewriting it. This whole change will help to reduce the constant use of descriptors on the slower path, improves the "slow implementation" on QCOM drivers (A7xx and older) for push descriptors, improves latency when compiling pipelines. The implementation on indexing descriptors was also refined. _Special Thanks_ 1.- Meowly The Bindless Smol (@Gidoly) Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4251pull/4253/head
committed by
crueter
No known key found for this signature in database
GPG Key ID: 425ACD2D4830EBC6
25 changed files with 852 additions and 81 deletions
-
2src/video_core/CMakeLists.txt
-
151src/video_core/renderer_vulkan/pipeline_helper.h
-
16src/video_core/renderer_vulkan/vk_buffer_cache.cpp
-
27src/video_core/renderer_vulkan/vk_buffer_cache.h
-
77src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
-
11src/video_core/renderer_vulkan/vk_compute_pipeline.h
-
141src/video_core/renderer_vulkan/vk_descriptor_buffer.cpp
-
71src/video_core/renderer_vulkan/vk_descriptor_buffer.h
-
117src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
-
17src/video_core/renderer_vulkan/vk_graphics_pipeline.h
-
9src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
-
2src/video_core/renderer_vulkan/vk_pipeline_cache.h
-
14src/video_core/renderer_vulkan/vk_rasterizer.cpp
-
2src/video_core/renderer_vulkan/vk_rasterizer.h
-
22src/video_core/renderer_vulkan/vk_resource_pool.cpp
-
10src/video_core/renderer_vulkan/vk_scheduler.cpp
-
5src/video_core/renderer_vulkan/vk_scheduler.h
-
15src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
-
7src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
-
21src/video_core/renderer_vulkan/vk_update_descriptor.cpp
-
56src/video_core/renderer_vulkan/vk_update_descriptor.h
-
50src/video_core/vulkan_common/vulkan_device.cpp
-
33src/video_core/vulkan_common/vulkan_device.h
-
6src/video_core/vulkan_common/vulkan_wrapper.cpp
-
51src/video_core/vulkan_common/vulkan_wrapper.h
@ -0,0 +1,141 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|||
|
|||
#include <algorithm>
|
|||
|
|||
#include "common/alignment.h"
|
|||
#include "common/assert.h"
|
|||
#include "common/logging.h"
|
|||
#include "video_core/renderer_vulkan/vk_descriptor_buffer.h"
|
|||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
|||
#include "video_core/vulkan_common/vulkan_device.h"
|
|||
|
|||
namespace Vulkan { |
|||
|
|||
DescriptorBufferRing::DescriptorBufferRing(const Device& device_, |
|||
MemoryAllocator& memory_allocator) |
|||
: device{device_} { |
|||
if (!device.IsExtDescriptorBufferSupported() || !device.IsBufferDeviceAddressSupported()) { |
|||
return; |
|||
} |
|||
const VkPhysicalDeviceDescriptorBufferPropertiesEXT& props{device.DescriptorBufferProperties()}; |
|||
alignment = std::max<VkDeviceSize>(props.descriptorBufferOffsetAlignment, 1); |
|||
|
|||
const VkDeviceSize max_bound{(std::min)({props.maxSamplerDescriptorBufferRange, |
|||
props.maxResourceDescriptorBufferRange, |
|||
props.samplerDescriptorBufferAddressSpaceSize, |
|||
props.resourceDescriptorBufferAddressSpaceSize, |
|||
props.descriptorBufferAddressSpaceSize})}; |
|||
const VkDeviceSize frame_size{device.IsTiler() ? TILER_FRAME_SIZE : DESKTOP_FRAME_SIZE}; |
|||
const VkDeviceSize chunk_size{ |
|||
Common::AlignDown((std::min)(frame_size, max_bound), alignment)}; |
|||
if (chunk_size <= alignment) { |
|||
LOG_DEBUG(Render_Vulkan, "Descriptor buffer binding limit of {} is unusable, disabling", |
|||
max_bound); |
|||
return; |
|||
} |
|||
chunk_capacity = chunk_size - alignment; |
|||
chunks_per_frame = static_cast<size_t>(frame_size / chunk_size); |
|||
|
|||
const VkBufferCreateInfo buffer_ci{ |
|||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, |
|||
.pNext = nullptr, |
|||
.flags = 0, |
|||
.size = chunk_size, |
|||
.usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | |
|||
VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT | |
|||
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, |
|||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, |
|||
.queueFamilyIndexCount = 0, |
|||
.pQueueFamilyIndices = nullptr, |
|||
}; |
|||
const size_t total_chunks{chunks_per_frame * FRAMES_IN_FLIGHT}; |
|||
chunks.reserve(total_chunks); |
|||
chunk_addresses.reserve(total_chunks); |
|||
chunk_hosts.reserve(total_chunks); |
|||
for (size_t index = 0; index < total_chunks; ++index) { |
|||
vk::Buffer buffer{memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::Upload)}; |
|||
if (!buffer.IsHostVisible()) { |
|||
LOG_DEBUG(Render_Vulkan, "Descriptor buffer is not host visible, disabling"); |
|||
chunks.clear(); |
|||
return; |
|||
} |
|||
if (!buffer.IsHostCoherent()) { |
|||
LOG_DEBUG(Render_Vulkan, "Descriptor buffer is not host coherent, disabling"); |
|||
chunks.clear(); |
|||
return; |
|||
} |
|||
if (device.HasDebuggingToolAttached()) { |
|||
buffer.SetObjectNameEXT("Descriptor buffer"); |
|||
} |
|||
const VkDeviceAddress raw_address{device.GetLogical().GetBufferDeviceAddress(*buffer)}; |
|||
const VkDeviceAddress address{Common::AlignUp(raw_address, alignment)}; |
|||
chunk_addresses.push_back(address); |
|||
chunk_hosts.push_back(buffer.Mapped().data() + (address - raw_address)); |
|||
chunks.push_back(std::move(buffer)); |
|||
} |
|||
} |
|||
|
|||
DescriptorBufferRing::~DescriptorBufferRing() = default; |
|||
|
|||
void DescriptorBufferRing::TickFrame() { |
|||
if (++frame_index >= FRAMES_IN_FLIGHT) { |
|||
frame_index = 0; |
|||
} |
|||
chunk_cursor = 0; |
|||
cursor = 0; |
|||
++generation; |
|||
frame_reused = true; |
|||
} |
|||
|
|||
void DescriptorBufferRing::TouchFrame(Scheduler& scheduler) { |
|||
frame_ticks[frame_index] = scheduler.CurrentTick(); |
|||
} |
|||
|
|||
DescriptorBufferRing::Allocation DescriptorBufferRing::Allocate(Scheduler& scheduler, |
|||
VkDeviceSize size) { |
|||
ASSERT(!chunks.empty()); |
|||
if (!CanAllocate(size)) { |
|||
LOG_DEBUG(Render_Vulkan, "Descriptor set of {} bytes exceeds chunk capacity {}", size, |
|||
chunk_capacity); |
|||
return Allocation{}; |
|||
} |
|||
const VkDeviceSize needed{Common::AlignUp(size, alignment)}; |
|||
if (frame_reused) { |
|||
frame_reused = false; |
|||
scheduler.Wait(frame_ticks[frame_index]); |
|||
} |
|||
if (cursor + needed > chunk_capacity) { |
|||
if (chunk_cursor + 1 < chunks_per_frame) { |
|||
++chunk_cursor; |
|||
} else { |
|||
LOG_DEBUG(Render_Vulkan, "Descriptor buffer frame exhausted, stalling on the GPU"); |
|||
scheduler.Finish(); |
|||
chunk_cursor = 0; |
|||
++generation; |
|||
} |
|||
cursor = 0; |
|||
} |
|||
const size_t chunk{frame_index * chunks_per_frame + chunk_cursor}; |
|||
const VkDeviceSize offset{cursor}; |
|||
cursor += needed; |
|||
frame_ticks[frame_index] = scheduler.CurrentTick(); |
|||
return Allocation{ |
|||
.host = chunk_hosts[chunk] + offset, |
|||
.offset = offset, |
|||
.chunk = static_cast<u32>(chunk), |
|||
.generation = generation, |
|||
}; |
|||
} |
|||
|
|||
VkDescriptorBufferBindingInfoEXT DescriptorBufferRing::BindingInfo(u32 chunk) const noexcept { |
|||
return VkDescriptorBufferBindingInfoEXT{ |
|||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT, |
|||
.pNext = nullptr, |
|||
.address = chunk_addresses[chunk], |
|||
.usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | |
|||
VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT, |
|||
}; |
|||
} |
|||
|
|||
} // namespace Vulkan
|
|||
@ -0,0 +1,71 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <array> |
|||
#include <vector> |
|||
|
|||
#include "common/alignment.h" |
|||
#include "common/common_types.h" |
|||
#include "video_core/vulkan_common/vulkan_memory_allocator.h" |
|||
#include "video_core/vulkan_common/vulkan_wrapper.h" |
|||
|
|||
namespace Vulkan { |
|||
|
|||
class Device; |
|||
class Scheduler; |
|||
|
|||
class DescriptorBufferRing final { |
|||
static constexpr size_t FRAMES_IN_FLIGHT = 8; |
|||
static constexpr VkDeviceSize TILER_FRAME_SIZE = 2 * 1024 * 1024; |
|||
static constexpr VkDeviceSize DESKTOP_FRAME_SIZE = 4 * 1024 * 1024; |
|||
|
|||
public: |
|||
explicit DescriptorBufferRing(const Device& device_, MemoryAllocator& memory_allocator); |
|||
~DescriptorBufferRing(); |
|||
|
|||
struct Allocation { |
|||
u8* host{}; |
|||
VkDeviceSize offset{}; |
|||
u32 chunk{}; |
|||
u64 generation{}; |
|||
}; |
|||
|
|||
[[nodiscard]] u64 CurrentGeneration() const noexcept { |
|||
return generation; |
|||
} |
|||
|
|||
void TouchFrame(Scheduler& scheduler); |
|||
|
|||
[[nodiscard]] bool CanAllocate(VkDeviceSize size) const noexcept { |
|||
return Common::AlignUp(size, alignment) <= chunk_capacity; |
|||
} |
|||
|
|||
void TickFrame(); |
|||
|
|||
[[nodiscard]] Allocation Allocate(Scheduler& scheduler, VkDeviceSize size); |
|||
|
|||
[[nodiscard]] VkDescriptorBufferBindingInfoEXT BindingInfo(u32 chunk) const noexcept; |
|||
|
|||
[[nodiscard]] bool IsValid() const noexcept { |
|||
return !chunks.empty(); |
|||
} |
|||
|
|||
private: |
|||
const Device& device; |
|||
std::vector<vk::Buffer> chunks; |
|||
std::vector<VkDeviceAddress> chunk_addresses; |
|||
std::vector<u8*> chunk_hosts; |
|||
VkDeviceSize alignment{1}; |
|||
VkDeviceSize chunk_capacity{}; |
|||
size_t chunks_per_frame{}; |
|||
size_t frame_index{}; |
|||
size_t chunk_cursor{}; |
|||
VkDeviceSize cursor{}; |
|||
u64 generation{1}; |
|||
std::array<u64, FRAMES_IN_FLIGHT> frame_ticks{}; |
|||
bool frame_reused{}; |
|||
}; |
|||
|
|||
} // namespace Vulkan |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue