mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 11:23:49 +00:00
unify graphics and compute on one Vulkan queue
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
@@ -23,8 +24,6 @@ public:
|
|||||||
CommandScheduler(HW::Context& registers, HW::UserConfig& user_config, HW::Shader& shaders)
|
CommandScheduler(HW::Context& registers, HW::UserConfig& user_config, HW::Shader& shaders)
|
||||||
: m_registers(registers), m_user_config(user_config), m_shaders(shaders) {}
|
: m_registers(registers), m_user_config(user_config), m_shaders(shaders) {}
|
||||||
|
|
||||||
void SetQueue(int queue) { m_queue = queue; }
|
|
||||||
int Queue() const { return m_queue; }
|
|
||||||
bool Active() const { return m_current >= 0 && m_current < BuffersNum; }
|
bool Active() const { return m_current >= 0 && m_current < BuffersNum; }
|
||||||
void CheckActive() const { EXIT_IF(!Active()); }
|
void CheckActive() const { EXIT_IF(!Active()); }
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@ public:
|
|||||||
}
|
}
|
||||||
for (auto& buf: m_buffers) {
|
for (auto& buf: m_buffers) {
|
||||||
EXIT_IF(buf != nullptr);
|
EXIT_IF(buf != nullptr);
|
||||||
buf = new RenderCommandBuffer(m_queue, m_registers, m_user_config, m_shaders);
|
buf = new RenderCommandBuffer(m_registers, m_user_config, m_shaders);
|
||||||
}
|
}
|
||||||
m_current = 0;
|
m_current = 0;
|
||||||
Current().Begin();
|
Current().Begin();
|
||||||
@@ -103,7 +102,6 @@ private:
|
|||||||
|
|
||||||
RenderCommandBuffer* m_buffers[BuffersNum] = {};
|
RenderCommandBuffer* m_buffers[BuffersNum] = {};
|
||||||
int m_current = -1;
|
int m_current = -1;
|
||||||
int m_queue = -1;
|
|
||||||
HW::Context& m_registers;
|
HW::Context& m_registers;
|
||||||
HW::UserConfig& m_user_config;
|
HW::UserConfig& m_user_config;
|
||||||
HW::Shader& m_shaders;
|
HW::Shader& m_shaders;
|
||||||
@@ -118,7 +116,7 @@ public:
|
|||||||
int64_t flip_arg = 0;
|
int64_t flip_arg = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
CommandProcessor(): m_scheduler(m_ctx, m_ucfg, m_sh_ctx) {}
|
CommandProcessor();
|
||||||
~CommandProcessor() { KYTY_NOT_IMPLEMENTED; }
|
~CommandProcessor() { KYTY_NOT_IMPLEMENTED; }
|
||||||
|
|
||||||
KYTY_CLASS_NO_COPY(CommandProcessor);
|
KYTY_CLASS_NO_COPY(CommandProcessor);
|
||||||
@@ -226,9 +224,6 @@ public:
|
|||||||
|
|
||||||
void Run(uint32_t* data, uint32_t num_dw);
|
void Run(uint32_t* data, uint32_t num_dw);
|
||||||
|
|
||||||
void SetQueue(int queue);
|
|
||||||
[[nodiscard]] int GetQueue() const { return m_scheduler.Queue(); }
|
|
||||||
|
|
||||||
[[nodiscard]] const FlipInfo& GetFlip() const { return m_flip; }
|
[[nodiscard]] const FlipInfo& GetFlip() const { return m_flip; }
|
||||||
void SetFlip(const FlipInfo& flip) { m_flip = flip; }
|
void SetFlip(const FlipInfo& flip) { m_flip = flip; }
|
||||||
|
|
||||||
@@ -264,11 +259,11 @@ private:
|
|||||||
uint64_t m_dispatch_indirect_args_base_addr = 0;
|
uint64_t m_dispatch_indirect_args_base_addr = 0;
|
||||||
uint32_t m_num_instances = 1;
|
uint32_t m_num_instances = 1;
|
||||||
|
|
||||||
inline static Common::Mutex m_mutex;
|
inline static Common::Mutex m_mutex;
|
||||||
inline static std::array<CommandProcessor*, GraphicContext::QUEUES_NUM> m_processors {};
|
inline static std::vector<CommandProcessor*> m_processors;
|
||||||
inline static bool m_readback_active = false;
|
inline static bool m_readback_active = false;
|
||||||
inline static bool m_readback_finished = false;
|
inline static bool m_readback_finished = false;
|
||||||
Common::Mutex m_run_mutex;
|
Common::Mutex m_run_mutex;
|
||||||
|
|
||||||
CommandScheduler m_scheduler;
|
CommandScheduler m_scheduler;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#include "graphics/guest_gpu/command_processor/pm4Dispatch.h"
|
#include "graphics/guest_gpu/command_processor/pm4Dispatch.h"
|
||||||
#include "graphics/guest_gpu/hardwareContext.h"
|
#include "graphics/guest_gpu/hardwareContext.h"
|
||||||
#include "graphics/guest_gpu/pm4.h"
|
#include "graphics/guest_gpu/pm4.h"
|
||||||
#include "graphics/host_gpu/graphicContext.h"
|
|
||||||
#include "graphics/host_gpu/objects/label.h"
|
#include "graphics/host_gpu/objects/label.h"
|
||||||
#include "graphics/host_gpu/renderer/render.h"
|
#include "graphics/host_gpu/renderer/render.h"
|
||||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||||
@@ -176,9 +175,6 @@ public:
|
|||||||
Start();
|
Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] int GetQueueId() const { return m_queue_id; }
|
|
||||||
void SetQueueId(int id) { m_queue_id = id; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Start() {
|
void Start() {
|
||||||
Common::Thread t(ThreadRun, this);
|
Common::Thread t(ThreadRun, this);
|
||||||
@@ -193,8 +189,7 @@ private:
|
|||||||
bool m_done = true;
|
bool m_done = true;
|
||||||
bool m_idle = true;
|
bool m_idle = true;
|
||||||
|
|
||||||
CommandProcessor* m_cp = nullptr;
|
CommandProcessor* m_cp = nullptr;
|
||||||
int m_queue_id = -1;
|
|
||||||
|
|
||||||
struct DirectBatch {
|
struct DirectBatch {
|
||||||
OwnedCmdBuffer buffer;
|
OwnedCmdBuffer buffer;
|
||||||
@@ -206,6 +201,10 @@ private:
|
|||||||
|
|
||||||
class Gpu {
|
class Gpu {
|
||||||
public:
|
public:
|
||||||
|
static constexpr uint32_t ComputePipeCount = 7;
|
||||||
|
static constexpr uint32_t RingsPerComputePipe = 8;
|
||||||
|
static constexpr uint32_t ComputeRingCount = ComputePipeCount * RingsPerComputePipe;
|
||||||
|
|
||||||
Gpu() {
|
Gpu() {
|
||||||
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
||||||
Init();
|
Init();
|
||||||
@@ -228,15 +227,15 @@ private:
|
|||||||
void Init();
|
void Init();
|
||||||
void WaitLocked();
|
void WaitLocked();
|
||||||
|
|
||||||
ComputeRing* GetRing(uint32_t ring_id);
|
ComputeRing* GetComputeRing(uint32_t ring_index);
|
||||||
|
|
||||||
Common::Mutex m_mutex;
|
Common::Mutex m_mutex;
|
||||||
|
|
||||||
CommandProcessor* m_gfx_cp = nullptr;
|
CommandProcessor* m_gfx_cp = nullptr;
|
||||||
GraphicsRing* m_gfx_ring = nullptr;
|
GraphicsRing* m_gfx_ring = nullptr;
|
||||||
|
|
||||||
CommandProcessor* m_compute_cp[8] = {};
|
std::array<CommandProcessor*, ComputePipeCount> m_compute_cp {};
|
||||||
ComputeRing* m_compute_ring[64] = {};
|
std::array<ComputeRing*, ComputeRingCount> m_compute_ring {};
|
||||||
|
|
||||||
std::atomic_int m_done_num = 0;
|
std::atomic_int m_done_num = 0;
|
||||||
};
|
};
|
||||||
@@ -274,19 +273,11 @@ void Gpu::SubmitCompute(uint32_t queue, uint32_t* cmd_buffer, uint32_t num_dw,
|
|||||||
GpuMutexLock lock(m_mutex);
|
GpuMutexLock lock(m_mutex);
|
||||||
|
|
||||||
constexpr uint32_t compute_queue_base = 0x20u;
|
constexpr uint32_t compute_queue_base = 0x20u;
|
||||||
constexpr uint32_t compute_queue_num = 7u * 8u;
|
|
||||||
EXIT_NOT_IMPLEMENTED(queue < compute_queue_base ||
|
EXIT_NOT_IMPLEMENTED(queue < compute_queue_base ||
|
||||||
queue >= compute_queue_base + compute_queue_num);
|
queue >= compute_queue_base + ComputeRingCount);
|
||||||
|
|
||||||
uint32_t compute_queue = queue - compute_queue_base;
|
uint32_t compute_queue = queue - compute_queue_base;
|
||||||
uint32_t pipe_id = (compute_queue >> 3u) & 0x7u;
|
auto* ring = GetComputeRing(compute_queue);
|
||||||
uint32_t queue_id = compute_queue & 0x7u;
|
|
||||||
EXIT_NOT_IMPLEMENTED(pipe_id >= 7u);
|
|
||||||
EXIT_NOT_IMPLEMENTED(queue_id >= 8u);
|
|
||||||
|
|
||||||
uint32_t ring_id = compute_queue + 1u;
|
|
||||||
|
|
||||||
auto* ring = GetRing(ring_id);
|
|
||||||
|
|
||||||
ring->Submit(std::move(buffer), trigger_agc_interrupt_on_done);
|
ring->Submit(std::move(buffer), trigger_agc_interrupt_on_done);
|
||||||
}
|
}
|
||||||
@@ -297,10 +288,10 @@ void Gpu::SubmitFlipPreparation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Gpu::Done() {
|
void Gpu::Done() {
|
||||||
GraphicsRing* gfx_ring = nullptr;
|
GraphicsRing* gfx_ring = nullptr;
|
||||||
CommandProcessor* gfx_cp = nullptr;
|
CommandProcessor* gfx_cp = nullptr;
|
||||||
ComputeRing* compute_rings[64] {};
|
std::array<ComputeRing*, ComputeRingCount> compute_rings {};
|
||||||
CommandProcessor* compute_cps[8] {};
|
std::array<CommandProcessor*, ComputePipeCount> compute_cps {};
|
||||||
|
|
||||||
{
|
{
|
||||||
GpuMutexLock lock(m_mutex);
|
GpuMutexLock lock(m_mutex);
|
||||||
@@ -308,8 +299,8 @@ void Gpu::Done() {
|
|||||||
gfx_ring = m_gfx_ring;
|
gfx_ring = m_gfx_ring;
|
||||||
gfx_cp = m_gfx_cp;
|
gfx_cp = m_gfx_cp;
|
||||||
|
|
||||||
std::copy(std::begin(m_compute_ring), std::end(m_compute_ring), std::begin(compute_rings));
|
compute_rings = m_compute_ring;
|
||||||
std::copy(std::begin(m_compute_cp), std::end(m_compute_cp), std::begin(compute_cps));
|
compute_cps = m_compute_cp;
|
||||||
|
|
||||||
m_done_num++;
|
m_done_num++;
|
||||||
}
|
}
|
||||||
@@ -347,15 +338,15 @@ int Gpu::GetFrameNum() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Gpu::WaitLocked() {
|
void Gpu::WaitLocked() {
|
||||||
GraphicsRing* gfx_ring = nullptr;
|
GraphicsRing* gfx_ring = nullptr;
|
||||||
CommandProcessor* gfx_cp = nullptr;
|
CommandProcessor* gfx_cp = nullptr;
|
||||||
ComputeRing* compute_rings[64] {};
|
std::array<ComputeRing*, ComputeRingCount> compute_rings {};
|
||||||
CommandProcessor* compute_cps[8] {};
|
std::array<CommandProcessor*, ComputePipeCount> compute_cps {};
|
||||||
|
|
||||||
gfx_ring = m_gfx_ring;
|
gfx_ring = m_gfx_ring;
|
||||||
gfx_cp = m_gfx_cp;
|
gfx_cp = m_gfx_cp;
|
||||||
std::copy(std::begin(m_compute_ring), std::end(m_compute_ring), std::begin(compute_rings));
|
compute_rings = m_compute_ring;
|
||||||
std::copy(std::begin(m_compute_cp), std::end(m_compute_cp), std::begin(compute_cps));
|
compute_cps = m_compute_cp;
|
||||||
|
|
||||||
if (gfx_ring != nullptr) {
|
if (gfx_ring != nullptr) {
|
||||||
gfx_ring->WaitForIdle();
|
gfx_ring->WaitForIdle();
|
||||||
@@ -381,40 +372,28 @@ void Gpu::Init() {
|
|||||||
|
|
||||||
m_gfx_cp = new CommandProcessor;
|
m_gfx_cp = new CommandProcessor;
|
||||||
m_gfx_ring = new GraphicsRing;
|
m_gfx_ring = new GraphicsRing;
|
||||||
m_gfx_cp->SetQueue(GraphicContext::QUEUE_GFX);
|
|
||||||
m_gfx_ring->SetCp(*m_gfx_cp);
|
m_gfx_ring->SetCp(*m_gfx_cp);
|
||||||
|
|
||||||
EXIT_IF(GraphicContext::QUEUE_COMPUTE_NUM < 8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ComputeRing* Gpu::GetRing(uint32_t ring_id) {
|
ComputeRing* Gpu::GetComputeRing(uint32_t ring_index) {
|
||||||
int v = static_cast<int>(ring_id - 1);
|
EXIT_IF(ring_index >= ComputeRingCount);
|
||||||
int pipe_id = v / 8;
|
const auto pipe_id = ring_index / RingsPerComputePipe;
|
||||||
int queue_id = v % 8;
|
|
||||||
|
|
||||||
if (m_compute_cp[pipe_id] == nullptr) {
|
if (m_compute_cp[pipe_id] == nullptr) {
|
||||||
m_compute_cp[pipe_id] = new CommandProcessor;
|
m_compute_cp[pipe_id] = new CommandProcessor;
|
||||||
m_compute_cp[pipe_id]->SetQueue(GraphicContext::QUEUE_COMPUTE_START + pipe_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_compute_ring[v] == nullptr) {
|
if (m_compute_ring[ring_index] == nullptr) {
|
||||||
m_compute_ring[v] = new ComputeRing;
|
m_compute_ring[ring_index] = new ComputeRing;
|
||||||
m_compute_ring[v]->SetQueueId(queue_id);
|
m_compute_ring[ring_index]->SetCp(*m_compute_cp[pipe_id]);
|
||||||
m_compute_ring[v]->SetCp(*m_compute_cp[pipe_id]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_compute_ring[v];
|
return m_compute_ring[ring_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandProcessor::SetQueue(int queue) {
|
CommandProcessor::CommandProcessor(): m_scheduler(m_ctx, m_ucfg, m_sh_ctx) {
|
||||||
Common::LockGuard lock(m_mutex);
|
Common::LockGuard lock(m_mutex);
|
||||||
if (queue < 0 || queue >= GraphicContext::QUEUES_NUM ||
|
m_processors.push_back(this);
|
||||||
(m_processors[queue] != nullptr && m_processors[queue] != this)) {
|
|
||||||
EXIT("invalid command-processor queue registration: queue=%d owner=%p\n", queue,
|
|
||||||
static_cast<const void*>(m_processors[queue]));
|
|
||||||
}
|
|
||||||
m_scheduler.SetQueue(queue);
|
|
||||||
m_processors[queue] = this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandProcessor::FinishReadbackTransaction() {
|
void CommandProcessor::FinishReadbackTransaction() {
|
||||||
@@ -429,19 +408,11 @@ void CommandProcessor::FinishReadbackTransaction() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CommandProcessor::FinishCommandProcessors() {
|
void CommandProcessor::FinishCommandProcessors() {
|
||||||
std::array<CommandProcessor*, GraphicContext::QUEUES_NUM> processors {};
|
|
||||||
uint32_t processor_count = 0;
|
|
||||||
for (auto* processor: m_processors) {
|
for (auto* processor: m_processors) {
|
||||||
if (processor == nullptr ||
|
|
||||||
std::find(processors.begin(), processors.begin() + processor_count, processor) !=
|
|
||||||
processors.begin() + processor_count) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
processors[processor_count++] = processor;
|
|
||||||
processor->m_scheduler.SubmitForReadback();
|
processor->m_scheduler.SubmitForReadback();
|
||||||
}
|
}
|
||||||
for (uint32_t i = 0; i < processor_count; i++) {
|
for (auto* processor: m_processors) {
|
||||||
processors[i]->m_scheduler.ResumeAfterReadback();
|
processor->m_scheduler.ResumeAfterReadback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -881,8 +852,8 @@ void ComputeRing::ThreadRun(void* data) {
|
|||||||
|
|
||||||
static std::atomic<uint32_t> compute_batch_log_count {0};
|
static std::atomic<uint32_t> compute_batch_log_count {0};
|
||||||
if (num_dw <= 128 && buffer != nullptr && compute_batch_log_count.fetch_add(1) < 32) {
|
if (num_dw <= 128 && buffer != nullptr && compute_batch_log_count.fetch_add(1) < 32) {
|
||||||
LOGF("compute direct batch: queue=%d, data=0x%016" PRIx64 ", num_dw=%" PRIu32 "\n",
|
LOGF("compute direct batch: data=0x%016" PRIx64 ", num_dw=%" PRIu32 "\n",
|
||||||
ring->m_queue_id, reinterpret_cast<uint64_t>(buffer), num_dw);
|
reinterpret_cast<uint64_t>(buffer), num_dw);
|
||||||
for (uint32_t i = 0; i < std::min<uint32_t>(num_dw, 16); i++) {
|
for (uint32_t i = 0; i < std::min<uint32_t>(num_dw, 16); i++) {
|
||||||
LOGF("\t compute[%02" PRIu32 "] = 0x%08" PRIx32 "\n", i, buffer[i]);
|
LOGF("\t compute[%02" PRIu32 "] = 0x%08" PRIx32 "\n", i, buffer[i]);
|
||||||
}
|
}
|
||||||
@@ -1356,11 +1327,11 @@ void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_g
|
|||||||
const auto& cs = m_sh_ctx.GetCs().cs_regs;
|
const auto& cs = m_sh_ctx.GetCs().cs_regs;
|
||||||
const auto& oa = m_ucfg.GetGdsOaCounter(m_ucfg.GetGdsOaState().GetIndex());
|
const auto& oa = m_ucfg.GetGdsOaCounter(m_ucfg.GetGdsOaState().GetIndex());
|
||||||
LOGF("QueuePoint DispatchDirect: frame=%u submit=%" PRIu64
|
LOGF("QueuePoint DispatchDirect: frame=%u submit=%" PRIu64
|
||||||
" queue=%d groups=%ux%ux%u local=%ux%ux%u mode=0x%08" PRIx32
|
" groups=%ux%ux%u local=%ux%ux%u mode=0x%08" PRIx32 " wave=%u cs=0x%016" PRIx64
|
||||||
" wave=%u cs=0x%016" PRIx64 " oa_index=%u oa_enabled=%s oa_addr=0x%04" PRIx32
|
" oa_index=%u oa_enabled=%s oa_addr=0x%04" PRIx32 " oa_space=0x%08" PRIx32
|
||||||
" oa_space=0x%08" PRIx32 "\n",
|
"\n",
|
||||||
frame_num, m_submit_id, m_scheduler.Queue(), thread_group_x, thread_group_y,
|
frame_num, m_submit_id, thread_group_x, thread_group_y, thread_group_z,
|
||||||
thread_group_z, std::max(cs.num_thread_x, 1u), std::max(cs.num_thread_y, 1u),
|
std::max(cs.num_thread_x, 1u), std::max(cs.num_thread_y, 1u),
|
||||||
std::max(cs.num_thread_z, 1u), mode, static_cast<uint32_t>(cs.wave_size),
|
std::max(cs.num_thread_z, 1u), mode, static_cast<uint32_t>(cs.wave_size),
|
||||||
cs.data_addr, m_ucfg.GetGdsOaState().GetIndex(),
|
cs.data_addr, m_ucfg.GetGdsOaState().GetIndex(),
|
||||||
oa.IsCounterEnabled() ? "true" : "false", oa.GetAddressBytes(),
|
oa.IsCounterEnabled() ? "true" : "false", oa.GetAddressBytes(),
|
||||||
|
|||||||
@@ -422,7 +422,7 @@ void TileCompute::Execute(bool to_tiled, const void* input, void* output, uint64
|
|||||||
graphics.device.updateDescriptorSets(static_cast<uint32_t>(writes.size()), writes.data(), 0,
|
graphics.device.updateDescriptorSets(static_cast<uint32_t>(writes.size()), writes.data(), 0,
|
||||||
nullptr);
|
nullptr);
|
||||||
|
|
||||||
CommandBuffer command(GraphicContext::QUEUE_UTIL);
|
CommandBuffer command;
|
||||||
command.Begin();
|
command.Begin();
|
||||||
auto vk_command = command.Handle();
|
auto vk_command = command.Handle();
|
||||||
if (input != nullptr) {
|
if (input != nullptr) {
|
||||||
@@ -520,9 +520,8 @@ void TileCompute::Release() {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void GpuDetile(const void* tiled, void* linear, uint64_t tiled_capacity,
|
void GpuDetile(const void* tiled, void* linear, uint64_t tiled_capacity, uint64_t linear_capacity,
|
||||||
uint64_t linear_capacity, std::span<const GpuTileInfo> infos,
|
std::span<const GpuTileInfo> infos, const GpuTileRecord& after) {
|
||||||
const GpuTileRecord& after) {
|
|
||||||
Common::LockGuard lock(g_tiler_mutex);
|
Common::LockGuard lock(g_tiler_mutex);
|
||||||
if (!g_tiler) {
|
if (!g_tiler) {
|
||||||
g_tiler = std::make_unique<TileCompute>(GetRenderContext().GetGraphics());
|
g_tiler = std::make_unique<TileCompute>(GetRenderContext().GetGraphics());
|
||||||
@@ -530,9 +529,8 @@ void GpuDetile(const void* tiled, void* linear, uint64_t tiled_capacity,
|
|||||||
g_tiler->Run(false, tiled, linear, tiled_capacity, linear_capacity, infos, after);
|
g_tiler->Run(false, tiled, linear, tiled_capacity, linear_capacity, infos, after);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GpuTile(const void* linear, void* tiled, uint64_t tiled_capacity,
|
void GpuTile(const void* linear, void* tiled, uint64_t tiled_capacity, uint64_t linear_capacity,
|
||||||
uint64_t linear_capacity, std::span<const GpuTileInfo> infos,
|
std::span<const GpuTileInfo> infos, const GpuTileRecord& before) {
|
||||||
const GpuTileRecord& before) {
|
|
||||||
Common::LockGuard lock(g_tiler_mutex);
|
Common::LockGuard lock(g_tiler_mutex);
|
||||||
if (!g_tiler) {
|
if (!g_tiler) {
|
||||||
g_tiler = std::make_unique<TileCompute>(GetRenderContext().GetGraphics());
|
g_tiler = std::make_unique<TileCompute>(GetRenderContext().GetGraphics());
|
||||||
|
|||||||
@@ -3,11 +3,9 @@
|
|||||||
|
|
||||||
#include "common/abi.h"
|
#include "common/abi.h"
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "common/threads.h"
|
|
||||||
#include "graphics/host_gpu/vulkanCommon.h" // IWYU pragma: export
|
#include "graphics/host_gpu/vulkanCommon.h" // IWYU pragma: export
|
||||||
#include "graphics/host_gpu/vulkanInstance.h"
|
#include "graphics/host_gpu/vulkanInstance.h"
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -35,16 +33,6 @@ struct VulkanSwapchain {
|
|||||||
uint32_t present_frame = 0;
|
uint32_t present_frame = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VulkanCommandPool {
|
|
||||||
Common::Mutex mutex;
|
|
||||||
vk::CommandPool pool = nullptr;
|
|
||||||
std::unique_ptr<vk::CommandBuffer[]> buffers;
|
|
||||||
std::unique_ptr<vk::Fence[]> fences;
|
|
||||||
std::unique_ptr<vk::Semaphore[]> semaphores;
|
|
||||||
std::unique_ptr<bool[]> busy;
|
|
||||||
uint32_t buffers_count = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GraphicContext: public VulkanInstance {
|
struct GraphicContext: public VulkanInstance {
|
||||||
[[nodiscard]] bool CreateAllocator();
|
[[nodiscard]] bool CreateAllocator();
|
||||||
void DestroyAllocator();
|
void DestroyAllocator();
|
||||||
@@ -60,9 +48,8 @@ struct GraphicContext: public VulkanInstance {
|
|||||||
std::vector<const char*>& device_extensions);
|
std::vector<const char*>& device_extensions);
|
||||||
void LoadHardwareRayTracingFunctions() const;
|
void LoadHardwareRayTracingFunctions() const;
|
||||||
|
|
||||||
uint32_t screen_width = 0;
|
uint32_t screen_width = 0;
|
||||||
uint32_t screen_height = 0;
|
uint32_t screen_height = 0;
|
||||||
std::array<Common::Mutex, QUEUES_NUM> queue_mutexes;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VulkanMemory {
|
struct VulkanMemory {
|
||||||
|
|||||||
@@ -166,14 +166,9 @@ bool MergeOverlappingBufferCacheRange(BufferCacheRange& merged,
|
|||||||
return true;
|
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 {
|
struct BufferCache::CachedBuffer {
|
||||||
uint64_t vaddr = 0;
|
uint64_t vaddr = 0;
|
||||||
uint64_t size = 0;
|
uint64_t size = 0;
|
||||||
uint64_t queue_mask = 0;
|
|
||||||
std::shared_ptr<VulkanBuffer> buffer;
|
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*>(command.get()), static_cast<const void*>(mapped),
|
||||||
static_cast<const void*>(readback.buffer));
|
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.usage = vk::BufferUsageFlagBits::eTransferDst;
|
||||||
readback.memory.property = vk::MemoryPropertyFlagBits::eHostVisible |
|
readback.memory.property = vk::MemoryPropertyFlagBits::eHostVisible |
|
||||||
vk::MemoryPropertyFlagBits::eHostCoherent |
|
vk::MemoryPropertyFlagBits::eHostCoherent |
|
||||||
vk::MemoryPropertyFlagBits::eHostCached;
|
vk::MemoryPropertyFlagBits::eHostCached;
|
||||||
cache.m_graphics.CreateBuffer(READBACK_CAPACITY, readback);
|
cache.m_graphics.CreateBuffer(READBACK_CAPACITY, readback);
|
||||||
cache.m_graphics.MapMemory(readback.memory, mapped);
|
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.store(State::Idle, std::memory_order_release);
|
||||||
state.notify_all();
|
state.notify_all();
|
||||||
continue;
|
continue;
|
||||||
@@ -550,7 +528,7 @@ BufferCache::~BufferCache() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!m_buffers.empty()) {
|
if (!m_buffers.empty()) {
|
||||||
Transfer::WaitForGraphicsIdle();
|
Transfer::WaitForQueueIdle();
|
||||||
}
|
}
|
||||||
m_buffers.clear();
|
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,
|
BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr, uint64_t size,
|
||||||
bool is_written, bool is_read, bool is_formatted) {
|
bool is_written, bool is_read, bool is_formatted) {
|
||||||
if (command.IsInvalid() || command.IsExecute() || vaddr == 0 || size == 0 ||
|
if (command.IsInvalid() || command.IsExecute() || vaddr == 0 || size == 0 ||
|
||||||
size > UINT64_MAX - vaddr || command.GetQueue() < 0 || command.GetQueue() >= 64) {
|
size > UINT64_MAX - vaddr) {
|
||||||
EXIT("BufferCache: invalid buffer request, queue=%d addr=0x%016" PRIx64
|
EXIT("BufferCache: invalid buffer request, addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||||
" size=0x%016" PRIx64 "\n",
|
vaddr, size);
|
||||||
command.GetQueue(), 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);
|
ValidateGpuAccess(vaddr, size, is_read, is_written);
|
||||||
const auto begin = AlignDown(vaddr);
|
const auto begin = AlignDown(vaddr);
|
||||||
const auto end = AlignUp(vaddr + size);
|
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",
|
" size=0x%016" PRIx64 " buffer=%p\n",
|
||||||
old.vaddr, old.size, static_cast<const void*>(old.buffer.get()));
|
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;
|
std::vector<std::pair<uint64_t, uint64_t>> uploads;
|
||||||
m_memory_tracker.ForEachUploadRange(
|
m_memory_tracker.ForEachUploadRange(
|
||||||
old.vaddr, old.size, false,
|
old.vaddr, old.size, false,
|
||||||
@@ -860,7 +829,6 @@ BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr,
|
|||||||
if (is_written) {
|
if (is_written) {
|
||||||
m_gpu_modified_ranges.Add(vaddr, size);
|
m_gpu_modified_ranges.Add(vaddr, size);
|
||||||
}
|
}
|
||||||
cached.queue_mask |= queue_mask;
|
|
||||||
command.RetainResourceUntilFence(cached.buffer);
|
command.RetainResourceUntilFence(cached.buffer);
|
||||||
return {*cached.buffer, vaddr - cached.vaddr};
|
return {*cached.buffer, vaddr - cached.vaddr};
|
||||||
}
|
}
|
||||||
@@ -944,7 +912,7 @@ BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t
|
|||||||
vaddr, size, GraphicsRunIsCommandProcessorThread(),
|
vaddr, size, GraphicsRunIsCommandProcessorThread(),
|
||||||
GraphicsRunSubmissionLockHeld(), LabelInCallback());
|
GraphicsRunSubmissionLockHeld(), LabelInCallback());
|
||||||
}
|
}
|
||||||
Transfer::WaitForGraphicsIdle();
|
Transfer::WaitForQueueIdle();
|
||||||
auto backing_writes = ReserveBackingWrites(m_page_manager, dirty_ranges);
|
auto backing_writes = ReserveBackingWrites(m_page_manager, dirty_ranges);
|
||||||
uint64_t downloaded = 0;
|
uint64_t downloaded = 0;
|
||||||
m_memory_tracker.ForEachDownloadRange<true>(
|
m_memory_tracker.ForEachDownloadRange<true>(
|
||||||
@@ -1039,10 +1007,6 @@ vk::BufferMemoryBarrier MakeDmaBarrier(VulkanBuffer& buffer, uint64_t offset, ui
|
|||||||
barrier.size = size;
|
barrier.size = size;
|
||||||
return barrier;
|
return barrier;
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::CommandBuffer GetDmaCommandBuffer(CommandBuffer& command) {
|
|
||||||
return command.Handle();
|
|
||||||
}
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void BufferCache::FillBuffer(CommandBuffer* command, uint64_t vaddr, uint64_t size,
|
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(
|
const auto before = MakeDmaBarrier(
|
||||||
dst, dst_offset, size, vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
|
dst, dst_offset, size, vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
|
||||||
vk::AccessFlagBits::eTransferWrite);
|
vk::AccessFlagBits::eTransferWrite);
|
||||||
const auto vk_buffer = GetDmaCommandBuffer(*command);
|
const auto vk_buffer = command->Handle();
|
||||||
vk_buffer.pipelineBarrier(
|
vk_buffer.pipelineBarrier(
|
||||||
vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eTransfer,
|
vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eTransfer,
|
||||||
vk::DependencyFlagBits::eByRegion, 0, nullptr, 1, &before, 0, nullptr);
|
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,
|
MakeDmaBarrier(src, src_offset, size, vk::AccessFlagBits::eMemoryWrite,
|
||||||
vk::AccessFlagBits::eTransferRead),
|
vk::AccessFlagBits::eTransferRead),
|
||||||
};
|
};
|
||||||
const auto vk_buffer = GetDmaCommandBuffer(*command);
|
const auto vk_buffer = command->Handle();
|
||||||
vk_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands,
|
vk_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands,
|
||||||
vk::PipelineStageFlagBits::eTransfer,
|
vk::PipelineStageFlagBits::eTransfer,
|
||||||
vk::DependencyFlagBits::eByRegion, 0, nullptr, 2, before, 0, nullptr);
|
vk::DependencyFlagBits::eByRegion, 0, nullptr, 2, before, 0, nullptr);
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ struct BufferBinding {
|
|||||||
|
|
||||||
[[nodiscard]] bool MergeOverlappingBufferCacheRange(BufferCacheRange& merged,
|
[[nodiscard]] bool MergeOverlappingBufferCacheRange(BufferCacheRange& merged,
|
||||||
BufferCacheRange candidate) noexcept;
|
BufferCacheRange candidate) noexcept;
|
||||||
[[nodiscard]] bool CanMergeBufferCacheQueueMask(uint64_t queue_mask, uint32_t queue) noexcept;
|
|
||||||
|
|
||||||
class BufferCache {
|
class BufferCache {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -18,17 +18,13 @@
|
|||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
static std::atomic<uint64_t> g_command_buffer_submit_seq = 0;
|
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) {
|
static void ResetNativeCommandBuffer(vk::CommandBuffer buffer) {
|
||||||
EXIT_IF(buffer == nullptr);
|
EXIT_IF(buffer == nullptr);
|
||||||
const auto result = buffer.reset(vk::CommandBufferResetFlagBits::eReleaseResources);
|
const auto result = buffer.reset(vk::CommandBufferResetFlagBits::eReleaseResources);
|
||||||
@@ -38,34 +34,34 @@ static void ResetNativeCommandBuffer(vk::CommandBuffer buffer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CommandPool {
|
struct CommandSlot {
|
||||||
public:
|
Common::Mutex* pool_mutex = nullptr;
|
||||||
CommandPool() = default;
|
uint32_t id = 0;
|
||||||
~CommandPool() // NOLINT
|
vk::CommandBuffer buffer = nullptr;
|
||||||
{
|
vk::Fence fence = nullptr;
|
||||||
// TODO(): check if destructor is called from std::_Exit()
|
bool busy = false;
|
||||||
// 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 {};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static RenderContext* g_render_ctx = nullptr;
|
class ThreadCommandPool {
|
||||||
static thread_local CommandPool g_command_pool;
|
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 {
|
RenderContext& GetRenderContext() noexcept {
|
||||||
return *g_render_ctx;
|
return *g_render_ctx;
|
||||||
@@ -96,152 +92,110 @@ void GraphicsRenderInit(GraphicContext& graphics) {
|
|||||||
g_render_ctx = new RenderContext(graphics);
|
g_render_ctx = new RenderContext(graphics);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsRenderReleaseThreadCommandPools() {
|
void GraphicsRenderReleaseThreadCommandPool() {
|
||||||
g_command_pool.DeleteAll();
|
g_command_pool.Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandBuffer::CommandBuffer(int queue)
|
CommandBuffer::CommandBuffer()
|
||||||
: m_graphics(GetRenderContext().GetGraphics()), m_queue(queue), m_host_stream(m_graphics) {
|
: m_graphics(GetRenderContext().GetGraphics()), m_slot(g_command_pool.Allocate()),
|
||||||
Allocate();
|
m_host_stream(m_graphics) {}
|
||||||
}
|
|
||||||
|
|
||||||
void CommandPool::Create(int queue_id) {
|
void ThreadCommandPool::Create() {
|
||||||
RequireValidQueueId(queue_id);
|
auto& graphics = GetRenderContext().GetGraphics();
|
||||||
|
EXIT_IF(m_pool != nullptr || graphics.queue_family == static_cast<uint32_t>(-1));
|
||||||
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;
|
|
||||||
|
|
||||||
vk::CommandPoolCreateInfo pool_info {};
|
vk::CommandPoolCreateInfo pool_info {};
|
||||||
pool_info.sType = vk::StructureType::eCommandPoolCreateInfo;
|
pool_info.sType = vk::StructureType::eCommandPoolCreateInfo;
|
||||||
pool_info.pNext = nullptr;
|
pool_info.pNext = nullptr;
|
||||||
pool_info.queueFamilyIndex = graphics.queues[queue_id].family;
|
pool_info.queueFamilyIndex = graphics.queue_family;
|
||||||
pool_info.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
|
pool_info.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
|
||||||
|
|
||||||
const auto result = graphics.device.createCommandPool(&pool_info, nullptr, &pool->pool);
|
const auto result = graphics.device.createCommandPool(&pool_info, nullptr, &m_pool);
|
||||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess || pool->pool == nullptr);
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess || m_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);
|
|
||||||
|
|
||||||
|
CommandSlot* ThreadCommandPool::CreateSlot() {
|
||||||
|
auto& graphics = GetRenderContext().GetGraphics();
|
||||||
vk::CommandBufferAllocateInfo alloc_info {};
|
vk::CommandBufferAllocateInfo alloc_info {};
|
||||||
alloc_info.sType = vk::StructureType::eCommandBufferAllocateInfo;
|
alloc_info.sType = vk::StructureType::eCommandBufferAllocateInfo;
|
||||||
alloc_info.commandPool = pool->pool;
|
alloc_info.commandPool = m_pool;
|
||||||
alloc_info.level = vk::CommandBufferLevel::ePrimary;
|
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::CommandBuffer buffer = nullptr;
|
||||||
vk::Result::eSuccess) {
|
if (graphics.device.allocateCommandBuffers(&alloc_info, &buffer) != vk::Result::eSuccess) {
|
||||||
EXIT("Can't allocate command buffers");
|
EXIT("Can't allocate command buffers");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < pool->buffers_count; i++) {
|
vk::FenceCreateInfo fence_info {};
|
||||||
pool->busy[i] = false;
|
fence_info.sType = vk::StructureType::eFenceCreateInfo;
|
||||||
|
fence_info.flags = vk::FenceCreateFlagBits::eSignaled;
|
||||||
|
|
||||||
vk::FenceCreateInfo fence_info {};
|
vk::Fence fence = nullptr;
|
||||||
fence_info.sType = vk::StructureType::eFenceCreateInfo;
|
if (graphics.device.createFence(&fence_info, nullptr, &fence) != vk::Result::eSuccess) {
|
||||||
fence_info.pNext = nullptr;
|
graphics.device.freeCommandBuffers(m_pool, 1, &buffer);
|
||||||
fence_info.flags = vk::FenceCreateFlagBits::eSignaled;
|
EXIT("Can't create fence");
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
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() {
|
CommandSlot* ThreadCommandPool::Allocate() {
|
||||||
auto& graphics = GetRenderContext().GetGraphics();
|
Common::LockGuard lock(m_mutex);
|
||||||
|
if (m_pool == nullptr) {
|
||||||
for (auto& pool: m_pools) {
|
Create();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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 {
|
bool CommandBuffer::IsInvalid() const {
|
||||||
if (m_pool != nullptr) {
|
return m_slot == nullptr;
|
||||||
Common::LockGuard lock(m_pool->mutex);
|
|
||||||
|
|
||||||
return (m_index == static_cast<uint32_t>(-1) || m_index >= m_pool->buffers_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::CommandBuffer CommandBuffer::Handle() const {
|
vk::CommandBuffer CommandBuffer::Handle() const {
|
||||||
EXIT_IF(IsInvalid());
|
EXIT_IF(IsInvalid());
|
||||||
|
|
||||||
const auto handle = m_pool->buffers[m_index];
|
const auto handle = m_slot->buffer;
|
||||||
EXIT_IF(handle == nullptr);
|
EXIT_IF(handle == nullptr);
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandBuffer::Allocate() {
|
void CommandBuffer::Release() {
|
||||||
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() {
|
|
||||||
EXIT_IF(IsInvalid());
|
EXIT_IF(IsInvalid());
|
||||||
|
|
||||||
Common::LockGuard lock(m_pool->mutex);
|
Common::LockGuard lock(*m_slot->pool_mutex);
|
||||||
|
|
||||||
WaitForFence();
|
WaitForFence();
|
||||||
|
|
||||||
m_host_stream.Release();
|
m_host_stream.Release();
|
||||||
|
|
||||||
m_pool->busy[m_index] = false;
|
m_slot->busy = false;
|
||||||
ResetNativeCommandBuffer(m_pool->buffers[m_index]);
|
ResetNativeCommandBuffer(m_slot->buffer);
|
||||||
ReleaseResourcesAfterFence();
|
ReleaseResourcesAfterFence();
|
||||||
m_index = static_cast<uint32_t>(-1);
|
m_slot = nullptr;
|
||||||
|
|
||||||
EXIT_NOT_IMPLEMENTED(!IsInvalid());
|
EXIT_NOT_IMPLEMENTED(!IsInvalid());
|
||||||
}
|
}
|
||||||
@@ -305,27 +259,22 @@ void CommandBuffer::Execute() {
|
|||||||
Submit(nullptr, {}, nullptr);
|
Submit(nullptr, {}, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandBuffer::ExecuteWithSemaphore(vk::Semaphore signal_semaphore) {
|
|
||||||
Submit(nullptr, {}, ResolveSignalSemaphore(signal_semaphore));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CommandBuffer::ExecuteWithSemaphore(vk::Semaphore wait_semaphore,
|
void CommandBuffer::ExecuteWithSemaphore(vk::Semaphore wait_semaphore,
|
||||||
vk::PipelineStageFlags wait_stage,
|
vk::PipelineStageFlags wait_stage,
|
||||||
vk::Semaphore signal_semaphore) {
|
vk::Semaphore signal_semaphore) {
|
||||||
EXIT_IF(wait_semaphore == nullptr);
|
EXIT_IF(wait_semaphore == nullptr || signal_semaphore == nullptr);
|
||||||
Submit(wait_semaphore, wait_stage, ResolveSignalSemaphore(signal_semaphore));
|
Submit(wait_semaphore, wait_stage, signal_semaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandBuffer::Submit(vk::Semaphore wait_semaphore, vk::PipelineStageFlags wait_stage,
|
void CommandBuffer::Submit(vk::Semaphore wait_semaphore, vk::PipelineStageFlags wait_stage,
|
||||||
vk::Semaphore signal_semaphore) {
|
vk::Semaphore signal_semaphore) {
|
||||||
RequireValidQueueId(m_queue);
|
|
||||||
EXIT_IF(IsInvalid());
|
EXIT_IF(IsInvalid());
|
||||||
EXIT_IF(m_execute);
|
EXIT_IF(m_execute);
|
||||||
|
|
||||||
const bool has_wait = wait_semaphore != nullptr;
|
const bool has_wait = wait_semaphore != nullptr;
|
||||||
const bool has_signal = signal_semaphore != nullptr;
|
const bool has_signal = signal_semaphore != nullptr;
|
||||||
auto buffer = Handle();
|
auto buffer = Handle();
|
||||||
auto fence = m_pool->fences[m_index];
|
auto fence = m_slot->fence;
|
||||||
|
|
||||||
vk::SubmitInfo submit_info {};
|
vk::SubmitInfo submit_info {};
|
||||||
submit_info.sType = vk::StructureType::eSubmitInfo;
|
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;
|
submit_info.pSignalSemaphores = has_signal ? &signal_semaphore : nullptr;
|
||||||
|
|
||||||
auto& graphics = GetRenderContext().GetGraphics();
|
auto& graphics = GetRenderContext().GetGraphics();
|
||||||
const auto& queue = graphics.queues[m_queue];
|
EXIT_IF(graphics.queue == nullptr);
|
||||||
|
|
||||||
auto result = graphics.device.resetFences(1, &fence);
|
auto result = graphics.device.resetFences(1, &fence);
|
||||||
if (result != vk::Result::eSuccess) {
|
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);
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||||
|
|
||||||
if (queue.mutex != nullptr) {
|
|
||||||
queue.mutex->Lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config::GraphicsDebugDumpEnabled()) {
|
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",
|
" debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
||||||
m_queue, m_index, static_cast<void*>(wait_semaphore),
|
m_slot->id, static_cast<void*>(wait_semaphore), static_cast<void*>(signal_semaphore),
|
||||||
static_cast<void*>(signal_semaphore), m_debug_op, m_debug_submit_id, m_debug_arg0,
|
m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3,
|
||||||
m_debug_arg1, m_debug_arg2, m_debug_arg3, m_debug_arg4);
|
m_debug_arg4);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = queue.vk_queue.submit(1, &submit_info, fence);
|
{
|
||||||
|
Common::LockGuard lock(graphics.queue_mutex);
|
||||||
if (queue.mutex != nullptr) {
|
m_submit_seq = g_command_buffer_submit_seq.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||||
queue.mutex->Unlock();
|
result = graphics.queue.submit(1, &submit_info, fence);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_execute = true;
|
m_execute = true;
|
||||||
m_fence_waited = false;
|
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) {
|
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",
|
" 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,
|
VulkanToString(result).c_str(), static_cast<int>(result), m_slot->id, m_submit_seq,
|
||||||
m_submit_seq, m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2,
|
m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3,
|
||||||
m_debug_arg3, m_debug_arg4);
|
m_debug_arg4);
|
||||||
}
|
}
|
||||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
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() {
|
void CommandBuffer::WaitForFence() {
|
||||||
FinalizeFence(false);
|
FinalizeFence(false);
|
||||||
}
|
}
|
||||||
@@ -398,13 +334,13 @@ void CommandBuffer::WaitForFenceOnly() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto device = GetRenderContext().GetGraphics().device;
|
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) {
|
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",
|
" 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,
|
VulkanToString(result).c_str(), static_cast<int>(result), m_slot->id, m_submit_seq,
|
||||||
m_submit_seq, m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2,
|
m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3,
|
||||||
m_debug_arg3, m_debug_arg4);
|
m_debug_arg4);
|
||||||
}
|
}
|
||||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||||
m_fence_waited = true;
|
m_fence_waited = true;
|
||||||
@@ -421,7 +357,7 @@ void CommandBuffer::FinalizeFence(bool reset_recording) {
|
|||||||
m_execute = false;
|
m_execute = false;
|
||||||
m_fence_waited = false;
|
m_fence_waited = false;
|
||||||
if (reset_recording) {
|
if (reset_recording) {
|
||||||
ResetNativeCommandBuffer(m_pool->buffers[m_index]);
|
ResetNativeCommandBuffer(m_slot->buffer);
|
||||||
m_recording_generation++;
|
m_recording_generation++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ DummyTextureCache::~DummyTextureCache() {
|
|||||||
if (!populated(m_sampled) && !populated(m_storage)) {
|
if (!populated(m_sampled) && !populated(m_storage)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Transfer::WaitForGraphicsIdle();
|
Transfer::WaitForQueueIdle();
|
||||||
const auto destroy = [](auto& slots) {
|
const auto destroy = [](auto& slots) {
|
||||||
for (auto& slot: slots) {
|
for (auto& slot: slots) {
|
||||||
if (slot.image != nullptr) {
|
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& slots = usage == Usage::Storage ? m_storage : m_sampled;
|
||||||
auto& slot = slots[DummyTextureIndex(uint_format, image_3d)];
|
auto& slot = slots[DummyTextureIndex(uint_format, image_3d)];
|
||||||
if (slot.image == nullptr) {
|
if (slot.image == nullptr) {
|
||||||
slot.image = ImageOps::CreateDummyTexture(uint_format, image_3d,
|
slot.image = ImageOps::CreateDummyTexture(uint_format, image_3d, usage == Usage::Storage);
|
||||||
usage == Usage::Storage);
|
|
||||||
}
|
}
|
||||||
return *slot.image;
|
return *slot.image;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,8 +50,7 @@ TextureImageCreateParams MakeImageParams(const ImageInfo& info, bool storage) {
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RenderTargetSupportsStorage(vk::Format format,
|
bool RenderTargetSupportsStorage(vk::Format format, vk::ImageCreateFlags flags) {
|
||||||
vk::ImageCreateFlags flags) {
|
|
||||||
const auto compatible = SrgbStorageViewFormat(format);
|
const auto compatible = SrgbStorageViewFormat(format);
|
||||||
const auto required_flags =
|
const auto required_flags =
|
||||||
vk::ImageCreateFlagBits::eMutableFormat | vk::ImageCreateFlagBits::eExtendedUsage;
|
vk::ImageCreateFlagBits::eMutableFormat | vk::ImageCreateFlagBits::eExtendedUsage;
|
||||||
@@ -72,12 +71,12 @@ vk::ImageCreateFlags RenderTargetCreateFlags(vk::Format format) {
|
|||||||
: vk::ImageCreateFlags {0};
|
: vk::ImageCreateFlags {0};
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::ImageUsageFlags RenderTargetUsage(vk::Format format,
|
vk::ImageUsageFlags RenderTargetUsage(vk::Format format, vk::ImageCreateFlags flags,
|
||||||
vk::ImageCreateFlags flags, uint32_t samples) {
|
uint32_t samples) {
|
||||||
auto& graphics = GetRenderContext().GetGraphics();
|
auto& graphics = GetRenderContext().GetGraphics();
|
||||||
auto usage = static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eColorAttachment) |
|
auto usage = static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eColorAttachment) |
|
||||||
static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eTransferSrc) |
|
static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eTransferSrc) |
|
||||||
static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eTransferDst);
|
static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eTransferDst);
|
||||||
if (samples == 1) {
|
if (samples == 1) {
|
||||||
usage |= vk::ImageUsageFlagBits::eSampled;
|
usage |= vk::ImageUsageFlagBits::eSampled;
|
||||||
if (RenderTargetSupportsStorage(format, flags)) {
|
if (RenderTargetSupportsStorage(format, flags)) {
|
||||||
@@ -155,21 +154,19 @@ GpuTextureVulkanImage* CreateTexture(const ImageInfo& info, bool storage,
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateTextureViews(GpuTextureVulkanImage& image,
|
void CreateTextureViews(GpuTextureVulkanImage& image, const ImageInfo& info, bool storage,
|
||||||
const ImageInfo& info, bool storage, vk::ComponentMapping components) {
|
vk::ComponentMapping components) {
|
||||||
if (storage) {
|
if (storage) {
|
||||||
TextureCreateImageViews(image, components, info.type, 0, 0, 1, info.depth, false,
|
TextureCreateImageViews(image, components, info.type, 0, 0, 1, info.depth, false,
|
||||||
TextureFormatUsage::Sampled | TextureFormatUsage::Storage);
|
TextureFormatUsage::Sampled | TextureFormatUsage::Storage);
|
||||||
} else {
|
} else {
|
||||||
TextureCreateImageViews(image, components, info.type, info.base_array,
|
TextureCreateImageViews(image, components, info.type, info.base_array, info.base_level,
|
||||||
info.base_level, info.view_levels, info.depth, true,
|
info.view_levels, info.depth, true, TextureFormatUsage::Sampled);
|
||||||
TextureFormatUsage::Sampled);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UploadRenderTargetLayers(RenderTextureVulkanImage& image,
|
void UploadRenderTargetLayers(RenderTextureVulkanImage& image, const RenderTargetInfo& info,
|
||||||
const RenderTargetInfo& info, uint32_t base_layer,
|
uint32_t base_layer, uint32_t layer_count, bool refresh) {
|
||||||
uint32_t layer_count, bool refresh) {
|
|
||||||
if (info.layers == 0 || info.size % info.layers != 0 || layer_count == 0 ||
|
if (info.layers == 0 || info.size % info.layers != 0 || layer_count == 0 ||
|
||||||
base_layer >= info.layers || layer_count > info.layers - base_layer ||
|
base_layer >= info.layers || layer_count > info.layers - base_layer ||
|
||||||
base_layer >= image.layers || layer_count > image.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);
|
info.samples, image.samples);
|
||||||
}
|
}
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
Transfer::WaitForGraphicsIdle();
|
Transfer::WaitForQueueIdle();
|
||||||
}
|
}
|
||||||
const auto slice_size = info.size / info.layers;
|
const auto slice_size = info.size / info.layers;
|
||||||
const auto upload_size = slice_size * layer_count;
|
const auto upload_size = slice_size * layer_count;
|
||||||
@@ -207,9 +204,9 @@ void UploadRenderTargetLayers(RenderTextureVulkanImage& image,
|
|||||||
region.dst_layer += base_layer;
|
region.dst_layer += base_layer;
|
||||||
}
|
}
|
||||||
const auto source_address = info.address + slice_size * base_layer;
|
const auto source_address = info.address + slice_size * base_layer;
|
||||||
TextureUploadGuestImage(image, reinterpret_cast<const void*>(source_address),
|
TextureUploadGuestImage(image, reinterpret_cast<const void*>(source_address), upload_size,
|
||||||
upload_size, regions, layout, format, info.width, info.height,
|
regions, layout, format, info.width, info.height, layer_count,
|
||||||
layer_count, info.levels, "TextureCache render target",
|
info.levels, "TextureCache render target",
|
||||||
vk::ImageLayout::eGeneral);
|
vk::ImageLayout::eGeneral);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -221,23 +218,22 @@ void UploadRenderTargetLayers(RenderTextureVulkanImage& image,
|
|||||||
"TextureCache render target");
|
"TextureCache render target");
|
||||||
auto regions = TextureBuildUploadRegions(layout, info.format, info.width, info.height, 1, 1,
|
auto regions = TextureBuildUploadRegions(layout, info.format, info.width, info.height, 1, 1,
|
||||||
true, false, TextureUploadDestination::MipLevels);
|
true, false, TextureUploadDestination::MipLevels);
|
||||||
TextureUploadGuestImage(image, reinterpret_cast<const void*>(info.address),
|
TextureUploadGuestImage(image, reinterpret_cast<const void*>(info.address), slice_size,
|
||||||
slice_size, regions, layout, format, info.width, info.height, 1, 1,
|
regions, layout, format, info.width, info.height, 1, 1,
|
||||||
"TextureCache render target", vk::ImageLayout::eGeneral);
|
"TextureCache render target", vk::ImageLayout::eGeneral);
|
||||||
} else {
|
} else {
|
||||||
Transfer::UploadImage(image, reinterpret_cast<const void*>(info.address),
|
Transfer::UploadImage(image, reinterpret_cast<const void*>(info.address), slice_size,
|
||||||
slice_size, info.pitch, vk::ImageLayout::eGeneral);
|
info.pitch, vk::ImageLayout::eGeneral);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UploadRenderTarget(RenderTextureVulkanImage& image,
|
void UploadRenderTarget(RenderTextureVulkanImage& image, const RenderTargetInfo& info,
|
||||||
const RenderTargetInfo& info, bool refresh) {
|
bool refresh) {
|
||||||
UploadRenderTargetLayers(image, info, 0, info.layers, refresh);
|
UploadRenderTargetLayers(image, info, 0, info.layers, refresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderTextureVulkanImage* CreateRenderTarget(
|
RenderTextureVulkanImage* CreateRenderTarget(const RenderTargetInfo& info) {
|
||||||
const RenderTargetInfo& info) {
|
auto& graphics = GetRenderContext().GetGraphics();
|
||||||
auto& graphics = GetRenderContext().GetGraphics();
|
|
||||||
auto* image = new RenderTextureVulkanImage;
|
auto* image = new RenderTextureVulkanImage;
|
||||||
image->extent.width = info.width;
|
image->extent.width = info.width;
|
||||||
image->extent.height = info.height;
|
image->extent.height = info.height;
|
||||||
@@ -270,7 +266,7 @@ RenderTextureVulkanImage* CreateRenderTarget(
|
|||||||
}
|
}
|
||||||
|
|
||||||
DepthStencilVulkanImage* CreateDepthTarget(const DepthTargetInfo& info) {
|
DepthStencilVulkanImage* CreateDepthTarget(const DepthTargetInfo& info) {
|
||||||
auto& graphics = GetRenderContext().GetGraphics();
|
auto& graphics = GetRenderContext().GetGraphics();
|
||||||
vk::ImageCreateInfo create {};
|
vk::ImageCreateInfo create {};
|
||||||
create.sType = vk::StructureType::eImageCreateInfo;
|
create.sType = vk::StructureType::eImageCreateInfo;
|
||||||
create.imageType = vk::ImageType::e2D;
|
create.imageType = vk::ImageType::e2D;
|
||||||
@@ -350,7 +346,7 @@ void ValidateVideoOut(const VideoOutInfo& info) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VideoOutVulkanImage* CreateVideoOut(const VideoOutInfo& info) {
|
VideoOutVulkanImage* CreateVideoOut(const VideoOutInfo& info) {
|
||||||
auto& graphics = GetRenderContext().GetGraphics();
|
auto& graphics = GetRenderContext().GetGraphics();
|
||||||
auto* image = new VideoOutVulkanImage;
|
auto* image = new VideoOutVulkanImage;
|
||||||
image->extent.width = info.width;
|
image->extent.width = info.width;
|
||||||
image->extent.height = info.height;
|
image->extent.height = info.height;
|
||||||
@@ -379,15 +375,14 @@ VideoOutVulkanImage* CreateVideoOut(const VideoOutInfo& info) {
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UploadVideoOut(VideoOutVulkanImage& image, const VideoOutInfo& info,
|
void UploadVideoOut(VideoOutVulkanImage& image, const VideoOutInfo& info, bool refresh) {
|
||||||
bool refresh) {
|
|
||||||
if (info.compression != VideoOutCompression::Uncompressed) {
|
if (info.compression != VideoOutCompression::Uncompressed) {
|
||||||
EXIT("TextureCache: compressed video-out guest upload is unsupported, "
|
EXIT("TextureCache: compressed video-out guest upload is unsupported, "
|
||||||
"addr=0x%016" PRIx64 " metadata=0x%016" PRIx64 " dcc=0x%08" PRIx32 "\n",
|
"addr=0x%016" PRIx64 " metadata=0x%016" PRIx64 " dcc=0x%08" PRIx32 "\n",
|
||||||
info.address, info.metadata_address, info.dcc_control);
|
info.address, info.metadata_address, info.dcc_control);
|
||||||
}
|
}
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
Transfer::WaitForGraphicsIdle();
|
Transfer::WaitForQueueIdle();
|
||||||
}
|
}
|
||||||
image.layout = vk::ImageLayout::eUndefined;
|
image.layout = vk::ImageLayout::eUndefined;
|
||||||
if (!info.bgra16) {
|
if (!info.bgra16) {
|
||||||
@@ -396,9 +391,9 @@ void UploadVideoOut(VideoOutVulkanImage& image, const VideoOutInfo& info,
|
|||||||
info.tile_mode, info.size, false, false, "VideoOut");
|
info.tile_mode, info.size, false, false, "VideoOut");
|
||||||
auto regions = TextureBuildUploadRegions(layout, info.format, info.width, info.height, 1, 1,
|
auto regions = TextureBuildUploadRegions(layout, info.format, info.width, info.height, 1, 1,
|
||||||
false, false, TextureUploadDestination::MipLevels);
|
false, false, TextureUploadDestination::MipLevels);
|
||||||
TextureUploadGuestImage(image, reinterpret_cast<const void*>(info.address),
|
TextureUploadGuestImage(image, reinterpret_cast<const void*>(info.address), info.size,
|
||||||
info.size, regions, layout, info.guest_format, info.width,
|
regions, layout, info.guest_format, info.width, info.height, 1, 1,
|
||||||
info.height, 1, 1, "VideoOut", vk::ImageLayout::eGeneral);
|
"VideoOut", vk::ImageLayout::eGeneral);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Transfer::ScratchBuffer scratch(info.size);
|
Transfer::ScratchBuffer scratch(info.size);
|
||||||
@@ -416,11 +411,10 @@ void UploadVideoOut(VideoOutVulkanImage& image, const VideoOutInfo& info,
|
|||||||
info.height,
|
info.height,
|
||||||
1,
|
1,
|
||||||
info.pitch};
|
info.pitch};
|
||||||
GpuDetile(reinterpret_cast<const void*>(info.address), scratch.Data(), info.size,
|
GpuDetile(reinterpret_cast<const void*>(info.address), scratch.Data(), info.size, info.size,
|
||||||
info.size, std::span<const GpuTileInfo>(&tile_info, 1));
|
std::span<const GpuTileInfo>(&tile_info, 1));
|
||||||
SwapVideoOutBgra16(scratch.Data(), info.size);
|
SwapVideoOutBgra16(scratch.Data(), info.size);
|
||||||
Transfer::UploadImage(image, scratch.Data(), info.size, info.pitch,
|
Transfer::UploadImage(image, scratch.Data(), info.size, info.pitch, vk::ImageLayout::eGeneral);
|
||||||
vk::ImageLayout::eGeneral);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwapVideoOutBgra16(void* data, uint64_t size) {
|
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,
|
GpuTextureVulkanImage* CreateDummyTexture(bool uint_format, bool image_3d, bool storage) {
|
||||||
bool storage) {
|
|
||||||
auto* image = storage ? static_cast<GpuTextureVulkanImage*>(new StorageTextureVulkanImage)
|
auto* image = storage ? static_cast<GpuTextureVulkanImage*>(new StorageTextureVulkanImage)
|
||||||
: new TextureVulkanImage;
|
: new TextureVulkanImage;
|
||||||
auto usage = storage ? TextureFormatUsage::Storage : TextureFormatUsage::Sampled;
|
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;
|
static constexpr uint32_t zero = 0;
|
||||||
Transfer::UploadImage(*image, &zero, sizeof(zero), 1, layout);
|
Transfer::UploadImage(*image, &zero, sizeof(zero), 1, layout);
|
||||||
TextureCreateImageViews(*image, components, params.type, 0, params.base_level,
|
TextureCreateImageViews(*image, components, params.type, 0, params.base_level, params.levels,
|
||||||
params.levels, params.depth, params.allow_cube_view, params.view_usage);
|
params.depth, params.allow_cube_view, params.view_usage);
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ struct DepthStencilVulkanImage;
|
|||||||
struct TextureVulkanImage;
|
struct TextureVulkanImage;
|
||||||
struct StorageTextureVulkanImage;
|
struct StorageTextureVulkanImage;
|
||||||
struct RenderTextureVulkanImage;
|
struct RenderTextureVulkanImage;
|
||||||
struct VulkanCommandPool;
|
struct CommandSlot;
|
||||||
struct VulkanBuffer;
|
struct VulkanBuffer;
|
||||||
struct VulkanDescriptorSet;
|
struct VulkanDescriptorSet;
|
||||||
struct VulkanFramebuffer;
|
struct VulkanFramebuffer;
|
||||||
@@ -68,19 +68,16 @@ private:
|
|||||||
|
|
||||||
class CommandBuffer {
|
class CommandBuffer {
|
||||||
public:
|
public:
|
||||||
explicit CommandBuffer(int queue);
|
CommandBuffer();
|
||||||
~CommandBuffer() { Free(); }
|
~CommandBuffer() { Release(); }
|
||||||
|
|
||||||
KYTY_CLASS_NO_COPY(CommandBuffer);
|
KYTY_CLASS_NO_COPY(CommandBuffer);
|
||||||
|
|
||||||
[[nodiscard]] bool IsInvalid() const;
|
[[nodiscard]] bool IsInvalid() const;
|
||||||
|
|
||||||
void Allocate();
|
|
||||||
void Free();
|
|
||||||
void Begin() const;
|
void Begin() const;
|
||||||
void End() const;
|
void End() const;
|
||||||
void Execute();
|
void Execute();
|
||||||
void ExecuteWithSemaphore(vk::Semaphore signal_semaphore = nullptr);
|
|
||||||
void ExecuteWithSemaphore(vk::Semaphore wait_semaphore, vk::PipelineStageFlags wait_stage,
|
void ExecuteWithSemaphore(vk::Semaphore wait_semaphore, vk::PipelineStageFlags wait_stage,
|
||||||
vk::Semaphore signal_semaphore);
|
vk::Semaphore signal_semaphore);
|
||||||
void SetDebugInfo(uint32_t op, uint64_t submit_id, uint32_t arg0 = 0, uint32_t arg1 = 0,
|
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]] vk::CommandBuffer Handle() const;
|
||||||
[[nodiscard]] GraphicContext& GetGraphics() const noexcept { return m_graphics; }
|
[[nodiscard]] GraphicContext& GetGraphics() const noexcept { return m_graphics; }
|
||||||
[[nodiscard]] int GetQueue() const { return m_queue; }
|
|
||||||
[[nodiscard]] bool IsExecute() const { return m_execute; }
|
[[nodiscard]] bool IsExecute() const { return m_execute; }
|
||||||
[[nodiscard]] uint64_t GetRecordingGeneration() const { return m_recording_generation; }
|
[[nodiscard]] uint64_t GetRecordingGeneration() const { return m_recording_generation; }
|
||||||
|
|
||||||
@@ -106,16 +102,14 @@ private:
|
|||||||
|
|
||||||
void Submit(vk::Semaphore wait_semaphore, vk::PipelineStageFlags wait_stage,
|
void Submit(vk::Semaphore wait_semaphore, vk::PipelineStageFlags wait_stage,
|
||||||
vk::Semaphore signal_semaphore);
|
vk::Semaphore signal_semaphore);
|
||||||
[[nodiscard]] vk::Semaphore ResolveSignalSemaphore(vk::Semaphore semaphore) const;
|
void Release();
|
||||||
void FinalizeFence(bool reset_recording);
|
void FinalizeFence(bool reset_recording);
|
||||||
void ReleaseResourcesAfterFence();
|
void ReleaseResourcesAfterFence();
|
||||||
void DeleteBuffersAfterFence();
|
void DeleteBuffersAfterFence();
|
||||||
void RecycleDescriptorsAfterFence();
|
void RecycleDescriptorsAfterFence();
|
||||||
|
|
||||||
GraphicContext& m_graphics;
|
GraphicContext& m_graphics;
|
||||||
VulkanCommandPool* m_pool = nullptr;
|
CommandSlot* m_slot = nullptr;
|
||||||
uint32_t m_index = static_cast<uint32_t>(-1);
|
|
||||||
int m_queue = -1;
|
|
||||||
bool m_execute = false;
|
bool m_execute = false;
|
||||||
bool m_fence_waited = false;
|
bool m_fence_waited = false;
|
||||||
uint64_t m_submit_seq = 0;
|
uint64_t m_submit_seq = 0;
|
||||||
@@ -135,10 +129,8 @@ private:
|
|||||||
|
|
||||||
class RenderCommandBuffer final: public CommandBuffer {
|
class RenderCommandBuffer final: public CommandBuffer {
|
||||||
public:
|
public:
|
||||||
RenderCommandBuffer(int queue, HW::Context& registers, HW::UserConfig& user_config,
|
RenderCommandBuffer(HW::Context& registers, HW::UserConfig& user_config, HW::Shader& shaders)
|
||||||
HW::Shader& shaders)
|
: m_registers(registers), m_user_config(user_config), m_shaders(shaders) {}
|
||||||
: CommandBuffer(queue), m_registers(registers), m_user_config(user_config),
|
|
||||||
m_shaders(shaders) {}
|
|
||||||
|
|
||||||
[[nodiscard]] HW::Context& GetRegisters() const noexcept { return m_registers; }
|
[[nodiscard]] HW::Context& GetRegisters() const noexcept { return m_registers; }
|
||||||
[[nodiscard]] HW::UserConfig& GetUserConfig() const noexcept { return m_user_config; }
|
[[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);
|
uint32_t thread_group_y, uint32_t thread_group_z, uint32_t mode);
|
||||||
|
|
||||||
void GraphicsRenderInit(GraphicContext& graphics);
|
void GraphicsRenderInit(GraphicContext& graphics);
|
||||||
void GraphicsRenderReleaseThreadCommandPools();
|
void GraphicsRenderReleaseThreadCommandPool();
|
||||||
|
|
||||||
[[nodiscard]] bool ResolveComputeImageClear(const ShaderComputeInputInfo& input, uint32_t group_x,
|
[[nodiscard]] bool ResolveComputeImageClear(const ShaderComputeInputInfo& input, uint32_t group_x,
|
||||||
uint32_t group_y, uint32_t group_z, uint32_t mode,
|
uint32_t group_y, uint32_t group_z, uint32_t mode,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
// Owner-tracked shared buffer/image transaction. External faults pause GPU submissions first;
|
// 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 {
|
class ResourceMutex final {
|
||||||
public:
|
public:
|
||||||
class FaultScope final {
|
class FaultScope final {
|
||||||
|
|||||||
@@ -1008,7 +1008,7 @@ void TextureCache::MaterializeImagesToGuestLocked(
|
|||||||
const std::vector<std::shared_ptr<CachedImage>>& images) {
|
const std::vector<std::shared_ptr<CachedImage>>& images) {
|
||||||
if (std::any_of(images.begin(), images.end(),
|
if (std::any_of(images.begin(), images.end(),
|
||||||
[](const auto& cached) { return cached->gpu_modified; })) {
|
[](const auto& cached) { return cached->gpu_modified; })) {
|
||||||
Transfer::WaitForGraphicsIdle();
|
Transfer::WaitForQueueIdle();
|
||||||
}
|
}
|
||||||
for (const auto& cached: images) {
|
for (const auto& cached: images) {
|
||||||
if (!cached->gpu_modified) {
|
if (!cached->gpu_modified) {
|
||||||
@@ -1269,7 +1269,7 @@ void TextureCache::RetireSampledTargetAliases(const ImageInfo& requested) {
|
|||||||
}
|
}
|
||||||
RequireRetirementIsolation(retire, "sampled target", requested.address, requested.size);
|
RequireRetirementIsolation(retire, "sampled target", requested.address, requested.size);
|
||||||
if (wait_idle) {
|
if (wait_idle) {
|
||||||
Transfer::WaitForGraphicsIdle();
|
Transfer::WaitForQueueIdle();
|
||||||
}
|
}
|
||||||
for (auto* cached: retire) {
|
for (auto* cached: retire) {
|
||||||
if (!cached->gpu_modified) {
|
if (!cached->gpu_modified) {
|
||||||
@@ -1360,7 +1360,7 @@ void TextureCache::RetireStorageDepthAliasLocked(const ImageInfo& requested) {
|
|||||||
if (selected == nullptr) {
|
if (selected == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Transfer::WaitForGraphicsIdle();
|
Transfer::WaitForQueueIdle();
|
||||||
const auto transfer = m_readback->DownloadDepthTarget(*selected, false);
|
const auto transfer = m_readback->DownloadDepthTarget(*selected, false);
|
||||||
for (const auto& range: transfer.Ranges()) {
|
for (const auto& range: transfer.Ranges()) {
|
||||||
m_memory_tracker.ForEachDownloadRange<true>(range.address, range.size,
|
m_memory_tracker.ForEachDownloadRange<true>(range.address, range.size,
|
||||||
@@ -1373,7 +1373,7 @@ void TextureCache::RetireStorageDepthAliasLocked(const ImageInfo& requested) {
|
|||||||
TextureCache::~TextureCache() {
|
TextureCache::~TextureCache() {
|
||||||
m_readback.reset();
|
m_readback.reset();
|
||||||
if (!m_images.empty()) {
|
if (!m_images.empty()) {
|
||||||
Transfer::WaitForGraphicsIdle();
|
Transfer::WaitForQueueIdle();
|
||||||
}
|
}
|
||||||
for (const auto& image: m_images) {
|
for (const auto& image: m_images) {
|
||||||
UnregisterImageLocked(*image, false);
|
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
|
// 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
|
// 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.
|
// guest backing. This is an uncommon ownership transition, not a frame lookup fast path.
|
||||||
Transfer::WaitForGraphicsIdle();
|
Transfer::WaitForQueueIdle();
|
||||||
for (auto* cached: storage_retire) {
|
for (auto* cached: storage_retire) {
|
||||||
if (!cached->gpu_modified || cached->buffer_modified || cached->info.IsCpuDirty() ||
|
if (!cached->gpu_modified || cached->buffer_modified || cached->info.IsCpuDirty() ||
|
||||||
!m_memory_tracker.IsRegionGpuModified(cached->info.address, cached->info.size) ||
|
!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());
|
cached->Address(), cached->Size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Transfer::WaitForGraphicsIdle();
|
Transfer::WaitForQueueIdle();
|
||||||
for (auto* cached: selected) {
|
for (auto* cached: selected) {
|
||||||
if (cached->gpu_modified) {
|
if (cached->gpu_modified) {
|
||||||
m_memory_tracker.UnmarkRegionAsGpuModified(cached->Address(), cached->Size());
|
m_memory_tracker.UnmarkRegionAsGpuModified(cached->Address(), cached->Size());
|
||||||
@@ -2992,7 +2992,7 @@ void TextureCache::SynchronizeColorImageToBufferLocked(CachedImage& cached, uint
|
|||||||
target.address, target.size);
|
target.address, target.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Transfer::WaitForGraphicsIdle();
|
Transfer::WaitForQueueIdle();
|
||||||
std::vector<ImageBufferCopy> regions;
|
std::vector<ImageBufferCopy> regions;
|
||||||
std::vector<BufferImageCopy> tiled_regions;
|
std::vector<BufferImageCopy> tiled_regions;
|
||||||
TextureUploadLayout tiled_layout {};
|
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,
|
info.layers, static_cast<int>(info.format), info.guest_format, info.bytes_per_element,
|
||||||
has_stencil, has_htile, cached.gpu_modified, cached.buffer_modified);
|
has_stencil, has_htile, cached.gpu_modified, cached.buffer_modified);
|
||||||
}
|
}
|
||||||
Transfer::WaitForGraphicsIdle();
|
Transfer::WaitForQueueIdle();
|
||||||
const auto regions = Transfer::MakeLayeredImageBufferCopies(
|
const auto regions = Transfer::MakeLayeredImageBufferCopies(
|
||||||
1, info.size, info.pitch, info.width, info.height, vk::ImageAspectFlagBits::eDepth);
|
1, info.size, info.pitch, info.width, info.height, vk::ImageAspectFlagBits::eDepth);
|
||||||
TileBlockLayout block {};
|
TileBlockLayout block {};
|
||||||
@@ -3790,7 +3790,7 @@ void TextureCache::UnmapMemory(uint64_t vaddr, uint64_t size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (wait_idle) {
|
if (wait_idle) {
|
||||||
Transfer::WaitForGraphicsIdle();
|
Transfer::WaitForQueueIdle();
|
||||||
}
|
}
|
||||||
for (auto& cached: m_images) {
|
for (auto& cached: m_images) {
|
||||||
if (!cached->OverlapsRange(vaddr, size, false) || !cached->gpu_modified) {
|
if (!cached->OverlapsRange(vaddr, size, false) || !cached->gpu_modified) {
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ void UploadPromotedD16Depth(DepthStencilVulkanImage& image, const DepthTargetInf
|
|||||||
|
|
||||||
void Tiler::DetileImage(GpuTextureVulkanImage& image, const ImageInfo& info,
|
void Tiler::DetileImage(GpuTextureVulkanImage& image, const ImageInfo& info,
|
||||||
const BufferImageCopySource& source, bool refresh, bool storage) const {
|
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 array_texture = TextureIsLayeredTexture(info.type);
|
||||||
const bool volume_texture = TextureIs3DTexture(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,
|
const BufferImageCopySource& source, bool refresh,
|
||||||
uint32_t base_layer) const {
|
uint32_t base_layer) const {
|
||||||
EXIT_NOT_IMPLEMENTED(info.samples != 1 || image.samples != 1);
|
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) {
|
if (DepthAspectTransferBytes(info.format) != info.bytes_per_element) {
|
||||||
switch (info.format) {
|
switch (info.format) {
|
||||||
@@ -149,7 +149,7 @@ void Tiler::DetileStencil(DepthStencilVulkanImage& image, const DepthTargetInfo&
|
|||||||
const BufferImageCopySource& source, bool refresh,
|
const BufferImageCopySource& source, bool refresh,
|
||||||
uint32_t base_layer) const {
|
uint32_t base_layer) const {
|
||||||
EXIT_NOT_IMPLEMENTED(info.samples != 1 || image.samples != 1);
|
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 format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt);
|
||||||
const auto pitch = TileGetTexturePitch(format, info.width, 1,
|
const auto pitch = TileGetTexturePitch(format, info.width, 1,
|
||||||
|
|||||||
@@ -82,39 +82,14 @@ bool GuestBufferIsTiled(uint64_t vaddr, uint64_t size) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaitForGraphicsIdle() {
|
void WaitForQueueIdle() {
|
||||||
auto& graphics = GetRenderContext().GetGraphics();
|
auto& graphics = GetRenderContext().GetGraphics();
|
||||||
Common::Mutex* locked[GraphicContext::QUEUES_NUM] {};
|
EXIT_IF(graphics.queue == nullptr);
|
||||||
int locked_num = 0;
|
Common::LockGuard lock(graphics.queue_mutex);
|
||||||
|
const auto result = graphics.queue.waitIdle();
|
||||||
for (int id = 0; id < GraphicContext::QUEUES_NUM; id++) {
|
|
||||||
auto* mutex = graphics.queues[id].mutex;
|
|
||||||
if (mutex == nullptr || graphics.queues[id].vk_queue == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool already_locked = false;
|
|
||||||
for (int i = 0; i < locked_num; i++) {
|
|
||||||
if (locked[i] == mutex) {
|
|
||||||
already_locked = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!already_locked) {
|
|
||||||
mutex->Lock();
|
|
||||||
locked[locked_num++] = mutex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = graphics.device.waitIdle();
|
|
||||||
|
|
||||||
for (int i = locked_num - 1; i >= 0; i--) {
|
|
||||||
locked[i]->Unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result != vk::Result::eSuccess) {
|
if (result != vk::Result::eSuccess) {
|
||||||
LOGF("vkDeviceWaitIdle failed: %s (%d)\n", VulkanToString(result).c_str(),
|
LOGF("vkQueueWaitIdle failed: %s (%d)\n", VulkanToString(result).c_str(),
|
||||||
static_cast<int>(result));
|
static_cast<int>(result));
|
||||||
}
|
}
|
||||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||||
@@ -126,9 +101,7 @@ static void SetImageLayout(vk::CommandBuffer buffer, VulkanImage& dst_image, uin
|
|||||||
|
|
||||||
template <typename Recorder>
|
template <typename Recorder>
|
||||||
static void ExecuteImmediateCommands(const Recorder& recorder) {
|
static void ExecuteImmediateCommands(const Recorder& recorder) {
|
||||||
// Keep synchronous utility submission behind one boundary so adopting a central scheduler does
|
CommandBuffer command;
|
||||||
// not touch every caller.
|
|
||||||
CommandBuffer command(GraphicContext::QUEUE_UTIL);
|
|
||||||
command.Begin();
|
command.Begin();
|
||||||
recorder(command, command.Handle());
|
recorder(command, command.Handle());
|
||||||
command.End();
|
command.End();
|
||||||
@@ -286,7 +259,7 @@ public:
|
|||||||
|
|
||||||
void UploadToBuffer(VulkanBuffer& dst_buffer, const void* src_data, uint64_t size,
|
void UploadToBuffer(VulkanBuffer& dst_buffer, const void* src_data, uint64_t size,
|
||||||
uint64_t dst_offset) {
|
uint64_t dst_offset) {
|
||||||
RecordUpload<true>(src_data, size, [&](CommandBuffer&, vk::CommandBuffer vk_command) {
|
RecordUpload(src_data, size, [&](CommandBuffer&, vk::CommandBuffer vk_command) {
|
||||||
SetBufferMemoryBarrier(
|
SetBufferMemoryBarrier(
|
||||||
vk_command, m_buffer.buffer, 0, size, vk::AccessFlagBits::eMemoryWrite,
|
vk_command, m_buffer.buffer, 0, size, vk::AccessFlagBits::eMemoryWrite,
|
||||||
vk::AccessFlagBits::eTransferRead, vk::PipelineStageFlagBits::eAllCommands,
|
vk::AccessFlagBits::eTransferRead, vk::PipelineStageFlagBits::eAllCommands,
|
||||||
@@ -314,7 +287,7 @@ public:
|
|||||||
vk::ImageAspectFlags copy_aspect, vk::ImageLayout initial_layout,
|
vk::ImageAspectFlags copy_aspect, vk::ImageLayout initial_layout,
|
||||||
vk::ImageLayout final_layout) {
|
vk::ImageLayout final_layout) {
|
||||||
const auto transition_aspects = GetTransferAspects(image, copy_aspect);
|
const auto transition_aspects = GetTransferAspects(image, copy_aspect);
|
||||||
RecordUpload<false>(src_data, size, [&](CommandBuffer& command, vk::CommandBuffer) {
|
RecordUpload(src_data, size, [&](CommandBuffer& command, vk::CommandBuffer) {
|
||||||
const auto region = MakeBufferImageCopy(0, src_pitch, copy_aspect, 0, 0, {0, 0, 0},
|
const auto region = MakeBufferImageCopy(0, src_pitch, copy_aspect, 0, 0, {0, 0, 0},
|
||||||
{image.extent.width, image.extent.height, 1});
|
{image.extent.width, image.extent.height, 1});
|
||||||
RecordBufferToImageCopy(command, m_buffer, image,
|
RecordBufferToImageCopy(command, m_buffer, image,
|
||||||
@@ -325,7 +298,7 @@ public:
|
|||||||
|
|
||||||
void UploadToImage(VulkanImage& dst_image, const void* src_data, uint64_t size,
|
void UploadToImage(VulkanImage& dst_image, const void* src_data, uint64_t size,
|
||||||
std::span<const BufferImageCopy> regions, vk::ImageLayout dst_layout) {
|
std::span<const BufferImageCopy> regions, vk::ImageLayout dst_layout) {
|
||||||
RecordUpload<false>(src_data, size, [&](CommandBuffer& command, vk::CommandBuffer) {
|
RecordUpload(src_data, size, [&](CommandBuffer& command, vk::CommandBuffer) {
|
||||||
vk::ImageAspectFlags transition_aspects = {};
|
vk::ImageAspectFlags transition_aspects = {};
|
||||||
for (const auto& region: regions) {
|
for (const auto& region: regions) {
|
||||||
transition_aspects |= GetTransferAspects(dst_image, region.aspect);
|
transition_aspects |= GetTransferAspects(dst_image, region.aspect);
|
||||||
@@ -400,13 +373,10 @@ private:
|
|||||||
static_cast<size_t>(size)));
|
static_cast<size_t>(size)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool WaitIdle, typename Recorder>
|
template <typename Recorder>
|
||||||
void RecordUpload(const void* src_data, uint64_t size, const Recorder& recorder) {
|
void RecordUpload(const void* src_data, uint64_t size, const Recorder& recorder) {
|
||||||
Common::LockGuard lock(m_mutex);
|
Common::LockGuard lock(m_mutex);
|
||||||
CopyFromHost(src_data, size, vk::BufferUsageFlagBits::eTransferSrc);
|
CopyFromHost(src_data, size, vk::BufferUsageFlagBits::eTransferSrc);
|
||||||
if constexpr (WaitIdle) {
|
|
||||||
WaitForGraphicsIdle();
|
|
||||||
}
|
|
||||||
ExecuteImmediateCommands(recorder);
|
ExecuteImmediateCommands(recorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -880,12 +850,7 @@ void CopyImageViaBuffer(CommandBuffer& buffer, VulkanImage& src_image,
|
|||||||
std::min<uint64_t>(src_image.extent.height, MAX_COPY_BUFFER_SIZE / row_bytes));
|
std::min<uint64_t>(src_image.extent.height, MAX_COPY_BUFFER_SIZE / row_bytes));
|
||||||
const auto copy_buffer_size = row_bytes * rows_per_chunk;
|
const auto copy_buffer_size = row_bytes * rows_per_chunk;
|
||||||
|
|
||||||
// Kyty does not yet have a cross-queue scheduler. Drain submitted work once before
|
auto& graphics = buffer.GetGraphics();
|
||||||
// recording the recreate copy; commands already recorded on this buffer remain ordered by the
|
|
||||||
// barriers below. The preservation policy stays isolated here so a future scheduler can replace
|
|
||||||
// this synchronization without changing texture-cache ownership logic.
|
|
||||||
auto& graphics = buffer.GetGraphics();
|
|
||||||
WaitForGraphicsIdle();
|
|
||||||
auto* copy_buffer = new VulkanBuffer;
|
auto* copy_buffer = new VulkanBuffer;
|
||||||
copy_buffer->usage =
|
copy_buffer->usage =
|
||||||
vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst;
|
vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst;
|
||||||
@@ -1077,14 +1042,6 @@ void DownloadBuffer(VulkanBuffer& src_buffer, uint64_t src_offset, void* dst_dat
|
|||||||
auto& graphics = GetRenderContext().GetGraphics();
|
auto& graphics = GetRenderContext().GetGraphics();
|
||||||
EXIT_IF(size == 0);
|
EXIT_IF(size == 0);
|
||||||
EXIT_IF(src_offset > src_buffer.buffer_size || size > src_buffer.buffer_size - src_offset);
|
EXIT_IF(src_offset > src_buffer.buffer_size || size > src_buffer.buffer_size - src_offset);
|
||||||
const auto family = graphics.queues[GraphicContext::QUEUE_UTIL].family;
|
|
||||||
EXIT_IF(family == static_cast<uint32_t>(-1));
|
|
||||||
for (int i = GraphicContext::QUEUE_COMPUTE_START;
|
|
||||||
i < GraphicContext::QUEUE_COMPUTE_START + GraphicContext::QUEUE_COMPUTE_NUM; i++) {
|
|
||||||
EXIT_IF(graphics.queues[i].family != family);
|
|
||||||
}
|
|
||||||
EXIT_IF(graphics.queues[GraphicContext::QUEUE_GFX].family != family);
|
|
||||||
|
|
||||||
if (src_buffer.memory.property & vk::MemoryPropertyFlagBits::eHostVisible) {
|
if (src_buffer.memory.property & vk::MemoryPropertyFlagBits::eHostVisible) {
|
||||||
void* mapped = nullptr;
|
void* mapped = nullptr;
|
||||||
graphics.MapMemory(src_buffer.memory, mapped);
|
graphics.MapMemory(src_buffer.memory, mapped);
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ struct ImageImageCopy {
|
|||||||
explicit ImageImageCopy(VulkanImage& source): src_image(source) {}
|
explicit ImageImageCopy(VulkanImage& source): src_image(source) {}
|
||||||
|
|
||||||
VulkanImage& src_image;
|
VulkanImage& src_image;
|
||||||
uint32_t src_level = 0;
|
uint32_t src_level = 0;
|
||||||
uint32_t dst_level = 0;
|
uint32_t dst_level = 0;
|
||||||
uint32_t width = 0;
|
uint32_t width = 0;
|
||||||
uint32_t height = 0;
|
uint32_t height = 0;
|
||||||
uint32_t src_layer = 0;
|
uint32_t src_layer = 0;
|
||||||
uint32_t dst_layer = 0;
|
uint32_t dst_layer = 0;
|
||||||
vk::ImageAspectFlags src_aspect = vk::ImageAspectFlagBits::eColor;
|
vk::ImageAspectFlags src_aspect = vk::ImageAspectFlagBits::eColor;
|
||||||
@@ -134,7 +134,7 @@ bool GuestBufferIsTiled(uint64_t vaddr, uint64_t size);
|
|||||||
bool IsBlockCompressedFormat(vk::Format format);
|
bool IsBlockCompressedFormat(vk::Format format);
|
||||||
uint32_t BlockCompressedBytesPerBlock(vk::Format format);
|
uint32_t BlockCompressedBytesPerBlock(vk::Format format);
|
||||||
|
|
||||||
void WaitForGraphicsIdle();
|
void WaitForQueueIdle();
|
||||||
|
|
||||||
inline std::pair<int, int> MipmapAtlasOffset(uint32_t lod, uint32_t width, uint32_t height) {
|
inline std::pair<int, int> MipmapAtlasOffset(uint32_t lod, uint32_t width, uint32_t height) {
|
||||||
uint32_t mip_width = width;
|
uint32_t mip_width = width;
|
||||||
|
|||||||
@@ -15,24 +15,7 @@ namespace Libs::Graphics {
|
|||||||
|
|
||||||
inline constexpr uint32_t VULKAN_TARGET_API_VERSION = VK_API_VERSION_1_3;
|
inline constexpr uint32_t VULKAN_TARGET_API_VERSION = VK_API_VERSION_1_3;
|
||||||
|
|
||||||
struct VulkanQueueInfo {
|
|
||||||
Common::Mutex* mutex = nullptr;
|
|
||||||
uint32_t family = static_cast<uint32_t>(-1);
|
|
||||||
uint32_t index = static_cast<uint32_t>(-1);
|
|
||||||
vk::Queue vk_queue = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VulkanInstance {
|
struct VulkanInstance {
|
||||||
static constexpr int QUEUES_NUM = 11;
|
|
||||||
static constexpr int QUEUE_GFX = 8;
|
|
||||||
static constexpr int QUEUE_GFX_NUM = 1;
|
|
||||||
static constexpr int QUEUE_UTIL = 9;
|
|
||||||
static constexpr int QUEUE_UTIL_NUM = 1;
|
|
||||||
static constexpr int QUEUE_PRESENT = 10;
|
|
||||||
static constexpr int QUEUE_PRESENT_NUM = 1;
|
|
||||||
static constexpr int QUEUE_COMPUTE_START = 0;
|
|
||||||
static constexpr int QUEUE_COMPUTE_NUM = 8;
|
|
||||||
|
|
||||||
vk::Instance instance = nullptr;
|
vk::Instance instance = nullptr;
|
||||||
vk::DebugUtilsMessengerEXT debug_messenger = nullptr;
|
vk::DebugUtilsMessengerEXT debug_messenger = nullptr;
|
||||||
vk::PhysicalDevice physical_device = nullptr;
|
vk::PhysicalDevice physical_device = nullptr;
|
||||||
@@ -48,7 +31,9 @@ struct VulkanInstance {
|
|||||||
uint32_t min_subgroup_size = 0;
|
uint32_t min_subgroup_size = 0;
|
||||||
uint32_t max_subgroup_size = 0;
|
uint32_t max_subgroup_size = 0;
|
||||||
vk::ShaderStageFlags required_subgroup_size_stages = {};
|
vk::ShaderStageFlags required_subgroup_size_stages = {};
|
||||||
VulkanQueueInfo queues[QUEUES_NUM];
|
Common::Mutex queue_mutex;
|
||||||
|
uint32_t queue_family = static_cast<uint32_t>(-1);
|
||||||
|
vk::Queue queue = nullptr;
|
||||||
|
|
||||||
[[nodiscard]] const vk::PhysicalDeviceProperties& GetPhysicalDeviceProperties() const {
|
[[nodiscard]] const vk::PhysicalDeviceProperties& GetPhysicalDeviceProperties() const {
|
||||||
return physical_device_properties;
|
return physical_device_properties;
|
||||||
|
|||||||
@@ -226,14 +226,12 @@ VulkanSwapchain::~VulkanSwapchain() = default;
|
|||||||
create_info.imageArrayLayers = 1;
|
create_info.imageArrayLayers = 1;
|
||||||
create_info.imageUsage =
|
create_info.imageUsage =
|
||||||
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferDst;
|
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferDst;
|
||||||
create_info.imageSharingMode = vk::SharingMode::eExclusive;
|
create_info.imageSharingMode = vk::SharingMode::eExclusive;
|
||||||
create_info.queueFamilyIndexCount = 0;
|
create_info.preTransform = r.capabilities.currentTransform;
|
||||||
create_info.pQueueFamilyIndices = nullptr;
|
create_info.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque;
|
||||||
create_info.preTransform = r.capabilities.currentTransform;
|
create_info.presentMode = vk::PresentModeKHR::eFifo;
|
||||||
create_info.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque;
|
create_info.clipped = VK_TRUE;
|
||||||
create_info.presentMode = vk::PresentModeKHR::eFifo;
|
create_info.oldSwapchain = nullptr;
|
||||||
create_info.clipped = VK_TRUE;
|
|
||||||
create_info.oldSwapchain = nullptr;
|
|
||||||
|
|
||||||
swapchain_format = create_info.imageFormat;
|
swapchain_format = create_info.imageFormat;
|
||||||
swapchain_extent = extent;
|
swapchain_extent = extent;
|
||||||
@@ -331,7 +329,7 @@ static void VulkanDeleteSwapchain(VulkanSwapchain* s) {
|
|||||||
auto swapchain_owner = std::unique_ptr<VulkanSwapchain>(s);
|
auto swapchain_owner = std::unique_ptr<VulkanSwapchain>(s);
|
||||||
auto& graphics = g_window_ctx->graphic_ctx;
|
auto& graphics = g_window_ctx->graphic_ctx;
|
||||||
|
|
||||||
Transfer::WaitForGraphicsIdle();
|
Transfer::WaitForQueueIdle();
|
||||||
|
|
||||||
if (s->image_acquired_semaphores != nullptr) {
|
if (s->image_acquired_semaphores != nullptr) {
|
||||||
for (uint32_t i = 0; i < s->swapchain_images_count; i++) {
|
for (uint32_t i = 0; i < s->swapchain_images_count; i++) {
|
||||||
@@ -380,15 +378,9 @@ static void VulkanRecreateSwapchain() {
|
|||||||
g_window_ctx->swapchain = VulkanCreateSwapchain(2);
|
g_window_ctx->swapchain = VulkanCreateSwapchain(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ValidatePreparedCommand(CommandBuffer& buffer) {
|
|
||||||
if (buffer.IsInvalid() || buffer.GetQueue() != GraphicContext::QUEUE_GFX) {
|
|
||||||
EXIT("prepared frames must be recorded on the graphics queue\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PreparedFrame& WindowPrepareFrame(CommandBuffer& buffer, VideoOutVulkanImage& image) {
|
PreparedFrame& WindowPrepareFrame(CommandBuffer& buffer, VideoOutVulkanImage& image) {
|
||||||
KYTY_PROFILER_FUNCTION();
|
KYTY_PROFILER_FUNCTION();
|
||||||
ValidatePreparedCommand(buffer);
|
EXIT_IF(buffer.IsInvalid());
|
||||||
if (image.format == vk::Format::eUndefined) {
|
if (image.format == vk::Format::eUndefined) {
|
||||||
EXIT("unsupported presentation source, image=%p\n", static_cast<const void*>(&image));
|
EXIT("unsupported presentation source, image=%p\n", static_cast<const void*>(&image));
|
||||||
}
|
}
|
||||||
@@ -408,7 +400,7 @@ PreparedFrame& WindowPrepareFrame(CommandBuffer& buffer, VideoOutVulkanImage& im
|
|||||||
PreparedFrame& WindowPrepareBlankFrame(CommandBuffer& buffer, uint32_t width, uint32_t height,
|
PreparedFrame& WindowPrepareBlankFrame(CommandBuffer& buffer, uint32_t width, uint32_t height,
|
||||||
bool opaque) {
|
bool opaque) {
|
||||||
KYTY_PROFILER_FUNCTION();
|
KYTY_PROFILER_FUNCTION();
|
||||||
ValidatePreparedCommand(buffer);
|
EXIT_IF(buffer.IsInvalid());
|
||||||
auto* pool = GetPreparedFramePool();
|
auto* pool = GetPreparedFramePool();
|
||||||
auto format = pool->GetFormat();
|
auto format = pool->GetFormat();
|
||||||
auto* frame = pool->Acquire();
|
auto* frame = pool->Acquire();
|
||||||
@@ -462,7 +454,7 @@ void WindowPresentFrame(PreparedFrame& frame) {
|
|||||||
}
|
}
|
||||||
EXIT_NOT_IMPLEMENTED(swapchain->current_index == static_cast<uint32_t>(-1));
|
EXIT_NOT_IMPLEMENTED(swapchain->current_index == static_cast<uint32_t>(-1));
|
||||||
if (frame.present_commands == nullptr) {
|
if (frame.present_commands == nullptr) {
|
||||||
frame.present_commands = std::make_unique<CommandBuffer>(GraphicContext::QUEUE_GFX);
|
frame.present_commands = std::make_unique<CommandBuffer>();
|
||||||
}
|
}
|
||||||
frame.present_commands->WaitForFenceAndReset();
|
frame.present_commands->WaitForFenceAndReset();
|
||||||
auto& buffer = *frame.present_commands;
|
auto& buffer = *frame.present_commands;
|
||||||
@@ -510,14 +502,10 @@ void WindowPresentFrame(PreparedFrame& frame) {
|
|||||||
present.waitSemaphoreCount = 1;
|
present.waitSemaphoreCount = 1;
|
||||||
present.pResults = nullptr;
|
present.pResults = nullptr;
|
||||||
|
|
||||||
const auto& queue = g_window_ctx->graphic_ctx.queues[GraphicContext::QUEUE_PRESENT];
|
auto& graphics = g_window_ctx->graphic_ctx;
|
||||||
|
{
|
||||||
if (queue.mutex != nullptr) {
|
Common::LockGuard lock(graphics.queue_mutex);
|
||||||
queue.mutex->Lock();
|
result = graphics.queue.presentKHR(&present);
|
||||||
}
|
|
||||||
result = queue.vk_queue.presentKHR(&present);
|
|
||||||
if (queue.mutex != nullptr) {
|
|
||||||
queue.mutex->Unlock();
|
|
||||||
}
|
}
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case vk::Result::eSuccess: break;
|
case vk::Result::eSuccess: break;
|
||||||
|
|||||||
@@ -124,128 +124,39 @@ static bool CheckFormat(vk::PhysicalDevice device, vk::Format format, bool tile,
|
|||||||
return (supported_features & features) == features;
|
return (supported_features & features) == features;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct QueueInfo {
|
static uint32_t VulkanFindQueueFamily(vk::PhysicalDevice device, vk::SurfaceKHR surface) {
|
||||||
uint32_t family = 0;
|
|
||||||
uint32_t index = 0;
|
|
||||||
bool graphics = false;
|
|
||||||
bool compute = false;
|
|
||||||
bool present = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VulkanQueues {
|
|
||||||
uint32_t family_count = 0;
|
|
||||||
std::vector<uint32_t> family_used;
|
|
||||||
std::vector<QueueInfo> available;
|
|
||||||
std::vector<QueueInfo> graphics;
|
|
||||||
std::vector<QueueInfo> compute;
|
|
||||||
std::vector<QueueInfo> present;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void VulkanDumpQueues(const VulkanQueues& qs) {
|
|
||||||
LOGF("Queues selected:\n"
|
|
||||||
"\t family_count = %u\n",
|
|
||||||
qs.family_count);
|
|
||||||
std::vector<std::string> nums;
|
|
||||||
for (auto u: qs.family_used) {
|
|
||||||
nums.push_back(fmt::format("{}", u));
|
|
||||||
}
|
|
||||||
LOGF("\t family_used = [%s]\n"
|
|
||||||
"\t graphics:\n",
|
|
||||||
Common::Concat(nums, ", ").c_str());
|
|
||||||
for (const auto& q: qs.graphics) {
|
|
||||||
LOGF("\t\t family = %u, index = %u\n", q.family, q.index);
|
|
||||||
}
|
|
||||||
LOGF("\t compute:\n");
|
|
||||||
for (const auto& q: qs.compute) {
|
|
||||||
LOGF("\t\t family = %u, index = %u\n", q.family, q.index);
|
|
||||||
}
|
|
||||||
LOGF("\t present:\n");
|
|
||||||
for (const auto& q: qs.present) {
|
|
||||||
LOGF("\t\t family = %u, index = %u\n", q.family, q.index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static VulkanQueues VulkanFindQueues(vk::PhysicalDevice device, vk::SurfaceKHR surface,
|
|
||||||
uint32_t graphics_num, uint32_t compute_num,
|
|
||||||
uint32_t present_num) {
|
|
||||||
EXIT_IF(device == nullptr);
|
EXIT_IF(device == nullptr);
|
||||||
EXIT_IF(surface == nullptr);
|
EXIT_IF(surface == nullptr);
|
||||||
|
|
||||||
VulkanQueues qs;
|
|
||||||
|
|
||||||
uint32_t queue_family_count = 0;
|
uint32_t queue_family_count = 0;
|
||||||
device.getQueueFamilyProperties(&queue_family_count, nullptr);
|
device.getQueueFamilyProperties(&queue_family_count, nullptr);
|
||||||
std::vector<vk::QueueFamilyProperties> queue_families(queue_family_count);
|
std::vector<vk::QueueFamilyProperties> queue_families(queue_family_count);
|
||||||
device.getQueueFamilyProperties(&queue_family_count, queue_families.data());
|
device.getQueueFamilyProperties(&queue_family_count, queue_families.data());
|
||||||
|
|
||||||
qs.family_count = queue_family_count;
|
const auto required = vk::QueueFlagBits::eGraphics | vk::QueueFlagBits::eCompute;
|
||||||
|
for (uint32_t family = 0; family < queue_family_count; family++) {
|
||||||
uint32_t family = 0;
|
const auto& properties = queue_families[family];
|
||||||
for (auto& f: queue_families) {
|
vk::Bool32 presentation_supported = VK_FALSE;
|
||||||
vk::Bool32 presentation_supported = VK_FALSE;
|
|
||||||
RequireVulkanSuccess(device.getSurfaceSupportKHR(family, surface, &presentation_supported),
|
RequireVulkanSuccess(device.getSurfaceSupportKHR(family, surface, &presentation_supported),
|
||||||
"vkGetPhysicalDeviceSurfaceSupportKHR");
|
"vkGetPhysicalDeviceSurfaceSupportKHR");
|
||||||
|
|
||||||
LOGF("\tqueue family: %s [count = %u], [present = %s]\n",
|
LOGF("\tqueue family: %s [count = %u], [present = %s]\n",
|
||||||
VulkanToString(f.queueFlags).c_str(), f.queueCount,
|
VulkanToString(properties.queueFlags).c_str(), properties.queueCount,
|
||||||
(presentation_supported == VK_TRUE ? "true" : "false"));
|
(presentation_supported == VK_TRUE ? "true" : "false"));
|
||||||
|
if (properties.queueCount != 0 && (properties.queueFlags & required) == required &&
|
||||||
for (uint32_t i = 0; i < f.queueCount; i++) {
|
presentation_supported == VK_TRUE) {
|
||||||
QueueInfo info;
|
LOGF("\tselected universal queue family %u\n", family);
|
||||||
info.family = family;
|
return family;
|
||||||
info.index = i;
|
|
||||||
info.graphics = static_cast<bool>(f.queueFlags & vk::QueueFlagBits::eGraphics);
|
|
||||||
info.compute = static_cast<bool>(f.queueFlags & vk::QueueFlagBits::eCompute);
|
|
||||||
info.present = (presentation_supported == VK_TRUE);
|
|
||||||
|
|
||||||
qs.available.push_back(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
qs.family_used.push_back(0);
|
|
||||||
|
|
||||||
family++;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto select_queues = [&qs](uint32_t count, auto matches, auto& selected) {
|
|
||||||
for (uint32_t i = 0; i < count; i++) {
|
|
||||||
auto it = std::find_if(qs.available.begin(), qs.available.end(), matches);
|
|
||||||
if (it == qs.available.end()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
qs.family_used[it->family]++;
|
|
||||||
selected.push_back(*it);
|
|
||||||
qs.available.erase(it);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
select_queues(graphics_num, [](const auto& q) { return q.graphics; }, qs.graphics);
|
|
||||||
|
|
||||||
const uint32_t graphics_family =
|
|
||||||
qs.graphics.empty() ? static_cast<uint32_t>(-1) : qs.graphics.front().family;
|
|
||||||
select_queues(
|
|
||||||
compute_num,
|
|
||||||
[graphics_family](const auto& q) { return q.compute && q.family == graphics_family; },
|
|
||||||
qs.compute);
|
|
||||||
if (compute_num != 0 && qs.compute.empty()) {
|
|
||||||
auto graphics_compute = std::find_if(qs.graphics.begin(), qs.graphics.end(),
|
|
||||||
[](const auto& q) { return q.compute; });
|
|
||||||
if (graphics_compute != qs.graphics.end()) {
|
|
||||||
// Reuse the universal graphics queue when Intel GPUs expose no spare compute queue.
|
|
||||||
qs.compute.push_back(*graphics_compute);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return static_cast<uint32_t>(-1);
|
||||||
select_queues(present_num, [](const auto& q) { return q.present; }, qs.present);
|
|
||||||
|
|
||||||
return qs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||||
static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surface,
|
static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surface,
|
||||||
const std::vector<const char*>& device_extensions,
|
const std::vector<const char*>& device_extensions,
|
||||||
SurfaceCapabilities& out_capabilities,
|
SurfaceCapabilities& out_capabilities,
|
||||||
vk::PhysicalDevice& out_device, VulkanQueues& out_queues) {
|
vk::PhysicalDevice& out_device, uint32_t& out_queue_family) {
|
||||||
EXIT_IF(instance == nullptr);
|
EXIT_IF(instance == nullptr);
|
||||||
EXIT_IF(surface == nullptr);
|
EXIT_IF(surface == nullptr);
|
||||||
|
|
||||||
@@ -255,8 +166,8 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
|
|||||||
});
|
});
|
||||||
EXIT_NOT_IMPLEMENTED(devices.empty());
|
EXIT_NOT_IMPLEMENTED(devices.empty());
|
||||||
|
|
||||||
vk::PhysicalDevice best_device = nullptr;
|
vk::PhysicalDevice best_device = nullptr;
|
||||||
VulkanQueues best_queues;
|
uint32_t best_queue_family = static_cast<uint32_t>(-1);
|
||||||
SurfaceCapabilities best_capabilities;
|
SurfaceCapabilities best_capabilities;
|
||||||
|
|
||||||
for (const auto& device: devices) {
|
for (const auto& device: devices) {
|
||||||
@@ -304,16 +215,9 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
|
|||||||
|
|
||||||
device.getFeatures2(&device_features2);
|
device.getFeatures2(&device_features2);
|
||||||
|
|
||||||
auto qs =
|
const auto queue_family = VulkanFindQueueFamily(device, surface);
|
||||||
VulkanFindQueues(device, surface, GraphicContext::QUEUE_GFX_NUM,
|
if (queue_family == static_cast<uint32_t>(-1)) {
|
||||||
GraphicContext::QUEUE_COMPUTE_NUM, GraphicContext::QUEUE_PRESENT_NUM);
|
LOGF("No universal graphics, compute, and presentation queue\n");
|
||||||
|
|
||||||
VulkanDumpQueues(qs);
|
|
||||||
|
|
||||||
if (qs.graphics.size() != GraphicContext::QUEUE_GFX_NUM ||
|
|
||||||
!(qs.compute.size() >= 1 && qs.compute.size() <= GraphicContext::QUEUE_COMPUTE_NUM) ||
|
|
||||||
qs.present.size() != GraphicContext::QUEUE_PRESENT_NUM) {
|
|
||||||
LOGF("Not enough queues\n");
|
|
||||||
skip_device = true;
|
skip_device = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,13 +408,13 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
|
|||||||
if (best_device == nullptr ||
|
if (best_device == nullptr ||
|
||||||
device_properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) {
|
device_properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) {
|
||||||
best_device = device;
|
best_device = device;
|
||||||
best_queues = qs;
|
best_queue_family = queue_family;
|
||||||
best_capabilities = std::move(candidate_capabilities);
|
best_capabilities = std::move(candidate_capabilities);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out_device = best_device;
|
out_device = best_device;
|
||||||
out_queues = best_queues;
|
out_queue_family = best_queue_family;
|
||||||
if (best_device != nullptr) {
|
if (best_device != nullptr) {
|
||||||
out_capabilities = std::move(best_capabilities);
|
out_capabilities = std::move(best_capabilities);
|
||||||
}
|
}
|
||||||
@@ -558,35 +462,19 @@ static void VulkanInitSubgroupSizeControl(vk::PhysicalDevice physical_device) {
|
|||||||
graphics.subgroup_size_control_enabled ? "true" : "false");
|
graphics.subgroup_size_control_enabled ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, vk::SurfaceKHR surface,
|
static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const VulkanExtensions& r,
|
||||||
const VulkanExtensions& r, const VulkanQueues& queues,
|
uint32_t queue_family,
|
||||||
const std::vector<const char*>& device_extensions) {
|
const std::vector<const char*>& device_extensions) {
|
||||||
EXIT_IF(physical_device == nullptr);
|
EXIT_IF(physical_device == nullptr);
|
||||||
auto& graphics = g_window_ctx->graphic_ctx;
|
auto& graphics = g_window_ctx->graphic_ctx;
|
||||||
EXIT_IF(surface == nullptr);
|
EXIT_IF(queue_family == static_cast<uint32_t>(-1));
|
||||||
|
|
||||||
std::vector<vk::DeviceQueueCreateInfo> queue_create_info(queues.family_count);
|
const float queue_priority = 1.0f;
|
||||||
std::vector<std::vector<float>> queue_priority(queues.family_count);
|
vk::DeviceQueueCreateInfo queue_create_info {};
|
||||||
uint32_t queue_create_info_num = 0;
|
queue_create_info.sType = vk::StructureType::eDeviceQueueCreateInfo;
|
||||||
|
queue_create_info.queueFamilyIndex = queue_family;
|
||||||
for (uint32_t i = 0; i < queues.family_count; i++) {
|
queue_create_info.queueCount = 1;
|
||||||
if (queues.family_used[i] != 0) {
|
queue_create_info.pQueuePriorities = &queue_priority;
|
||||||
for (uint32_t pi = 0; pi < queues.family_used[i]; pi++) {
|
|
||||||
queue_priority[queue_create_info_num].push_back(1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
queue_create_info[queue_create_info_num].sType =
|
|
||||||
vk::StructureType::eDeviceQueueCreateInfo;
|
|
||||||
queue_create_info[queue_create_info_num].pNext = nullptr;
|
|
||||||
queue_create_info[queue_create_info_num].flags = {};
|
|
||||||
queue_create_info[queue_create_info_num].queueFamilyIndex = i;
|
|
||||||
queue_create_info[queue_create_info_num].queueCount = queues.family_used[i];
|
|
||||||
queue_create_info[queue_create_info_num].pQueuePriorities =
|
|
||||||
queue_priority[queue_create_info_num].data();
|
|
||||||
|
|
||||||
queue_create_info_num++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::PhysicalDeviceColorWriteEnableFeaturesEXT color_write_ext {};
|
vk::PhysicalDeviceColorWriteEnableFeaturesEXT color_write_ext {};
|
||||||
color_write_ext.sType = vk::StructureType::ePhysicalDeviceColorWriteEnableFeaturesEXT;
|
color_write_ext.sType = vk::StructureType::ePhysicalDeviceColorWriteEnableFeaturesEXT;
|
||||||
@@ -678,8 +566,8 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, vk::Sur
|
|||||||
create_info.sType = vk::StructureType::eDeviceCreateInfo;
|
create_info.sType = vk::StructureType::eDeviceCreateInfo;
|
||||||
create_info.pNext = &features13;
|
create_info.pNext = &features13;
|
||||||
create_info.flags = {};
|
create_info.flags = {};
|
||||||
create_info.pQueueCreateInfos = queue_create_info.data();
|
create_info.pQueueCreateInfos = &queue_create_info;
|
||||||
create_info.queueCreateInfoCount = queue_create_info_num;
|
create_info.queueCreateInfoCount = 1;
|
||||||
create_info.enabledLayerCount =
|
create_info.enabledLayerCount =
|
||||||
(r.enable_validation_layers ? static_cast<uint32_t>(r.required_layers.size()) : 0);
|
(r.enable_validation_layers ? static_cast<uint32_t>(r.required_layers.size()) : 0);
|
||||||
create_info.ppEnabledLayerNames =
|
create_info.ppEnabledLayerNames =
|
||||||
@@ -865,64 +753,6 @@ static VKAPI_ATTR vk::Result VKAPI_CALL VulkanCreateDebugUtilsMessengerEXT(
|
|||||||
return vk::Result::eErrorExtensionNotPresent;
|
return vk::Result::eErrorExtensionNotPresent;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void VulkanCreateQueues(const VulkanQueues& queues) {
|
|
||||||
auto& graphics = g_window_ctx->graphic_ctx;
|
|
||||||
EXIT_IF(graphics.device == nullptr);
|
|
||||||
EXIT_IF(queues.graphics.size() != 1);
|
|
||||||
EXIT_IF(queues.present.size() != 1);
|
|
||||||
EXIT_IF(!(queues.compute.size() >= 1 &&
|
|
||||||
queues.compute.size() <= GraphicContext::QUEUE_COMPUTE_NUM));
|
|
||||||
|
|
||||||
auto get_queue = [&graphics](int id, const QueueInfo& info) {
|
|
||||||
graphics.queues[id].family = info.family;
|
|
||||||
graphics.queues[id].index = info.index;
|
|
||||||
EXIT_IF(graphics.queues[id].vk_queue != nullptr);
|
|
||||||
graphics.device.getQueue(graphics.queues[id].family, graphics.queues[id].index,
|
|
||||||
&graphics.queues[id].vk_queue);
|
|
||||||
EXIT_NOT_IMPLEMENTED(graphics.queues[id].vk_queue == nullptr);
|
|
||||||
};
|
|
||||||
|
|
||||||
get_queue(GraphicContext::QUEUE_GFX, queues.graphics[0]);
|
|
||||||
graphics.queues[GraphicContext::QUEUE_UTIL].family =
|
|
||||||
graphics.queues[GraphicContext::QUEUE_GFX].family;
|
|
||||||
graphics.queues[GraphicContext::QUEUE_UTIL].index =
|
|
||||||
graphics.queues[GraphicContext::QUEUE_GFX].index;
|
|
||||||
graphics.queues[GraphicContext::QUEUE_UTIL].vk_queue =
|
|
||||||
graphics.queues[GraphicContext::QUEUE_GFX].vk_queue;
|
|
||||||
LOGF("Vulkan queue: using graphics queue for utility submissions to preserve resource "
|
|
||||||
"ordering\n");
|
|
||||||
get_queue(GraphicContext::QUEUE_PRESENT, queues.present[0]);
|
|
||||||
|
|
||||||
for (int id = 0; id < GraphicContext::QUEUE_COMPUTE_NUM; id++) {
|
|
||||||
get_queue(GraphicContext::QUEUE_COMPUTE_START + id,
|
|
||||||
queues.compute[id % queues.compute.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int id = 0; id < GraphicContext::QUEUES_NUM; id++) {
|
|
||||||
auto& queue = graphics.queues[id];
|
|
||||||
if (queue.vk_queue == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
EXIT_IF(queue.mutex != nullptr);
|
|
||||||
|
|
||||||
for (int other_id = 0; other_id < id; other_id++) {
|
|
||||||
auto& other = graphics.queues[other_id];
|
|
||||||
if (other.vk_queue != queue.vk_queue) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
EXIT_IF(other.mutex == nullptr);
|
|
||||||
queue.mutex = other.mutex;
|
|
||||||
LOGF("Vulkan queue: sharing mutex for queue ids %d and %d\n", other_id, id);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queue.mutex == nullptr) {
|
|
||||||
queue.mutex = &graphics.queue_mutexes[id];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void VulkanCheckInstanceVersion() {
|
static void VulkanCheckInstanceVersion() {
|
||||||
uint32_t version = VK_API_VERSION_1_0;
|
uint32_t version = VK_API_VERSION_1_0;
|
||||||
|
|
||||||
@@ -1061,11 +891,11 @@ void VulkanCreate(WindowContext& window) {
|
|||||||
|
|
||||||
window.surface_capabilities = new SurfaceCapabilities {};
|
window.surface_capabilities = new SurfaceCapabilities {};
|
||||||
|
|
||||||
VulkanQueues queues;
|
uint32_t queue_family = static_cast<uint32_t>(-1);
|
||||||
|
|
||||||
VulkanFindPhysicalDevice(window.graphic_ctx.instance, window.surface, device_extensions,
|
VulkanFindPhysicalDevice(window.graphic_ctx.instance, window.surface, device_extensions,
|
||||||
*window.surface_capabilities, window.graphic_ctx.physical_device,
|
*window.surface_capabilities, window.graphic_ctx.physical_device,
|
||||||
queues);
|
queue_family);
|
||||||
|
|
||||||
if (window.graphic_ctx.physical_device == nullptr) {
|
if (window.graphic_ctx.physical_device == nullptr) {
|
||||||
EXIT("Could not find suitable device");
|
EXIT("Could not find suitable device");
|
||||||
@@ -1102,19 +932,20 @@ void VulkanCreate(WindowContext& window) {
|
|||||||
|
|
||||||
VulkanInitSubgroupSizeControl(window.graphic_ctx.physical_device);
|
VulkanInitSubgroupSizeControl(window.graphic_ctx.physical_device);
|
||||||
|
|
||||||
window.graphic_ctx.device = VulkanCreateDevice(window.graphic_ctx.physical_device,
|
window.graphic_ctx.device =
|
||||||
window.surface, r, queues, device_extensions);
|
VulkanCreateDevice(window.graphic_ctx.physical_device, r, queue_family, device_extensions);
|
||||||
if (window.graphic_ctx.device == nullptr) {
|
if (window.graphic_ctx.device == nullptr) {
|
||||||
EXIT("Could not create device");
|
EXIT("Could not create device");
|
||||||
}
|
}
|
||||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(window.graphic_ctx.device);
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(window.graphic_ctx.device);
|
||||||
|
window.graphic_ctx.queue_family = queue_family;
|
||||||
|
window.graphic_ctx.device.getQueue(queue_family, 0, &window.graphic_ctx.queue);
|
||||||
|
EXIT_IF(window.graphic_ctx.queue == nullptr);
|
||||||
|
|
||||||
if (!window.graphic_ctx.CreateAllocator()) {
|
if (!window.graphic_ctx.CreateAllocator()) {
|
||||||
EXIT("Could not create Vulkan memory allocator");
|
EXIT("Could not create Vulkan memory allocator");
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanCreateQueues(queues);
|
|
||||||
|
|
||||||
window.swapchain = VulkanCreateSwapchain(2);
|
window.swapchain = VulkanCreateSwapchain(2);
|
||||||
RenderDocSetActiveWindow(window.graphic_ctx.instance, window.window);
|
RenderDocSetActiveWindow(window.graphic_ctx.instance, window.window);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -855,6 +855,22 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] vk::Device Device() const { return m_device; }
|
[[nodiscard]] vk::Device Device() const { return m_device; }
|
||||||
|
|
||||||
|
void CheckCommandPoolGrowth() {
|
||||||
|
EnsureRuntimeContext();
|
||||||
|
std::array<CommandBuffer, 12> commands;
|
||||||
|
for (auto &command : commands) {
|
||||||
|
Require("CommandPoolGrowth", "allocation", !command.IsInvalid(),
|
||||||
|
"unified command pool failed to grow");
|
||||||
|
command.Begin();
|
||||||
|
command.End();
|
||||||
|
command.Execute();
|
||||||
|
}
|
||||||
|
for (auto &command : commands) {
|
||||||
|
command.WaitForFence();
|
||||||
|
}
|
||||||
|
std::printf("[host] %-32s ok\n", "CommandPoolGrowth");
|
||||||
|
}
|
||||||
|
|
||||||
void CheckMutableStorageSrgbView() {
|
void CheckMutableStorageSrgbView() {
|
||||||
constexpr const char *name = "StorageTextureMutableSrgbView";
|
constexpr const char *name = "StorageTextureMutableSrgbView";
|
||||||
vk::ImageCreateInfo image_info{};
|
vk::ImageCreateInfo image_info{};
|
||||||
@@ -1350,7 +1366,7 @@ public:
|
|||||||
target_info.tile_mode = linear;
|
target_info.tile_mode = linear;
|
||||||
|
|
||||||
{
|
{
|
||||||
CommandBuffer command(GraphicContext::QUEUE_GFX);
|
CommandBuffer command;
|
||||||
(void)texture_cache.FindTexture(command, sampled_info, false);
|
(void)texture_cache.FindTexture(command, sampled_info, false);
|
||||||
auto& target = texture_cache.FindRenderTarget(command, target_info);
|
auto& target = texture_cache.FindRenderTarget(command, target_info);
|
||||||
|
|
||||||
@@ -3047,12 +3063,8 @@ private:
|
|||||||
m_physical_device.getProperties(
|
m_physical_device.getProperties(
|
||||||
&m_runtime_context.physical_device_properties);
|
&m_runtime_context.physical_device_properties);
|
||||||
m_runtime_context.physical_device_memory_properties = m_memory_properties;
|
m_runtime_context.physical_device_memory_properties = m_memory_properties;
|
||||||
for (auto &queue : m_runtime_context.queues) {
|
m_runtime_context.queue_family = m_queue_family;
|
||||||
queue.mutex = &m_runtime_queue_mutex;
|
m_runtime_context.queue = m_queue;
|
||||||
queue.family = m_queue_family;
|
|
||||||
queue.index = 0;
|
|
||||||
queue.vk_queue = m_queue;
|
|
||||||
}
|
|
||||||
|
|
||||||
VmaVulkanFunctions functions{};
|
VmaVulkanFunctions functions{};
|
||||||
functions.vkGetInstanceProcAddr =
|
functions.vkGetInstanceProcAddr =
|
||||||
@@ -3172,7 +3184,7 @@ private:
|
|||||||
if (m_device != nullptr) {
|
if (m_device != nullptr) {
|
||||||
RequireVulkanSuccess(m_device.waitIdle(), "vkDeviceWaitIdle");
|
RequireVulkanSuccess(m_device.waitIdle(), "vkDeviceWaitIdle");
|
||||||
if (m_runtime_context.allocator != nullptr) {
|
if (m_runtime_context.allocator != nullptr) {
|
||||||
GraphicsRenderReleaseThreadCommandPools();
|
GraphicsRenderReleaseThreadCommandPool();
|
||||||
Transfer::ReleaseCachedResources();
|
Transfer::ReleaseCachedResources();
|
||||||
vmaDestroyAllocator(m_runtime_context.allocator);
|
vmaDestroyAllocator(m_runtime_context.allocator);
|
||||||
m_runtime_context.allocator = nullptr;
|
m_runtime_context.allocator = nullptr;
|
||||||
@@ -3413,7 +3425,6 @@ private:
|
|||||||
vk::CommandPool m_command_pool = nullptr;
|
vk::CommandPool m_command_pool = nullptr;
|
||||||
u32 m_queue_family = 0;
|
u32 m_queue_family = 0;
|
||||||
vk::PhysicalDeviceMemoryProperties m_memory_properties{};
|
vk::PhysicalDeviceMemoryProperties m_memory_properties{};
|
||||||
Common::Mutex m_runtime_queue_mutex;
|
|
||||||
GraphicContext m_runtime_context{};
|
GraphicContext m_runtime_context{};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -10540,13 +10551,6 @@ void CheckBufferCacheRangeMerge() {
|
|||||||
MergeOverlappingBufferCacheRange(merged, {0x10000, 0x1000}) &&
|
MergeOverlappingBufferCacheRange(merged, {0x10000, 0x1000}) &&
|
||||||
merged.address == 0xc000 && merged.size == 0xe000,
|
merged.address == 0xc000 && merged.size == 0xe000,
|
||||||
"contained range changed the cache union");
|
"contained range changed the cache union");
|
||||||
Require("BufferCacheRangeMerge", "queue ownership",
|
|
||||||
CanMergeBufferCacheQueueMask(0, 3) &&
|
|
||||||
CanMergeBufferCacheQueueMask(uint64_t{1} << 3u, 3) &&
|
|
||||||
!CanMergeBufferCacheQueueMask(
|
|
||||||
(uint64_t{1} << 2u) | (uint64_t{1} << 3u), 3) &&
|
|
||||||
!CanMergeBufferCacheQueueMask(0, 64),
|
|
||||||
"cross-queue or invalid queue ownership was accepted");
|
|
||||||
std::printf("[host] %-32s ok\n", "BufferCacheRangeMerge");
|
std::printf("[host] %-32s ok\n", "BufferCacheRangeMerge");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13907,26 +13911,27 @@ struct FenceLifetimeProbe {
|
|||||||
bool *destroyed = nullptr;
|
bool *destroyed = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
void CheckCrossQueueImageLifetime() {
|
void CheckSharedFenceResourceLifetime() {
|
||||||
bool destroyed = false;
|
bool destroyed = false;
|
||||||
auto image = std::make_shared<FenceLifetimeProbe>(&destroyed);
|
auto image = std::make_shared<FenceLifetimeProbe>(&destroyed);
|
||||||
FenceResourceRetainer graphics;
|
FenceResourceRetainer first;
|
||||||
FenceResourceRetainer compute;
|
FenceResourceRetainer second;
|
||||||
graphics.Retain(image);
|
first.Retain(image);
|
||||||
compute.Retain(image);
|
second.Retain(image);
|
||||||
graphics.Retain(image);
|
first.Retain(image);
|
||||||
image.reset();
|
image.reset();
|
||||||
Require("CrossQueueImageLifetime", "retained",
|
Require("SharedFenceResourceLifetime", "retained",
|
||||||
!destroyed && !graphics.Empty() && !compute.Empty(),
|
!destroyed && !first.Empty() && !second.Empty(),
|
||||||
"cache removal destroyed an image retained by command buffers");
|
"cache removal destroyed an image retained by command buffers");
|
||||||
graphics.ReleaseAfterFence();
|
first.ReleaseAfterFence();
|
||||||
Require("CrossQueueImageLifetime", "first fence",
|
Require("SharedFenceResourceLifetime", "first fence",
|
||||||
!destroyed && graphics.Empty() && !compute.Empty(),
|
!destroyed && first.Empty() && !second.Empty(),
|
||||||
"first command-buffer fence destroyed another queue's image");
|
"first command-buffer fence destroyed another buffer's image");
|
||||||
compute.ReleaseAfterFence();
|
second.ReleaseAfterFence();
|
||||||
Require("CrossQueueImageLifetime", "last fence", destroyed && compute.Empty(),
|
Require("SharedFenceResourceLifetime", "last fence",
|
||||||
|
destroyed && second.Empty(),
|
||||||
"last referencing command-buffer fence did not destroy the image");
|
"last referencing command-buffer fence did not destroy the image");
|
||||||
std::printf("[host] %-32s ok\n", "CrossQueueImageLifetime");
|
std::printf("[host] %-32s ok\n", "SharedFenceResourceLifetime");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckHostDmaMetadataReuse() {
|
void CheckHostDmaMetadataReuse() {
|
||||||
@@ -14216,7 +14221,7 @@ int main(int argc, char **argv) {
|
|||||||
CheckStencilAttachmentAccess();
|
CheckStencilAttachmentAccess();
|
||||||
CheckDepthTargetFootprints();
|
CheckDepthTargetFootprints();
|
||||||
CheckHtileClearTargetResolution();
|
CheckHtileClearTargetResolution();
|
||||||
CheckCrossQueueImageLifetime();
|
CheckSharedFenceResourceLifetime();
|
||||||
CheckHostDmaMetadataReuse();
|
CheckHostDmaMetadataReuse();
|
||||||
#else
|
#else
|
||||||
(void)argc;
|
(void)argc;
|
||||||
@@ -14228,6 +14233,7 @@ int main(int argc, char **argv) {
|
|||||||
CheckEmbeddedFetchLaneSpill();
|
CheckEmbeddedFetchLaneSpill();
|
||||||
CheckPs5GameExampleImageClearRuntimeShape();
|
CheckPs5GameExampleImageClearRuntimeShape();
|
||||||
VulkanHarness vulkan;
|
VulkanHarness vulkan;
|
||||||
|
vulkan.CheckCommandPoolGrowth();
|
||||||
vulkan.CheckGpuTilerCpuParity();
|
vulkan.CheckGpuTilerCpuParity();
|
||||||
vulkan.CheckQueryRegionImageClassification();
|
vulkan.CheckQueryRegionImageClassification();
|
||||||
vulkan.CheckMutableStorageSrgbView();
|
vulkan.CheckMutableStorageSrgbView();
|
||||||
|
|||||||
Reference in New Issue
Block a user