mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-22 11:56:58 +02:00
remove readback hacks, allows to read/write guest memory directly
This commit is contained in:
@@ -12,8 +12,6 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class CommandProcessorGuestAccessScope;
|
||||
|
||||
inline constexpr uint32_t AcquireGcrGl2Writeback = 1u << 15u;
|
||||
|
||||
bool TestWaitRegMemValue(uint64_t value, uint64_t ref, uint64_t mask, uint32_t func);
|
||||
@@ -72,6 +70,21 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void SubmitForReadback() {
|
||||
if (!Active()) {
|
||||
return;
|
||||
}
|
||||
SubmitCurrent();
|
||||
}
|
||||
|
||||
void ResumeAfterReadback() {
|
||||
if (!Active()) {
|
||||
return;
|
||||
}
|
||||
Current()->WaitForFenceAndReset();
|
||||
Current()->Begin();
|
||||
}
|
||||
|
||||
private:
|
||||
CommandBuffer* SubmitCurrent() {
|
||||
auto* submitted = Current();
|
||||
@@ -107,22 +120,26 @@ public:
|
||||
|
||||
void Reset();
|
||||
|
||||
void BufferInit();
|
||||
void BufferFlush();
|
||||
void BufferFlushAndWait();
|
||||
void BufferWait();
|
||||
[[nodiscard]] int BeginReadbackTransaction(bool mutex_preowned) {
|
||||
if (!mutex_preowned) {
|
||||
m_mutex.Lock();
|
||||
void BufferInit();
|
||||
void BufferFlush();
|
||||
void BufferFlushAndWait();
|
||||
void BufferWait();
|
||||
void BeginReadbackTransaction() {
|
||||
m_mutex.Lock();
|
||||
if (m_readback_active) {
|
||||
EXIT("nested command-processor readback transaction\n");
|
||||
}
|
||||
m_scheduler.Flush();
|
||||
m_scheduler.WaitAll();
|
||||
return m_scheduler.Queue();
|
||||
m_readback_active = true;
|
||||
m_readback_finished = false;
|
||||
}
|
||||
void EndReadbackTransaction(bool mutex_preowned) {
|
||||
if (!mutex_preowned) {
|
||||
m_mutex.Unlock();
|
||||
void FinishReadbackTransaction();
|
||||
void EndReadbackTransaction() {
|
||||
if (!m_readback_active) {
|
||||
EXIT("command-processor readback transaction is not active\n");
|
||||
}
|
||||
m_readback_active = false;
|
||||
m_readback_finished = false;
|
||||
m_mutex.Unlock();
|
||||
}
|
||||
|
||||
void RunLock() { m_run_mutex.Lock(); }
|
||||
@@ -164,7 +181,6 @@ public:
|
||||
uint32_t value);
|
||||
void PrepareCpuFlip();
|
||||
void SynchronizeGpu();
|
||||
void SynchronizeGpuRange(uint64_t vaddr, uint64_t size);
|
||||
void MemoryBarrier();
|
||||
void TriggerEopEventAtEndOfPipe(uint32_t interrupt_context_id);
|
||||
void RenderTextureBarrier(uint64_t vaddr, uint64_t size);
|
||||
@@ -182,9 +198,6 @@ public:
|
||||
void PopMarker() {}
|
||||
|
||||
void PrefetchL2(void* addr, uint32_t size) {}
|
||||
void ClearGds(uint64_t dw_offset, uint32_t dw_num, uint32_t clear_value);
|
||||
void ReadGds(uint32_t* dst, uint32_t dw_offset, uint32_t dw_size);
|
||||
|
||||
void ResetDeCe();
|
||||
void WaitCe();
|
||||
void WaitDeDiff(uint32_t diff);
|
||||
@@ -208,7 +221,7 @@ public:
|
||||
|
||||
void Run(uint32_t* data, uint32_t num_dw);
|
||||
|
||||
void SetQueue(int queue) { m_scheduler.SetQueue(queue); }
|
||||
void SetQueue(int queue);
|
||||
[[nodiscard]] int GetQueue() const { return m_scheduler.Queue(); }
|
||||
|
||||
[[nodiscard]] const FlipInfo& GetFlip() const { return m_flip; }
|
||||
@@ -218,8 +231,6 @@ public:
|
||||
void SetSubmitId(uint64_t submit_id) { m_submit_id = submit_id; }
|
||||
|
||||
private:
|
||||
friend class CommandProcessorGuestAccessScope;
|
||||
|
||||
struct Counter {
|
||||
Common::Mutex mutex;
|
||||
Common::CondVar cond_var;
|
||||
@@ -231,6 +242,7 @@ private:
|
||||
uint32_t cache_action, uint32_t event_index, uint32_t event_write_source,
|
||||
void* dst_gpu_addr, T value, uint32_t interrupt_selector,
|
||||
uint32_t interrupt_context_id);
|
||||
void FinishCommandProcessors();
|
||||
|
||||
CommandBuffer* CurrentBuffer() { return m_scheduler.Current(); }
|
||||
void CheckBuffer() const { m_scheduler.CheckActive(); }
|
||||
@@ -250,8 +262,11 @@ private:
|
||||
uint64_t m_dispatch_indirect_args_base_addr = 0;
|
||||
uint32_t m_num_instances = 1;
|
||||
|
||||
Common::Mutex m_mutex;
|
||||
Common::Mutex m_run_mutex;
|
||||
inline static Common::Mutex m_mutex;
|
||||
inline static std::array<CommandProcessor*, GraphicContext::QUEUES_NUM> m_processors {};
|
||||
inline static bool m_readback_active = false;
|
||||
inline static bool m_readback_finished = false;
|
||||
Common::Mutex m_run_mutex;
|
||||
|
||||
CommandScheduler m_scheduler;
|
||||
|
||||
|
||||
@@ -2233,7 +2233,7 @@ KYTY_CP_OP_PARSER(CpOpCopyData) {
|
||||
cp->WriteReferenceClock(dst, num_bytes);
|
||||
return 5;
|
||||
}
|
||||
const auto dma_src = CopyDataSrcToDma(src_sel);
|
||||
const auto dma_src = CopyDataSrcToDma(src_sel);
|
||||
if (dma_src == 2 && num_bytes == 8) {
|
||||
EXIT("unsupported 64-bit immediate copyData\n");
|
||||
}
|
||||
@@ -2636,7 +2636,6 @@ KYTY_CP_OP_PARSER(CpOpIndirectCxRegs) {
|
||||
if (indirect_buffer == nullptr) {
|
||||
EXIT("indirect CX registers have null address, num_regs = %" PRIu32 "\n", indirect_num_dw);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < indirect_num_dw; i++, indirect_buffer += 2) {
|
||||
auto cmd_offset = indirect_buffer[0];
|
||||
auto value = indirect_buffer[1];
|
||||
@@ -2695,13 +2694,14 @@ KYTY_CP_OP_PARSER(CpOpIndirectShRegs) {
|
||||
if (indirect_buffer == nullptr) {
|
||||
EXIT("indirect SH registers have null address, num_regs = %" PRIu32 "\n", indirect_num_dw);
|
||||
}
|
||||
const auto indirect_address = reinterpret_cast<uint64_t>(indirect_buffer);
|
||||
|
||||
for (uint32_t i = 0; i < indirect_num_dw; i++, indirect_buffer += 2) {
|
||||
auto raw_cmd_offset = indirect_buffer[0];
|
||||
auto cmd_offset = NormalizeRegisterOffset(raw_cmd_offset);
|
||||
auto value = indirect_buffer[1];
|
||||
|
||||
//Not sure if this is correct
|
||||
// Not sure if this is correct
|
||||
if (raw_cmd_offset != cmd_offset) {
|
||||
LOGF_COLOR(Log::Color::Red,
|
||||
"\t temporary: normalized indirect SH register offset 0x%08" PRIx32
|
||||
@@ -2709,7 +2709,7 @@ KYTY_CP_OP_PARSER(CpOpIndirectShRegs) {
|
||||
raw_cmd_offset, cmd_offset);
|
||||
}
|
||||
|
||||
//Not sure if this is correct
|
||||
// Not sure if this is correct
|
||||
if (cmd_offset == Pm4::SH_NOP || raw_cmd_offset == 0xffffffffu) {
|
||||
continue;
|
||||
}
|
||||
@@ -2725,8 +2725,7 @@ KYTY_CP_OP_PARSER(CpOpIndirectShRegs) {
|
||||
if (pfunc == nullptr) {
|
||||
LOGF("unknown indirect SH register: index=%" PRIu32 "/%" PRIu32 ", regs=0x%016" PRIx64
|
||||
", offset=0x%08" PRIx32 ", value=0x%08" PRIx32 "\n",
|
||||
i, indirect_num_dw, reinterpret_cast<uint64_t>(indirect_buffer - i * 2),
|
||||
cmd_offset, value);
|
||||
i, indirect_num_dw, indirect_address, cmd_offset, value);
|
||||
auto* dump_regs = indirect_buffer - i * 2;
|
||||
for (uint32_t j = 0; j < indirect_num_dw && j < 16; j++) {
|
||||
LOGF("\t sh_indirect[%" PRIu32 "] offset=0x%08" PRIx32 ", value=0x%08" PRIx32 "\n",
|
||||
@@ -2758,13 +2757,12 @@ KYTY_CP_OP_PARSER(CpOpIndirectUcRegs) {
|
||||
if (indirect_buffer == nullptr) {
|
||||
EXIT("indirect UC registers have null address, num_regs = %" PRIu32 "\n", indirect_num_dw);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < indirect_num_dw; i++, indirect_buffer += 2) {
|
||||
auto raw_cmd_offset = indirect_buffer[0];
|
||||
auto cmd_offset = NormalizeRegisterOffset(raw_cmd_offset);
|
||||
auto value = indirect_buffer[1];
|
||||
|
||||
//Not sure if this is correct
|
||||
// Not sure if this is correct
|
||||
if (raw_cmd_offset != cmd_offset) {
|
||||
LOGF_COLOR(Log::Color::Red,
|
||||
"\t temporary: normalized indirect UC register offset 0x%08" PRIx32
|
||||
|
||||
@@ -33,19 +33,9 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
enum class CommandProcessorGuestAccess : uint32_t {
|
||||
None,
|
||||
WaitPoll,
|
||||
DirectFence,
|
||||
};
|
||||
|
||||
static thread_local CommandProcessor* g_current_run_cp = nullptr;
|
||||
static thread_local uint32_t g_submission_pause_depth = 0;
|
||||
static thread_local bool g_gpu_mutex_owned = false;
|
||||
static thread_local bool g_cp_readback_owned = false;
|
||||
static thread_local bool g_cp_readback_preowned = false;
|
||||
static thread_local CommandProcessorGuestAccess g_guest_memory_access =
|
||||
CommandProcessorGuestAccess::None;
|
||||
static thread_local CommandProcessor* g_current_run_cp = nullptr;
|
||||
static thread_local uint32_t g_submission_pause_depth = 0;
|
||||
static thread_local bool g_gpu_mutex_owned = false;
|
||||
|
||||
class GpuMutexLock final {
|
||||
public:
|
||||
@@ -68,54 +58,13 @@ private:
|
||||
Common::Mutex& m_mutex;
|
||||
};
|
||||
|
||||
class CommandProcessorGuestAccessScope final {
|
||||
public:
|
||||
CommandProcessorGuestAccessScope(CommandProcessorGuestAccess access, uint64_t vaddr,
|
||||
uint64_t size) {
|
||||
if (access == CommandProcessorGuestAccess::None ||
|
||||
g_guest_memory_access != CommandProcessorGuestAccess::None ||
|
||||
g_current_run_cp == nullptr) {
|
||||
EXIT("invalid nested command-processor guest-memory access\n");
|
||||
}
|
||||
if (g_render_ctx == nullptr) {
|
||||
EXIT("command-processor guest-memory access has no render context\n");
|
||||
}
|
||||
m_cp = g_current_run_cp;
|
||||
if (access == CommandProcessorGuestAccess::WaitPoll) {
|
||||
// Preserve serialized scheduler semantics: a poll owns the command processor before it
|
||||
// enters the shared buffer/texture transaction, and releases both before yielding.
|
||||
m_cp->m_mutex.Lock();
|
||||
m_cp_mutex_owned = true;
|
||||
}
|
||||
m_resource_lock =
|
||||
g_render_ctx->GetGpuResources()->LockCommandProcessorGuestAccess(vaddr, size);
|
||||
g_guest_memory_access = access;
|
||||
}
|
||||
~CommandProcessorGuestAccessScope() {
|
||||
if (g_guest_memory_access == CommandProcessorGuestAccess::None ||
|
||||
!m_resource_lock.owns_lock() || m_cp == nullptr) {
|
||||
EXIT("command-processor guest-memory access scope is not active\n");
|
||||
}
|
||||
g_guest_memory_access = CommandProcessorGuestAccess::None;
|
||||
m_resource_lock.unlock();
|
||||
if (m_cp_mutex_owned) {
|
||||
m_cp->m_mutex.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CommandProcessor* m_cp = nullptr;
|
||||
bool m_cp_mutex_owned = false;
|
||||
std::unique_lock<ResourceMutex> m_resource_lock;
|
||||
KYTY_CLASS_NO_COPY(CommandProcessorGuestAccessScope);
|
||||
};
|
||||
|
||||
struct OwnedCmdBuffer {
|
||||
std::vector<uint32_t> storage;
|
||||
uint32_t* data = nullptr;
|
||||
uint32_t num_dw = 0;
|
||||
|
||||
OwnedCmdBuffer() = default;
|
||||
OwnedCmdBuffer(const uint32_t* src, uint32_t count) { Assign(src, count); }
|
||||
|
||||
OwnedCmdBuffer(const OwnedCmdBuffer& other): storage(other.storage), num_dw(other.num_dw) {
|
||||
data = (storage.empty() ? other.data : storage.data());
|
||||
@@ -165,9 +114,8 @@ public:
|
||||
|
||||
KYTY_CLASS_NO_COPY(GraphicsRing);
|
||||
|
||||
void Submit(uint32_t* cmd_draw_buffer, uint32_t num_draw_dw, uint32_t* cmd_const_buffer,
|
||||
uint32_t num_const_dw, int handle, int index, int flip_mode, int64_t flip_arg,
|
||||
bool trigger_agc_interrupt_on_done);
|
||||
void Submit(OwnedCmdBuffer draw_buffer, OwnedCmdBuffer const_buffer, int handle, int index,
|
||||
int flip_mode, int64_t flip_arg, bool trigger_agc_interrupt_on_done);
|
||||
void SubmitFlipPreparation();
|
||||
void Done();
|
||||
void WaitForIdle();
|
||||
@@ -217,7 +165,7 @@ public:
|
||||
|
||||
KYTY_CLASS_NO_COPY(ComputeRing);
|
||||
|
||||
void Submit(uint32_t* cmd_buffer, uint32_t num_dw, bool trigger_agc_interrupt_on_done);
|
||||
void Submit(OwnedCmdBuffer buffer, bool trigger_agc_interrupt_on_done);
|
||||
void Done();
|
||||
void WaitForIdle();
|
||||
bool IsIdle();
|
||||
@@ -311,15 +259,18 @@ void GraphicsRunInit() {
|
||||
|
||||
void Gpu::Submit(uint32_t* cmd_draw_buffer, uint32_t num_draw_dw, uint32_t* cmd_const_buffer,
|
||||
uint32_t num_const_dw, bool trigger_agc_interrupt_on_done) {
|
||||
GpuMutexLock lock(m_mutex);
|
||||
OwnedCmdBuffer draw_buffer(cmd_draw_buffer, num_draw_dw);
|
||||
OwnedCmdBuffer const_buffer(cmd_const_buffer, num_const_dw);
|
||||
GpuMutexLock lock(m_mutex);
|
||||
|
||||
m_gfx_ring->Submit(cmd_draw_buffer, num_draw_dw, cmd_const_buffer, num_const_dw, 0, 0, 0, 0,
|
||||
m_gfx_ring->Submit(std::move(draw_buffer), std::move(const_buffer), 0, 0, 0, 0,
|
||||
trigger_agc_interrupt_on_done);
|
||||
}
|
||||
|
||||
void Gpu::SubmitCompute(uint32_t queue, uint32_t* cmd_buffer, uint32_t num_dw,
|
||||
bool trigger_agc_interrupt_on_done) {
|
||||
GpuMutexLock lock(m_mutex);
|
||||
OwnedCmdBuffer buffer(cmd_buffer, num_dw);
|
||||
GpuMutexLock lock(m_mutex);
|
||||
|
||||
constexpr uint32_t compute_queue_base = 0x20u;
|
||||
constexpr uint32_t compute_queue_num = 7u * 8u;
|
||||
@@ -336,7 +287,7 @@ void Gpu::SubmitCompute(uint32_t queue, uint32_t* cmd_buffer, uint32_t num_dw,
|
||||
|
||||
auto* ring = GetRing(ring_id);
|
||||
|
||||
ring->Submit(cmd_buffer, num_dw, trigger_agc_interrupt_on_done);
|
||||
ring->Submit(std::move(buffer), trigger_agc_interrupt_on_done);
|
||||
}
|
||||
|
||||
void Gpu::SubmitFlipPreparation() {
|
||||
@@ -454,6 +405,45 @@ ComputeRing* Gpu::GetRing(uint32_t ring_id) {
|
||||
return m_compute_ring[v];
|
||||
}
|
||||
|
||||
void CommandProcessor::SetQueue(int queue) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
if (queue < 0 || queue >= GraphicContext::QUEUES_NUM ||
|
||||
(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() {
|
||||
if (GraphicsRunCurrentCommandProcessor() == nullptr || !m_readback_active) {
|
||||
EXIT("GPU readback finish requires a command-processor thread\n");
|
||||
}
|
||||
if (m_readback_finished) {
|
||||
return;
|
||||
}
|
||||
FinishCommandProcessors();
|
||||
m_readback_finished = true;
|
||||
}
|
||||
|
||||
void CommandProcessor::FinishCommandProcessors() {
|
||||
std::array<CommandProcessor*, GraphicContext::QUEUES_NUM> processors {};
|
||||
uint32_t processor_count = 0;
|
||||
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();
|
||||
}
|
||||
for (uint32_t i = 0; i < processor_count; i++) {
|
||||
processors[i]->m_scheduler.ResumeAfterReadback();
|
||||
}
|
||||
}
|
||||
|
||||
void CommandProcessor::Reset() {
|
||||
BufferWait();
|
||||
|
||||
@@ -598,13 +588,6 @@ bool TestWaitRegMemValue(uint64_t value, uint64_t ref, uint64_t mask, uint32_t f
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T LoadGuestWaitValue(const T* addr) {
|
||||
CommandProcessorGuestAccessScope guest_access(CommandProcessorGuestAccess::WaitPoll,
|
||||
reinterpret_cast<uint64_t>(addr), sizeof(*addr));
|
||||
return *addr;
|
||||
}
|
||||
|
||||
static void YieldCommandProcessorWait(uint32_t poll_interval_cycles) noexcept {
|
||||
// PS5 specifies GPU poll cycles, not host microseconds. Yield at the scheduler boundary now;
|
||||
// a future resumable guest GPU scheduler can replace this one function without touching polling
|
||||
@@ -625,12 +608,9 @@ void CommandProcessor::WaitRegMem(uint32_t func, const T* addr, T ref, T mask, u
|
||||
const auto log_width = static_cast<int>(sizeof(T) * 2u);
|
||||
const auto bits = static_cast<unsigned>(sizeof(T) * 8u);
|
||||
|
||||
if (!TestWaitRegMemValue(LoadGuestWaitValue(addr), ref, mask, func)) {
|
||||
SynchronizeGpuRange(addr_value, sizeof(*addr));
|
||||
}
|
||||
uint64_t spin_count = 0;
|
||||
for (;;) {
|
||||
const auto value = LoadGuestWaitValue(addr);
|
||||
const auto value = *addr;
|
||||
if (TestWaitRegMemValue(value, ref, mask, func)) {
|
||||
break;
|
||||
}
|
||||
@@ -670,10 +650,7 @@ void CommandProcessor::WriteData(uint32_t* dst, const uint32_t* src, uint32_t dw
|
||||
return;
|
||||
}
|
||||
|
||||
const auto size = static_cast<uint64_t>(dw_num) * sizeof(uint32_t);
|
||||
CommandProcessorGuestAccessScope guest_access(CommandProcessorGuestAccess::DirectFence,
|
||||
reinterpret_cast<uint64_t>(dst), size);
|
||||
memcpy(dst, src, static_cast<size_t>(size));
|
||||
memcpy(dst, src, static_cast<size_t>(dw_num) * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void CommandProcessor::WriteReferenceClock(uint64_t dst_address, uint32_t num_bytes) {
|
||||
@@ -684,11 +661,8 @@ void CommandProcessor::WriteReferenceClock(uint64_t dst_address, uint32_t num_by
|
||||
num_bytes);
|
||||
}
|
||||
const auto value = GraphicsRenderReadReferenceClock();
|
||||
CommandProcessorGuestAccessScope guest_access(CommandProcessorGuestAccess::DirectFence,
|
||||
dst_address, num_bytes);
|
||||
std::memcpy(reinterpret_cast<void*>(dst_address), &value, num_bytes);
|
||||
LOGF("\t copy_data reference clock: dst=0x%016" PRIx64 " value=0x%016" PRIx64
|
||||
" size=%u\n",
|
||||
LOGF("\t copy_data reference clock: dst=0x%016" PRIx64 " value=0x%016" PRIx64 " size=%u\n",
|
||||
dst_address, value, num_bytes);
|
||||
}
|
||||
|
||||
@@ -729,9 +703,9 @@ void CommandProcessor::DmaData(uint8_t engine, uint8_t dst_sel, uint8_t dst_cach
|
||||
EXIT("unsupported dmaData source selector 0x%02" PRIx8 "\n", src_sel);
|
||||
}
|
||||
|
||||
void GraphicsRing::Submit(uint32_t* cmd_draw_buffer, uint32_t num_draw_dw,
|
||||
uint32_t* cmd_const_buffer, uint32_t num_const_dw, int handle, int index,
|
||||
int flip_mode, int64_t flip_arg, bool trigger_agc_interrupt_on_done) {
|
||||
void GraphicsRing::Submit(OwnedCmdBuffer draw_buffer, OwnedCmdBuffer const_buffer, int handle,
|
||||
int index, int flip_mode, int64_t flip_arg,
|
||||
bool trigger_agc_interrupt_on_done) {
|
||||
EXIT_IF(m_cp == nullptr);
|
||||
|
||||
Common::LockGuard lock(m_mutex);
|
||||
@@ -748,9 +722,9 @@ void GraphicsRing::Submit(uint32_t* cmd_draw_buffer, uint32_t num_draw_dw,
|
||||
m_cp->Reset();
|
||||
}
|
||||
|
||||
auto& buf = m_cmd_batches.emplace_back();
|
||||
buf.draw_buffer.Assign(cmd_draw_buffer, num_draw_dw);
|
||||
buf.const_buffer.Assign(cmd_const_buffer, num_const_dw);
|
||||
auto& buf = m_cmd_batches.emplace_back();
|
||||
buf.draw_buffer = std::move(draw_buffer);
|
||||
buf.const_buffer = std::move(const_buffer);
|
||||
buf.flip.handle = handle;
|
||||
buf.flip.index = index;
|
||||
buf.flip.flip_mode = flip_mode;
|
||||
@@ -942,12 +916,11 @@ void ComputeRing::ThreadRun(void* data) {
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeRing::Submit(uint32_t* cmd_buffer, uint32_t num_dw,
|
||||
bool trigger_agc_interrupt_on_done) {
|
||||
void ComputeRing::Submit(OwnedCmdBuffer buffer, bool trigger_agc_interrupt_on_done) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
EXIT_IF(cmd_buffer == nullptr);
|
||||
EXIT_IF(num_dw == 0);
|
||||
EXIT_IF(buffer.data == nullptr);
|
||||
EXIT_IF(buffer.num_dw == 0);
|
||||
|
||||
WindowWaitForGraphicInitialized();
|
||||
GraphicsRenderCreateContext();
|
||||
@@ -957,7 +930,7 @@ void ComputeRing::Submit(uint32_t* cmd_buffer, uint32_t num_dw,
|
||||
}
|
||||
|
||||
DirectBatch batch {};
|
||||
batch.buffer.Assign(cmd_buffer, num_dw);
|
||||
batch.buffer = std::move(buffer);
|
||||
batch.trigger_agc_interrupt_on_done = trigger_agc_interrupt_on_done;
|
||||
m_direct_batches.push_back(std::move(batch));
|
||||
|
||||
@@ -1229,35 +1202,38 @@ void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiato
|
||||
EXIT_NOT_IMPLEMENTED((draw_initiator & ~0x20u) != 2u);
|
||||
EXIT_NOT_IMPLEMENTED(m_draw_indirect_args_base_addr == 0);
|
||||
|
||||
const auto args_addr = m_draw_indirect_args_base_addr + data_offset;
|
||||
const auto* args_addr =
|
||||
reinterpret_cast<const void*>(m_draw_indirect_args_base_addr + data_offset);
|
||||
|
||||
if (!indexed) {
|
||||
auto* args = reinterpret_cast<const DrawIndirectArgs*>(args_addr);
|
||||
if (args->instance_count != 1u || args->start_vertex_location != 0u ||
|
||||
args->start_instance_location != 0u) {
|
||||
DrawIndirectArgs args {};
|
||||
std::memcpy(&args, args_addr, sizeof(args));
|
||||
if (args.instance_count != 1u || args.start_vertex_location != 0u ||
|
||||
args.start_instance_location != 0u) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1) < 64) {
|
||||
LOGF("\t warning: partial DrawIndirect args: vertex_count=%" PRIu32
|
||||
", instance_count=%" PRIu32 ", start_vertex=%" PRIu32
|
||||
", start_instance=%" PRIu32 "\n",
|
||||
args->vertex_count_per_instance, args->instance_count,
|
||||
args->start_vertex_location, args->start_instance_location);
|
||||
args.vertex_count_per_instance, args.instance_count,
|
||||
args.start_vertex_location, args.start_instance_location);
|
||||
}
|
||||
}
|
||||
DrawIndexAuto(args->vertex_count_per_instance, 0, 0, args->instance_count,
|
||||
args->start_vertex_location, args->start_instance_location);
|
||||
DrawIndexAuto(args.vertex_count_per_instance, 0, 0, args.instance_count,
|
||||
args.start_vertex_location, args.start_instance_location);
|
||||
return;
|
||||
}
|
||||
|
||||
auto* args = reinterpret_cast<const DrawIndexedIndirectArgs*>(args_addr);
|
||||
if (args->base_vertex_location != 0u || args->start_instance_location != 0u) {
|
||||
DrawIndexedIndirectArgs args {};
|
||||
std::memcpy(&args, args_addr, sizeof(args));
|
||||
if (args.base_vertex_location != 0u || args.start_instance_location != 0u) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1) < 64) {
|
||||
LOGF("\t warning: partial DrawIndexIndirect args: index_count=%" PRIu32
|
||||
", instance_count=%" PRIu32 ", start_index=%" PRIu32 ", base_vertex=%" PRIu32
|
||||
", start_instance=%" PRIu32 "\n",
|
||||
args->index_count_per_instance, args->instance_count, args->start_index_location,
|
||||
args->base_vertex_location, args->start_instance_location);
|
||||
args.index_count_per_instance, args.instance_count, args.start_index_location,
|
||||
args.base_vertex_location, args.start_instance_location);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1270,22 +1246,22 @@ void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiato
|
||||
}
|
||||
|
||||
auto* index_addr = reinterpret_cast<const void*>(
|
||||
m_index_base_addr + static_cast<uint64_t>(args->start_index_location) * index_size);
|
||||
m_index_base_addr + static_cast<uint64_t>(args.start_index_location) * index_size);
|
||||
|
||||
const uint32_t index_count =
|
||||
(m_index_buffer_size != 0 ? std::min(args->index_count_per_instance, m_index_buffer_size)
|
||||
: args->index_count_per_instance);
|
||||
if (GraphicsRunDebugDumpEnabled() && index_count != args->index_count_per_instance) {
|
||||
(m_index_buffer_size != 0 ? std::min(args.index_count_per_instance, m_index_buffer_size)
|
||||
: args.index_count_per_instance);
|
||||
if (GraphicsRunDebugDumpEnabled() && index_count != args.index_count_per_instance) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1, std::memory_order_relaxed) < 64) {
|
||||
LOGF("\t DrawIndexIndirect: clamped index_count from %" PRIu32 " to %" PRIu32
|
||||
" using INDEX_BUFFER_SIZE\n",
|
||||
args->index_count_per_instance, index_count);
|
||||
args.index_count_per_instance, index_count);
|
||||
}
|
||||
}
|
||||
|
||||
DrawIndex(index_count, index_addr, 0, 1, args->instance_count, nullptr, 0,
|
||||
static_cast<int32_t>(args->base_vertex_location), args->start_instance_location);
|
||||
DrawIndex(index_count, index_addr, 0, 1, args.instance_count, nullptr, 0,
|
||||
static_cast<int32_t>(args.base_vertex_location), args.start_instance_location);
|
||||
}
|
||||
|
||||
void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_count_or_count,
|
||||
@@ -1503,18 +1479,6 @@ void CommandProcessor::DrawIndexAuto(uint32_t index_count, uint32_t flags,
|
||||
first_instance);
|
||||
}
|
||||
|
||||
void CommandProcessor::ClearGds(uint64_t dw_offset, uint32_t dw_num, uint32_t clear_value) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
GraphicsRenderClearGds(dw_offset, dw_num, clear_value);
|
||||
}
|
||||
|
||||
void CommandProcessor::ReadGds(uint32_t* dst, uint32_t dw_offset, uint32_t dw_size) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
GraphicsRenderReadGds(dst, dw_offset, dw_size);
|
||||
}
|
||||
|
||||
void CommandProcessor::WaitFlipDone(uint32_t video_out_handle, uint32_t display_buffer_index) {
|
||||
BufferFlush();
|
||||
|
||||
@@ -1569,11 +1533,9 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
||||
}
|
||||
|
||||
auto write32 = [&](bool with_writeback) {
|
||||
auto* dst = static_cast<uint32_t*>(dst_gpu_addr);
|
||||
auto data = static_cast<uint32_t>(value);
|
||||
CommandProcessorGuestAccessScope guest_access(CommandProcessorGuestAccess::DirectFence,
|
||||
reinterpret_cast<uint64_t>(dst),
|
||||
sizeof(*dst));
|
||||
auto* dst = static_cast<uint32_t*>(dst_gpu_addr);
|
||||
auto data = static_cast<uint32_t>(value);
|
||||
std::memcpy(dst, &data, sizeof(data));
|
||||
|
||||
if (with_interrupt) {
|
||||
if (with_writeback) {
|
||||
@@ -1594,8 +1556,10 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
||||
case 0x01:
|
||||
if constexpr (sizeof(T) == sizeof(uint32_t)) {
|
||||
if (eop_event_type == 0x2f && cache_action == 0x00 && event_index == 0x06) {
|
||||
GraphicsRenderWriteAtEndOfPipeGds32(m_submit_id, CurrentBuffer(),
|
||||
static_cast<uint32_t*>(dst_gpu_addr),
|
||||
auto* dst = static_cast<uint32_t*>(dst_gpu_addr);
|
||||
SynchronizeGpu();
|
||||
GraphicsRenderReadGds(dst, value & 0xffffu, value >> 16u);
|
||||
GraphicsRenderWriteAtEndOfPipeGds32(m_submit_id, CurrentBuffer(), dst,
|
||||
value & 0xffffu, value >> 16u);
|
||||
return;
|
||||
}
|
||||
@@ -1615,10 +1579,8 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
||||
}
|
||||
} else {
|
||||
auto write64 = [&](bool with_writeback) {
|
||||
auto* dst = static_cast<uint64_t*>(dst_gpu_addr);
|
||||
CommandProcessorGuestAccessScope guest_access(
|
||||
CommandProcessorGuestAccess::DirectFence, reinterpret_cast<uint64_t>(dst),
|
||||
sizeof(*dst));
|
||||
auto* dst = static_cast<uint64_t*>(dst_gpu_addr);
|
||||
std::memcpy(dst, &value, sizeof(value));
|
||||
|
||||
if (with_interrupt) {
|
||||
if (with_writeback) {
|
||||
@@ -1699,16 +1661,16 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
||||
break;
|
||||
case 0x04:
|
||||
if constexpr (sizeof(T) == sizeof(uint64_t)) {
|
||||
CommandProcessorGuestAccessScope guest_access(
|
||||
CommandProcessorGuestAccess::DirectFence,
|
||||
reinterpret_cast<uint64_t>(dst_gpu_addr), sizeof(uint64_t));
|
||||
const auto clock = GraphicsRenderReadReferenceClock();
|
||||
std::memcpy(dst_gpu_addr, &clock, sizeof(clock));
|
||||
switch (cache_action) {
|
||||
case 0x00:
|
||||
if (((eop_event_type == 0x04 && event_index == 0x05) ||
|
||||
(eop_event_type == 0x28 && event_index == 0x00)) &&
|
||||
!with_interrupt) {
|
||||
GraphicsRenderWriteAtEndOfPipeClockCounter(
|
||||
m_submit_id, CurrentBuffer(), static_cast<uint64_t*>(dst_gpu_addr));
|
||||
m_submit_id, CurrentBuffer(), static_cast<uint64_t*>(dst_gpu_addr),
|
||||
clock);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -1718,7 +1680,8 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
||||
(eop_event_type == 0x28 && event_index == 0x00)) &&
|
||||
!with_interrupt) {
|
||||
GraphicsRenderWriteAtEndOfPipeClockCounterWithWriteBack(
|
||||
m_submit_id, CurrentBuffer(), static_cast<uint64_t*>(dst_gpu_addr));
|
||||
m_submit_id, CurrentBuffer(), static_cast<uint64_t*>(dst_gpu_addr),
|
||||
clock);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -1850,8 +1813,8 @@ void CommandProcessor::Flip() {
|
||||
}
|
||||
|
||||
auto* command = CurrentBuffer();
|
||||
auto request = GraphicsRenderPrepareDisplayBufferFlip(
|
||||
command, m_flip.handle, m_flip.index, m_flip.flip_mode, m_flip.flip_arg);
|
||||
auto request = GraphicsRenderPrepareDisplayBufferFlip(command, m_flip.handle, m_flip.index,
|
||||
m_flip.flip_mode, m_flip.flip_arg);
|
||||
GraphicsRenderWriteAtEndOfPipeOnlyFlip(m_submit_id, command, m_flip.handle, m_flip.index,
|
||||
m_flip.flip_mode, m_flip.flip_arg, request);
|
||||
m_scheduler.Flush();
|
||||
@@ -1869,12 +1832,10 @@ void CommandProcessor::Flip(void* dst_gpu_addr, uint32_t value) {
|
||||
reinterpret_cast<uint64_t>(dst_gpu_addr), value);
|
||||
}
|
||||
|
||||
std::memcpy(dst_gpu_addr, &value, sizeof(value));
|
||||
auto* command = CurrentBuffer();
|
||||
auto request = GraphicsRenderPrepareDisplayBufferFlip(
|
||||
command, m_flip.handle, m_flip.index, m_flip.flip_mode, m_flip.flip_arg);
|
||||
CommandProcessorGuestAccessScope guest_access(CommandProcessorGuestAccess::DirectFence,
|
||||
reinterpret_cast<uint64_t>(dst_gpu_addr),
|
||||
sizeof(uint32_t));
|
||||
auto request = GraphicsRenderPrepareDisplayBufferFlip(command, m_flip.handle, m_flip.index,
|
||||
m_flip.flip_mode, m_flip.flip_arg);
|
||||
GraphicsRenderWriteAtEndOfPipeWithFlip32(
|
||||
m_submit_id, command, static_cast<uint32_t*>(dst_gpu_addr), value, m_flip.handle,
|
||||
m_flip.index, m_flip.flip_mode, m_flip.flip_arg, request);
|
||||
@@ -1899,12 +1860,10 @@ void CommandProcessor::FlipWithInterrupt(uint32_t eop_event_type, uint32_t cache
|
||||
if (eop_event_type != 0x00000004 || cache_action != 0x00000038) {
|
||||
EXIT("unknown event type\n");
|
||||
}
|
||||
std::memcpy(dst_gpu_addr, &value, sizeof(value));
|
||||
auto* command = CurrentBuffer();
|
||||
auto request = GraphicsRenderPrepareDisplayBufferFlip(command, m_flip.handle, m_flip.index,
|
||||
m_flip.flip_mode, m_flip.flip_arg);
|
||||
CommandProcessorGuestAccessScope guest_access(CommandProcessorGuestAccess::DirectFence,
|
||||
reinterpret_cast<uint64_t>(dst_gpu_addr),
|
||||
sizeof(uint32_t));
|
||||
GraphicsRenderWriteAtEndOfPipeWithInterruptWriteBackFlip32(
|
||||
m_submit_id, command, static_cast<uint32_t*>(dst_gpu_addr), value, m_flip.handle,
|
||||
m_flip.index, m_flip.flip_mode, m_flip.flip_arg, request);
|
||||
@@ -1929,15 +1888,8 @@ void CommandProcessor::PrepareCpuFlip() {
|
||||
}
|
||||
|
||||
void CommandProcessor::SynchronizeGpu() {
|
||||
BufferFlush();
|
||||
BufferWait();
|
||||
}
|
||||
|
||||
void CommandProcessor::SynchronizeGpuRange(uint64_t vaddr, uint64_t size) {
|
||||
if (vaddr == 0 || size == 0 || size > UINT64_MAX - vaddr) {
|
||||
EXIT("invalid GPU synchronization range\n");
|
||||
}
|
||||
SynchronizeGpu();
|
||||
Common::LockGuard lock(m_mutex);
|
||||
FinishCommandProcessors();
|
||||
}
|
||||
|
||||
void GraphicsRunSubmit(uint32_t* cmd_draw_buffer, uint32_t num_draw_dw, uint32_t* cmd_const_buffer,
|
||||
@@ -2002,14 +1954,15 @@ bool GraphicsRunIsCommandProcessorThread() noexcept {
|
||||
return g_current_run_cp != nullptr;
|
||||
}
|
||||
|
||||
bool GraphicsRunIsGuestMemoryPoll() noexcept {
|
||||
return g_current_run_cp != nullptr &&
|
||||
g_guest_memory_access == CommandProcessorGuestAccess::WaitPoll;
|
||||
CommandProcessor* GraphicsRunCurrentCommandProcessor() noexcept {
|
||||
return g_current_run_cp;
|
||||
}
|
||||
|
||||
bool GraphicsRunIsCommandProcessorGuestAccess() noexcept {
|
||||
return g_current_run_cp != nullptr &&
|
||||
g_guest_memory_access != CommandProcessorGuestAccess::None;
|
||||
void GraphicsRunFinishCommandProcessors() {
|
||||
if (g_current_run_cp == nullptr) {
|
||||
EXIT("GPU readback finish requires a command-processor thread\n");
|
||||
}
|
||||
g_current_run_cp->FinishReadbackTransaction();
|
||||
}
|
||||
|
||||
bool GraphicsRunSubmissionLockHeld() noexcept {
|
||||
@@ -2020,35 +1973,4 @@ bool GraphicsRunGpuLockHeld() noexcept {
|
||||
return g_gpu_mutex_owned;
|
||||
}
|
||||
|
||||
int GraphicsRunBeginCommandProcessorReadback() {
|
||||
if (g_current_run_cp == nullptr || g_submission_pause_depth != 0 || g_gpu_mutex_owned ||
|
||||
g_cp_readback_owned || g_guest_memory_access == CommandProcessorGuestAccess::None) {
|
||||
EXIT("GPU command-thread readback requires an unpaused command processor\n");
|
||||
}
|
||||
const auto queue = g_current_run_cp->GetQueue();
|
||||
if (queue < 0 || queue >= GraphicContext::QUEUES_NUM) {
|
||||
EXIT("GPU command-thread readback has invalid queue %d\n", queue);
|
||||
}
|
||||
g_cp_readback_owned = true;
|
||||
// Direct fences enter from an already serialized command handler. Wait polls acquire the
|
||||
// same processor mutex in CommandProcessorGuestAccessScope before the resource transaction.
|
||||
g_cp_readback_preowned = true;
|
||||
const auto synchronized_queue =
|
||||
g_current_run_cp->BeginReadbackTransaction(g_cp_readback_preowned);
|
||||
if (synchronized_queue != queue) {
|
||||
EXIT("GPU command-thread queue changed during readback\n");
|
||||
}
|
||||
return synchronized_queue;
|
||||
}
|
||||
|
||||
void GraphicsRunEndCommandProcessorReadback() {
|
||||
if (g_current_run_cp == nullptr || !g_cp_readback_owned) {
|
||||
EXIT("GPU command-thread readback ended without ownership\n");
|
||||
}
|
||||
const auto preowned = g_cp_readback_preowned;
|
||||
g_cp_readback_owned = false;
|
||||
g_cp_readback_preowned = false;
|
||||
g_current_run_cp->EndReadbackTransaction(preowned);
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class CommandProcessor;
|
||||
|
||||
class GraphicsRunSubmissionLock final {
|
||||
public:
|
||||
GraphicsRunSubmissionLock();
|
||||
@@ -23,17 +25,11 @@ void GraphicsRunSubmitFlipPreparation();
|
||||
void GraphicsRunWait();
|
||||
void GraphicsRunDone();
|
||||
int GraphicsRunGetFrameNum();
|
||||
[[nodiscard]] bool GraphicsRunIsCommandProcessorThread() noexcept;
|
||||
[[nodiscard]] bool GraphicsRunIsGuestMemoryPoll() noexcept;
|
||||
[[nodiscard]] bool GraphicsRunIsCommandProcessorGuestAccess() noexcept;
|
||||
[[nodiscard]] bool GraphicsRunSubmissionLockHeld() noexcept;
|
||||
[[nodiscard]] bool GraphicsRunGpuLockHeld() noexcept;
|
||||
// Begin/end an exclusive same-command-processor readback transaction. The begin call finishes
|
||||
// work already recorded on that queue and retains the processor lock so its peer draw/constant
|
||||
// job cannot record new work until BufferCache publishes the faulted page.
|
||||
[[nodiscard]] int GraphicsRunBeginCommandProcessorReadback();
|
||||
void GraphicsRunEndCommandProcessorReadback();
|
||||
|
||||
[[nodiscard]] bool GraphicsRunIsCommandProcessorThread() noexcept;
|
||||
[[nodiscard]] CommandProcessor* GraphicsRunCurrentCommandProcessor() noexcept;
|
||||
void GraphicsRunFinishCommandProcessors();
|
||||
[[nodiscard]] bool GraphicsRunSubmissionLockHeld() noexcept;
|
||||
[[nodiscard]] bool GraphicsRunGpuLockHeld() noexcept;
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GRAPHICSRUN_H_ */
|
||||
|
||||
Reference in New Issue
Block a user