Files
Kyty/source/emulator/src/Graphics/GraphicsRun.cpp
T
2021-12-01 19:29:27 +10:00

2335 lines
69 KiB
C++

#include "Emulator/Graphics/GraphicsRun.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/LinkList.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/Threads.h"
#include "Emulator/Config.h"
#include "Emulator/Graphics/AsyncJob.h"
#include "Emulator/Graphics/GraphicContext.h"
#include "Emulator/Graphics/GraphicsRender.h"
#include "Emulator/Graphics/HardwareContext.h"
#include "Emulator/Graphics/Pm4.h"
#include "Emulator/Graphics/VideoOut.h"
#include "Emulator/Graphics/Window.h"
#include "Emulator/Profiler.h"
#ifdef KYTY_EMU_ENABLED
#define KYTY_HW_CTX_PARSER_ARGS \
[[maybe_unused]] CommandProcessor *cp, uint32_t cmd_id, [[maybe_unused]] uint32_t cmd_offset, const uint32_t *buffer, \
[[maybe_unused]] uint32_t dw
#define KYTY_HW_CTX_PARSER(f) static uint32_t f(KYTY_HW_CTX_PARSER_ARGS)
#define KYTY_CP_OP_PARSER_ARGS \
[[maybe_unused]] CommandProcessor *cp, [[maybe_unused]] uint32_t cmd_id, [[maybe_unused]] const uint32_t *buffer, \
[[maybe_unused]] uint32_t dw, [[maybe_unused]] uint32_t num_dw
#define KYTY_CP_OP_PARSER(f) static uint32_t f(KYTY_CP_OP_PARSER_ARGS)
namespace Kyty::Libs::Graphics {
class CommandProcessor;
class CommandProcessor
{
public:
struct FlipInfo
{
int handle = 0;
int index = 0;
int flip_mode = 0;
int64_t flip_arg = 0;
};
CommandProcessor() = default;
virtual ~CommandProcessor() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(CommandProcessor);
void Reset();
void BufferInit();
void BufferFlush();
void BufferWait();
HardwareContext* GetCtx();
UserConfig* GetUcfg();
void SetIndexType(uint32_t index_type_and_size);
void DrawIndex(uint32_t index_count, const void* index_addr, uint32_t flags, uint32_t type);
void DrawIndexAuto(uint32_t index_count, uint32_t flags);
void WriteAtEndOfPipe32(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, void* dst_gpu_addr, uint32_t value);
void WriteAtEndOfPipe64(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, void* dst_gpu_addr, uint64_t value);
void FlipWithInterrupt(uint32_t eop_event_type, uint32_t cache_action, void* dst_gpu_addr, uint32_t value);
void WriteBack();
void MemoryBarrier();
void DispatchDirect(uint32_t thread_group_x, uint32_t thread_group_y, uint32_t thread_group_z, uint32_t mode);
void WaitFlipDone(uint32_t video_out_handle, uint32_t display_buffer_index);
void TriggerEvent(uint32_t event_type, uint32_t event_index);
void SetUserDataMarker(UserSgprType type) { m_user_data_marker = type; }
[[nodiscard]] UserSgprType GetUserDataMarker() const { return m_user_data_marker; }
void SetEmbeddedDataMarker(const uint32_t* buffer, uint32_t num_dw, uint32_t align) {}
void PushMarker(const char* str) {}
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);
void IncremenetDe();
void IncremenetCe();
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 WaitRegMem(uint32_t func, bool me, bool mem, const uint32_t* addr, uint32_t ref, uint32_t mask, uint32_t poll);
void WriteData(uint32_t* dst, const uint32_t* src, uint32_t dw_num, uint32_t write_control);
void Run(uint32_t* data, uint32_t num_dw);
void SetQueue(int queue) { m_queue = queue; }
[[nodiscard]] const FlipInfo& GetFlip() const { return m_flip; }
void SetFlip(const FlipInfo& flip) { m_flip = flip; }
private:
static constexpr int VK_BUFFERS_NUM = 4;
struct Counter
{
Core::Mutex mutex;
Core::CondVar cond_var;
uint32_t value = 0;
};
HardwareContext m_ctx;
UserConfig m_ucfg;
uint32_t m_index_type_and_size = 0;
UserSgprType m_user_data_marker = UserSgprType::Unknown;
Core::Mutex m_mutex;
CommandBuffer* m_buffer[VK_BUFFERS_NUM] = {};
int m_current_buffer = -1;
int m_queue = -1;
Counter m_de_counter;
Counter m_ce_counter;
uint32_t m_const_ram[0x3000] = {0};
FlipInfo m_flip;
};
class GraphicsRing
{
public:
GraphicsRing(): m_job1("Thread_Gfx_Draw"), m_job2("Thread_Gfx_Const") { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
virtual ~GraphicsRing() { KYTY_NOT_IMPLEMENTED; }
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);
void Done();
void WaitForIdle();
void SetCp(CommandProcessor* cp)
{
m_cp = cp;
Start();
}
private:
void Start()
{
Core::Thread t(ThreadBatchRun, this);
t.Detach();
}
struct CmdBuffer
{
uint32_t* data = nullptr;
uint32_t num_dw = 0;
};
struct CmdBatch
{
CmdBuffer draw_buffer;
CmdBuffer const_buffer;
CommandProcessor::FlipInfo flip;
};
static void ThreadBatchRun(void* data);
CmdBatch GetCmdBatch();
Core::Mutex m_mutex;
Core::CondVar m_cond_var;
Core::CondVar m_idle_cond_var;
Core::List<CmdBatch> m_cmd_batches;
bool m_done = true;
bool m_idle = true;
AsyncJob m_job1;
AsyncJob m_job2;
CommandProcessor* m_cp = nullptr;
};
class ComputeRing
{
public:
ComputeRing() = default;
virtual ~ComputeRing() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(ComputeRing);
void MapComputeQueue(uint32_t* ring_addr, uint32_t ring_size_dw, uint32_t* read_ptr_addr);
void DingDong(uint32_t offset_dw);
void Done();
void WaitForIdle();
void SetCp(CommandProcessor* cp)
{
m_cp = cp;
Start();
}
[[nodiscard]] int GetQueueId() const { return m_queue_id; }
void SetQueueId(int id) { m_queue_id = id; }
void SetActive(bool flag);
bool IsActive();
private:
void Start()
{
Core::Thread t(ThreadRun, this);
t.Detach();
}
static void ThreadRun(void* data);
Core::Mutex m_mutex;
Core::CondVar m_cond_var;
Core::CondVar m_idle_cond_var;
bool m_done = true;
bool m_idle = true;
CommandProcessor* m_cp = nullptr;
int m_queue_id = -1;
bool m_active = false;
uint32_t* m_ring_addr = nullptr;
uint32_t* m_read_ptr_addr = nullptr;
uint32_t* m_internal_buffer = nullptr;
uint32_t m_ring_size_dw = 0;
uint32_t m_run_offset_dw = 0;
};
class Gpu
{
public:
Gpu()
{
EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread());
Init();
}
virtual ~Gpu() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(Gpu);
void Submit(uint32_t* cmd_draw_buffer, uint32_t num_draw_dw, uint32_t* cmd_const_buffer, uint32_t num_const_dw);
void SubmitAndFlip(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);
uint32_t MapComputeQueue(uint32_t pipe_id, uint32_t queue_id, uint32_t* ring_addr, uint32_t ring_size_dw, uint32_t* read_ptr_addr);
void Unmap(uint32_t ring_id);
void DingDong(uint32_t ring_id, uint32_t offset_dw);
void Done();
void Wait();
int GetFrameNum();
private:
void Init();
ComputeRing* GetRing(uint32_t ring_id);
Core::Mutex m_mutex;
CommandProcessor* m_gfx_cp = nullptr;
GraphicsRing* m_gfx_ring = nullptr;
CommandProcessor* m_compute_cp[8] = {};
ComputeRing* m_compute_ring[64] = {};
int m_done_num = 0;
};
using hw_ctx_parser_func_t = uint32_t (*)(KYTY_HW_CTX_PARSER_ARGS);
using cp_op_ctx_parser_func_t = uint32_t (*)(KYTY_CP_OP_PARSER_ARGS);
static hw_ctx_parser_func_t g_hw_ctx_func[1024] = {};
// static uint32_t g_hw_ctx_param[1024] = {};
static cp_op_ctx_parser_func_t g_cp_op_func[256] = {};
static Gpu* g_gpu = nullptr;
KYTY_HW_CTX_PARSER(hw_ctx_hardware_screen_offset);
KYTY_HW_CTX_PARSER(hw_ctx_set_clip_control);
KYTY_HW_CTX_PARSER(hw_ctx_set_cs_shader);
KYTY_HW_CTX_PARSER(hw_ctx_set_depth_render_target);
KYTY_HW_CTX_PARSER(hw_ctx_set_guard_bands);
KYTY_HW_CTX_PARSER(hw_ctx_set_ps_input);
KYTY_HW_CTX_PARSER(hw_ctx_set_ps_shader);
KYTY_HW_CTX_PARSER(hw_ctx_set_render_target_mask);
KYTY_HW_CTX_PARSER(hw_ctx_set_render_target);
KYTY_HW_CTX_PARSER(hw_ctx_set_screen_scissor);
KYTY_HW_CTX_PARSER(hw_ctx_set_stencil_info);
KYTY_HW_CTX_PARSER(hw_ctx_set_viewport_scale_offset);
KYTY_HW_CTX_PARSER(hw_ctx_set_viewport_transform_control);
KYTY_HW_CTX_PARSER(hw_ctx_set_viewport_z);
KYTY_HW_CTX_PARSER(hw_ctx_set_vs_shader);
KYTY_HW_CTX_PARSER(hw_ctx_set_render_control);
KYTY_HW_CTX_PARSER(hw_ctx_set_depth_control);
KYTY_HW_CTX_PARSER(hw_ctx_set_depth_clear);
KYTY_HW_CTX_PARSER(hw_ctx_set_vs_embedded);
KYTY_HW_CTX_PARSER(hw_ctx_set_mode_control);
KYTY_HW_CTX_PARSER(hw_ctx_set_blend_control);
KYTY_HW_CTX_PARSER(hw_ctx_set_shader_stages);
KYTY_CP_OP_PARSER(cp_op_acquire_mem);
KYTY_CP_OP_PARSER(cp_op_dispatch_direct);
KYTY_CP_OP_PARSER(cp_op_dispatch_reset);
KYTY_CP_OP_PARSER(cp_op_dma_data);
KYTY_CP_OP_PARSER(cp_op_draw_index_auto);
KYTY_CP_OP_PARSER(cp_op_draw_index);
KYTY_CP_OP_PARSER(cp_op_draw_reset);
KYTY_CP_OP_PARSER(cp_op_dump_const_ram);
KYTY_CP_OP_PARSER(cp_op_event_write);
KYTY_CP_OP_PARSER(cp_op_event_write_eop);
KYTY_CP_OP_PARSER(cp_op_event_write_eos);
KYTY_CP_OP_PARSER(cp_op_increment_ce_counter);
KYTY_CP_OP_PARSER(cp_op_increment_de_counter);
KYTY_CP_OP_PARSER(cp_op_index_type);
KYTY_CP_OP_PARSER(cp_op_marker);
KYTY_CP_OP_PARSER(cp_op_nop);
KYTY_CP_OP_PARSER(cp_op_release_mem);
KYTY_CP_OP_PARSER(cp_op_set_context_reg);
KYTY_CP_OP_PARSER(cp_op_set_shader_reg);
KYTY_CP_OP_PARSER(cp_op_set_uconfig_reg);
KYTY_CP_OP_PARSER(cp_op_wait_flip_done);
KYTY_CP_OP_PARSER(cp_op_wait_on_address);
KYTY_CP_OP_PARSER(cp_op_wait_on_ce_counter);
KYTY_CP_OP_PARSER(cp_op_wait_on_de_counter_diff);
KYTY_CP_OP_PARSER(cp_op_wait_reg_mem);
KYTY_CP_OP_PARSER(cp_op_write_const_ram);
KYTY_CP_OP_PARSER(cp_op_write_data);
KYTY_CP_OP_PARSER(cp_op_push_marker);
KYTY_CP_OP_PARSER(cp_op_pop_marker);
static void graphics_init_jmp_tables()
{
for (auto& func: g_hw_ctx_func)
{
func = nullptr;
}
g_hw_ctx_func[Pm4::DB_RENDER_CONTROL] = hw_ctx_set_render_control;
g_hw_ctx_func[Pm4::DB_DEPTH_CLEAR] = hw_ctx_set_depth_clear;
g_hw_ctx_func[0x00c] = hw_ctx_set_screen_scissor;
g_hw_ctx_func[Pm4::DB_Z_INFO] = hw_ctx_set_depth_render_target;
g_hw_ctx_func[Pm4::DB_STENCIL_INFO] = hw_ctx_set_stencil_info;
g_hw_ctx_func[0x08d] = hw_ctx_hardware_screen_offset;
g_hw_ctx_func[0x08e] = hw_ctx_set_render_target_mask;
g_hw_ctx_func[Pm4::SPI_PS_INPUT_CNTL_0] = hw_ctx_set_ps_input;
g_hw_ctx_func[Pm4::DB_DEPTH_CONTROL] = hw_ctx_set_depth_control;
g_hw_ctx_func[0x204] = hw_ctx_set_clip_control;
g_hw_ctx_func[Pm4::PA_SU_SC_MODE_CNTL] = hw_ctx_set_mode_control;
g_hw_ctx_func[0x206] = hw_ctx_set_viewport_transform_control;
g_hw_ctx_func[Pm4::VGT_SHADER_STAGES_EN] = hw_ctx_set_shader_stages;
g_hw_ctx_func[0x2fa] = hw_ctx_set_guard_bands;
for (uint32_t slot = 0; slot < 8; slot++)
{
g_hw_ctx_func[Pm4::CB_COLOR0_BASE + slot * 15] = hw_ctx_set_render_target;
g_hw_ctx_func[Pm4::CB_BLEND0_CONTROL + slot * 1] = hw_ctx_set_blend_control;
}
for (uint32_t viewport = 0; viewport < 16; viewport++)
{
g_hw_ctx_func[Pm4::PA_SC_VPORT_ZMIN_0 + viewport * 2] = hw_ctx_set_viewport_z;
g_hw_ctx_func[Pm4::PA_CL_VPORT_XSCALE + viewport * 6] = hw_ctx_set_viewport_scale_offset;
}
for (auto& func: g_cp_op_func)
{
func = nullptr;
}
g_cp_op_func[Pm4::IT_NOP] = cp_op_nop;
g_cp_op_func[Pm4::IT_INDEX_TYPE] = cp_op_index_type;
g_cp_op_func[Pm4::IT_WAIT_REG_MEM] = cp_op_wait_reg_mem;
g_cp_op_func[Pm4::IT_WRITE_DATA] = cp_op_write_data;
g_cp_op_func[Pm4::IT_EVENT_WRITE] = cp_op_event_write;
g_cp_op_func[Pm4::IT_EVENT_WRITE_EOP] = cp_op_event_write_eop;
g_cp_op_func[Pm4::IT_EVENT_WRITE_EOS] = cp_op_event_write_eos;
g_cp_op_func[Pm4::IT_RELEASE_MEM] = cp_op_release_mem;
g_cp_op_func[Pm4::IT_DMA_DATA] = cp_op_dma_data;
g_cp_op_func[Pm4::IT_ACQUIRE_MEM] = cp_op_acquire_mem;
g_cp_op_func[Pm4::IT_SET_CONTEXT_REG] = cp_op_set_context_reg;
g_cp_op_func[Pm4::IT_SET_SH_REG] = cp_op_set_shader_reg;
g_cp_op_func[Pm4::IT_SET_UCONFIG_REG] = cp_op_set_uconfig_reg;
g_cp_op_func[Pm4::IT_WRITE_CONST_RAM] = cp_op_write_const_ram;
g_cp_op_func[Pm4::IT_DUMP_CONST_RAM] = cp_op_dump_const_ram;
g_cp_op_func[Pm4::IT_INCREMENT_CE_COUNTER] = cp_op_increment_ce_counter;
g_cp_op_func[Pm4::IT_INCREMENT_DE_COUNTER] = cp_op_increment_de_counter;
g_cp_op_func[Pm4::IT_WAIT_ON_CE_COUNTER] = cp_op_wait_on_ce_counter;
g_cp_op_func[Pm4::IT_WAIT_ON_DE_COUNTER_DIFF] = cp_op_wait_on_de_counter_diff;
}
void GraphicsRunInit()
{
EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread());
EXIT_IF(g_gpu != nullptr);
graphics_init_jmp_tables();
g_gpu = new Gpu;
}
void Gpu::Submit(uint32_t* cmd_draw_buffer, uint32_t num_draw_dw, uint32_t* cmd_const_buffer, uint32_t num_const_dw)
{
Core::LockGuard lock(m_mutex);
m_gfx_ring->Submit(cmd_draw_buffer, num_draw_dw, cmd_const_buffer, num_const_dw, 0, 0, 0, 0);
}
void Gpu::SubmitAndFlip(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)
{
m_gfx_ring->Submit(cmd_draw_buffer, num_draw_dw, cmd_const_buffer, num_const_dw, handle, index, flip_mode, flip_arg);
}
uint32_t Gpu::MapComputeQueue(uint32_t pipe_id, uint32_t queue_id, uint32_t* ring_addr, uint32_t ring_size_dw, uint32_t* read_ptr_addr)
{
EXIT_NOT_IMPLEMENTED(ring_addr == nullptr);
EXIT_NOT_IMPLEMENTED(ring_size_dw == 0);
EXIT_NOT_IMPLEMENTED(read_ptr_addr == nullptr);
Core::LockGuard lock(m_mutex);
auto v = pipe_id * 8 + queue_id;
EXIT_NOT_IMPLEMENTED(v >= 64);
for (int i = 0; i < 8; i++)
{
EXIT_NOT_IMPLEMENTED(m_compute_ring[pipe_id * 8 + i] != nullptr && m_compute_ring[pipe_id * 8 + i]->IsActive());
}
auto* ring = GetRing(v + 1);
ring->MapComputeQueue(ring_addr, ring_size_dw, read_ptr_addr);
EXIT_IF(!ring->IsActive());
*read_ptr_addr = 0;
return v + 1;
}
void Gpu::Unmap(uint32_t ring_id)
{
Core::LockGuard lock(m_mutex);
EXIT_NOT_IMPLEMENTED(ring_id < 1 || ring_id > 64);
auto* ring = GetRing(ring_id);
EXIT_NOT_IMPLEMENTED(!ring->IsActive());
ring->SetActive(false);
}
void Gpu::DingDong(uint32_t ring_id, uint32_t offset_dw)
{
Core::LockGuard lock(m_mutex);
EXIT_NOT_IMPLEMENTED(ring_id < 1 || ring_id > 64);
EXIT_NOT_IMPLEMENTED(offset_dw == 0);
auto* ring = GetRing(ring_id);
EXIT_NOT_IMPLEMENTED(!ring->IsActive());
ring->DingDong(offset_dw);
}
void Gpu::Done()
{
Core::LockGuard lock(m_mutex);
m_gfx_ring->Done();
for (auto& cr: m_compute_ring)
{
if (cr != nullptr)
{
cr->Done();
}
}
m_done_num++;
}
int Gpu::GetFrameNum()
{
Core::LockGuard lock(m_mutex);
return m_done_num;
}
void Gpu::Wait()
{
Core::LockGuard lock(m_mutex);
m_gfx_ring->WaitForIdle();
m_gfx_cp->BufferWait();
for (auto& cr: m_compute_ring)
{
if (cr != nullptr)
{
cr->WaitForIdle();
}
}
for (auto& cp: m_compute_cp)
{
if (cp != nullptr)
{
cp->BufferWait();
}
}
}
void Gpu::Init()
{
EXIT_IF(m_gfx_cp != nullptr);
EXIT_IF(m_gfx_ring != nullptr);
m_gfx_cp = new CommandProcessor;
m_gfx_ring = new GraphicsRing;
m_gfx_cp->SetQueue(GraphicContext::QUEUE_GFX);
m_gfx_ring->SetCp(m_gfx_cp);
EXIT_IF(GraphicContext::QUEUE_COMPUTE_NUM < 8);
}
ComputeRing* Gpu::GetRing(uint32_t ring_id)
{
int v = static_cast<int>(ring_id - 1);
int pipe_id = v / 8;
int queue_id = v % 8;
if (m_compute_cp[pipe_id] == nullptr)
{
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)
{
m_compute_ring[v] = new ComputeRing;
m_compute_ring[v]->SetQueueId(queue_id);
m_compute_ring[v]->SetActive(false);
m_compute_ring[v]->SetCp(m_compute_cp[pipe_id]);
}
return m_compute_ring[v];
}
void CommandProcessor::Reset()
{
Core::LockGuard lock(m_mutex);
BufferWait();
m_ucfg.Reset();
m_ctx.Reset();
m_index_type_and_size = 0;
m_user_data_marker = UserSgprType::Unknown;
std::memset(m_const_ram, 0, sizeof(m_const_ram));
}
void CommandProcessor::BufferInit()
{
Core::LockGuard lock(m_mutex);
if (m_current_buffer < 0)
{
for (auto& buf: m_buffer)
{
EXIT_IF(buf != nullptr);
buf = new CommandBuffer;
buf->SetQueue(m_queue);
}
m_current_buffer = 0;
m_buffer[m_current_buffer]->Begin();
}
}
void CommandProcessor::BufferFlush()
{
Core::LockGuard lock(m_mutex);
EXIT_IF(m_current_buffer < 0 || m_current_buffer >= VK_BUFFERS_NUM);
EXIT_IF(m_buffer[m_current_buffer] == nullptr);
m_buffer[m_current_buffer]->End();
m_buffer[m_current_buffer]->Execute();
m_current_buffer = (m_current_buffer + 1) % VK_BUFFERS_NUM;
EXIT_IF(m_buffer[m_current_buffer] == nullptr);
m_buffer[m_current_buffer]->WaitForFenceAndReset();
m_buffer[m_current_buffer]->Begin();
}
void CommandProcessor::BufferWait()
{
BufferInit();
Core::LockGuard lock(m_mutex);
for (auto& buf: m_buffer)
{
EXIT_IF(buf == nullptr);
buf->WaitForFenceAndReset();
}
}
void CommandProcessor::ResetDeCe()
{
m_de_counter.mutex.Lock();
m_de_counter.value = 0;
m_de_counter.cond_var.Signal();
m_de_counter.mutex.Unlock();
m_ce_counter.mutex.Lock();
m_ce_counter.value = 0;
m_ce_counter.cond_var.Signal();
m_ce_counter.mutex.Unlock();
}
void CommandProcessor::WaitCe()
{
m_de_counter.mutex.Lock();
auto de_value = m_de_counter.value;
m_de_counter.mutex.Unlock();
m_ce_counter.mutex.Lock();
while (!(m_ce_counter.value > de_value))
{
m_ce_counter.cond_var.Wait(&m_ce_counter.mutex);
}
m_ce_counter.mutex.Unlock();
}
void CommandProcessor::WaitDeDiff(uint32_t diff)
{
m_ce_counter.mutex.Lock();
auto ce_value = m_ce_counter.value;
m_ce_counter.mutex.Unlock();
m_de_counter.mutex.Lock();
while (!(ce_value - m_de_counter.value < diff))
{
m_de_counter.cond_var.Wait(&m_de_counter.mutex);
}
m_de_counter.mutex.Unlock();
}
void CommandProcessor::IncremenetDe()
{
BufferFlush();
BufferWait();
m_de_counter.mutex.Lock();
m_de_counter.value++;
m_de_counter.cond_var.Signal();
m_de_counter.mutex.Unlock();
}
void CommandProcessor::IncremenetCe()
{
m_ce_counter.mutex.Lock();
m_ce_counter.value++;
m_ce_counter.cond_var.Signal();
m_ce_counter.mutex.Unlock();
}
void CommandProcessor::WriteConstRam(uint32_t offset, const uint32_t* src, uint32_t dw_num)
{
Core::LockGuard lock(m_mutex);
memcpy(m_const_ram + offset / 4, src, dw_num * 4);
}
void CommandProcessor::DumpConstRam(uint32_t* dst, uint32_t offset, uint32_t dw_num)
{
Core::LockGuard lock(m_mutex);
memcpy(dst, m_const_ram + offset / 4, dw_num * 4);
}
void CommandProcessor::WaitRegMem(uint32_t func, bool me, bool mem, const uint32_t* addr, uint32_t ref, uint32_t mask, uint32_t poll)
{
EXIT_NOT_IMPLEMENTED(func != 3);
EXIT_NOT_IMPLEMENTED(!me);
EXIT_NOT_IMPLEMENTED(!mem);
EXIT_NOT_IMPLEMENTED(poll != 10);
BufferFlush();
while (((*addr) & mask) != ref)
{
Core::Thread::SleepMicro(10);
}
}
void CommandProcessor::WriteData(uint32_t* dst, const uint32_t* src, uint32_t dw_num, uint32_t write_control)
{
Core::LockGuard lock(m_mutex);
EXIT_NOT_IMPLEMENTED(write_control != 0x04100500);
memcpy(dst, src, dw_num * 4);
}
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)
{
EXIT_IF(m_cp == nullptr);
Core::LockGuard lock(m_mutex);
WindowWaitForGraphicInitialized();
GraphicsRenderCreateContext();
if (m_done)
{
while (!m_idle)
{
m_idle_cond_var.Wait(&m_mutex);
}
m_done = false;
m_cp->Reset();
}
CmdBatch buf {};
buf.draw_buffer.data = cmd_draw_buffer;
buf.draw_buffer.num_dw = num_draw_dw;
buf.const_buffer.data = cmd_const_buffer;
buf.const_buffer.num_dw = num_const_dw;
buf.flip.handle = handle;
buf.flip.index = index;
buf.flip.flip_mode = flip_mode;
buf.flip.flip_arg = flip_arg;
m_cmd_batches.Add(buf);
m_cond_var.Signal();
}
void GraphicsRing::Done()
{
Core::LockGuard lock(m_mutex);
if (m_done)
{
while (!m_idle)
{
m_idle_cond_var.Wait(&m_mutex);
}
}
m_done = true;
}
void GraphicsRing::WaitForIdle()
{
Core::LockGuard lock(m_mutex);
while (!m_idle)
{
m_idle_cond_var.Wait(&m_mutex);
}
}
GraphicsRing::CmdBatch GraphicsRing::GetCmdBatch()
{
Core::LockGuard lock(m_mutex);
while (m_cmd_batches.Size() == 0)
{
m_idle = true;
m_idle_cond_var.Signal();
m_cond_var.Wait(&m_mutex);
}
m_idle = false;
auto first = m_cmd_batches.First();
CmdBatch buf = m_cmd_batches.At(first);
m_cmd_batches.Remove(first);
return buf;
}
void GraphicsRing::ThreadBatchRun(void* data)
{
EXIT_IF(data == nullptr);
auto* ring = static_cast<GraphicsRing*>(data);
auto* cp = ring->m_cp;
EXIT_IF(ring == nullptr);
EXIT_IF(cp == nullptr);
for (;;)
{
CmdBatch buf = ring->GetCmdBatch();
cp->BufferInit();
cp->ResetDeCe();
cp->SetFlip(buf.flip);
ring->m_job1.Execute([cp, buf](void* /*unused*/) { cp->Run(buf.draw_buffer.data, buf.draw_buffer.num_dw); });
ring->m_job2.Execute([cp, buf](void* /*unused*/) { cp->Run(buf.const_buffer.data, buf.const_buffer.num_dw); });
ring->m_job1.Wait();
ring->m_job2.Wait();
cp->BufferFlush();
}
}
void ComputeRing::ThreadRun(void* data)
{
EXIT_IF(data == nullptr);
auto* ring = static_cast<ComputeRing*>(data);
auto* cp = ring->m_cp;
EXIT_IF(ring == nullptr);
EXIT_IF(cp == nullptr);
uint32_t* buffer = nullptr;
KYTY_PROFILER_THREAD("Thread_Compute");
ring->m_mutex.Lock();
for (;;)
{
while (!(ring->m_active && ring->m_run_offset_dw > 0))
{
ring->m_idle = true;
ring->m_idle_cond_var.Signal();
ring->m_cond_var.Wait(&ring->m_mutex);
}
ring->m_idle = false;
auto pos = *ring->m_read_ptr_addr;
auto num_dw = ring->m_run_offset_dw;
auto next_pos = pos + num_dw;
auto ring_size = ring->m_ring_size_dw;
auto* ring_addr = ring->m_ring_addr;
if (next_pos <= ring_size)
{
buffer = ring_addr + pos;
} else
{
auto d1 = next_pos - ring_size;
auto d2 = num_dw - d1;
buffer = ring->m_internal_buffer;
memcpy(buffer, ring_addr + pos, d2);
memcpy(buffer + d2, ring_addr, d1);
}
ring->m_mutex.Unlock();
cp->BufferInit();
cp->ResetDeCe();
cp->Run(buffer, num_dw);
cp->BufferFlush();
ring->m_mutex.Lock();
ring->m_run_offset_dw -= num_dw;
*ring->m_read_ptr_addr = next_pos % ring_size;
}
}
void ComputeRing::MapComputeQueue(uint32_t* ring_addr, uint32_t ring_size_dw, uint32_t* read_ptr_addr)
{
Core::LockGuard lock(m_mutex);
EXIT_IF(m_active);
if (m_ring_size_dw != ring_size_dw)
{
delete[] m_internal_buffer;
m_internal_buffer = new uint32_t[ring_size_dw];
}
m_ring_addr = ring_addr;
m_ring_size_dw = ring_size_dw;
m_read_ptr_addr = read_ptr_addr;
m_run_offset_dw = 0;
*m_read_ptr_addr = 0;
m_active = true;
m_cond_var.Signal();
}
void ComputeRing::DingDong(uint32_t offset_dw)
{
Core::LockGuard lock(m_mutex);
EXIT_IF(!m_active);
if (m_done)
{
while (!m_idle)
{
m_idle_cond_var.Wait(&m_mutex);
}
m_done = false;
/*m_cp->Reset();*/
}
m_run_offset_dw += offset_dw;
m_cond_var.Signal();
}
void ComputeRing::Done()
{
Core::LockGuard lock(m_mutex);
if (m_done)
{
while (!m_idle)
{
m_idle_cond_var.Wait(&m_mutex);
}
}
m_done = true;
}
void ComputeRing::WaitForIdle()
{
Core::LockGuard lock(m_mutex);
while (!m_idle)
{
m_idle_cond_var.Wait(&m_mutex);
}
}
void ComputeRing::SetActive(bool flag)
{
Core::LockGuard lock(m_mutex);
m_active = flag;
m_cond_var.Signal();
}
bool ComputeRing::IsActive()
{
Core::LockGuard lock(m_mutex);
return m_active;
}
void CommandProcessor::Run(uint32_t* data, uint32_t num_dw)
{
KYTY_PROFILER_BLOCK("CommandProcessor::Run");
auto* cmd = data;
auto dw = num_dw;
for (;;)
{
if (dw == 0)
{
break;
}
EXIT_NOT_IMPLEMENTED(dw < 2);
EXIT_NOT_IMPLEMENTED(dw > num_dw);
auto cmd_id = *cmd++;
auto op = (cmd_id >> 8u) & 0xffu;
auto pfunc = g_cp_op_func[op];
if (pfunc == nullptr)
{
EXIT("unknown op\n\t%05" PRIx32 ":\n\tcmd_id = %08" PRIx32 "\n", num_dw - dw, cmd_id);
}
auto s = pfunc(this, cmd_id, cmd, dw, num_dw);
cmd += s;
dw -= s + 1;
}
}
HardwareContext* CommandProcessor::GetCtx()
{
Core::LockGuard lock(m_mutex);
// EXIT_NOT_IMPLEMENTED(m_drawing);
return &m_ctx;
}
UserConfig* CommandProcessor::GetUcfg()
{
Core::LockGuard lock(m_mutex);
return &m_ucfg;
}
void CommandProcessor::SetIndexType(uint32_t index_type_and_size)
{
Core::LockGuard lock(m_mutex);
m_index_type_and_size = index_type_and_size;
}
void CommandProcessor::DrawIndex(uint32_t index_count, const void* index_addr, uint32_t flags, uint32_t type)
{
Core::LockGuard lock(m_mutex);
EXIT_IF(m_current_buffer < 0 || m_current_buffer >= VK_BUFFERS_NUM);
GraphicsRenderDrawIndex(m_buffer[m_current_buffer], &m_ctx, &m_ucfg, m_index_type_and_size, index_count, index_addr, flags, type);
}
void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_group_y, uint32_t thread_group_z, uint32_t mode)
{
Core::LockGuard lock(m_mutex);
EXIT_IF(m_current_buffer < 0 || m_current_buffer >= VK_BUFFERS_NUM);
GraphicsRenderDispatchDirect(m_buffer[m_current_buffer], &m_ctx, thread_group_x, thread_group_y, thread_group_z, mode);
}
void CommandProcessor::DrawIndexAuto(uint32_t index_count, uint32_t flags)
{
Core::LockGuard lock(m_mutex);
EXIT_IF(m_current_buffer < 0 || m_current_buffer >= VK_BUFFERS_NUM);
GraphicsRenderDrawIndexAuto(m_buffer[m_current_buffer], &m_ctx, &m_ucfg, index_count, flags);
}
void CommandProcessor::ClearGds(uint64_t dw_offset, uint32_t dw_num, uint32_t clear_value)
{
Core::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)
{
Core::LockGuard lock(m_mutex);
GraphicsRenderReadGds(dst, dw_offset, dw_size);
}
void CommandProcessor::WaitFlipDone(uint32_t video_out_handle, uint32_t display_buffer_index)
{
BufferFlush();
VideoOut::VideoOutWaitFlipDone(static_cast<int>(video_out_handle), static_cast<int>(display_buffer_index));
}
void CommandProcessor::WriteAtEndOfPipe32(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, void* dst_gpu_addr, uint32_t value)
{
Core::LockGuard lock(m_mutex);
EXIT_IF(m_current_buffer < 0 || m_current_buffer >= VK_BUFFERS_NUM);
printf("CommandProcessor::WriteAtEndOfPipe32()\n");
printf("\t cache_policy = 0x%08" PRIx32 "\n", cache_policy);
printf("\t event_write_dest = 0x%08" PRIx32 "\n", event_write_dest);
printf("\t eop_event_type = 0x%08" PRIx32 "\n", eop_event_type);
printf("\t cache_action = 0x%08" PRIx32 "\n", cache_action);
printf("\t event_index = 0x%08" PRIx32 "\n", event_index);
printf("\t event_write_source = 0x%08" PRIx32 "\n", event_write_source);
printf("\t dst_gpu_addr = 0x%016" PRIx64 "\n", reinterpret_cast<uint64_t>(dst_gpu_addr));
printf("\t value = 0x%08" PRIx32 "\n", value);
EXIT_NOT_IMPLEMENTED(cache_policy != 0x00000000);
EXIT_NOT_IMPLEMENTED(event_write_dest != 0x00000000);
// EXIT_NOT_IMPLEMENTED(event_write_source != 0x00000002);
if (event_write_source == 0x00000002 && eop_event_type == 0x0000002f && cache_action == 0x00000000 && event_index == 0x00000006)
{
GraphicsRenderWriteAtEndOfPipe(m_buffer[m_current_buffer], static_cast<uint32_t*>(dst_gpu_addr), value);
} else if (event_write_source == 0x00000001 && eop_event_type == 0x0000002f && cache_action == 0x00000000 && event_index == 0x00000006)
{
GraphicsRenderWriteAtEndOfPipeGds(m_buffer[m_current_buffer], static_cast<uint32_t*>(dst_gpu_addr), value & 0xffffu, value >> 16u);
} else
{
EXIT("unknown event type\n");
}
}
void CommandProcessor::WriteAtEndOfPipe64(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, void* dst_gpu_addr, uint64_t value)
{
Core::LockGuard lock(m_mutex);
EXIT_IF(m_current_buffer < 0 || m_current_buffer >= VK_BUFFERS_NUM);
printf("CommandProcessor::WriteAtEndOfPipe64()\n");
printf("\t cache_policy = 0x%08" PRIx32 "\n", cache_policy);
printf("\t event_write_dest = 0x%08" PRIx32 "\n", event_write_dest);
printf("\t eop_event_type = 0x%08" PRIx32 "\n", eop_event_type);
printf("\t cache_action = 0x%08" PRIx32 "\n", cache_action);
printf("\t event_index = 0x%08" PRIx32 "\n", event_index);
printf("\t event_write_source = 0x%08" PRIx32 "\n", event_write_source);
printf("\t dst_gpu_addr = 0x%016" PRIx64 "\n", reinterpret_cast<uint64_t>(dst_gpu_addr));
printf("\t value = 0x%016" PRIx64 "\n", value);
EXIT_NOT_IMPLEMENTED(cache_policy != 0x00000000);
EXIT_NOT_IMPLEMENTED(event_write_dest != 0x00000000);
// EXIT_NOT_IMPLEMENTED(event_write_source != 0x00000002);
if (eop_event_type == 0x00000004 && cache_action == 0x00000000 && event_index == 0x00000005 && event_write_source == 0x00000002)
{
GraphicsRenderWriteAtEndOfPipe(m_buffer[m_current_buffer], static_cast<uint64_t*>(dst_gpu_addr), value);
} else if (eop_event_type == 0x00000004 && cache_action == 0x00000000 && event_index == 0x00000005 && event_write_source == 0x00000001)
{
GraphicsRenderWriteAtEndOfPipe(m_buffer[m_current_buffer], static_cast<uint32_t*>(dst_gpu_addr), value);
} else if (((eop_event_type == 0x00000004 && event_index == 0x00000005) ||
(eop_event_type == 0x00000028 && event_index == 0x00000005) ||
(eop_event_type == 0x0000002f && event_index == 0x00000006)) &&
cache_action == 0x00000038 && event_write_source == 0x00000002)
{
GraphicsRenderWriteAtEndOfPipeWithWriteBack(m_buffer[m_current_buffer], static_cast<uint64_t*>(dst_gpu_addr), value);
} else if (eop_event_type == 0x00000004 && cache_action == 0x00000000 && event_index == 0x00000005 && event_write_source == 0x00000004)
{
GraphicsRenderWriteAtEndOfPipeClockCounter(m_buffer[m_current_buffer], static_cast<uint64_t*>(dst_gpu_addr));
} else
{
EXIT("unknown event type\n");
}
}
void CommandProcessor::MemoryBarrier()
{
Core::LockGuard lock(m_mutex);
EXIT_IF(m_current_buffer < 0 || m_current_buffer >= VK_BUFFERS_NUM);
GraphicsRenderMemoryBarrier(m_buffer[m_current_buffer]);
}
void CommandProcessor::TriggerEvent(uint32_t event_type, uint32_t event_index)
{
Core::LockGuard lock(m_mutex);
printf("CommandProcessor::TriggerEvent()\n");
printf("\t event_type = 0x%08" PRIx32 "\n", event_type);
printf("\t event_index = 0x%08" PRIx32 "\n", event_index);
if (event_type == 0x00000016 && event_index == 0x00000007)
{
MemoryBarrier();
} else
{
EXIT("unknown event type\n");
}
}
void CommandProcessor::FlipWithInterrupt(uint32_t eop_event_type, uint32_t cache_action, void* dst_gpu_addr, uint32_t value)
{
Core::LockGuard lock(m_mutex);
EXIT_IF(m_current_buffer < 0 || m_current_buffer >= VK_BUFFERS_NUM);
printf("CommandProcessor::FlipWithInterrupt()\n");
printf("\t eop_event_type = 0x%08" PRIx32 "\n", eop_event_type);
printf("\t cache_action = 0x%08" PRIx32 "\n", cache_action);
printf("\t dst_gpu_addr = 0x%016" PRIx64 "\n", reinterpret_cast<uint64_t>(dst_gpu_addr));
printf("\t value = 0x%08" PRIx32 "\n", value);
if (eop_event_type == 0x00000004 && cache_action == 0x00000038)
{
GraphicsRenderWriteAtEndOfPipeWithInterruptWriteBackFlip(m_buffer[m_current_buffer], static_cast<uint32_t*>(dst_gpu_addr), value,
m_flip.handle, m_flip.index, m_flip.flip_mode, m_flip.flip_arg);
} else
{
EXIT("unknown event type\n");
}
}
void CommandProcessor::WriteBack()
{
Core::LockGuard lock(m_mutex);
GraphicsRenderWriteBack();
}
void GraphicsRunSubmit(uint32_t* cmd_draw_buffer, uint32_t num_draw_dw, uint32_t* cmd_const_buffer, uint32_t num_const_dw)
{
EXIT_IF(cmd_draw_buffer == nullptr);
EXIT_IF(num_draw_dw == 0);
EXIT_IF(g_gpu == nullptr);
g_gpu->Submit(cmd_draw_buffer, num_draw_dw, cmd_const_buffer, num_const_dw);
}
void GraphicsRunSubmitAndFlip(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)
{
EXIT_IF(cmd_draw_buffer == nullptr);
EXIT_IF(num_draw_dw == 0);
EXIT_IF(g_gpu == nullptr);
g_gpu->SubmitAndFlip(cmd_draw_buffer, num_draw_dw, cmd_const_buffer, num_const_dw, handle, index, flip_mode, flip_arg);
}
void GraphicsRunDingDong(uint32_t ring_id, uint32_t offset_dw)
{
EXIT_IF(g_gpu == nullptr);
g_gpu->DingDong(ring_id, offset_dw);
}
uint32_t GraphicsRunMapComputeQueue(uint32_t pipe_id, uint32_t queue_id, uint32_t* ring_addr, uint32_t ring_size_dw,
uint32_t* read_ptr_addr)
{
EXIT_IF(g_gpu == nullptr);
return g_gpu->MapComputeQueue(pipe_id, queue_id, ring_addr, ring_size_dw, read_ptr_addr);
}
void GraphicsRunUnmapComputeQueue(uint32_t id)
{
EXIT_IF(g_gpu == nullptr);
g_gpu->Unmap(id);
}
void GraphicsRunWait()
{
EXIT_IF(g_gpu == nullptr);
g_gpu->Wait();
}
void GraphicsRunDone()
{
EXIT_IF(g_gpu == nullptr);
g_gpu->Done();
}
int GraphicsRunGetFrameNum()
{
EXIT_IF(g_gpu == nullptr);
return g_gpu->GetFrameNum();
}
KYTY_CP_OP_PARSER(cp_op_set_context_reg)
{
KYTY_PROFILER_FUNCTION();
auto cmd_offset = buffer[0];
auto pfunc = g_hw_ctx_func[cmd_offset & 0x3ffu];
if (pfunc == nullptr)
{
EXIT("unknown command\n\t%05" PRIx32 ":\n\tcmd_id = %08" PRIx32 "\n\tcmd_offset = %08" PRIx32 "\n", num_dw - dw, cmd_id,
cmd_offset);
}
auto s = pfunc(cp, cmd_id, cmd_offset, buffer + 1, dw);
EXIT_IF(s == 0);
return s + 1;
}
KYTY_CP_OP_PARSER(cp_op_set_uconfig_reg)
{
KYTY_PROFILER_FUNCTION();
auto cmd_offset = buffer[0];
switch (cmd_offset & 0x1fffu)
{
case 0x0242:
cp->GetUcfg()->SetPrimitiveType(buffer[1]);
return 2;
break;
default:
EXIT("unknown command\n\t%05" PRIx32 ":\n\tcmd_id = %08" PRIx32 "\n\tcmd_offset = %08" PRIx32 "\n", num_dw - dw, cmd_id,
cmd_offset);
}
return 1;
}
KYTY_CP_OP_PARSER(cp_op_index_type)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0002A00);
cp->SetIndexType(buffer[0]);
return 1;
}
KYTY_CP_OP_PARSER(cp_op_push_marker)
{
KYTY_PROFILER_FUNCTION();
auto dw_num = (cmd_id >> 16u) & 0x3fffu;
const char* str = reinterpret_cast<const char*>(buffer);
printf("Push marker: %s\n", str);
cp->PushMarker(str);
return dw_num + 1;
}
KYTY_CP_OP_PARSER(cp_op_pop_marker)
{
KYTY_PROFILER_FUNCTION();
auto dw_num = (cmd_id >> 16u) & 0x3fffu;
printf("Pop marker\n");
cp->PopMarker();
return dw_num + 1;
}
KYTY_CP_OP_PARSER(cp_op_nop)
{
KYTY_PROFILER_FUNCTION();
auto r = (cmd_id >> 2u) & 0x3fu;
switch (r)
{
case Pm4::R_ZERO:
if ((buffer[0] & 0xffff0000u) == 0x68750000u)
{
return cp_op_marker(cp, cmd_id, buffer, dw, num_dw);
}
break;
case Pm4::R_VS: return hw_ctx_set_vs_shader(cp, cmd_id, 0, buffer, dw); break;
case Pm4::R_PS: return hw_ctx_set_ps_shader(cp, cmd_id, 0, buffer, dw); break;
case Pm4::R_CS: return hw_ctx_set_cs_shader(cp, cmd_id, 0, buffer, dw); break;
case Pm4::R_DRAW_INDEX: return cp_op_draw_index(cp, cmd_id, buffer, dw, num_dw); break;
case Pm4::R_DRAW_INDEX_AUTO: return cp_op_draw_index_auto(cp, cmd_id, buffer, dw, num_dw); break;
case Pm4::R_DISPATCH_DIRECT: return cp_op_dispatch_direct(cp, cmd_id, buffer, dw, num_dw); break;
case Pm4::R_DISPATCH_RESET: return cp_op_dispatch_reset(cp, cmd_id, buffer, dw, num_dw); break;
case Pm4::R_DISPATCH_WAIT_MEM: return cp_op_wait_on_address(cp, cmd_id, buffer, dw, num_dw); break;
case Pm4::R_DRAW_RESET: return cp_op_draw_reset(cp, cmd_id, buffer, dw, num_dw); break;
case Pm4::R_WAIT_FLIP_DONE: return cp_op_wait_flip_done(cp, cmd_id, buffer, dw, num_dw); break;
case Pm4::R_PUSH_MARKER: return cp_op_push_marker(cp, cmd_id, buffer, dw, num_dw); break;
case Pm4::R_POP_MARKER: return cp_op_pop_marker(cp, cmd_id, buffer, dw, num_dw); break;
case Pm4::R_VS_EMBEDDED: return hw_ctx_set_vs_embedded(cp, cmd_id, 0, buffer, dw); break;
default: break;
}
EXIT("unknown r at %05" PRIx32 ": %u\n", num_dw - dw, r);
return 0;
}
KYTY_CP_OP_PARSER(cp_op_draw_index)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC008100C);
uint32_t index_count = buffer[0];
auto* index_addr = reinterpret_cast<void*>(buffer[1] | (static_cast<uint64_t>(buffer[2]) << 32u));
uint32_t flags = buffer[3];
uint32_t type = buffer[4];
cp->DrawIndex(index_count, index_addr, flags, type);
return 9;
}
KYTY_CP_OP_PARSER(cp_op_draw_index_auto)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0051010);
uint32_t index_count = buffer[0];
uint32_t flags = buffer[1];
cp->DrawIndexAuto(index_count, flags);
return 6;
}
KYTY_CP_OP_PARSER(cp_op_dispatch_direct)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0071020);
uint32_t thread_group_x = buffer[0];
uint32_t thread_group_y = buffer[1];
uint32_t thread_group_z = buffer[2];
uint32_t mode = buffer[3];
cp->DispatchDirect(thread_group_x, thread_group_y, thread_group_z, mode);
return 8;
}
KYTY_CP_OP_PARSER(cp_op_event_write)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xc0004600);
uint32_t event_index = (buffer[0] >> 8u) & 0x7u;
uint32_t event_type = (buffer[0]) & 0x3fu;
cp->TriggerEvent(event_type, event_index);
return 1;
}
KYTY_CP_OP_PARSER(cp_op_event_write_eop)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0044700);
uint32_t cache_policy = (buffer[0] >> 25u) & 0x3u;
uint32_t event_write_dest = ((buffer[0] >> 23u) & 0x10u) | ((buffer[2] >> 16u) & 0x01u);
uint32_t eop_event_type = (buffer[0]) & 0x3fu;
uint32_t cache_action = (buffer[0] >> 12u) & 0x3fu;
uint32_t event_index = (buffer[0] >> 8u) & 0x7u;
uint32_t event_write_source = ((buffer[2] >> 29u) & 0x7u);
auto* dst_gpu_addr = reinterpret_cast<void*>(buffer[1] | (static_cast<uint64_t>(buffer[2] & 0xffffu) << 32u));
uint64_t value = (buffer[3] | (static_cast<uint64_t>(buffer[4]) << 32u));
cp->WriteAtEndOfPipe64(cache_policy, event_write_dest, eop_event_type, cache_action, event_index, event_write_source, dst_gpu_addr,
value);
return 5;
}
KYTY_CP_OP_PARSER(cp_op_event_write_eos)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0034802);
uint32_t cache_policy = (buffer[0] >> 25u) & 0x3u;
uint32_t event_write_dest = 0;
uint32_t eop_event_type = (buffer[0]) & 0x3fu;
uint32_t cache_action = (buffer[0] >> 12u) & 0x3fu;
uint32_t event_index = (buffer[0] >> 8u) & 0x7u;
uint32_t event_write_source = ((buffer[2] >> 29u) & 0x7u);
auto* dst_gpu_addr = reinterpret_cast<void*>(buffer[1] | (static_cast<uint64_t>(buffer[2] & 0xffffu) << 32u));
uint32_t value = buffer[3];
cp->WriteAtEndOfPipe32(cache_policy, event_write_dest, eop_event_type, cache_action, event_index, event_write_source, dst_gpu_addr,
value);
return 4;
}
KYTY_CP_OP_PARSER(cp_op_release_mem)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0054902);
uint32_t cache_policy = (buffer[0] >> 25u) & 0x3u;
uint32_t cache_action = (buffer[0] >> 12u) & 0x3fu;
uint32_t eop_event_type = (buffer[0]) & 0x3fu;
uint32_t event_index = (buffer[0] >> 8u) & 0x7u;
uint32_t event_write_dest = ((buffer[0] >> 23u) & 0x10u) | ((buffer[1] >> 16u) & 0x01u);
uint32_t event_write_source = (buffer[1] >> 29u) & 0x7u;
uint32_t interrupt_selector = (buffer[1] >> 24u) & 0x7u;
auto* dst_gpu_addr = reinterpret_cast<void*>(buffer[2] | (static_cast<uint64_t>(buffer[3]) << 32u));
uint64_t value = (buffer[4] | (static_cast<uint64_t>(buffer[5]) << 32u));
EXIT_NOT_IMPLEMENTED(interrupt_selector != 0x3);
cp->WriteAtEndOfPipe64(cache_policy, event_write_dest, eop_event_type, cache_action, event_index, event_write_source, dst_gpu_addr,
value);
return 6;
}
KYTY_CP_OP_PARSER(cp_op_set_shader_reg)
{
KYTY_PROFILER_FUNCTION();
auto reg_num = (cmd_id >> 16u) & 0x3fffu;
uint32_t reg = buffer[0];
if (reg >= Pm4::SPI_SHADER_USER_DATA_VS_0 && reg <= Pm4::SPI_SHADER_USER_DATA_VS_15)
{
for (uint32_t i = 0; i < reg_num; i++)
{
cp->GetCtx()->SetVsUserSgpr(reg - Pm4::SPI_SHADER_USER_DATA_VS_0 + i, buffer[i + 1], cp->GetUserDataMarker());
}
} else if (reg >= Pm4::SPI_SHADER_USER_DATA_PS_0 && reg <= Pm4::SPI_SHADER_USER_DATA_PS_15)
{
for (uint32_t i = 0; i < reg_num; i++)
{
cp->GetCtx()->SetPsUserSgpr(reg - Pm4::SPI_SHADER_USER_DATA_PS_0 + i, buffer[i + 1], cp->GetUserDataMarker());
}
} else if (reg >= Pm4::COMPUTE_USER_DATA_0 && reg <= Pm4::COMPUTE_USER_DATA_15)
{
for (uint32_t i = 0; i < reg_num; i++)
{
cp->GetCtx()->SetCsUserSgpr(reg - Pm4::COMPUTE_USER_DATA_0 + i, buffer[i + 1], cp->GetUserDataMarker());
}
} else
{
EXIT("unknown reg: %u\n", reg);
}
cp->SetUserDataMarker(UserSgprType::Unknown);
return 1 + reg_num;
}
KYTY_CP_OP_PARSER(cp_op_write_data)
{
KYTY_PROFILER_FUNCTION();
auto dw_num = (cmd_id >> 16u) & 0x3fffu;
auto write_control = buffer[0];
auto* dst = reinterpret_cast<uint32_t*>(buffer[1] | (static_cast<uint64_t>(buffer[2]) << 32u));
cp->WriteData(dst, buffer + 3, dw_num - 2, write_control);
return 1 + dw_num;
}
KYTY_CP_OP_PARSER(cp_op_dispatch_reset)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0001024);
cp->Reset();
return 1;
}
KYTY_CP_OP_PARSER(cp_op_draw_reset)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xc0001014);
cp->Reset();
return 1;
}
KYTY_CP_OP_PARSER(cp_op_wait_flip_done)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xc0051018);
auto video_out_handle = buffer[0];
auto display_buffer_index = buffer[1];
cp->WaitFlipDone(video_out_handle, display_buffer_index);
return 6;
}
KYTY_CP_OP_PARSER(cp_op_write_const_ram)
{
KYTY_PROFILER_FUNCTION();
auto dw_num = (cmd_id >> 16u) & 0x3fffu;
auto offset = buffer[0];
EXIT_NOT_IMPLEMENTED(dw_num >= 0x3000);
EXIT_NOT_IMPLEMENTED(offset > 0xbffc);
EXIT_NOT_IMPLEMENTED((offset & 0x3u) != 0);
cp->WriteConstRam(offset, buffer + 1, dw_num);
return 1 + dw_num;
}
KYTY_CP_OP_PARSER(cp_op_dump_const_ram)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0038300);
auto offset = buffer[0];
auto dw_num = buffer[1];
auto* dst = reinterpret_cast<uint32_t*>(buffer[2] | (static_cast<uint64_t>(buffer[3]) << 32u));
EXIT_NOT_IMPLEMENTED(dw_num >= 0x3000);
EXIT_NOT_IMPLEMENTED(offset > 0xbffc);
EXIT_NOT_IMPLEMENTED((offset & 0x3u) != 0);
cp->DumpConstRam(dst, offset, dw_num);
return 4;
}
KYTY_CP_OP_PARSER(cp_op_wait_reg_mem)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0053C00);
auto func = buffer[0] & 0x7u;
bool me = (buffer[0] & 0x100u) == 0;
bool mem = (buffer[0] & 0x10u) != 0;
auto* addr = reinterpret_cast<uint32_t*>(buffer[1] | (static_cast<uint64_t>(buffer[2]) << 32u));
auto ref = buffer[3];
auto mask = buffer[4];
auto poll = buffer[5];
cp->WaitRegMem(func, me, mem, addr, ref, mask, poll);
return 6;
}
KYTY_CP_OP_PARSER(cp_op_wait_on_address)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC00C1028);
auto* addr = reinterpret_cast<uint32_t*>(buffer[0] | (static_cast<uint64_t>(buffer[1]) << 32u));
bool me = true;
bool mem = true;
auto mask = buffer[2];
auto func = buffer[3];
auto ref = buffer[4];
auto poll = 10;
cp->WaitRegMem(func, me, mem, addr, ref, mask, poll);
return 13;
}
KYTY_CP_OP_PARSER(cp_op_acquire_mem)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0055800);
uint32_t stall_mode = buffer[0] >> 31u;
uint32_t cache_action = buffer[0] & 0x7fffffffu;
uint32_t size_lo = buffer[1];
uint32_t size_hi = buffer[2];
uint32_t base_lo = buffer[3];
uint32_t base_hi = buffer[4];
uint32_t poll = buffer[5];
EXIT_NOT_IMPLEMENTED(stall_mode != 1);
EXIT_NOT_IMPLEMENTED(cache_action != 0x00C40000 && cache_action != 0x00400000);
EXIT_NOT_IMPLEMENTED(size_lo != 1);
EXIT_NOT_IMPLEMENTED(size_hi != 0);
EXIT_NOT_IMPLEMENTED(base_lo != 0);
EXIT_NOT_IMPLEMENTED(base_hi != 0);
EXIT_NOT_IMPLEMENTED(poll != 10);
cp->MemoryBarrier();
if (cache_action == 0x00C40000)
{
cp->WriteBack();
}
return 6;
}
KYTY_CP_OP_PARSER(cp_op_wait_on_de_counter_diff)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0008800);
cp->WaitDeDiff(buffer[0]);
return 1;
}
KYTY_CP_OP_PARSER(cp_op_wait_on_ce_counter)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0008600);
EXIT_NOT_IMPLEMENTED(buffer[0] != 1);
cp->WaitCe();
return 1;
}
KYTY_CP_OP_PARSER(cp_op_increment_de_counter)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0008500);
EXIT_NOT_IMPLEMENTED(buffer[0] != 0);
cp->IncremenetDe();
return 1;
}
KYTY_CP_OP_PARSER(cp_op_increment_ce_counter)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0008400);
EXIT_NOT_IMPLEMENTED(buffer[0] != 1);
cp->IncremenetCe();
return 1;
}
KYTY_CP_OP_PARSER(cp_op_marker)
{
KYTY_PROFILER_FUNCTION();
// EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0001000);
uint32_t id = buffer[0] & 0xfffu;
uint32_t align = (buffer[0] >> 12u) & 0xfu;
uint32_t len_dw = ((cmd_id >> 16u) & 0x3fffu);
switch (id)
{
case 0x0: cp->SetEmbeddedDataMarker(buffer + 1, len_dw, align); break;
case 0x4: cp->SetUserDataMarker(UserSgprType::Vsharp); break;
case 0xd: cp->SetUserDataMarker(UserSgprType::Region); break;
case 0x781:
{
auto* addr = reinterpret_cast<void*>(buffer[1] | (static_cast<uint64_t>(buffer[2]) << 32u));
uint32_t value = buffer[3];
uint32_t eop_event_type = buffer[4];
uint32_t cache_action = buffer[5];
cp->FlipWithInterrupt(eop_event_type, cache_action, addr, value);
break;
}
default: EXIT("unknown marker at %05" PRIx32 ": 0x%" PRIx32 "\n", num_dw - dw, id); break;
}
return len_dw + 1;
}
KYTY_CP_OP_PARSER(cp_op_dma_data)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0055000);
uint32_t type = buffer[0];
uint32_t type2 = (buffer[5] >> 21u);
uint64_t src = (buffer[1] | (static_cast<uint64_t>(buffer[2]) << 32u));
uint64_t dst = (buffer[3] | (static_cast<uint64_t>(buffer[4]) << 32u));
uint32_t size = buffer[5] & 0x1fffffu;
if (type == 0x60000000 && dst == 0x0003022C && type2 == 0x141u)
{
auto* addr = reinterpret_cast<void*>(src);
cp->PrefetchL2(addr, size);
} else if (type == 0xc4104000 && type2 == 0x0u)
{
EXIT_NOT_IMPLEMENTED((dst & 0x3u) != 0);
EXIT_NOT_IMPLEMENTED((size & 0x3u) != 0);
cp->ClearGds(dst / 4, size / 4, static_cast<uint32_t>(src));
} else if (type == 0xa4004000 && type2 == 0x0u)
{
EXIT_NOT_IMPLEMENTED((src & 0x3u) != 0);
EXIT_NOT_IMPLEMENTED((size & 0x3u) != 0);
auto* addr = reinterpret_cast<uint32_t*>(dst);
cp->ReadGds(addr, src / 4, size / 4);
} else
{
EXIT("unknown dma\n");
}
return 6;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_depth_render_target)
{
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::DB_Z_INFO);
uint32_t count = 1;
if (cmd_id == 0xC0016900)
{
DepthRenderTargetZInfo r;
r.expclear_enabled = (buffer[0] & 0x08000000u) != 0;
r.format = (buffer[0] >> Pm4::DB_Z_INFO_FORMAT_SHIFT) & Pm4::DB_Z_INFO_FORMAT_MASK;
r.num_samples = (buffer[0] >> Pm4::DB_Z_INFO_NUM_SAMPLES_SHIFT) & Pm4::DB_Z_INFO_NUM_SAMPLES_MASK;
r.tile_mode_index = (buffer[0] >> Pm4::DB_Z_INFO_TILE_MODE_INDEX_SHIFT) & Pm4::DB_Z_INFO_TILE_MODE_INDEX_MASK;
r.tile_surface_enable = ((buffer[0] >> Pm4::DB_Z_INFO_TILE_SURFACE_ENABLE_SHIFT) & Pm4::DB_Z_INFO_TILE_SURFACE_ENABLE_MASK) != 0;
r.zrange_precision = (buffer[0] >> Pm4::DB_Z_INFO_ZRANGE_PRECISION_SHIFT) & Pm4::DB_Z_INFO_ZRANGE_PRECISION_MASK;
cp->GetCtx()->SetDepthRenderTargetZInfo(r);
} else if (cmd_id == 0xC0086900)
{
if (dw >= 22 && buffer[8] == 0xC0016900 && buffer[9] == Pm4::DB_DEPTH_INFO && buffer[11] == 0xC0016900 &&
buffer[12] == Pm4::DB_DEPTH_VIEW && buffer[14] == 0xC0016900 && buffer[15] == Pm4::DB_HTILE_DATA_BASE &&
buffer[17] == 0xC0016900 && buffer[18] == Pm4::DB_HTILE_SURFACE && buffer[20] == 0xC0001000)
{
count = 22;
DepthRenderTarget z;
z.z_info.expclear_enabled = (buffer[0] & 0x08000000u) != 0;
z.z_info.format = (buffer[0] >> Pm4::DB_Z_INFO_FORMAT_SHIFT) & Pm4::DB_Z_INFO_FORMAT_MASK;
z.z_info.num_samples = (buffer[0] >> Pm4::DB_Z_INFO_NUM_SAMPLES_SHIFT) & Pm4::DB_Z_INFO_NUM_SAMPLES_MASK;
z.z_info.tile_mode_index = (buffer[0] >> Pm4::DB_Z_INFO_TILE_MODE_INDEX_SHIFT) & Pm4::DB_Z_INFO_TILE_MODE_INDEX_MASK;
z.z_info.tile_surface_enable = KYTY_PM4_GET(buffer[0], DB_Z_INFO, TILE_SURFACE_ENABLE) != 0;
z.z_info.zrange_precision = (buffer[0] >> Pm4::DB_Z_INFO_ZRANGE_PRECISION_SHIFT) & Pm4::DB_Z_INFO_ZRANGE_PRECISION_MASK;
z.stencil_info.expclear_enabled = (buffer[1] & 0x08000000u) != 0;
z.stencil_info.tile_split = (buffer[1] >> 13u) & 0x7u;
z.stencil_info.format = KYTY_PM4_GET(buffer[1], DB_STENCIL_INFO, FORMAT);
z.stencil_info.tile_mode_index = KYTY_PM4_GET(buffer[1], DB_STENCIL_INFO, TILE_MODE_INDEX);
z.stencil_info.tile_stencil_disable = KYTY_PM4_GET(buffer[1], DB_STENCIL_INFO, TILE_STENCIL_DISABLE);
if (Config::IsNeo())
{
EXIT_NOT_IMPLEMENTED((buffer[2] & 0xffu) != 0);
EXIT_NOT_IMPLEMENTED((buffer[3] & 0xffu) != 0);
EXIT_NOT_IMPLEMENTED((buffer[4] & 0xffu) != 0);
EXIT_NOT_IMPLEMENTED((buffer[5] & 0xffu) != 0);
}
z.z_read_base_addr = static_cast<uint64_t>(buffer[2]) << 8u;
z.stencil_read_base_addr = static_cast<uint64_t>(buffer[3]) << 8u;
z.z_write_base_addr = static_cast<uint64_t>(buffer[4]) << 8u;
z.stencil_write_base_addr = static_cast<uint64_t>(buffer[5]) << 8u;
z.pitch_div8_minus1 = (buffer[6] >> Pm4::DB_DEPTH_SIZE_PITCH_TILE_MAX_SHIFT) & Pm4::DB_DEPTH_SIZE_PITCH_TILE_MAX_MASK;
z.height_div8_minus1 = (buffer[6] >> Pm4::DB_DEPTH_SIZE_HEIGHT_TILE_MAX_SHIFT) & Pm4::DB_DEPTH_SIZE_HEIGHT_TILE_MAX_MASK;
z.slice_div64_minus1 = (buffer[7] >> Pm4::DB_DEPTH_SLICE_SLICE_TILE_MAX_SHIFT) & Pm4::DB_DEPTH_SLICE_SLICE_TILE_MAX_MASK;
z.depth_info.addr5_swizzle_mask = KYTY_PM4_GET(buffer[10], DB_DEPTH_INFO, ADDR5_SWIZZLE_MASK);
z.depth_info.array_mode = (buffer[10] >> Pm4::DB_DEPTH_INFO_ARRAY_MODE_SHIFT) & Pm4::DB_DEPTH_INFO_ARRAY_MODE_MASK;
z.depth_info.pipe_config = (buffer[10] >> Pm4::DB_DEPTH_INFO_PIPE_CONFIG_SHIFT) & Pm4::DB_DEPTH_INFO_PIPE_CONFIG_MASK;
z.depth_info.bank_width = (buffer[10] >> Pm4::DB_DEPTH_INFO_BANK_WIDTH_SHIFT) & Pm4::DB_DEPTH_INFO_BANK_WIDTH_MASK;
z.depth_info.bank_height = (buffer[10] >> Pm4::DB_DEPTH_INFO_BANK_HEIGHT_SHIFT) & Pm4::DB_DEPTH_INFO_BANK_HEIGHT_MASK;
z.depth_info.macro_tile_aspect = KYTY_PM4_GET(buffer[10], DB_DEPTH_INFO, MACRO_TILE_ASPECT);
z.depth_info.num_banks = (buffer[10] >> Pm4::DB_DEPTH_INFO_NUM_BANKS_SHIFT) & Pm4::DB_DEPTH_INFO_NUM_BANKS_MASK;
z.depth_view.slice_start = (buffer[13] >> Pm4::DB_DEPTH_VIEW_SLICE_START_SHIFT) & Pm4::DB_DEPTH_VIEW_SLICE_START_MASK;
z.depth_view.slice_max = (buffer[13] >> Pm4::DB_DEPTH_VIEW_SLICE_MAX_SHIFT) & Pm4::DB_DEPTH_VIEW_SLICE_MAX_MASK;
z.htile_data_base_addr = static_cast<uint64_t>(buffer[16]) << 8u;
z.htile_surface.linear = (buffer[19] >> Pm4::DB_HTILE_SURFACE_LINEAR_SHIFT) & Pm4::DB_HTILE_SURFACE_LINEAR_MASK;
z.htile_surface.full_cache = (buffer[19] >> Pm4::DB_HTILE_SURFACE_FULL_CACHE_SHIFT) & Pm4::DB_HTILE_SURFACE_FULL_CACHE_MASK;
z.htile_surface.htile_uses_preload_win = KYTY_PM4_GET(buffer[19], DB_HTILE_SURFACE, HTILE_USES_PRELOAD_WIN);
z.htile_surface.preload = (buffer[19] >> Pm4::DB_HTILE_SURFACE_PRELOAD_SHIFT) & Pm4::DB_HTILE_SURFACE_PRELOAD_MASK;
z.htile_surface.prefetch_width = KYTY_PM4_GET(buffer[19], DB_HTILE_SURFACE, PREFETCH_WIDTH);
z.htile_surface.prefetch_height = KYTY_PM4_GET(buffer[19], DB_HTILE_SURFACE, PREFETCH_HEIGHT);
z.htile_surface.dst_outside_zero_to_one = KYTY_PM4_GET(buffer[19], DB_HTILE_SURFACE, DST_OUTSIDE_ZERO_TO_ONE);
z.width = (buffer[21] >> 0u) & 0xffffu;
z.height = (buffer[21] >> 16u) & 0xffffu;
cp->GetCtx()->SetDepthRenderTarget(z);
} else
{
KYTY_NOT_IMPLEMENTED;
}
} else
{
KYTY_NOT_IMPLEMENTED;
}
return count;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_ps_input)
{
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::SPI_PS_INPUT_CNTL_0);
uint32_t count = 0;
if (cmd_id == 0xC0016900)
{
cp->GetCtx()->SetPsInputSettings(0, buffer[0]);
count = 1;
} else if (cmd_id == 0xC0026900)
{
cp->GetCtx()->SetPsInputSettings(0, buffer[0]);
cp->GetCtx()->SetPsInputSettings(1, buffer[1]);
count = 2;
} else if (cmd_id == 0xc0046900)
{
cp->GetCtx()->SetPsInputSettings(0, buffer[0]);
cp->GetCtx()->SetPsInputSettings(1, buffer[1]);
cp->GetCtx()->SetPsInputSettings(2, buffer[2]);
cp->GetCtx()->SetPsInputSettings(3, buffer[3]);
count = 4;
}
EXIT_NOT_IMPLEMENTED(count == 0);
return count;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_stencil_info)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::DB_STENCIL_INFO);
DepthRenderTargetStencilInfo r;
r.expclear_enabled = (buffer[0] & 0x08000000u) != 0;
r.tile_split = (buffer[0] >> 13u) & 0x7u;
r.format = (buffer[0] >> Pm4::DB_STENCIL_INFO_FORMAT_SHIFT) & Pm4::DB_STENCIL_INFO_FORMAT_MASK;
r.tile_mode_index = (buffer[0] >> Pm4::DB_STENCIL_INFO_TILE_MODE_INDEX_SHIFT) & Pm4::DB_STENCIL_INFO_TILE_MODE_INDEX_MASK;
r.tile_stencil_disable =
((buffer[0] >> Pm4::DB_STENCIL_INFO_TILE_STENCIL_DISABLE_SHIFT) & Pm4::DB_STENCIL_INFO_TILE_STENCIL_DISABLE_MASK) != 0;
cp->GetCtx()->SetDepthRenderTargetStencilInfo(r);
return 1;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_render_control)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::DB_RENDER_CONTROL);
RenderControl r;
r.depth_clear_enable = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, DEPTH_CLEAR_ENABLE) != 0;
r.stencil_clear_enable = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, STENCIL_CLEAR_ENABLE) != 0;
r.resummarize_enable = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, RESUMMARIZE_ENABLE) != 0;
r.stencil_compress_disable = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, STENCIL_COMPRESS_DISABLE) != 0;
r.depth_compress_disable = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, DEPTH_COMPRESS_DISABLE) != 0;
r.copy_centroid = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, COPY_CENTROID) != 0;
r.copy_sample = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, COPY_SAMPLE);
cp->GetCtx()->SetRenderControl(r);
return 1;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_mode_control)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::PA_SU_SC_MODE_CNTL);
ModeControl r;
r.cull_front = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, CULL_FRONT) != 0;
r.cull_back = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, CULL_BACK) != 0;
r.face = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, FACE) != 0;
r.poly_mode = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, POLY_MODE);
r.polymode_front_ptype = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, POLYMODE_FRONT_PTYPE);
r.polymode_back_ptype = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, POLYMODE_BACK_PTYPE);
r.poly_offset_front_enable = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, POLY_OFFSET_FRONT_ENABLE) != 0;
r.poly_offset_back_enable = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, POLY_OFFSET_BACK_ENABLE) != 0;
r.vtx_window_offset_enable = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, VTX_WINDOW_OFFSET_ENABLE) != 0;
r.provoking_vtx_last = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, PROVOKING_VTX_LAST) != 0;
r.persp_corr_dis = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, PERSP_CORR_DIS) != 0;
cp->GetCtx()->SetModeControl(r);
return 1;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_depth_control)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::DB_DEPTH_CONTROL);
DepthControl r;
r.stencil_enable = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, STENCIL_ENABLE) != 0;
r.z_enable = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, Z_ENABLE) != 0;
r.z_write_enable = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, Z_WRITE_ENABLE) != 0;
r.depth_bounds_enable = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, DEPTH_BOUNDS_ENABLE) != 0;
r.zfunc = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, ZFUNC);
r.backface_enable = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, BACKFACE_ENABLE) != 0;
r.stencilfunc = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, STENCILFUNC);
r.stencilfunc_bf = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, STENCILFUNC_BF);
cp->GetCtx()->SetDepthControl(r);
return 1;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_depth_clear)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::DB_DEPTH_CLEAR);
cp->GetCtx()->SetDepthClearValue(*reinterpret_cast<const float*>(buffer));
return 1;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_shader_stages)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::VGT_SHADER_STAGES_EN);
cp->GetCtx()->SetShaderStages(buffer[0]);
return 1;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_blend_control)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
uint32_t param = (cmd_offset - Pm4::CB_BLEND0_CONTROL) / 1;
BlendControl r;
r.color_srcblend = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_COLOR_SRCBLEND_SHIFT) & Pm4::CB_BLEND0_CONTROL_COLOR_SRCBLEND_MASK;
r.color_comb_fcn = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_COLOR_COMB_FCN_SHIFT) & Pm4::CB_BLEND0_CONTROL_COLOR_COMB_FCN_MASK;
r.color_destblend = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_COLOR_DESTBLEND_SHIFT) & Pm4::CB_BLEND0_CONTROL_COLOR_DESTBLEND_MASK;
r.alpha_srcblend = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_ALPHA_SRCBLEND_SHIFT) & Pm4::CB_BLEND0_CONTROL_ALPHA_SRCBLEND_MASK;
r.alpha_comb_fcn = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_ALPHA_COMB_FCN_SHIFT) & Pm4::CB_BLEND0_CONTROL_ALPHA_COMB_FCN_MASK;
r.alpha_destblend = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_ALPHA_DESTBLEND_SHIFT) & Pm4::CB_BLEND0_CONTROL_ALPHA_DESTBLEND_MASK;
r.separate_alpha_blend =
((buffer[0] >> Pm4::CB_BLEND0_CONTROL_SEPARATE_ALPHA_BLEND_SHIFT) & Pm4::CB_BLEND0_CONTROL_SEPARATE_ALPHA_BLEND_MASK) != 0;
r.enable = ((buffer[0] >> Pm4::CB_BLEND0_CONTROL_ENABLE_SHIFT) & Pm4::CB_BLEND0_CONTROL_ENABLE_MASK) != 0;
cp->GetCtx()->SetBlendControl(param, r);
return 1;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_render_target)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC00E6900);
uint32_t param = (cmd_offset - Pm4::CB_COLOR0_BASE) / 15;
uint32_t count = 14;
if (dw >= 16 && buffer[14] == 0xC0001000)
{
count = 16;
}
RenderTarget r;
r.base_addr = static_cast<uint64_t>(buffer[0]) << 8u;
r.pitch_div8_minus1 = buffer[1] & 0x7ffu;
r.fmask_pitch_div8_minus1 = (buffer[1] >> 20u) & 0x7ffu;
r.slice_div64_minus1 = buffer[2] & 0x3fffffu;
r.base_array_slice_index = buffer[3] & 0x7ffu;
r.last_array_slice_index = (buffer[3] >> 13u) & 0x7ffu;
r.fmask_compression_enable = (buffer[4] & 0x4000u) != 0;
r.fmask_compression_mode = (buffer[4] >> 26u) & 0x3u;
r.cmask_fast_clear_enable = (buffer[4] & 0x2000u) != 0;
r.dcc_compression_enable = (buffer[4] & 0x10000000u) != 0;
r.neo_mode = (buffer[4] & 0x80000000u) != 0;
r.cmask_tile_mode = (buffer[4] >> 19u) & 0x1u;
r.cmask_tile_mode_neo = (buffer[4] >> 29u) & 0x3u;
r.format = (buffer[4] >> 2u) & 0x1fu;
r.channel_type = (buffer[4] >> 8u) & 0x7u;
r.channel_order = (buffer[4] >> 11u) & 0x3u;
r.force_dest_alpha_to_one = (buffer[5] & 0x20000u) != 0;
r.tile_mode = buffer[5] & 0x1fu;
r.fmask_tile_mode = (buffer[5] >> 5u) & 0x1fu;
r.num_samples = (buffer[5] >> 12u) & 0x7u;
r.num_fragments = (buffer[5] >> 15u) & 0x3u;
r.dcc_max_uncompressed_block_size = (buffer[6] >> 2u) & 0x3u;
r.dcc_max_compressed_block_size = (buffer[6] >> 5u) & 0x3u;
r.dcc_min_compressed_block_size = (buffer[6] >> 4u) & 0x1u;
r.dcc_color_transform = (buffer[6] >> 7u) & 0x3u;
r.dcc_enable_overwrite_combiner = (buffer[6] & 0x1u) != 0;
r.dcc_force_independent_blocks = (buffer[6] & 0x200u) != 0;
r.cmask_addr = static_cast<uint64_t>(buffer[7]) << 8u;
r.cmask_slice_minus1 = buffer[8] & 0x3fffu;
r.fmask_addr = static_cast<uint64_t>(buffer[9]) << 8u;
r.fmask_slice_minus1 = buffer[10] & 0x3fffffu;
r.clear_color_word0 = buffer[11];
r.clear_color_word1 = buffer[12];
r.dcc_addr = static_cast<uint64_t>(buffer[13]) << 8u;
r.width = (count == 16 ? (buffer[15] >> 0u) & 0xffffu : 0);
r.height = (count == 16 ? (buffer[15] >> 16u) & 0xffffu : 0);
cp->GetCtx()->SetRenderTarget(param, r);
return count;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_render_target_mask)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != 0x0000008E);
cp->GetCtx()->SetRenderTargetMask(*buffer);
return 1;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_viewport_z)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0026900);
uint32_t param = (cmd_offset - Pm4::PA_SC_VPORT_ZMIN_0) / 2;
EXIT_NOT_IMPLEMENTED(param != 0);
cp->GetCtx()->SetViewportZ(param, *reinterpret_cast<const float*>(buffer), *reinterpret_cast<const float*>(buffer + 1));
return 2;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_viewport_scale_offset)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0066900);
uint32_t param = (cmd_offset - Pm4::PA_CL_VPORT_XSCALE) / 6;
EXIT_NOT_IMPLEMENTED(param != 0);
cp->GetCtx()->SetViewportScaleOffset(param, *reinterpret_cast<const float*>(buffer), *reinterpret_cast<const float*>(buffer + 1),
*reinterpret_cast<const float*>(buffer + 2), *reinterpret_cast<const float*>(buffer + 3),
*reinterpret_cast<const float*>(buffer + 4), *reinterpret_cast<const float*>(buffer + 5));
return 6;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_viewport_transform_control)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != 0x00000206);
cp->GetCtx()->SetViewportTransformControl(*buffer);
return 1;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_clip_control)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != 0x00000204);
ClipControl r;
r.user_clip_planes = buffer[0] & 0x3fu;
r.user_clip_plane_mode = (buffer[0] >> 14u) & 0x3u;
r.clip_space = (buffer[0] >> 19u) & 0x1u;
r.vertex_kill_mode = (buffer[0] >> 21u) & 0x1u;
r.min_z_clip_enable = (buffer[0] >> 26u) & 0x1u;
r.max_z_clip_enable = (buffer[0] >> 27u) & 0x1u;
r.user_clip_plane_negate_y = (buffer[0] & 0x00002000u) != 0;
r.clip_enable = (buffer[0] & 0x00010000u) != 0;
r.user_clip_plane_cull_only = (buffer[0] & 0x00020000u) != 0;
r.cull_on_clipping_error_disable = (buffer[0] & 0x00100000u) != 0;
r.linear_attribute_clip_enable = (buffer[0] & 0x01000000u) != 0;
r.force_viewport_index_from_vs_enable = (buffer[0] & 0x02000000u) != 0;
cp->GetCtx()->SetClipControl(r);
return 1;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_screen_scissor)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0026900);
EXIT_NOT_IMPLEMENTED(cmd_offset != 0x0000000C);
int left = static_cast<int16_t>(static_cast<uint16_t>(buffer[0] & 0xffffu));
int top = static_cast<int16_t>(static_cast<uint16_t>((buffer[0] >> 16u) & 0xffffu));
int right = static_cast<int16_t>(static_cast<uint16_t>(buffer[1] & 0xffffu));
int bottom = static_cast<int16_t>(static_cast<uint16_t>((buffer[1] >> 16u) & 0xffffu));
cp->GetCtx()->SetScreenScissor(left, top, right, bottom);
return 2;
}
KYTY_HW_CTX_PARSER(hw_ctx_hardware_screen_offset)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != 0x0000008D);
uint32_t x = static_cast<uint16_t>(buffer[0] & 0xffffu);
uint32_t y = static_cast<uint16_t>((buffer[0] >> 16u) & 0xffffu);
cp->GetCtx()->SetHardwareScreenOffset(x, y);
return 1;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_guard_bands)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0046900);
EXIT_NOT_IMPLEMENTED(cmd_offset != 0x000002FA);
auto vert_clip = *reinterpret_cast<const float*>(&buffer[0]);
auto vert_discard = *reinterpret_cast<const float*>(&buffer[1]);
auto horz_clip = *reinterpret_cast<const float*>(&buffer[2]);
auto horz_discard = *reinterpret_cast<const float*>(&buffer[3]);
cp->GetCtx()->SetGuardBands(horz_clip, vert_clip, horz_discard, vert_discard);
return 4;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_vs_shader)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC01B1004);
auto shader_modifier = buffer[0];
cp->GetCtx()->SetVsShader(reinterpret_cast<const VsStageRegisters*>(buffer + 1), shader_modifier);
return 28;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_vs_embedded)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xc01b1034);
auto shader_modifier = buffer[0];
auto id = buffer[1];
cp->GetCtx()->SetVsEmbedded(id, shader_modifier);
return 28;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_ps_shader)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0261008);
PsStageRegisters r {};
r.data_addr = (static_cast<uint64_t>(buffer[0]) << 8u) | (static_cast<uint64_t>(buffer[1]) << 40u);
r.vgprs = (buffer[2] >> Pm4::SPI_SHADER_PGM_RSRC1_PS_VGPRS_SHIFT) & Pm4::SPI_SHADER_PGM_RSRC1_PS_VGPRS_MASK;
r.sgprs = (buffer[2] >> Pm4::SPI_SHADER_PGM_RSRC1_PS_SGPRS_SHIFT) & Pm4::SPI_SHADER_PGM_RSRC1_PS_SGPRS_MASK;
r.scratch_en = (buffer[3] >> Pm4::SPI_SHADER_PGM_RSRC2_PS_SCRATCH_EN_SHIFT) & Pm4::SPI_SHADER_PGM_RSRC2_PS_SCRATCH_EN_MASK;
r.user_sgpr = (buffer[3] >> Pm4::SPI_SHADER_PGM_RSRC2_PS_USER_SGPR_SHIFT) & Pm4::SPI_SHADER_PGM_RSRC2_PS_USER_SGPR_MASK;
r.wave_cnt_en = (buffer[3] >> Pm4::SPI_SHADER_PGM_RSRC2_PS_WAVE_CNT_EN_SHIFT) & Pm4::SPI_SHADER_PGM_RSRC2_PS_WAVE_CNT_EN_MASK;
r.shader_z_format = buffer[4];
for (uint32_t i = 0; i < 8; i++)
{
r.target_output_mode[i] = (buffer[5] >> (i * 4)) & 0xFu;
}
r.ps_input_ena = buffer[6];
r.ps_input_addr = buffer[7];
r.ps_in_control = buffer[8];
r.baryc_cntl = buffer[9];
r.conservative_z_export_value = (buffer[10] >> 13u) & 0x3u;
r.shader_z_behavior = (buffer[10] >> 4u) & 0x3u;
r.shader_kill_enable = (buffer[10] & 0x40u) != 0;
r.shader_z_export_enable = (buffer[10] & 0x1u) != 0;
r.shader_execute_on_noop = (buffer[10] & 0x400u) != 0;
r.m_cbShaderMask = buffer[11];
cp->GetCtx()->SetPsShader(&r);
return 39;
}
KYTY_HW_CTX_PARSER(hw_ctx_set_cs_shader)
{
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC017101C);
auto shader_modifier = buffer[0];
CsStageRegisters r {};
r.data_addr = (static_cast<uint64_t>(buffer[1]) << 8u) | (static_cast<uint64_t>(buffer[2]) << 40u);
r.vgprs = (buffer[3] >> Pm4::COMPUTE_PGM_RSRC1_VGPRS_SHIFT) & Pm4::COMPUTE_PGM_RSRC1_VGPRS_MASK;
r.sgprs = (buffer[3] >> Pm4::COMPUTE_PGM_RSRC1_SGPRS_SHIFT) & Pm4::COMPUTE_PGM_RSRC1_SGPRS_MASK;
r.bulky = (buffer[3] >> Pm4::COMPUTE_PGM_RSRC1_BULKY_SHIFT) & Pm4::COMPUTE_PGM_RSRC1_BULKY_MASK;
r.scratch_en = (buffer[4] >> Pm4::COMPUTE_PGM_RSRC2_SCRATCH_EN_SHIFT) & Pm4::COMPUTE_PGM_RSRC2_SCRATCH_EN_MASK;
r.user_sgpr = (buffer[4] >> Pm4::COMPUTE_PGM_RSRC2_USER_SGPR_SHIFT) & Pm4::COMPUTE_PGM_RSRC2_USER_SGPR_MASK;
r.tgid_x_en = (buffer[4] >> Pm4::COMPUTE_PGM_RSRC2_TGID_X_EN_SHIFT) & Pm4::COMPUTE_PGM_RSRC2_TGID_X_EN_MASK;
r.tgid_y_en = (buffer[4] >> Pm4::COMPUTE_PGM_RSRC2_TGID_Y_EN_SHIFT) & Pm4::COMPUTE_PGM_RSRC2_TGID_Y_EN_MASK;
r.tgid_z_en = (buffer[4] >> Pm4::COMPUTE_PGM_RSRC2_TGID_Z_EN_SHIFT) & Pm4::COMPUTE_PGM_RSRC2_TGID_Z_EN_MASK;
r.tg_size_en = (buffer[4] >> Pm4::COMPUTE_PGM_RSRC2_TG_SIZE_EN_SHIFT) & Pm4::COMPUTE_PGM_RSRC2_TG_SIZE_EN_MASK;
r.tidig_comp_cnt = (buffer[4] >> Pm4::COMPUTE_PGM_RSRC2_TIDIG_COMP_CNT_SHIFT) & Pm4::COMPUTE_PGM_RSRC2_TIDIG_COMP_CNT_MASK;
r.lds_size = (buffer[4] >> Pm4::COMPUTE_PGM_RSRC2_LDS_SIZE_SHIFT) & Pm4::COMPUTE_PGM_RSRC2_LDS_SIZE_MASK;
r.num_thread_x = buffer[5];
r.num_thread_y = buffer[6];
r.num_thread_z = buffer[7];
cp->GetCtx()->SetCsShader(&r, shader_modifier);
return 24;
}
} // namespace Kyty::Libs::Graphics
#endif // KYTY_EMU_ENABLED