mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-29 16:51:20 +00:00
refactor(gpu): use a single ordered PM4 scheduler
This commit is contained in:
1 parent
dbcb444c05
commit
f60007e923
12 files changed
+680
-918
No files matched your search
@@ -1,96 +0,0 @@
|
|||||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_ASYNCJOB_H_
|
|
||||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_ASYNCJOB_H_
|
|
||||||
|
|
||||||
#include "common/common.h"
|
|
||||||
#include "common/profiler.h"
|
|
||||||
#include "common/threads.h"
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace Libs::Graphics {
|
|
||||||
|
|
||||||
class AsyncJob final {
|
|
||||||
public:
|
|
||||||
using Task = std::function<void()>;
|
|
||||||
|
|
||||||
explicit AsyncJob(std::string thread_name = {})
|
|
||||||
: m_thread_name(std::move(thread_name)), m_worker_thread(WorkerEntry, this) {}
|
|
||||||
|
|
||||||
~AsyncJob() {
|
|
||||||
{
|
|
||||||
Common::LockGuard lock(m_mutex);
|
|
||||||
m_stop_requested = true;
|
|
||||||
m_task_available_condition.Signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_worker_thread.Join();
|
|
||||||
}
|
|
||||||
|
|
||||||
KYTY_CLASS_NO_COPY(AsyncJob);
|
|
||||||
|
|
||||||
void Execute(Task task) {
|
|
||||||
Common::LockGuard lock(m_mutex);
|
|
||||||
while (m_is_busy) {
|
|
||||||
m_idle_condition.Wait(&m_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_task = std::move(task);
|
|
||||||
m_is_busy = true;
|
|
||||||
m_task_available_condition.Signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Wait() {
|
|
||||||
Common::LockGuard lock(m_mutex);
|
|
||||||
while (m_is_busy) {
|
|
||||||
m_idle_condition.Wait(&m_mutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void WorkerEntry(void* data) { static_cast<AsyncJob*>(data)->WorkerLoop(); }
|
|
||||||
|
|
||||||
void WorkerLoop() {
|
|
||||||
if (!m_thread_name.empty()) {
|
|
||||||
KYTY_PROFILER_THREAD(m_thread_name.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
Task task;
|
|
||||||
{
|
|
||||||
Common::LockGuard lock(m_mutex);
|
|
||||||
while (!m_is_busy && !m_stop_requested) {
|
|
||||||
m_task_available_condition.Wait(&m_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_is_busy) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
task = std::move(m_task);
|
|
||||||
}
|
|
||||||
|
|
||||||
task();
|
|
||||||
|
|
||||||
{
|
|
||||||
Common::LockGuard lock(m_mutex);
|
|
||||||
m_is_busy = false;
|
|
||||||
m_idle_condition.SignalAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string m_thread_name;
|
|
||||||
Common::Mutex m_mutex;
|
|
||||||
Common::CondVar m_task_available_condition;
|
|
||||||
Common::CondVar m_idle_condition;
|
|
||||||
Task m_task;
|
|
||||||
bool m_is_busy = false;
|
|
||||||
bool m_stop_requested = false;
|
|
||||||
Common::Thread m_worker_thread;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Libs::Graphics
|
|
||||||
|
|
||||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_ASYNCJOB_H_ */
|
|
||||||
@@ -2,12 +2,10 @@
|
|||||||
#define GRAPHICS_GUEST_GPU_COMMAND_PROCESSOR_COMMAND_PROCESSOR_H
|
#define GRAPHICS_GUEST_GPU_COMMAND_PROCESSOR_COMMAND_PROCESSOR_H
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/threads.h"
|
|
||||||
#include "graphics/guest_gpu/hardwareContext.h"
|
#include "graphics/guest_gpu/hardwareContext.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"
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -17,94 +15,25 @@ inline constexpr uint32_t AcquireGcrGl2Writeback = 1u << 15u;
|
|||||||
|
|
||||||
bool TestWaitRegMemValue(uint64_t value, uint64_t ref, uint64_t mask, uint32_t func);
|
bool TestWaitRegMemValue(uint64_t value, uint64_t ref, uint64_t mask, uint32_t func);
|
||||||
|
|
||||||
class CommandScheduler {
|
enum class Pm4ProcessResult { Complete, Blocked };
|
||||||
|
|
||||||
|
class Pm4Execution {
|
||||||
public:
|
public:
|
||||||
static constexpr int BuffersNum = 8;
|
[[nodiscard]] bool MadeProgress() const noexcept { return m_made_progress; }
|
||||||
|
|
||||||
CommandScheduler(HW::Context& registers, HW::UserConfig& user_config, HW::Shader& shaders)
|
|
||||||
: m_registers(registers), m_user_config(user_config), m_shaders(shaders) {}
|
|
||||||
|
|
||||||
bool Active() const { return m_current >= 0 && m_current < BuffersNum; }
|
|
||||||
void CheckActive() const { EXIT_IF(!Active()); }
|
|
||||||
|
|
||||||
RenderCommandBuffer& Current() const {
|
|
||||||
CheckActive();
|
|
||||||
EXIT_IF(m_buffers[m_current] == nullptr);
|
|
||||||
return *m_buffers[m_current];
|
|
||||||
}
|
|
||||||
|
|
||||||
void Init() {
|
|
||||||
if (m_current >= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (auto& buf: m_buffers) {
|
|
||||||
EXIT_IF(buf != nullptr);
|
|
||||||
buf = new RenderCommandBuffer(m_registers, m_user_config, m_shaders);
|
|
||||||
}
|
|
||||||
m_current = 0;
|
|
||||||
Current().Begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Flush() {
|
|
||||||
SubmitCurrent();
|
|
||||||
BeginNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
CommandBuffer& FlushAndGetSubmitted() {
|
|
||||||
auto& submitted = SubmitCurrent();
|
|
||||||
BeginNext();
|
|
||||||
return submitted;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CopyBuffers(std::array<CommandBuffer*, BuffersNum>& out) const {
|
|
||||||
for (int i = 0; i < BuffersNum; i++) {
|
|
||||||
auto* buf = m_buffers[i];
|
|
||||||
EXIT_IF(buf == nullptr);
|
|
||||||
out[i] = buf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WaitAll() {
|
|
||||||
for (auto* buf: m_buffers) {
|
|
||||||
EXIT_IF(buf == nullptr);
|
|
||||||
buf->WaitForFenceAndReset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SubmitForReadback() {
|
|
||||||
if (!Active()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SubmitCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResumeAfterReadback() {
|
|
||||||
if (!Active()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Current().WaitForFenceAndReset();
|
|
||||||
Current().Begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CommandBuffer& SubmitCurrent() {
|
friend class CommandProcessor;
|
||||||
auto& submitted = Current();
|
|
||||||
submitted.End();
|
|
||||||
submitted.Execute();
|
|
||||||
return submitted;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BeginNext() {
|
struct BufferCursor {
|
||||||
m_current = (m_current + 1) % BuffersNum;
|
uint32_t* next_packet = nullptr;
|
||||||
Current().WaitForFenceAndReset();
|
uint32_t remaining_dw = 0;
|
||||||
Current().Begin();
|
uint32_t total_dw = 0;
|
||||||
}
|
uint32_t deferred_advance_dw = 0;
|
||||||
|
};
|
||||||
|
|
||||||
RenderCommandBuffer* m_buffers[BuffersNum] = {};
|
std::vector<BufferCursor> m_buffer_stack;
|
||||||
int m_current = -1;
|
bool m_suspended = false;
|
||||||
HW::Context& m_registers;
|
bool m_made_progress = false;
|
||||||
HW::UserConfig& m_user_config;
|
|
||||||
HW::Shader& m_shaders;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CommandProcessor {
|
class CommandProcessor {
|
||||||
@@ -116,8 +45,8 @@ public:
|
|||||||
int64_t flip_arg = 0;
|
int64_t flip_arg = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
CommandProcessor();
|
CommandProcessor() = default;
|
||||||
~CommandProcessor() { KYTY_NOT_IMPLEMENTED; }
|
~CommandProcessor() = default;
|
||||||
|
|
||||||
KYTY_CLASS_NO_COPY(CommandProcessor);
|
KYTY_CLASS_NO_COPY(CommandProcessor);
|
||||||
|
|
||||||
@@ -128,7 +57,6 @@ public:
|
|||||||
void BufferFlushAndWait();
|
void BufferFlushAndWait();
|
||||||
void BufferWait();
|
void BufferWait();
|
||||||
void BeginReadbackTransaction() {
|
void BeginReadbackTransaction() {
|
||||||
m_mutex.Lock();
|
|
||||||
if (m_readback_active) {
|
if (m_readback_active) {
|
||||||
EXIT("nested command-processor readback transaction\n");
|
EXIT("nested command-processor readback transaction\n");
|
||||||
}
|
}
|
||||||
@@ -142,12 +70,8 @@ public:
|
|||||||
}
|
}
|
||||||
m_readback_active = false;
|
m_readback_active = false;
|
||||||
m_readback_finished = false;
|
m_readback_finished = false;
|
||||||
m_mutex.Unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunLock() { m_run_mutex.Lock(); }
|
|
||||||
void RunUnlock() { m_run_mutex.Unlock(); }
|
|
||||||
|
|
||||||
HW::Context& GetCtx() { return m_ctx; }
|
HW::Context& GetCtx() { return m_ctx; }
|
||||||
HW::UserConfig& GetUcfg() { return m_ucfg; }
|
HW::UserConfig& GetUcfg() { return m_ucfg; }
|
||||||
HW::Shader& GetShCtx() { return m_sh_ctx; }
|
HW::Shader& GetShCtx() { return m_sh_ctx; }
|
||||||
@@ -202,10 +126,11 @@ public:
|
|||||||
|
|
||||||
void PrefetchL2(void* addr, uint32_t size) {}
|
void PrefetchL2(void* addr, uint32_t size) {}
|
||||||
void ResetDeCe();
|
void ResetDeCe();
|
||||||
|
void SetCeComplete(bool complete) { m_ce_complete = complete; }
|
||||||
void WaitCe();
|
void WaitCe();
|
||||||
void WaitDeDiff(uint32_t diff);
|
void WaitDeDiff(uint32_t diff);
|
||||||
void IncremenetDe();
|
void IncrementDe();
|
||||||
void IncremenetCe();
|
void IncrementCe();
|
||||||
|
|
||||||
void WriteConstRam(uint32_t offset, const uint32_t* src, uint32_t dw_num);
|
void WriteConstRam(uint32_t offset, const uint32_t* src, uint32_t dw_num);
|
||||||
void DumpConstRam(uint32_t* dst, uint32_t offset, uint32_t dw_num);
|
void DumpConstRam(uint32_t* dst, uint32_t offset, uint32_t dw_num);
|
||||||
@@ -222,30 +147,26 @@ public:
|
|||||||
const volatile void* address, uint32_t count_in_dwords);
|
const volatile void* address, uint32_t count_in_dwords);
|
||||||
[[nodiscard]] bool ShouldSkipPredicatedPackets() const { return m_predicate_skip; }
|
[[nodiscard]] bool ShouldSkipPredicatedPackets() const { return m_predicate_skip; }
|
||||||
|
|
||||||
void Run(uint32_t* data, uint32_t num_dw);
|
Pm4ProcessResult Process(Pm4Execution& execution, uint32_t* buffer, uint32_t size_dw);
|
||||||
|
void ProcessIndirectBuffer(uint32_t* buffer, uint32_t size_dw);
|
||||||
|
|
||||||
[[nodiscard]] const FlipInfo& GetFlip() const { return m_flip; }
|
void SetFlip(const FlipInfo& flip) { m_flip = flip; }
|
||||||
void SetFlip(const FlipInfo& flip) { m_flip = flip; }
|
|
||||||
|
|
||||||
[[nodiscard]] uint64_t GetSubmitId() const { return m_submit_id; }
|
[[nodiscard]] uint64_t GetSubmitId() const { return m_submit_id; }
|
||||||
void SetSubmitId(uint64_t submit_id) { m_submit_id = submit_id; }
|
void SetSubmitId(uint64_t submit_id) { m_submit_id = submit_id; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Counter {
|
|
||||||
Common::Mutex mutex;
|
|
||||||
Common::CondVar cond_var;
|
|
||||||
uint32_t value = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_write_dest, uint32_t eop_event_type,
|
void WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_write_dest, uint32_t eop_event_type,
|
||||||
uint32_t cache_action, uint32_t event_index, uint32_t event_write_source,
|
uint32_t cache_action, uint32_t event_index, uint32_t event_write_source,
|
||||||
void* dst_gpu_addr, T value, uint32_t interrupt_selector,
|
void* dst_gpu_addr, T value, uint32_t interrupt_selector,
|
||||||
uint32_t interrupt_context_id);
|
uint32_t interrupt_context_id);
|
||||||
void FinishCommandProcessors();
|
void ProcessPm4(Pm4Execution& execution, size_t stop_depth);
|
||||||
|
void SuspendPm4();
|
||||||
|
|
||||||
RenderCommandBuffer& CurrentBuffer() { return m_scheduler.Current(); }
|
CommandScheduler& GetScheduler() const { return GetRenderContext().GetCommandScheduler(); }
|
||||||
void CheckBuffer() const { m_scheduler.CheckActive(); }
|
RenderCommandBuffer& CurrentBuffer() { return GetScheduler().Current(); }
|
||||||
|
void CheckBuffer() const { GetScheduler().CheckActive(); }
|
||||||
GpuResourceManager& GetGpuResources() const { return GetRenderContext().GetGpuResources(); }
|
GpuResourceManager& GetGpuResources() const { return GetRenderContext().GetGpuResources(); }
|
||||||
|
|
||||||
HW::Context m_ctx;
|
HW::Context m_ctx;
|
||||||
@@ -259,16 +180,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;
|
uint32_t m_de_count = 0;
|
||||||
inline static std::vector<CommandProcessor*> m_processors;
|
uint32_t m_ce_count = 0;
|
||||||
inline static bool m_readback_active = false;
|
bool m_ce_complete = false;
|
||||||
inline static bool m_readback_finished = false;
|
bool m_readback_active = false;
|
||||||
Common::Mutex m_run_mutex;
|
bool m_readback_finished = false;
|
||||||
|
|
||||||
CommandScheduler m_scheduler;
|
|
||||||
|
|
||||||
Counter m_de_counter;
|
|
||||||
Counter m_ce_counter;
|
|
||||||
|
|
||||||
uint32_t m_const_ram[0x3000] = {0};
|
uint32_t m_const_ram[0x3000] = {0};
|
||||||
|
|
||||||
|
|||||||
@@ -1984,7 +1984,6 @@ KYTY_CP_OP_PARSER(CpOpAcquireMem) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stall_mode == 0) {
|
if (stall_mode == 0) {
|
||||||
cp.BufferFlush();
|
|
||||||
cp.BufferWait();
|
cp.BufferWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2166,10 +2165,10 @@ KYTY_CP_OP_PARSER(CpOpBranch) {
|
|||||||
reinterpret_cast<uint64_t>(else_buffer), else_num_dw);
|
reinterpret_cast<uint64_t>(else_buffer), else_num_dw);
|
||||||
|
|
||||||
if (take_then) {
|
if (take_then) {
|
||||||
cp.Run(then_buffer, then_num_dw);
|
cp.ProcessIndirectBuffer(then_buffer, then_num_dw);
|
||||||
} else if (mode == 2 && else_num_dw != 0) {
|
} else if (mode == 2 && else_num_dw != 0) {
|
||||||
EXIT_NOT_IMPLEMENTED(else_buffer == nullptr);
|
EXIT_NOT_IMPLEMENTED(else_buffer == nullptr);
|
||||||
cp.Run(else_buffer, else_num_dw);
|
cp.ProcessIndirectBuffer(else_buffer, else_num_dw);
|
||||||
}
|
}
|
||||||
|
|
||||||
return payload_dw;
|
return payload_dw;
|
||||||
@@ -2496,7 +2495,7 @@ KYTY_CP_OP_PARSER(CpOpIncrementCeCounter) {
|
|||||||
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0008400);
|
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0008400);
|
||||||
EXIT_NOT_IMPLEMENTED(buffer[0] != 1);
|
EXIT_NOT_IMPLEMENTED(buffer[0] != 1);
|
||||||
|
|
||||||
cp.IncremenetCe();
|
cp.IncrementCe();
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -2507,7 +2506,7 @@ KYTY_CP_OP_PARSER(CpOpIncrementDeCounter) {
|
|||||||
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0008500);
|
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0008500);
|
||||||
EXIT_NOT_IMPLEMENTED(buffer[0] != 0);
|
EXIT_NOT_IMPLEMENTED(buffer[0] != 0);
|
||||||
|
|
||||||
cp.IncremenetDe();
|
cp.IncrementDe();
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -2607,7 +2606,7 @@ KYTY_CP_OP_PARSER(CpOpIndirectBuffer) {
|
|||||||
|
|
||||||
GraphicsDbgDumpDcb("ci", indirect_num_dw, indirect_buffer);
|
GraphicsDbgDumpDcb("ci", indirect_num_dw, indirect_buffer);
|
||||||
|
|
||||||
cp.Run(indirect_buffer, indirect_num_dw);
|
cp.ProcessIndirectBuffer(indirect_buffer, indirect_num_dw);
|
||||||
|
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -27,7 +27,7 @@ void GraphicsRunDone();
|
|||||||
int GraphicsRunGetFrameNum();
|
int GraphicsRunGetFrameNum();
|
||||||
[[nodiscard]] bool GraphicsRunIsCommandProcessorThread() noexcept;
|
[[nodiscard]] bool GraphicsRunIsCommandProcessorThread() noexcept;
|
||||||
[[nodiscard]] CommandProcessor* GraphicsRunCurrentCommandProcessor() noexcept;
|
[[nodiscard]] CommandProcessor* GraphicsRunCurrentCommandProcessor() noexcept;
|
||||||
void GraphicsRunFinishCommandProcessors();
|
void GraphicsRunFinishScheduler();
|
||||||
[[nodiscard]] bool GraphicsRunSubmissionLockHeld() noexcept;
|
[[nodiscard]] bool GraphicsRunSubmissionLockHeld() noexcept;
|
||||||
[[nodiscard]] bool GraphicsRunGpuLockHeld() noexcept;
|
[[nodiscard]] bool GraphicsRunGpuLockHeld() noexcept;
|
||||||
} // namespace Libs::Graphics
|
} // namespace Libs::Graphics
|
||||||
|
|||||||
@@ -554,7 +554,7 @@ bool BufferCache::InvalidateMemory(PageFaultAccess access, uint64_t vaddr, uint6
|
|||||||
return action == CpuFaultAction::Continue;
|
return action == CpuFaultAction::Continue;
|
||||||
}
|
}
|
||||||
if (GraphicsRunIsCommandProcessorThread()) {
|
if (GraphicsRunIsCommandProcessorThread()) {
|
||||||
GraphicsRunFinishCommandProcessors();
|
GraphicsRunFinishScheduler();
|
||||||
}
|
}
|
||||||
m_readback->Request(access, vaddr, size);
|
m_readback->Request(access, vaddr, size);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
|
||||||
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
|
void CommandScheduler::Begin(HW::Context& registers, HW::UserConfig& user_config,
|
||||||
|
HW::Shader& shaders) {
|
||||||
|
m_registers = ®isters;
|
||||||
|
m_user_config = &user_config;
|
||||||
|
m_shaders = &shaders;
|
||||||
|
|
||||||
|
if (!Active()) {
|
||||||
|
for (auto& buffer: m_buffers) {
|
||||||
|
buffer = std::make_unique<RenderCommandBuffer>();
|
||||||
|
}
|
||||||
|
m_current = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BindCurrent();
|
||||||
|
if (!m_recording) {
|
||||||
|
Current().Begin();
|
||||||
|
m_recording = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandScheduler::Flush() {
|
||||||
|
SubmitCurrent();
|
||||||
|
BeginNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandBuffer& CommandScheduler::FlushAndGetSubmitted() {
|
||||||
|
auto& submitted = SubmitCurrent();
|
||||||
|
BeginNext();
|
||||||
|
return submitted;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandScheduler::Finish() {
|
||||||
|
CheckActive();
|
||||||
|
if (m_recording) {
|
||||||
|
SubmitCurrent();
|
||||||
|
}
|
||||||
|
for (auto& buffer: m_buffers) {
|
||||||
|
buffer->WaitForFenceAndReset();
|
||||||
|
}
|
||||||
|
BindCurrent();
|
||||||
|
Current().Begin();
|
||||||
|
m_recording = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandScheduler::FinishCurrent() {
|
||||||
|
auto& submitted = SubmitCurrent();
|
||||||
|
submitted.WaitForFenceAndReset();
|
||||||
|
submitted.Begin();
|
||||||
|
m_recording = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandScheduler::CheckActive() const {
|
||||||
|
EXIT_IF(!Active() || m_current >= BufferCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderCommandBuffer& CommandScheduler::Current() const {
|
||||||
|
CheckActive();
|
||||||
|
EXIT_IF(m_buffers[m_current] == nullptr);
|
||||||
|
return *m_buffers[m_current];
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandScheduler::BindCurrent() const {
|
||||||
|
EXIT_IF(m_registers == nullptr || m_user_config == nullptr || m_shaders == nullptr);
|
||||||
|
Current().Bind(*m_registers, *m_user_config, *m_shaders);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandBuffer& CommandScheduler::SubmitCurrent() {
|
||||||
|
CheckActive();
|
||||||
|
EXIT_IF(!m_recording);
|
||||||
|
auto& submitted = Current();
|
||||||
|
submitted.End();
|
||||||
|
submitted.Execute();
|
||||||
|
m_recording = false;
|
||||||
|
return submitted;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandScheduler::BeginNext() {
|
||||||
|
EXIT_IF(m_recording);
|
||||||
|
m_current = (m_current + 1) % BufferCount;
|
||||||
|
Current().WaitForFenceAndReset();
|
||||||
|
BindCurrent();
|
||||||
|
Current().Begin();
|
||||||
|
m_recording = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Libs::Graphics
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_COMMANDSCHEDULER_H_
|
||||||
|
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_COMMANDSCHEDULER_H_
|
||||||
|
|
||||||
|
#include "common/common.h"
|
||||||
|
#include "graphics/host_gpu/renderer/render.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
|
class CommandScheduler {
|
||||||
|
public:
|
||||||
|
static constexpr int BufferCount = 8;
|
||||||
|
|
||||||
|
CommandScheduler() = default;
|
||||||
|
~CommandScheduler() = default;
|
||||||
|
KYTY_CLASS_NO_COPY(CommandScheduler);
|
||||||
|
|
||||||
|
void Begin(HW::Context& registers, HW::UserConfig& user_config, HW::Shader& shaders);
|
||||||
|
void Flush();
|
||||||
|
CommandBuffer& FlushAndGetSubmitted();
|
||||||
|
void Finish();
|
||||||
|
void FinishCurrent();
|
||||||
|
|
||||||
|
[[nodiscard]] bool Active() const noexcept { return m_current >= 0; }
|
||||||
|
void CheckActive() const;
|
||||||
|
RenderCommandBuffer& Current() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void BindCurrent() const;
|
||||||
|
CommandBuffer& SubmitCurrent();
|
||||||
|
void BeginNext();
|
||||||
|
|
||||||
|
std::array<std::unique_ptr<RenderCommandBuffer>, BufferCount> m_buffers;
|
||||||
|
int m_current = -1;
|
||||||
|
bool m_recording = false;
|
||||||
|
HW::Context* m_registers = nullptr;
|
||||||
|
HW::UserConfig* m_user_config = nullptr;
|
||||||
|
HW::Shader* m_shaders = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Libs::Graphics
|
||||||
|
|
||||||
|
#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_COMMANDSCHEDULER_H_
|
||||||
@@ -129,17 +129,22 @@ private:
|
|||||||
|
|
||||||
class RenderCommandBuffer final: public CommandBuffer {
|
class RenderCommandBuffer final: public CommandBuffer {
|
||||||
public:
|
public:
|
||||||
RenderCommandBuffer(HW::Context& registers, HW::UserConfig& user_config, HW::Shader& shaders)
|
RenderCommandBuffer() = default;
|
||||||
: m_registers(registers), m_user_config(user_config), m_shaders(shaders) {}
|
|
||||||
|
|
||||||
[[nodiscard]] HW::Context& GetRegisters() const noexcept { return m_registers; }
|
void Bind(HW::Context& registers, HW::UserConfig& user_config, HW::Shader& shaders) noexcept {
|
||||||
[[nodiscard]] HW::UserConfig& GetUserConfig() const noexcept { return m_user_config; }
|
m_registers = ®isters;
|
||||||
[[nodiscard]] HW::Shader& GetShaders() const noexcept { return m_shaders; }
|
m_user_config = &user_config;
|
||||||
|
m_shaders = &shaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] HW::Context& GetRegisters() const noexcept { return *m_registers; }
|
||||||
|
[[nodiscard]] HW::UserConfig& GetUserConfig() const noexcept { return *m_user_config; }
|
||||||
|
[[nodiscard]] HW::Shader& GetShaders() const noexcept { return *m_shaders; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HW::Context& m_registers;
|
HW::Context* m_registers = nullptr;
|
||||||
HW::UserConfig& m_user_config;
|
HW::UserConfig* m_user_config = nullptr;
|
||||||
HW::Shader& m_shaders;
|
HW::Shader* m_shaders = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
void RenderDrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer, uint32_t index_type_and_size,
|
void RenderDrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer, uint32_t index_type_and_size,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "common/threads.h"
|
#include "common/threads.h"
|
||||||
#include "graphics/host_gpu/renderer/bufferCache.h"
|
#include "graphics/host_gpu/renderer/bufferCache.h"
|
||||||
|
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||||
#include "graphics/host_gpu/renderer/descriptorCache.h"
|
#include "graphics/host_gpu/renderer/descriptorCache.h"
|
||||||
#include "graphics/host_gpu/renderer/framebufferCache.h"
|
#include "graphics/host_gpu/renderer/framebufferCache.h"
|
||||||
#include "graphics/host_gpu/renderer/gdsBuffer.h"
|
#include "graphics/host_gpu/renderer/gdsBuffer.h"
|
||||||
@@ -30,6 +31,7 @@ public:
|
|||||||
[[nodiscard]] GraphicContext& GetGraphics() const noexcept { return m_graphics; }
|
[[nodiscard]] GraphicContext& GetGraphics() const noexcept { return m_graphics; }
|
||||||
|
|
||||||
Common::Mutex& GetMutex() { return m_mutex; }
|
Common::Mutex& GetMutex() { return m_mutex; }
|
||||||
|
CommandScheduler& GetCommandScheduler() { return m_command_scheduler; }
|
||||||
PipelineCache& GetPipelineCache() { return m_pipeline_cache; }
|
PipelineCache& GetPipelineCache() { return m_pipeline_cache; }
|
||||||
DescriptorCache& GetDescriptorCache() { return m_descriptor_cache; }
|
DescriptorCache& GetDescriptorCache() { return m_descriptor_cache; }
|
||||||
FramebufferCache& GetFramebufferCache() { return m_framebuffer_cache; }
|
FramebufferCache& GetFramebufferCache() { return m_framebuffer_cache; }
|
||||||
@@ -52,6 +54,7 @@ private:
|
|||||||
|
|
||||||
GraphicContext& m_graphics;
|
GraphicContext& m_graphics;
|
||||||
Common::Mutex m_mutex;
|
Common::Mutex m_mutex;
|
||||||
|
CommandScheduler m_command_scheduler;
|
||||||
PipelineCache m_pipeline_cache;
|
PipelineCache m_pipeline_cache;
|
||||||
DescriptorCache m_descriptor_cache;
|
DescriptorCache m_descriptor_cache;
|
||||||
FramebufferCache m_framebuffer_cache;
|
FramebufferCache m_framebuffer_cache;
|
||||||
|
|||||||
@@ -3653,7 +3653,7 @@ bool TextureCache::InvalidateMemory(PageFaultAccess access, uint64_t vaddr, uint
|
|||||||
|
|
||||||
if (needs_readback) {
|
if (needs_readback) {
|
||||||
if (GraphicsRunIsCommandProcessorThread()) {
|
if (GraphicsRunIsCommandProcessorThread()) {
|
||||||
GraphicsRunFinishCommandProcessors();
|
GraphicsRunFinishScheduler();
|
||||||
}
|
}
|
||||||
m_readback->Request(access, vaddr, size);
|
m_readback->Request(access, vaddr, size);
|
||||||
action = m_memory_tracker.BeginCpuFault(vaddr, size, access);
|
action = m_memory_tracker.BeginCpuFault(vaddr, size, access);
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/emulatorConfig.h"
|
#include "common/emulatorConfig.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "graphics/guest_gpu/command_processor/commandProcessor.h"
|
||||||
|
#include "graphics/guest_gpu/command_processor/pm4Dispatch.h"
|
||||||
#include "graphics/guest_gpu/gpu_defs.h"
|
#include "graphics/guest_gpu/gpu_defs.h"
|
||||||
#include "graphics/guest_gpu/gpu_format.h"
|
#include "graphics/guest_gpu/gpu_format.h"
|
||||||
#include "graphics/guest_gpu/hardwareContext.h"
|
#include "graphics/guest_gpu/hardwareContext.h"
|
||||||
|
#include "graphics/guest_gpu/pm4.h"
|
||||||
#include "graphics/guest_gpu/tile.h"
|
#include "graphics/guest_gpu/tile.h"
|
||||||
#include "graphics/host_gpu/gpuTiler.h"
|
#include "graphics/host_gpu/gpuTiler.h"
|
||||||
#include "graphics/host_gpu/hostMemory.h"
|
#include "graphics/host_gpu/hostMemory.h"
|
||||||
@@ -14083,6 +14086,102 @@ void CheckClipControlDepthClipState() {
|
|||||||
std::printf("[host] %-32s ok\n", "ClipControlDepthClipState");
|
std::printf("[host] %-32s ok\n", "ClipControlDepthClipState");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckPm4WaitResume() {
|
||||||
|
GraphicsInitJmpTables();
|
||||||
|
CommandProcessor processor;
|
||||||
|
|
||||||
|
uint32_t label = 0;
|
||||||
|
uint32_t prefix = 0;
|
||||||
|
uint32_t child_observation = UINT32_MAX;
|
||||||
|
uint32_t suffix = 0;
|
||||||
|
const auto address = [](const void *value) {
|
||||||
|
return reinterpret_cast<uint64_t>(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::array<uint32_t, 12> child{};
|
||||||
|
child[0] = KYTY_PM4(5, Pm4::IT_WRITE_DATA, 0);
|
||||||
|
child[1] = 0;
|
||||||
|
child[2] = static_cast<uint32_t>(address(&child_observation));
|
||||||
|
child[3] = static_cast<uint32_t>(address(&child_observation) >> 32u);
|
||||||
|
child[4] = 0;
|
||||||
|
child[5] = KYTY_PM4(7, Pm4::IT_NOP, Pm4::R_WAIT_MEM_32);
|
||||||
|
child[6] = static_cast<uint32_t>(address(&label));
|
||||||
|
child[7] = static_cast<uint32_t>(address(&label) >> 32u);
|
||||||
|
child[8] = UINT32_MAX;
|
||||||
|
child[9] = 1;
|
||||||
|
child[10] = 0x10u | 3u;
|
||||||
|
|
||||||
|
std::array<uint32_t, 4> nested{};
|
||||||
|
nested[0] = KYTY_PM4(4, Pm4::IT_INDIRECT_BUFFER, 0);
|
||||||
|
nested[1] = static_cast<uint32_t>(address(child.data()));
|
||||||
|
nested[2] = static_cast<uint32_t>(address(child.data()) >> 32u);
|
||||||
|
nested[3] = 0x0f200000u | static_cast<uint32_t>(child.size());
|
||||||
|
|
||||||
|
std::array<uint32_t, 14> commands{};
|
||||||
|
commands[0] = KYTY_PM4(5, Pm4::IT_WRITE_DATA, 0);
|
||||||
|
commands[1] = 0;
|
||||||
|
commands[2] = static_cast<uint32_t>(address(&prefix));
|
||||||
|
commands[3] = static_cast<uint32_t>(address(&prefix) >> 32u);
|
||||||
|
commands[4] = 11;
|
||||||
|
commands[5] = KYTY_PM4(4, Pm4::IT_INDIRECT_BUFFER, 0);
|
||||||
|
commands[6] = static_cast<uint32_t>(address(nested.data()));
|
||||||
|
commands[7] = static_cast<uint32_t>(address(nested.data()) >> 32u);
|
||||||
|
commands[8] = 0x0f200000u | static_cast<uint32_t>(nested.size());
|
||||||
|
commands[9] = KYTY_PM4(5, Pm4::IT_WRITE_DATA, 0);
|
||||||
|
commands[10] = 0;
|
||||||
|
commands[11] = static_cast<uint32_t>(address(&suffix));
|
||||||
|
commands[12] = static_cast<uint32_t>(address(&suffix) >> 32u);
|
||||||
|
commands[13] = 22;
|
||||||
|
|
||||||
|
Pm4Execution execution;
|
||||||
|
Require("Pm4WaitResume", "suspend",
|
||||||
|
processor.Process(execution, commands.data(), commands.size()) ==
|
||||||
|
Pm4ProcessResult::Blocked &&
|
||||||
|
prefix == 11 && child_observation == 0 && suffix == 0,
|
||||||
|
"blocked indirect wait did not preserve its command position");
|
||||||
|
|
||||||
|
label = 1;
|
||||||
|
child[4] = 1;
|
||||||
|
Require("Pm4WaitResume", "resume",
|
||||||
|
processor.Process(execution, commands.data(), commands.size()) ==
|
||||||
|
Pm4ProcessResult::Complete &&
|
||||||
|
prefix == 11 && child_observation == 0 && suffix == 22,
|
||||||
|
"resumed indirect wait replayed a child or did not finish its parent");
|
||||||
|
std::printf("[host] %-32s ok\n", "Pm4WaitResume");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckPm4CeCompletion() {
|
||||||
|
GraphicsInitJmpTables();
|
||||||
|
CommandProcessor processor;
|
||||||
|
uint32_t suffix = 0;
|
||||||
|
const auto address = reinterpret_cast<uint64_t>(&suffix);
|
||||||
|
|
||||||
|
std::array<uint32_t, 7> commands{};
|
||||||
|
commands[0] = 0xc0008600u;
|
||||||
|
commands[1] = 1;
|
||||||
|
commands[2] = KYTY_PM4(5, Pm4::IT_WRITE_DATA, 0);
|
||||||
|
commands[3] = 0;
|
||||||
|
commands[4] = static_cast<uint32_t>(address);
|
||||||
|
commands[5] = static_cast<uint32_t>(address >> 32u);
|
||||||
|
commands[6] = 33;
|
||||||
|
|
||||||
|
processor.ResetDeCe();
|
||||||
|
Pm4Execution execution;
|
||||||
|
Require("Pm4CeCompletion", "wait",
|
||||||
|
processor.Process(execution, commands.data(), commands.size()) ==
|
||||||
|
Pm4ProcessResult::Blocked &&
|
||||||
|
suffix == 0,
|
||||||
|
"DE did not wait for an active CE stream");
|
||||||
|
|
||||||
|
processor.SetCeComplete(true);
|
||||||
|
Require("Pm4CeCompletion", "complete",
|
||||||
|
processor.Process(execution, commands.data(), commands.size()) ==
|
||||||
|
Pm4ProcessResult::Complete &&
|
||||||
|
suffix == 33,
|
||||||
|
"DE remained blocked after the CE stream completed");
|
||||||
|
std::printf("[host] %-32s ok\n", "Pm4CeCompletion");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace Libs::Graphics
|
} // namespace Libs::Graphics
|
||||||
|
|
||||||
@@ -14229,6 +14328,8 @@ int main(int argc, char **argv) {
|
|||||||
#endif
|
#endif
|
||||||
CheckClipControlDepthClipState();
|
CheckClipControlDepthClipState();
|
||||||
CheckReferenceClockScale();
|
CheckReferenceClockScale();
|
||||||
|
CheckPm4WaitResume();
|
||||||
|
CheckPm4CeCompletion();
|
||||||
CheckEmbeddedFetchVertexOffset();
|
CheckEmbeddedFetchVertexOffset();
|
||||||
CheckEmbeddedFetchLaneSpill();
|
CheckEmbeddedFetchLaneSpill();
|
||||||
CheckPs5GameExampleImageClearRuntimeShape();
|
CheckPs5GameExampleImageClearRuntimeShape();
|
||||||
|
|||||||
Reference in new issue
Block a user