Initial commit

This commit is contained in:
nmzik committed 2026-07-16 10:21:41 +02:00
commit 147188b143
427 files changed
+205923

No files matched your search

+432
View File
@@ -0,0 +1,432 @@
#include "graphics/host_gpu/objects/label.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "kernel/memory.h"
#include "common/threads.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/render.h"
#include <algorithm>
#include <atomic>
#include <memory>
#include <vector>
namespace Libs::Graphics {
enum LabelStatus {
New,
Active,
ActiveDeleted,
NotActive,
};
struct LabelCallbacks {
uint64_t* dst_gpu_addr64 = nullptr;
uint64_t value64 = 0;
uint32_t* dst_gpu_addr32 = nullptr;
uint32_t value32 = 0;
LabelCallback callback_1 = nullptr;
LabelCallback callback_2 = nullptr;
uint64_t args[LABEL_ARGS_MAX] = {};
};
struct LabelEvent final {
VkDevice device = nullptr;
VkEvent event = nullptr;
~LabelEvent() {
if (event != nullptr) {
vkDestroyEvent(device, event, nullptr);
}
}
};
struct LabelSubmission {
std::shared_ptr<LabelEvent> completion;
LabelCallbacks callbacks;
};
struct Label {
VkDevice device = nullptr;
LabelStatus status = LabelStatus::New;
LabelCallbacks callbacks;
std::vector<LabelSubmission> submissions;
};
class LabelManager {
public:
LabelManager() {
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
Common::Thread t(ThreadRun, this);
t.Detach();
}
~LabelManager() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(LabelManager);
Label* Create64(GraphicContext* ctx, uint64_t* dst_gpu_addr, uint64_t value,
LabelCallback callback_1, LabelCallback callback_2, const uint64_t* args);
Label* Create32(GraphicContext* ctx, uint32_t* dst_gpu_addr, uint32_t value,
LabelCallback callback_1, LabelCallback callback_2, const uint64_t* args);
void Delete(Label* label);
void Set(CommandBuffer* buffer, Label* label);
void Drain();
private:
static void ThreadRun(void* data);
template <typename T>
Label* Create(GraphicContext* ctx, T* dst_gpu_addr, T value, LabelCallback callback_1,
LabelCallback callback_2, const uint64_t* args);
bool Remove(Label* label);
static void Destroy(Label* label);
Common::Mutex m_mutex;
Common::CondVar m_cond_var;
std::vector<Label*> m_labels;
uint64_t m_callbacks_in_flight = 0;
};
static LabelManager* g_label_manager = nullptr;
static thread_local bool g_in_label_callback = false;
template <typename T>
void WriteGuestLabel(T* address, T value) {
LabelWriteGuestMemory(address, &value, sizeof(value));
}
class LabelCallbackScope final {
public:
LabelCallbackScope() {
if (g_in_label_callback) {
EXIT("recursive GPU label callback\n");
}
g_in_label_callback = true;
}
~LabelCallbackScope() {
if (!g_in_label_callback) {
EXIT("GPU label callback scope is not active\n");
}
g_in_label_callback = false;
}
};
void LabelManager::ThreadRun(void* data) {
auto* manager = static_cast<LabelManager*>(data);
for (;;) {
manager->m_mutex.Lock();
uint64_t active_count = 0;
std::vector<Label*> deleted_labels;
std::vector<LabelCallbacks> fired_labels;
std::vector<LabelSubmission> finished_submissions;
deleted_labels.reserve(manager->m_labels.size());
for (auto& label: manager->m_labels) {
for (auto it = label->submissions.begin(); it != label->submissions.end();) {
active_count++;
if (it->completion == nullptr || it->completion->device == nullptr ||
it->completion->event == nullptr) {
EXIT("GPU label submission has no completion event\n");
}
const auto status =
vkGetEventStatus(it->completion->device, it->completion->event);
switch (status) {
case VK_EVENT_SET:
fired_labels.push_back(it->callbacks);
finished_submissions.push_back(*it);
it = label->submissions.erase(it);
break;
case VK_EVENT_RESET: ++it; break;
default: EXIT("vkGetEventStatus returned an unexpected result\n");
}
}
if (label->submissions.empty()) {
switch (label->status) {
case LabelStatus::ActiveDeleted: deleted_labels.push_back(label); break;
case LabelStatus::Active: label->status = LabelStatus::NotActive; break;
default: break;
}
}
}
if (active_count == 0) {
manager->m_cond_var.Wait(&manager->m_mutex);
}
if (fired_labels.size() > UINT64_MAX - manager->m_callbacks_in_flight) {
EXIT("GPU label callback count overflow\n");
}
manager->m_callbacks_in_flight += fired_labels.size();
for (auto& label: deleted_labels) {
bool removed = manager->Remove(label);
EXIT_NOT_IMPLEMENTED(!removed);
}
manager->m_mutex.Unlock();
// Each completion event is shared with the recording command buffer's fence retainer.
// The event is destroyed only after both the label thread observed it and that command
// buffer completed, even when either side wins the race.
(void)finished_submissions;
for (auto& label: deleted_labels) {
Destroy(label);
}
for (auto& label: fired_labels) {
LabelCallbackScope callback_scope;
bool write = true;
if (label.callback_1 != nullptr) {
write = label.callback_1(label.args);
}
if (write && label.dst_gpu_addr64 != nullptr) {
// Publishing EOP fences through physical backing keeps a fence word coherent even
// when unrelated bytes on its host page are
// protected for BufferCache GPU ownership.
WriteGuestLabel(label.dst_gpu_addr64, label.value64);
static std::atomic<uint32_t> log_count {0};
if (log_count.fetch_add(1) < 256) {
LOGF_COLOR(Log::Color::BrightGreen,
"EndOfPipe Signal!!! [0x%016" PRIx64 "] <- 0x%016" PRIx64 "\n",
reinterpret_cast<uint64_t>(label.dst_gpu_addr64), label.value64);
}
}
if (write && label.dst_gpu_addr32 != nullptr) {
WriteGuestLabel(label.dst_gpu_addr32, label.value32);
static std::atomic<uint32_t> log_count {0};
if (log_count.fetch_add(1) < 256) {
LOGF_COLOR(Log::Color::BrightGreen,
"EndOfPipe Signal!!! [0x%016" PRIx64 "] <- 0x%08" PRIx32 "\n",
reinterpret_cast<uint64_t>(label.dst_gpu_addr32), label.value32);
}
}
if (label.callback_2 != nullptr) {
label.callback_2(label.args);
}
}
if (!fired_labels.empty()) {
Common::LockGuard lock(manager->m_mutex);
if (manager->m_callbacks_in_flight < fired_labels.size()) {
EXIT("GPU label callback count underflow\n");
}
manager->m_callbacks_in_flight -= fired_labels.size();
manager->m_cond_var.SignalAll();
}
Common::Thread::SleepMicro(100);
}
}
template <typename T>
Label* LabelManager::Create(GraphicContext* ctx, T* dst_gpu_addr, T value, LabelCallback callback_1,
LabelCallback callback_2, const uint64_t* args) {
static_assert(sizeof(T) == sizeof(uint32_t) || sizeof(T) == sizeof(uint64_t));
EXIT_IF(ctx == nullptr);
Common::LockGuard lock(m_mutex);
auto* label = new Label;
label->status = LabelStatus::New;
if constexpr (sizeof(T) == sizeof(uint64_t)) {
label->callbacks.dst_gpu_addr64 = dst_gpu_addr;
label->callbacks.value64 = value;
label->callbacks.dst_gpu_addr32 = nullptr;
label->callbacks.value32 = 0;
} else {
label->callbacks.dst_gpu_addr32 = dst_gpu_addr;
label->callbacks.value32 = value;
label->callbacks.dst_gpu_addr64 = nullptr;
label->callbacks.value64 = 0;
}
label->device = ctx->device;
label->callbacks.callback_1 = callback_1;
label->callbacks.callback_2 = callback_2;
if (args != nullptr) {
for (int i = 0; i < LABEL_ARGS_MAX; i++) {
label->callbacks.args[i] = args[i];
}
}
m_labels.push_back(label);
return label;
}
Label* LabelManager::Create64(GraphicContext* ctx, uint64_t* dst_gpu_addr, uint64_t value,
LabelCallback callback_1, LabelCallback callback_2,
const uint64_t* args) {
return Create(ctx, dst_gpu_addr, value, callback_1, callback_2, args);
}
Label* LabelManager::Create32(GraphicContext* ctx, uint32_t* dst_gpu_addr, uint32_t value,
LabelCallback callback_1, LabelCallback callback_2,
const uint64_t* args) {
return Create(ctx, dst_gpu_addr, value, callback_1, callback_2, args);
}
bool LabelManager::Remove(Label* label) {
EXIT_IF(label == nullptr);
EXIT_IF(label->device == nullptr);
Common::LockGuard lock(m_mutex);
const auto it = std::find(m_labels.begin(), m_labels.end(), label);
EXIT_NOT_IMPLEMENTED(it == m_labels.end());
EXIT_NOT_IMPLEMENTED(label->status != LabelStatus::NotActive &&
label->status != LabelStatus::Active &&
label->status != LabelStatus::ActiveDeleted);
if (!label->submissions.empty()) {
label->status = LabelStatus::ActiveDeleted;
return false;
}
m_labels.erase(it);
return true;
}
void LabelManager::Destroy(Label* label) {
EXIT_IF(label == nullptr);
EXIT_IF(label->device == nullptr);
EXIT_NOT_IMPLEMENTED(!label->submissions.empty());
delete label;
}
void LabelManager::Delete(Label* label) {
if (Remove(label)) {
Destroy(label);
}
}
void LabelManager::Set(CommandBuffer* buffer, Label* label) {
EXIT_IF(label == nullptr);
EXIT_IF(buffer == nullptr);
EXIT_IF(buffer->IsInvalid());
EXIT_IF(label->device == nullptr);
Common::LockGuard lock(m_mutex);
const auto it = std::find(m_labels.begin(), m_labels.end(), label);
EXIT_NOT_IMPLEMENTED(it == m_labels.end());
EXIT_NOT_IMPLEMENTED(label->status != LabelStatus::New &&
label->status != LabelStatus::NotActive &&
label->status != LabelStatus::Active);
label->status = LabelStatus::Active;
LabelSubmission submission {};
submission.callbacks = label->callbacks;
submission.completion = std::make_shared<LabelEvent>();
submission.completion->device = label->device;
auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()];
EXIT_NOT_IMPLEMENTED(vk_buffer == nullptr);
VkEventCreateInfo create_info {};
create_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
create_info.pNext = nullptr;
create_info.flags = 0;
vkCreateEvent(label->device, &create_info, nullptr, &submission.completion->event);
EXIT_NOT_IMPLEMENTED(submission.completion->event == nullptr);
buffer->RetainResourceUntilFence(submission.completion);
// Labels can be reused before an earlier end-of-pipe event has been
// observed by the polling thread. Capture a separate Vulkan event and
// callback snapshot for each set so older writes are not lost.
vkResetEvent(label->device, submission.completion->event);
vkCmdSetEvent(vk_buffer, submission.completion->event, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
label->submissions.push_back(submission);
m_cond_var.SignalAll();
}
void LabelManager::Drain() {
m_mutex.Lock();
for (;;) {
const bool pending = std::any_of(m_labels.begin(), m_labels.end(), [](const Label* label) {
return !label->submissions.empty();
});
if (!pending && m_callbacks_in_flight == 0) {
m_mutex.Unlock();
return;
}
m_cond_var.SignalAll();
m_cond_var.Wait(&m_mutex);
}
}
void LabelInit() {
EXIT_IF(g_label_manager != nullptr);
g_label_manager = new LabelManager;
}
Label* LabelCreate64(GraphicContext* ctx, uint64_t* dst_gpu_addr, uint64_t value,
LabelCallback callback_1, LabelCallback callback_2, const uint64_t* args) {
EXIT_IF(g_label_manager == nullptr);
return g_label_manager->Create64(ctx, dst_gpu_addr, value, callback_1, callback_2, args);
}
Label* LabelCreate32(GraphicContext* ctx, uint32_t* dst_gpu_addr, uint32_t value,
LabelCallback callback_1, LabelCallback callback_2, const uint64_t* args) {
EXIT_IF(g_label_manager == nullptr);
return g_label_manager->Create32(ctx, dst_gpu_addr, value, callback_1, callback_2, args);
}
void LabelDelete(Label* label) {
EXIT_IF(g_label_manager == nullptr);
g_label_manager->Delete(label);
}
void LabelSet(CommandBuffer* buffer, Label* label) {
EXIT_IF(g_label_manager == nullptr);
g_label_manager->Set(buffer, label);
}
void LabelDrain() {
EXIT_IF(g_label_manager == nullptr);
g_label_manager->Drain();
}
void LabelWriteGuestMemory(void* address, const void* data, uint64_t size) {
if (address == nullptr || data == nullptr || size == 0) {
EXIT("invalid guest label write\n");
}
if (!Libs::LibKernel::Memory::TryWriteBacking(reinterpret_cast<uint64_t>(address), data, size)) {
std::memcpy(address, data, static_cast<size_t>(size));
}
}
bool LabelInCallback() noexcept {
return g_in_label_callback;
}
} // namespace Libs::Graphics