unify graphics and compute on one Vulkan queue

This commit is contained in:
nmzik
2026-07-22 17:44:16 +02:00
parent cf595beedf
commit dbcb444c05
19 changed files with 352 additions and 751 deletions
+10 -46
View File
@@ -166,14 +166,9 @@ bool MergeOverlappingBufferCacheRange(BufferCacheRange& merged,
return true;
}
bool CanMergeBufferCacheQueueMask(uint64_t queue_mask, uint32_t queue) noexcept {
return queue < 64 && (queue_mask & ~(uint64_t {1} << queue)) == 0;
}
struct BufferCache::CachedBuffer {
uint64_t vaddr = 0;
uint64_t size = 0;
uint64_t queue_mask = 0;
uint64_t vaddr = 0;
uint64_t size = 0;
std::shared_ptr<VulkanBuffer> buffer;
};
@@ -392,30 +387,13 @@ struct BufferCache::ReadbackWorker {
static_cast<const void*>(command.get()), static_cast<const void*>(mapped),
static_cast<const void*>(readback.buffer));
}
const auto family = cache.m_graphics.queues[GraphicContext::QUEUE_UTIL].family;
if (family == static_cast<uint32_t>(-1) ||
cache.m_graphics.queues[GraphicContext::QUEUE_GFX].family != family) {
EXIT("BufferCache: utility and graphics queues must share a valid family, "
"util=%u "
"gfx=%u\n",
family, cache.m_graphics.queues[GraphicContext::QUEUE_GFX].family);
}
for (int i = GraphicContext::QUEUE_COMPUTE_START;
i < GraphicContext::QUEUE_COMPUTE_START + GraphicContext::QUEUE_COMPUTE_NUM;
i++) {
if (cache.m_graphics.queues[i].family != family) {
EXIT("BufferCache: compute queue %d family mismatch, expected=%u "
"actual=%u\n",
i, family, cache.m_graphics.queues[i].family);
}
}
readback.usage = vk::BufferUsageFlagBits::eTransferDst;
readback.memory.property = vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent |
vk::MemoryPropertyFlagBits::eHostCached;
cache.m_graphics.CreateBuffer(READBACK_CAPACITY, readback);
cache.m_graphics.MapMemory(readback.memory, mapped);
command = std::make_unique<CommandBuffer>(GraphicContext::QUEUE_UTIL);
command = std::make_unique<CommandBuffer>();
state.store(State::Idle, std::memory_order_release);
state.notify_all();
continue;
@@ -550,7 +528,7 @@ BufferCache::~BufferCache() {
}
}
if (!m_buffers.empty()) {
Transfer::WaitForGraphicsIdle();
Transfer::WaitForQueueIdle();
}
m_buffers.clear();
}
@@ -649,13 +627,10 @@ void BufferCache::UnmapMemory(uint64_t vaddr, uint64_t size) {
BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr, uint64_t size,
bool is_written, bool is_read, bool is_formatted) {
if (command.IsInvalid() || command.IsExecute() || vaddr == 0 || size == 0 ||
size > UINT64_MAX - vaddr || command.GetQueue() < 0 || command.GetQueue() >= 64) {
EXIT("BufferCache: invalid buffer request, queue=%d addr=0x%016" PRIx64
" size=0x%016" PRIx64 "\n",
command.GetQueue(), vaddr, size);
size > UINT64_MAX - vaddr) {
EXIT("BufferCache: invalid buffer request, addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
vaddr, size);
}
const auto queue = static_cast<uint32_t>(command.GetQueue());
const auto queue_mask = uint64_t {1} << queue;
ValidateGpuAccess(vaddr, size, is_read, is_written);
const auto begin = AlignDown(vaddr);
const auto end = AlignUp(vaddr + size);
@@ -754,12 +729,6 @@ BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr,
" size=0x%016" PRIx64 " buffer=%p\n",
old.vaddr, old.size, static_cast<const void*>(old.buffer.get()));
}
if (!CanMergeBufferCacheQueueMask(old.queue_mask, queue)) {
EXIT("BufferCache: cross-queue overlap merge is unsupported, "
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 " used_queues=0x%016" PRIx64
" requested_queue=%u\n",
old.vaddr, old.size, old.queue_mask, queue);
}
std::vector<std::pair<uint64_t, uint64_t>> uploads;
m_memory_tracker.ForEachUploadRange(
old.vaddr, old.size, false,
@@ -860,7 +829,6 @@ BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr,
if (is_written) {
m_gpu_modified_ranges.Add(vaddr, size);
}
cached.queue_mask |= queue_mask;
command.RetainResourceUntilFence(cached.buffer);
return {*cached.buffer, vaddr - cached.vaddr};
}
@@ -944,7 +912,7 @@ BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t
vaddr, size, GraphicsRunIsCommandProcessorThread(),
GraphicsRunSubmissionLockHeld(), LabelInCallback());
}
Transfer::WaitForGraphicsIdle();
Transfer::WaitForQueueIdle();
auto backing_writes = ReserveBackingWrites(m_page_manager, dirty_ranges);
uint64_t downloaded = 0;
m_memory_tracker.ForEachDownloadRange<true>(
@@ -1039,10 +1007,6 @@ vk::BufferMemoryBarrier MakeDmaBarrier(VulkanBuffer& buffer, uint64_t offset, ui
barrier.size = size;
return barrier;
}
vk::CommandBuffer GetDmaCommandBuffer(CommandBuffer& command) {
return command.Handle();
}
} // namespace
void BufferCache::FillBuffer(CommandBuffer* command, uint64_t vaddr, uint64_t size,
@@ -1086,7 +1050,7 @@ void BufferCache::FillBuffer(CommandBuffer* command, uint64_t vaddr, uint64_t si
const auto before = MakeDmaBarrier(
dst, dst_offset, size, vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
vk::AccessFlagBits::eTransferWrite);
const auto vk_buffer = GetDmaCommandBuffer(*command);
const auto vk_buffer = command->Handle();
vk_buffer.pipelineBarrier(
vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eTransfer,
vk::DependencyFlagBits::eByRegion, 0, nullptr, 1, &before, 0, nullptr);
@@ -1179,7 +1143,7 @@ void BufferCache::CopyBuffer(CommandBuffer* command, uint64_t dst_vaddr, uint64_
MakeDmaBarrier(src, src_offset, size, vk::AccessFlagBits::eMemoryWrite,
vk::AccessFlagBits::eTransferRead),
};
const auto vk_buffer = GetDmaCommandBuffer(*command);
const auto vk_buffer = command->Handle();
vk_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands,
vk::PipelineStageFlagBits::eTransfer,
vk::DependencyFlagBits::eByRegion, 0, nullptr, 2, before, 0, nullptr);
@@ -42,7 +42,6 @@ struct BufferBinding {
[[nodiscard]] bool MergeOverlappingBufferCacheRange(BufferCacheRange& merged,
BufferCacheRange candidate) noexcept;
[[nodiscard]] bool CanMergeBufferCacheQueueMask(uint64_t queue_mask, uint32_t queue) noexcept;
class BufferCache {
public:
+111 -175
View File
@@ -18,17 +18,13 @@
#include "graphics/host_gpu/vulkanCommon.h"
#include <algorithm>
#include <array>
#include <atomic>
#include <cstring>
#include <deque>
#include <memory>
namespace Libs::Graphics {
static std::atomic<uint64_t> g_command_buffer_submit_seq = 0;
static void RequireValidQueueId(int queue_id) {
EXIT_IF(queue_id < 0 || queue_id >= GraphicContext::QUEUES_NUM);
}
static void ResetNativeCommandBuffer(vk::CommandBuffer buffer) {
EXIT_IF(buffer == nullptr);
const auto result = buffer.reset(vk::CommandBufferResetFlagBits::eReleaseResources);
@@ -38,34 +34,34 @@ static void ResetNativeCommandBuffer(vk::CommandBuffer buffer) {
}
}
class CommandPool {
public:
CommandPool() = default;
~CommandPool() // NOLINT
{
// TODO(): check if destructor is called from std::_Exit()
// DeleteAll();
}
KYTY_CLASS_NO_COPY(CommandPool);
VulkanCommandPool* GetPool(int queue_id) {
RequireValidQueueId(queue_id);
if (m_pools[queue_id] == nullptr) {
Create(queue_id);
}
return m_pools[queue_id];
}
void DeleteAll();
private:
void Create(int queue_id);
std::array<VulkanCommandPool*, GraphicContext::QUEUES_NUM> m_pools {};
struct CommandSlot {
Common::Mutex* pool_mutex = nullptr;
uint32_t id = 0;
vk::CommandBuffer buffer = nullptr;
vk::Fence fence = nullptr;
bool busy = false;
};
static RenderContext* g_render_ctx = nullptr;
static thread_local CommandPool g_command_pool;
class ThreadCommandPool {
public:
ThreadCommandPool() = default;
KYTY_CLASS_NO_COPY(ThreadCommandPool);
CommandSlot* Allocate();
void Destroy();
private:
void Create();
CommandSlot* CreateSlot();
Common::Mutex m_mutex;
vk::CommandPool m_pool = nullptr;
std::deque<CommandSlot> m_slots;
};
static RenderContext* g_render_ctx = nullptr;
static thread_local ThreadCommandPool g_command_pool;
RenderContext& GetRenderContext() noexcept {
return *g_render_ctx;
@@ -96,152 +92,110 @@ void GraphicsRenderInit(GraphicContext& graphics) {
g_render_ctx = new RenderContext(graphics);
}
void GraphicsRenderReleaseThreadCommandPools() {
g_command_pool.DeleteAll();
void GraphicsRenderReleaseThreadCommandPool() {
g_command_pool.Destroy();
}
CommandBuffer::CommandBuffer(int queue)
: m_graphics(GetRenderContext().GetGraphics()), m_queue(queue), m_host_stream(m_graphics) {
Allocate();
}
CommandBuffer::CommandBuffer()
: m_graphics(GetRenderContext().GetGraphics()), m_slot(g_command_pool.Allocate()),
m_host_stream(m_graphics) {}
void CommandPool::Create(int queue_id) {
RequireValidQueueId(queue_id);
auto& graphics = GetRenderContext().GetGraphics();
auto*& pool = m_pools[queue_id];
EXIT_IF(pool != nullptr);
EXIT_IF(graphics.queues[queue_id].family == static_cast<uint32_t>(-1));
pool = new VulkanCommandPool;
void ThreadCommandPool::Create() {
auto& graphics = GetRenderContext().GetGraphics();
EXIT_IF(m_pool != nullptr || graphics.queue_family == static_cast<uint32_t>(-1));
vk::CommandPoolCreateInfo pool_info {};
pool_info.sType = vk::StructureType::eCommandPoolCreateInfo;
pool_info.pNext = nullptr;
pool_info.queueFamilyIndex = graphics.queues[queue_id].family;
pool_info.queueFamilyIndex = graphics.queue_family;
pool_info.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
const auto result = graphics.device.createCommandPool(&pool_info, nullptr, &pool->pool);
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess || pool->pool == nullptr);
pool->buffers_count = 8;
pool->buffers = std::make_unique<vk::CommandBuffer[]>(pool->buffers_count);
pool->fences = std::make_unique<vk::Fence[]>(pool->buffers_count);
pool->semaphores = std::make_unique<vk::Semaphore[]>(pool->buffers_count);
pool->busy = std::make_unique<bool[]>(pool->buffers_count);
const auto result = graphics.device.createCommandPool(&pool_info, nullptr, &m_pool);
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess || m_pool == nullptr);
}
CommandSlot* ThreadCommandPool::CreateSlot() {
auto& graphics = GetRenderContext().GetGraphics();
vk::CommandBufferAllocateInfo alloc_info {};
alloc_info.sType = vk::StructureType::eCommandBufferAllocateInfo;
alloc_info.commandPool = pool->pool;
alloc_info.commandPool = m_pool;
alloc_info.level = vk::CommandBufferLevel::ePrimary;
alloc_info.commandBufferCount = pool->buffers_count;
alloc_info.commandBufferCount = 1;
if (graphics.device.allocateCommandBuffers(&alloc_info, pool->buffers.get()) !=
vk::Result::eSuccess) {
vk::CommandBuffer buffer = nullptr;
if (graphics.device.allocateCommandBuffers(&alloc_info, &buffer) != vk::Result::eSuccess) {
EXIT("Can't allocate command buffers");
}
for (uint32_t i = 0; i < pool->buffers_count; i++) {
pool->busy[i] = false;
vk::FenceCreateInfo fence_info {};
fence_info.sType = vk::StructureType::eFenceCreateInfo;
fence_info.flags = vk::FenceCreateFlagBits::eSignaled;
vk::FenceCreateInfo fence_info {};
fence_info.sType = vk::StructureType::eFenceCreateInfo;
fence_info.pNext = nullptr;
fence_info.flags = vk::FenceCreateFlagBits::eSignaled;
if (graphics.device.createFence(&fence_info, nullptr, &pool->fences[i]) !=
vk::Result::eSuccess) {
EXIT("Can't create fence");
}
vk::SemaphoreCreateInfo semaphore_info {};
semaphore_info.sType = vk::StructureType::eSemaphoreCreateInfo;
semaphore_info.pNext = nullptr;
semaphore_info.flags = {};
if (graphics.device.createSemaphore(&semaphore_info, nullptr, &pool->semaphores[i]) !=
vk::Result::eSuccess) {
EXIT("Can't create semaphore");
}
EXIT_IF(pool->buffers[i] == nullptr);
EXIT_IF(pool->fences[i] == nullptr);
EXIT_IF(pool->semaphores[i] == nullptr);
vk::Fence fence = nullptr;
if (graphics.device.createFence(&fence_info, nullptr, &fence) != vk::Result::eSuccess) {
graphics.device.freeCommandBuffers(m_pool, 1, &buffer);
EXIT("Can't create fence");
}
auto& slot = m_slots.emplace_back();
slot.pool_mutex = &m_mutex;
slot.id = static_cast<uint32_t>(m_slots.size() - 1);
slot.buffer = buffer;
slot.fence = fence;
return &slot;
}
void CommandPool::DeleteAll() {
auto& graphics = GetRenderContext().GetGraphics();
for (auto& pool: m_pools) {
if (pool != nullptr) {
for (uint32_t i = 0; i < pool->buffers_count; i++) {
graphics.device.destroySemaphore(pool->semaphores[i], nullptr);
graphics.device.destroyFence(pool->fences[i], nullptr);
}
graphics.device.freeCommandBuffers(pool->pool, pool->buffers_count,
pool->buffers.get());
graphics.device.destroyCommandPool(pool->pool, nullptr);
delete pool;
pool = nullptr;
}
CommandSlot* ThreadCommandPool::Allocate() {
Common::LockGuard lock(m_mutex);
if (m_pool == nullptr) {
Create();
}
auto it = std::ranges::find_if(m_slots, [](const auto& slot) { return !slot.busy; });
auto* slot = it != m_slots.end() ? &*it : CreateSlot();
slot->busy = true;
ResetNativeCommandBuffer(slot->buffer);
return slot;
}
void ThreadCommandPool::Destroy() {
Common::LockGuard lock(m_mutex);
if (m_pool == nullptr) {
return;
}
EXIT_IF(std::ranges::any_of(m_slots, [](const auto& slot) { return slot.busy; }));
auto& graphics = GetRenderContext().GetGraphics();
for (const auto& slot: m_slots) {
graphics.device.destroyFence(slot.fence, nullptr);
}
graphics.device.destroyCommandPool(m_pool, nullptr);
m_slots.clear();
m_pool = nullptr;
}
bool CommandBuffer::IsInvalid() const {
if (m_pool != nullptr) {
Common::LockGuard lock(m_pool->mutex);
return (m_index == static_cast<uint32_t>(-1) || m_index >= m_pool->buffers_count);
}
return true;
return m_slot == nullptr;
}
vk::CommandBuffer CommandBuffer::Handle() const {
EXIT_IF(IsInvalid());
const auto handle = m_pool->buffers[m_index];
const auto handle = m_slot->buffer;
EXIT_IF(handle == nullptr);
return handle;
}
void CommandBuffer::Allocate() {
EXIT_IF(!IsInvalid());
m_pool = g_command_pool.GetPool(m_queue);
Common::LockGuard lock(m_pool->mutex);
for (uint32_t i = 0; i < m_pool->buffers_count; i++) {
if (!m_pool->busy[i]) {
m_pool->busy[i] = true;
ResetNativeCommandBuffer(m_pool->buffers[i]);
m_index = i;
break;
}
}
EXIT_NOT_IMPLEMENTED(IsInvalid());
}
void CommandBuffer::Free() {
void CommandBuffer::Release() {
EXIT_IF(IsInvalid());
Common::LockGuard lock(m_pool->mutex);
Common::LockGuard lock(*m_slot->pool_mutex);
WaitForFence();
m_host_stream.Release();
m_pool->busy[m_index] = false;
ResetNativeCommandBuffer(m_pool->buffers[m_index]);
m_slot->busy = false;
ResetNativeCommandBuffer(m_slot->buffer);
ReleaseResourcesAfterFence();
m_index = static_cast<uint32_t>(-1);
m_slot = nullptr;
EXIT_NOT_IMPLEMENTED(!IsInvalid());
}
@@ -305,27 +259,22 @@ void CommandBuffer::Execute() {
Submit(nullptr, {}, nullptr);
}
void CommandBuffer::ExecuteWithSemaphore(vk::Semaphore signal_semaphore) {
Submit(nullptr, {}, ResolveSignalSemaphore(signal_semaphore));
}
void CommandBuffer::ExecuteWithSemaphore(vk::Semaphore wait_semaphore,
vk::PipelineStageFlags wait_stage,
vk::Semaphore signal_semaphore) {
EXIT_IF(wait_semaphore == nullptr);
Submit(wait_semaphore, wait_stage, ResolveSignalSemaphore(signal_semaphore));
EXIT_IF(wait_semaphore == nullptr || signal_semaphore == nullptr);
Submit(wait_semaphore, wait_stage, signal_semaphore);
}
void CommandBuffer::Submit(vk::Semaphore wait_semaphore, vk::PipelineStageFlags wait_stage,
vk::Semaphore signal_semaphore) {
RequireValidQueueId(m_queue);
EXIT_IF(IsInvalid());
EXIT_IF(m_execute);
const bool has_wait = wait_semaphore != nullptr;
const bool has_signal = signal_semaphore != nullptr;
auto buffer = Handle();
auto fence = m_pool->fences[m_index];
auto fence = m_slot->fence;
vk::SubmitInfo submit_info {};
submit_info.sType = vk::StructureType::eSubmitInfo;
@@ -339,7 +288,7 @@ void CommandBuffer::Submit(vk::Semaphore wait_semaphore, vk::PipelineStageFlags
submit_info.pSignalSemaphores = has_signal ? &signal_semaphore : nullptr;
auto& graphics = GetRenderContext().GetGraphics();
const auto& queue = graphics.queues[m_queue];
EXIT_IF(graphics.queue == nullptr);
auto result = graphics.device.resetFences(1, &fence);
if (result != vk::Result::eSuccess) {
@@ -348,46 +297,33 @@ void CommandBuffer::Submit(vk::Semaphore wait_semaphore, vk::PipelineStageFlags
}
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
if (queue.mutex != nullptr) {
queue.mutex->Lock();
}
if (Config::GraphicsDebugDumpEnabled()) {
LOGF("vkQueueSubmit begin: queue=%d index=%u wait_semaphore=%p signal_semaphore=%p"
LOGF("vkQueueSubmit begin: slot=%u wait_semaphore=%p signal_semaphore=%p"
" debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
m_queue, m_index, static_cast<void*>(wait_semaphore),
static_cast<void*>(signal_semaphore), m_debug_op, m_debug_submit_id, m_debug_arg0,
m_debug_arg1, m_debug_arg2, m_debug_arg3, m_debug_arg4);
m_slot->id, static_cast<void*>(wait_semaphore), static_cast<void*>(signal_semaphore),
m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3,
m_debug_arg4);
}
result = queue.vk_queue.submit(1, &submit_info, fence);
if (queue.mutex != nullptr) {
queue.mutex->Unlock();
{
Common::LockGuard lock(graphics.queue_mutex);
m_submit_seq = g_command_buffer_submit_seq.fetch_add(1, std::memory_order_relaxed) + 1;
result = graphics.queue.submit(1, &submit_info, fence);
}
m_execute = true;
m_fence_waited = false;
m_submit_seq = g_command_buffer_submit_seq.fetch_add(1, std::memory_order_relaxed) + 1;
if (result != vk::Result::eSuccess) {
LOGF("vkQueueSubmit failed: %s (%d), queue=%d index=%u submit_seq=%" PRIu64
LOGF("vkQueueSubmit failed: %s (%d), slot=%u submit_seq=%" PRIu64
" debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
VulkanToString(result).c_str(), static_cast<int>(result), m_queue, m_index,
m_submit_seq, m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2,
m_debug_arg3, m_debug_arg4);
VulkanToString(result).c_str(), static_cast<int>(result), m_slot->id, m_submit_seq,
m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3,
m_debug_arg4);
}
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
}
vk::Semaphore CommandBuffer::ResolveSignalSemaphore(vk::Semaphore semaphore) const {
if (semaphore != nullptr) {
return semaphore;
}
EXIT_IF(IsInvalid());
return m_pool->semaphores[m_index];
}
void CommandBuffer::WaitForFence() {
FinalizeFence(false);
}
@@ -398,13 +334,13 @@ void CommandBuffer::WaitForFenceOnly() {
return;
}
auto device = GetRenderContext().GetGraphics().device;
auto result = device.waitForFences(1, &m_pool->fences[m_index], VK_TRUE, UINT64_MAX);
auto result = device.waitForFences(1, &m_slot->fence, VK_TRUE, UINT64_MAX);
if (result != vk::Result::eSuccess) {
LOGF("vkWaitForFences failed: %s (%d), queue=%d index=%u submit_seq=%" PRIu64
LOGF("vkWaitForFences failed: %s (%d), slot=%u submit_seq=%" PRIu64
" debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
VulkanToString(result).c_str(), static_cast<int>(result), m_queue, m_index,
m_submit_seq, m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2,
m_debug_arg3, m_debug_arg4);
VulkanToString(result).c_str(), static_cast<int>(result), m_slot->id, m_submit_seq,
m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3,
m_debug_arg4);
}
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
m_fence_waited = true;
@@ -421,7 +357,7 @@ void CommandBuffer::FinalizeFence(bool reset_recording) {
m_execute = false;
m_fence_waited = false;
if (reset_recording) {
ResetNativeCommandBuffer(m_pool->buffers[m_index]);
ResetNativeCommandBuffer(m_slot->buffer);
m_recording_generation++;
}
}
@@ -24,7 +24,7 @@ DummyTextureCache::~DummyTextureCache() {
if (!populated(m_sampled) && !populated(m_storage)) {
return;
}
Transfer::WaitForGraphicsIdle();
Transfer::WaitForQueueIdle();
const auto destroy = [](auto& slots) {
for (auto& slot: slots) {
if (slot.image != nullptr) {
@@ -42,8 +42,7 @@ VulkanImage& DummyTextureCache::Get(Usage usage, bool uint_format, bool image_3d
auto& slots = usage == Usage::Storage ? m_storage : m_sampled;
auto& slot = slots[DummyTextureIndex(uint_format, image_3d)];
if (slot.image == nullptr) {
slot.image = ImageOps::CreateDummyTexture(uint_format, image_3d,
usage == Usage::Storage);
slot.image = ImageOps::CreateDummyTexture(uint_format, image_3d, usage == Usage::Storage);
}
return *slot.image;
}
+37 -44
View File
@@ -50,8 +50,7 @@ TextureImageCreateParams MakeImageParams(const ImageInfo& info, bool storage) {
return params;
}
bool RenderTargetSupportsStorage(vk::Format format,
vk::ImageCreateFlags flags) {
bool RenderTargetSupportsStorage(vk::Format format, vk::ImageCreateFlags flags) {
const auto compatible = SrgbStorageViewFormat(format);
const auto required_flags =
vk::ImageCreateFlagBits::eMutableFormat | vk::ImageCreateFlagBits::eExtendedUsage;
@@ -72,12 +71,12 @@ vk::ImageCreateFlags RenderTargetCreateFlags(vk::Format format) {
: vk::ImageCreateFlags {0};
}
vk::ImageUsageFlags RenderTargetUsage(vk::Format format,
vk::ImageCreateFlags flags, uint32_t samples) {
vk::ImageUsageFlags RenderTargetUsage(vk::Format format, vk::ImageCreateFlags flags,
uint32_t samples) {
auto& graphics = GetRenderContext().GetGraphics();
auto usage = static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eColorAttachment) |
static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eTransferSrc) |
static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eTransferDst);
auto usage = static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eColorAttachment) |
static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eTransferSrc) |
static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eTransferDst);
if (samples == 1) {
usage |= vk::ImageUsageFlagBits::eSampled;
if (RenderTargetSupportsStorage(format, flags)) {
@@ -155,21 +154,19 @@ GpuTextureVulkanImage* CreateTexture(const ImageInfo& info, bool storage,
return image;
}
void CreateTextureViews(GpuTextureVulkanImage& image,
const ImageInfo& info, bool storage, vk::ComponentMapping components) {
void CreateTextureViews(GpuTextureVulkanImage& image, const ImageInfo& info, bool storage,
vk::ComponentMapping components) {
if (storage) {
TextureCreateImageViews(image, components, info.type, 0, 0, 1, info.depth, false,
TextureFormatUsage::Sampled | TextureFormatUsage::Storage);
} else {
TextureCreateImageViews(image, components, info.type, info.base_array,
info.base_level, info.view_levels, info.depth, true,
TextureFormatUsage::Sampled);
TextureCreateImageViews(image, components, info.type, info.base_array, info.base_level,
info.view_levels, info.depth, true, TextureFormatUsage::Sampled);
}
}
void UploadRenderTargetLayers(RenderTextureVulkanImage& image,
const RenderTargetInfo& info, uint32_t base_layer,
uint32_t layer_count, bool refresh) {
void UploadRenderTargetLayers(RenderTextureVulkanImage& image, const RenderTargetInfo& info,
uint32_t base_layer, uint32_t layer_count, bool refresh) {
if (info.layers == 0 || info.size % info.layers != 0 || layer_count == 0 ||
base_layer >= info.layers || layer_count > info.layers - base_layer ||
base_layer >= image.layers || layer_count > image.layers - base_layer) {
@@ -182,7 +179,7 @@ void UploadRenderTargetLayers(RenderTextureVulkanImage& image,
info.samples, image.samples);
}
if (refresh) {
Transfer::WaitForGraphicsIdle();
Transfer::WaitForQueueIdle();
}
const auto slice_size = info.size / info.layers;
const auto upload_size = slice_size * layer_count;
@@ -207,9 +204,9 @@ void UploadRenderTargetLayers(RenderTextureVulkanImage& image,
region.dst_layer += base_layer;
}
const auto source_address = info.address + slice_size * base_layer;
TextureUploadGuestImage(image, reinterpret_cast<const void*>(source_address),
upload_size, regions, layout, format, info.width, info.height,
layer_count, info.levels, "TextureCache render target",
TextureUploadGuestImage(image, reinterpret_cast<const void*>(source_address), upload_size,
regions, layout, format, info.width, info.height, layer_count,
info.levels, "TextureCache render target",
vk::ImageLayout::eGeneral);
return;
}
@@ -221,23 +218,22 @@ void UploadRenderTargetLayers(RenderTextureVulkanImage& image,
"TextureCache render target");
auto regions = TextureBuildUploadRegions(layout, info.format, info.width, info.height, 1, 1,
true, false, TextureUploadDestination::MipLevels);
TextureUploadGuestImage(image, reinterpret_cast<const void*>(info.address),
slice_size, regions, layout, format, info.width, info.height, 1, 1,
TextureUploadGuestImage(image, reinterpret_cast<const void*>(info.address), slice_size,
regions, layout, format, info.width, info.height, 1, 1,
"TextureCache render target", vk::ImageLayout::eGeneral);
} else {
Transfer::UploadImage(image, reinterpret_cast<const void*>(info.address),
slice_size, info.pitch, vk::ImageLayout::eGeneral);
Transfer::UploadImage(image, reinterpret_cast<const void*>(info.address), slice_size,
info.pitch, vk::ImageLayout::eGeneral);
}
}
void UploadRenderTarget(RenderTextureVulkanImage& image,
const RenderTargetInfo& info, bool refresh) {
void UploadRenderTarget(RenderTextureVulkanImage& image, const RenderTargetInfo& info,
bool refresh) {
UploadRenderTargetLayers(image, info, 0, info.layers, refresh);
}
RenderTextureVulkanImage* CreateRenderTarget(
const RenderTargetInfo& info) {
auto& graphics = GetRenderContext().GetGraphics();
RenderTextureVulkanImage* CreateRenderTarget(const RenderTargetInfo& info) {
auto& graphics = GetRenderContext().GetGraphics();
auto* image = new RenderTextureVulkanImage;
image->extent.width = info.width;
image->extent.height = info.height;
@@ -270,7 +266,7 @@ RenderTextureVulkanImage* CreateRenderTarget(
}
DepthStencilVulkanImage* CreateDepthTarget(const DepthTargetInfo& info) {
auto& graphics = GetRenderContext().GetGraphics();
auto& graphics = GetRenderContext().GetGraphics();
vk::ImageCreateInfo create {};
create.sType = vk::StructureType::eImageCreateInfo;
create.imageType = vk::ImageType::e2D;
@@ -350,7 +346,7 @@ void ValidateVideoOut(const VideoOutInfo& info) {
}
VideoOutVulkanImage* CreateVideoOut(const VideoOutInfo& info) {
auto& graphics = GetRenderContext().GetGraphics();
auto& graphics = GetRenderContext().GetGraphics();
auto* image = new VideoOutVulkanImage;
image->extent.width = info.width;
image->extent.height = info.height;
@@ -379,15 +375,14 @@ VideoOutVulkanImage* CreateVideoOut(const VideoOutInfo& info) {
return image;
}
void UploadVideoOut(VideoOutVulkanImage& image, const VideoOutInfo& info,
bool refresh) {
void UploadVideoOut(VideoOutVulkanImage& image, const VideoOutInfo& info, bool refresh) {
if (info.compression != VideoOutCompression::Uncompressed) {
EXIT("TextureCache: compressed video-out guest upload is unsupported, "
"addr=0x%016" PRIx64 " metadata=0x%016" PRIx64 " dcc=0x%08" PRIx32 "\n",
info.address, info.metadata_address, info.dcc_control);
}
if (refresh) {
Transfer::WaitForGraphicsIdle();
Transfer::WaitForQueueIdle();
}
image.layout = vk::ImageLayout::eUndefined;
if (!info.bgra16) {
@@ -396,9 +391,9 @@ void UploadVideoOut(VideoOutVulkanImage& image, const VideoOutInfo& info,
info.tile_mode, info.size, false, false, "VideoOut");
auto regions = TextureBuildUploadRegions(layout, info.format, info.width, info.height, 1, 1,
false, false, TextureUploadDestination::MipLevels);
TextureUploadGuestImage(image, reinterpret_cast<const void*>(info.address),
info.size, regions, layout, info.guest_format, info.width,
info.height, 1, 1, "VideoOut", vk::ImageLayout::eGeneral);
TextureUploadGuestImage(image, reinterpret_cast<const void*>(info.address), info.size,
regions, layout, info.guest_format, info.width, info.height, 1, 1,
"VideoOut", vk::ImageLayout::eGeneral);
return;
}
Transfer::ScratchBuffer scratch(info.size);
@@ -416,11 +411,10 @@ void UploadVideoOut(VideoOutVulkanImage& image, const VideoOutInfo& info,
info.height,
1,
info.pitch};
GpuDetile(reinterpret_cast<const void*>(info.address), scratch.Data(), info.size,
info.size, std::span<const GpuTileInfo>(&tile_info, 1));
GpuDetile(reinterpret_cast<const void*>(info.address), scratch.Data(), info.size, info.size,
std::span<const GpuTileInfo>(&tile_info, 1));
SwapVideoOutBgra16(scratch.Data(), info.size);
Transfer::UploadImage(image, scratch.Data(), info.size, info.pitch,
vk::ImageLayout::eGeneral);
Transfer::UploadImage(image, scratch.Data(), info.size, info.pitch, vk::ImageLayout::eGeneral);
}
void SwapVideoOutBgra16(void* data, uint64_t size) {
@@ -430,8 +424,7 @@ void SwapVideoOutBgra16(void* data, uint64_t size) {
}
}
GpuTextureVulkanImage* CreateDummyTexture(bool uint_format, bool image_3d,
bool storage) {
GpuTextureVulkanImage* CreateDummyTexture(bool uint_format, bool image_3d, bool storage) {
auto* image = storage ? static_cast<GpuTextureVulkanImage*>(new StorageTextureVulkanImage)
: new TextureVulkanImage;
auto usage = storage ? TextureFormatUsage::Storage : TextureFormatUsage::Sampled;
@@ -443,8 +436,8 @@ GpuTextureVulkanImage* CreateDummyTexture(bool uint_format, bool image_3d,
static constexpr uint32_t zero = 0;
Transfer::UploadImage(*image, &zero, sizeof(zero), 1, layout);
TextureCreateImageViews(*image, components, params.type, 0, params.base_level,
params.levels, params.depth, params.allow_cube_view, params.view_usage);
TextureCreateImageViews(*image, components, params.type, 0, params.base_level, params.levels,
params.depth, params.allow_cube_view, params.view_usage);
return image;
}
+12 -20
View File
@@ -26,7 +26,7 @@ struct DepthStencilVulkanImage;
struct TextureVulkanImage;
struct StorageTextureVulkanImage;
struct RenderTextureVulkanImage;
struct VulkanCommandPool;
struct CommandSlot;
struct VulkanBuffer;
struct VulkanDescriptorSet;
struct VulkanFramebuffer;
@@ -68,19 +68,16 @@ private:
class CommandBuffer {
public:
explicit CommandBuffer(int queue);
~CommandBuffer() { Free(); }
CommandBuffer();
~CommandBuffer() { Release(); }
KYTY_CLASS_NO_COPY(CommandBuffer);
[[nodiscard]] bool IsInvalid() const;
void Allocate();
void Free();
void Begin() const;
void End() const;
void Execute();
void ExecuteWithSemaphore(vk::Semaphore signal_semaphore = nullptr);
void ExecuteWithSemaphore(vk::Semaphore wait_semaphore, vk::PipelineStageFlags wait_stage,
vk::Semaphore signal_semaphore);
void SetDebugInfo(uint32_t op, uint64_t submit_id, uint32_t arg0 = 0, uint32_t arg1 = 0,
@@ -97,7 +94,6 @@ public:
[[nodiscard]] vk::CommandBuffer Handle() const;
[[nodiscard]] GraphicContext& GetGraphics() const noexcept { return m_graphics; }
[[nodiscard]] int GetQueue() const { return m_queue; }
[[nodiscard]] bool IsExecute() const { return m_execute; }
[[nodiscard]] uint64_t GetRecordingGeneration() const { return m_recording_generation; }
@@ -106,16 +102,14 @@ private:
void Submit(vk::Semaphore wait_semaphore, vk::PipelineStageFlags wait_stage,
vk::Semaphore signal_semaphore);
[[nodiscard]] vk::Semaphore ResolveSignalSemaphore(vk::Semaphore semaphore) const;
void FinalizeFence(bool reset_recording);
void ReleaseResourcesAfterFence();
void DeleteBuffersAfterFence();
void RecycleDescriptorsAfterFence();
void Release();
void FinalizeFence(bool reset_recording);
void ReleaseResourcesAfterFence();
void DeleteBuffersAfterFence();
void RecycleDescriptorsAfterFence();
GraphicContext& m_graphics;
VulkanCommandPool* m_pool = nullptr;
uint32_t m_index = static_cast<uint32_t>(-1);
int m_queue = -1;
CommandSlot* m_slot = nullptr;
bool m_execute = false;
bool m_fence_waited = false;
uint64_t m_submit_seq = 0;
@@ -135,10 +129,8 @@ private:
class RenderCommandBuffer final: public CommandBuffer {
public:
RenderCommandBuffer(int queue, HW::Context& registers, HW::UserConfig& user_config,
HW::Shader& shaders)
: CommandBuffer(queue), m_registers(registers), m_user_config(user_config),
m_shaders(shaders) {}
RenderCommandBuffer(HW::Context& registers, HW::UserConfig& user_config, HW::Shader& shaders)
: m_registers(registers), m_user_config(user_config), m_shaders(shaders) {}
[[nodiscard]] HW::Context& GetRegisters() const noexcept { return m_registers; }
[[nodiscard]] HW::UserConfig& GetUserConfig() const noexcept { return m_user_config; }
@@ -162,7 +154,7 @@ void RenderDispatchDirect(uint64_t submit_id, RenderCommandBuffer& buffer, uint3
uint32_t thread_group_y, uint32_t thread_group_z, uint32_t mode);
void GraphicsRenderInit(GraphicContext& graphics);
void GraphicsRenderReleaseThreadCommandPools();
void GraphicsRenderReleaseThreadCommandPool();
[[nodiscard]] bool ResolveComputeImageClear(const ShaderComputeInputInfo& input, uint32_t group_x,
uint32_t group_y, uint32_t group_z, uint32_t mode,
@@ -9,7 +9,7 @@
namespace Libs::Graphics {
// Owner-tracked shared buffer/image transaction. External faults pause GPU submissions first;
// command-processor faults drain their queue before entering this transaction.
// command-processor faults drain pending guest processors before entering this transaction.
class ResourceMutex final {
public:
class FaultScope final {
@@ -1008,7 +1008,7 @@ void TextureCache::MaterializeImagesToGuestLocked(
const std::vector<std::shared_ptr<CachedImage>>& images) {
if (std::any_of(images.begin(), images.end(),
[](const auto& cached) { return cached->gpu_modified; })) {
Transfer::WaitForGraphicsIdle();
Transfer::WaitForQueueIdle();
}
for (const auto& cached: images) {
if (!cached->gpu_modified) {
@@ -1269,7 +1269,7 @@ void TextureCache::RetireSampledTargetAliases(const ImageInfo& requested) {
}
RequireRetirementIsolation(retire, "sampled target", requested.address, requested.size);
if (wait_idle) {
Transfer::WaitForGraphicsIdle();
Transfer::WaitForQueueIdle();
}
for (auto* cached: retire) {
if (!cached->gpu_modified) {
@@ -1360,7 +1360,7 @@ void TextureCache::RetireStorageDepthAliasLocked(const ImageInfo& requested) {
if (selected == nullptr) {
return;
}
Transfer::WaitForGraphicsIdle();
Transfer::WaitForQueueIdle();
const auto transfer = m_readback->DownloadDepthTarget(*selected, false);
for (const auto& range: transfer.Ranges()) {
m_memory_tracker.ForEachDownloadRange<true>(range.address, range.size,
@@ -1373,7 +1373,7 @@ void TextureCache::RetireStorageDepthAliasLocked(const ImageInfo& requested) {
TextureCache::~TextureCache() {
m_readback.reset();
if (!m_images.empty()) {
Transfer::WaitForGraphicsIdle();
Transfer::WaitForQueueIdle();
}
for (const auto& image: m_images) {
UnregisterImageLocked(*image, false);
@@ -1488,7 +1488,7 @@ VulkanImage& TextureCache::FindTexture(CommandBuffer& command, const ImageInfo&
// Kyty does not yet copy between those independently allocated Vulkan images, so use the
// existing synchronized tiled readback seam and rebuild the complete chain from coherent
// guest backing. This is an uncommon ownership transition, not a frame lookup fast path.
Transfer::WaitForGraphicsIdle();
Transfer::WaitForQueueIdle();
for (auto* cached: storage_retire) {
if (!cached->gpu_modified || cached->buffer_modified || cached->info.IsCpuDirty() ||
!m_memory_tracker.IsRegionGpuModified(cached->info.address, cached->info.size) ||
@@ -2632,7 +2632,7 @@ void TextureCache::UnregisterVideoOutSurfaces(const std::vector<VideoOutVulkanIm
cached->Address(), cached->Size());
}
}
Transfer::WaitForGraphicsIdle();
Transfer::WaitForQueueIdle();
for (auto* cached: selected) {
if (cached->gpu_modified) {
m_memory_tracker.UnmarkRegionAsGpuModified(cached->Address(), cached->Size());
@@ -2992,7 +2992,7 @@ void TextureCache::SynchronizeColorImageToBufferLocked(CachedImage& cached, uint
target.address, target.size);
}
Transfer::WaitForGraphicsIdle();
Transfer::WaitForQueueIdle();
std::vector<ImageBufferCopy> regions;
std::vector<BufferImageCopy> tiled_regions;
TextureUploadLayout tiled_layout {};
@@ -3100,7 +3100,7 @@ void TextureCache::SynchronizeDepthImageToBufferLocked(CachedImage& cached, uint
info.layers, static_cast<int>(info.format), info.guest_format, info.bytes_per_element,
has_stencil, has_htile, cached.gpu_modified, cached.buffer_modified);
}
Transfer::WaitForGraphicsIdle();
Transfer::WaitForQueueIdle();
const auto regions = Transfer::MakeLayeredImageBufferCopies(
1, info.size, info.pitch, info.width, info.height, vk::ImageAspectFlagBits::eDepth);
TileBlockLayout block {};
@@ -3790,7 +3790,7 @@ void TextureCache::UnmapMemory(uint64_t vaddr, uint64_t size) {
}
}
if (wait_idle) {
Transfer::WaitForGraphicsIdle();
Transfer::WaitForQueueIdle();
}
for (auto& cached: m_images) {
if (!cached->OverlapsRange(vaddr, size, false) || !cached->gpu_modified) {
+3 -3
View File
@@ -106,7 +106,7 @@ void UploadPromotedD16Depth(DepthStencilVulkanImage& image, const DepthTargetInf
void Tiler::DetileImage(GpuTextureVulkanImage& image, const ImageInfo& info,
const BufferImageCopySource& source, bool refresh, bool storage) const {
if (refresh) Transfer::WaitForGraphicsIdle();
if (refresh) Transfer::WaitForQueueIdle();
const bool array_texture = TextureIsLayeredTexture(info.type);
const bool volume_texture = TextureIs3DTexture(info.type);
@@ -127,7 +127,7 @@ void Tiler::DetileImage(DepthStencilVulkanImage& image, const DepthTargetInfo& i
const BufferImageCopySource& source, bool refresh,
uint32_t base_layer) const {
EXIT_NOT_IMPLEMENTED(info.samples != 1 || image.samples != 1);
if (refresh) Transfer::WaitForGraphicsIdle();
if (refresh) Transfer::WaitForQueueIdle();
if (DepthAspectTransferBytes(info.format) != info.bytes_per_element) {
switch (info.format) {
@@ -149,7 +149,7 @@ void Tiler::DetileStencil(DepthStencilVulkanImage& image, const DepthTargetInfo&
const BufferImageCopySource& source, bool refresh,
uint32_t base_layer) const {
EXIT_NOT_IMPLEMENTED(info.samples != 1 || image.samples != 1);
if (refresh) Transfer::WaitForGraphicsIdle();
if (refresh) Transfer::WaitForQueueIdle();
const auto format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt);
const auto pitch = TileGetTexturePitch(format, info.width, 1,