diff --git a/rpcs3/Emu/Cell/Modules/cellGcmSys.cpp b/rpcs3/Emu/Cell/Modules/cellGcmSys.cpp index 8fd7ef03c6..5e8c6997d8 100644 --- a/rpcs3/Emu/Cell/Modules/cellGcmSys.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGcmSys.cpp @@ -298,13 +298,32 @@ error_code cellGcmBindZcull(u8 index, u32 offset, u32 width, u32 height, u32 cul cellGcmSys.warning("cellGcmBindZcull(index=%d, offset=0x%x, width=%d, height=%d, cullStart=0x%x, zFormat=0x%x, aaFormat=0x%x, zCullDir=0x%x, zCullFormat=0x%x, sFunc=0x%x, sRef=0x%x, sMask=0x%x)", index, offset, width, height, cullStart, zFormat, aaFormat, zCullDir, zCullFormat, sFunc, sRef, sMask); - if (index >= rsx::limits::zculls_count) + auto& gcm_cfg = g_fxo->get(); + + GcmZcullInfo zcull{}; + zcull.offset = offset; + zcull.width = width; + zcull.height = height; + zcull.cullStart = cullStart; + zcull.zFormat = zFormat; + zcull.aaFormat = aaFormat; + zcull.zcullDir = zCullDir; + zcull.zcullFormat = zCullFormat; + zcull.sFunc = sFunc; + zcull.sRef = sRef; + zcull.sMask = sMask; + zcull.bound = true; + + const auto gcm_zcull = zcull.pack(); + + std::lock_guard lock(gcm_cfg.gcmio_mutex); + + if (auto err = sys_rsx_context_attribute(0x5555'5555, 0x301, index, u64{gcm_zcull.region} << 32 | gcm_zcull.size, u64{gcm_zcull.start} << 32 | gcm_zcull.offset, u64{gcm_zcull.status0} << 32 | gcm_zcull.status1)) { - return CELL_GCM_ERROR_INVALID_VALUE; + return err; } - - rsx::get_current_renderer()->zculls[index].bound = true; - + + vm::_ptr(gcm_cfg.zculls_addr)[index] = gcm_zcull; return CELL_OK; } @@ -391,6 +410,12 @@ error_code _cellGcmInitBody(ppu_thread& ppu, vm::pptr contex render->main_mem_size = 0x10000000; } + render->isHLE = true; + render->local_mem_size = gcm_cfg.local_size; + + ensure(sys_rsx_device_map(ppu, vm::var{}, vm::null, 0x8) == CELL_OK); + ensure(sys_rsx_context_allocate(ppu, vm::var{}, vm::var{}, vm::var{}, vm::var{}, 0, gcm_cfg.system_mode) == CELL_OK); + if (gcmMapEaIoAddress(ppu, ioAddress, 0, ioSize, false) != CELL_OK) { return CELL_GCM_ERROR_FAILURE; @@ -403,18 +428,14 @@ error_code _cellGcmInitBody(ppu_thread& ppu, vm::pptr contex gcm_cfg.current_config.memoryFrequency = 650000000; gcm_cfg.current_config.coreFrequency = 500000000; - // Create contexts - const auto area = vm::reserve_map(vm::rsx_context, 0, 0x10000000, 0x403); - const u32 rsx_ctxaddr = area ? area->alloc(0x400000) : 0; + const u32 rsx_ctxaddr = render->device_addr; ensure(rsx_ctxaddr); g_defaultCommandBufferBegin = ioAddress; g_defaultCommandBufferFragmentCount = cmdSize / (32 * 1024); gcm_cfg.gcm_info.context_addr = rsx_ctxaddr; - gcm_cfg.gcm_info.control_addr = rsx_ctxaddr + 0x100000; - gcm_cfg.gcm_info.label_addr = rsx_ctxaddr + 0x300000; - + gcm_cfg.gcm_info.control_addr = render->dma_address; gcm_cfg.current_context.begin.set(g_defaultCommandBufferBegin + 4096); // 4 kb reserved at the beginning gcm_cfg.current_context.end.set(g_defaultCommandBufferBegin + 32 * 1024 - 4); // 4b at the end for jump gcm_cfg.current_context.current = gcm_cfg.current_context.begin; @@ -430,21 +451,13 @@ error_code _cellGcmInitBody(ppu_thread& ppu, vm::pptr contex // 0x40 is to offset CellGcmControl from RsxDmaControl gcm_cfg.gcm_info.control_addr += 0x40; - auto& ctrl = vm::_ref(gcm_cfg.gcm_info.control_addr); - ctrl.put = 0; - ctrl.get = 0; - ctrl.ref = 0; // Set later to -1 at RSX initialization vm::var _tid; vm::var _name = vm::make_str("_gcm_intr_thread"); ppu_execute<&sys_ppu_thread_create>(ppu, +_tid, 0x10000, 0, 1, 0x4000, SYS_PPU_THREAD_CREATE_INTERRUPT, +_name); render->intr_thread = idm::get>(static_cast(*_tid)); render->intr_thread->state -= cpu_flag::stop; - render->isHLE = true; - render->label_addr = gcm_cfg.gcm_info.label_addr; - render->device_addr = gcm_cfg.gcm_info.context_addr; - render->local_mem_size = gcm_cfg.local_size; - render->init(gcm_cfg.gcm_info.control_addr - 0x40); + thread_ctrl::notify(*render->intr_thread); return CELL_OK; } @@ -516,7 +529,10 @@ void cellGcmSetFlipHandler(vm::ptr handler) { cellGcmSys.warning("cellGcmSetFlipHandler(handler=*0x%x)", handler); - rsx::get_current_renderer()->flip_handler = handler; + if (const auto rsx = rsx::get_current_renderer(); rsx->is_inited) + { + rsx->flip_handler = handler; + } } error_code cellGcmSetFlipHandler2() @@ -665,51 +681,69 @@ void cellGcmSetUserHandler(vm::ptr handler) { cellGcmSys.warning("cellGcmSetUserHandler(handler=*0x%x)", handler); - rsx::get_current_renderer()->user_handler = handler; + if (const auto rsx = rsx::get_current_renderer(); rsx->is_inited) + { + rsx->user_handler = handler; + } } -void cellGcmSetUserCommand(vm::ptr ctxt, u32 cause) +void cellGcmSetUserCommand(ppu_thread& ppu, vm::ptr ctxt, u32 cause) { - cellGcmSys.todo("cellGcmSetUserCommand(ctxt=*0x%x, cause=0x%x)", ctxt, cause); + cellGcmSys.trace("cellGcmSetUserCommand(ctxt=*0x%x, cause=0x%x)", ctxt, cause); + + if (ctxt->current + 2 >= ctxt->end) + { + if (s32 res = ctxt->callback(ppu, ctxt, 8 /* ??? */)) + { + cellGcmSys.error("cellGcmSetUserCommand(): callback failed (0x%08x)", res); + return; + } + } + + rsx::make_command(ctxt->current, GCM_SET_USER_COMMAND, { cause }); } void cellGcmSetVBlankHandler(vm::ptr handler) { cellGcmSys.warning("cellGcmSetVBlankHandler(handler=*0x%x)", handler); - rsx::get_current_renderer()->vblank_handler = handler; + if (const auto rsx = rsx::get_current_renderer(); rsx->is_inited) + { + rsx->vblank_handler = handler; + } } -void cellGcmSetWaitFlip(vm::ptr ctxt) +void cellGcmSetWaitFlip(ppu_thread& ppu, vm::ptr ctxt) { - cellGcmSys.warning("cellGcmSetWaitFlip(ctxt=*0x%x)", ctxt); + cellGcmSys.trace("cellGcmSetWaitFlip(ctxt=*0x%x)", ctxt); - // TODO: emit RSX command for "wait flip" operation + if (ctxt->current + 2 >= ctxt->end) + { + if (s32 res = ctxt->callback(ppu, ctxt, 8 /* ??? */)) + { + cellGcmSys.error("cellGcmSetWaitFlip(): callback failed (0x%08x)", res); + return; + } + } + + rsx::make_command(ctxt->current, NV406E_SEMAPHORE_OFFSET, { 0x10u, 0 }); } -error_code cellGcmSetWaitFlipUnsafe() +void cellGcmSetWaitFlipUnsafe(vm::ptr ctxt) { - cellGcmSys.todo("cellGcmSetWaitFlipUnsafe()"); + cellGcmSys.trace("cellGcmSetWaitFlipUnsafe(ctxt=*0x%x)", ctxt); - return CELL_OK; + rsx::make_command(ctxt->current, NV406E_SEMAPHORE_OFFSET, { 0x10u, 0 }); } void cellGcmSetZcull(u8 index, u32 offset, u32 width, u32 height, u32 cullStart, u32 zFormat, u32 aaFormat, u32 zCullDir, u32 zCullFormat, u32 sFunc, u32 sRef, u32 sMask) { - cellGcmSys.todo("cellGcmSetZcull(index=%d, offset=0x%x, width=%d, height=%d, cullStart=0x%x, zFormat=0x%x, aaFormat=0x%x, zCullDir=0x%x, zCullFormat=0x%x, sFunc=0x%x, sRef=0x%x, sMask=0x%x)", + cellGcmSys.warning("cellGcmSetZcull(index=%d, offset=0x%x, width=%d, height=%d, cullStart=0x%x, zFormat=0x%x, aaFormat=0x%x, zCullDir=0x%x, zCullFormat=0x%x, sFunc=0x%x, sRef=0x%x, sMask=0x%x)", index, offset, width, height, cullStart, zFormat, aaFormat, zCullDir, zCullFormat, sFunc, sRef, sMask); auto& gcm_cfg = g_fxo->get(); - if (index >= rsx::limits::zculls_count) - { - cellGcmSys.error("cellGcmSetZcull: CELL_GCM_ERROR_INVALID_VALUE"); - return; - } - - const auto render = rsx::get_current_renderer(); - - auto& zcull = render->zculls[index]; + GcmZcullInfo zcull{}; zcull.offset = offset; zcull.width = width; zcull.height = height; @@ -721,9 +755,18 @@ void cellGcmSetZcull(u8 index, u32 offset, u32 width, u32 height, u32 cullStart, zcull.sFunc = sFunc; zcull.sRef = sRef; zcull.sMask = sMask; - zcull.bound = (zCullFormat > 0); + zcull.bound = true; - vm::_ptr(gcm_cfg.zculls_addr)[index] = zcull.pack(); + const auto gcm_zcull = zcull.pack(); + + // The second difference between BindZcull and this function (second is no return value) is that this function is not thread-safe + // But take care anyway + std::lock_guard lock(gcm_cfg.gcmio_mutex); + + if (!sys_rsx_context_attribute(0x5555'5555, 0x301, index, u64{gcm_zcull.region} << 32 | gcm_zcull.size, u64{gcm_zcull.start} << 32 | gcm_zcull.offset, u64{gcm_zcull.status0} << 32 | gcm_zcull.status1)) + { + vm::_ptr(gcm_cfg.zculls_addr)[index] = gcm_zcull; + } } error_code cellGcmUnbindTile(u8 index) @@ -878,7 +921,12 @@ void cellGcmSetGraphicsHandler(vm::ptr handler) void cellGcmSetQueueHandler(vm::ptr handler) { - cellGcmSys.todo("cellGcmSetQueueHandler(handler=*0x%x)", handler); + cellGcmSys.warning("cellGcmSetQueueHandler(handler=*0x%x)", handler); + + if (const auto rsx = rsx::get_current_renderer(); rsx->is_inited) + { + rsx->queue_handler = handler; + } } error_code cellGcmSetSecondVHandler(vm::ptr handler) @@ -1264,18 +1312,17 @@ error_code _cellGcmSetFlipCommand2() void _cellGcmSetFlipCommandWithWaitLabel(ppu_thread& ppu, vm::ptr ctx, u32 id, u32 label_index, u32 label_value) { - cellGcmSys.todo("cellGcmSetFlipCommandWithWaitLabel(ctx=*0x%x, id=0x%x, label_index=0x%x, label_value=0x%x)", ctx, id, label_index, label_value); + cellGcmSys.warning("cellGcmSetFlipCommandWithWaitLabel(ctx=*0x%x, id=0x%x, label_index=0x%x, label_value=0x%x)", ctx, id, label_index, label_value); auto& gcm_cfg = g_fxo->get(); + rsx::make_command(ctx->current, NV406E_SEMAPHORE_OFFSET, { label_index * 0x10, label_value }); + if (auto error = gcmSetPrepareFlip(ppu, ctx, id); error < 0) { // TODO: On actual fw this function doesn't have error checks at all cellGcmSys.error("cellGcmSetFlipCommandWithWaitLabel(): gcmSetPrepareFlip failed with %s", CellGcmError{error + 0u}); } - - // TODO: Fix this (must enqueue WaitLabel command instead) - vm::write32(gcm_cfg.gcm_info.label_addr + 0x10 * label_index, label_value); } error_code cellGcmSetTile(u8 index, u8 location, u32 offset, u32 size, u32 pitch, u8 comp, u16 base, u8 bank) diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp index cb3e7226ce..dcda9280b5 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp @@ -656,7 +656,10 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64 //a6 high = status0 = (zcullDir << 1) | (zcullFormat << 2) | ((sFunc & 0xF) << 12) | (sRef << 16) | (sMask << 24); //a6 low = status1 = (0x2000 << 0) | (0x20 << 16); - ensure(a3 < std::size(render->zculls)); + if (a3 >= std::size(render->zculls)) + { + return SYS_RSX_CONTEXT_ATTRIBUTE_ERROR; + } if (!render->is_fifo_idle()) { diff --git a/rpcs3/Emu/RSX/GCM.h b/rpcs3/Emu/RSX/GCM.h index 8a993bca2e..004dbe5a48 100644 --- a/rpcs3/Emu/RSX/GCM.h +++ b/rpcs3/Emu/RSX/GCM.h @@ -39,7 +39,6 @@ struct gcmInfo u32 config_addr; u32 context_addr; u32 control_addr; - u32 label_addr; u32 command_size = 0x400; u32 segment_size = 0x100; }; diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index b8ee7af39d..9125c1e928 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -703,15 +703,16 @@ namespace rsx if (isHLE) { - if (vblank_handler) + if (auto ptr = vblank_handler) { intr_thread->cmd_list ({ { ppu_cmd::set_args, 1 }, u64{1}, - { ppu_cmd::lle_call, vblank_handler }, + { ppu_cmd::lle_call, ptr }, { ppu_cmd::sleep, 0 } }); + intr_thread->cmd_notify++; intr_thread->cmd_notify.notify_one(); } } @@ -2183,6 +2184,9 @@ namespace rsx void thread::reset() { rsx::method_registers.reset(); + m_graphics_state = pipeline_state::all_dirty; + m_rtts_dirty = true; + m_framebuffer_state_contested = false; } void thread::init(u32 ctrlAddress) @@ -3278,12 +3282,12 @@ namespace rsx return; } - if (flip_handler) + if (auto ptr = flip_handler) { intr_thread->cmd_list ({ { ppu_cmd::set_args, 1 }, u64{ 1 }, - { ppu_cmd::lle_call, flip_handler }, + { ppu_cmd::lle_call, ptr }, { ppu_cmd::sleep, 0 } }); diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 504e222828..258def7975 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -641,6 +641,7 @@ namespace rsx vm::ptr flip_handler = vm::null; vm::ptr user_handler = vm::null; vm::ptr vblank_handler = vm::null; + vm::ptr queue_handler = vm::null; atomic_t vblank_count{0}; bool capture_current_frame = false; diff --git a/rpcs3/Emu/RSX/rsx_methods.cpp b/rpcs3/Emu/RSX/rsx_methods.cpp index 367bea7040..3f3a44c714 100644 --- a/rpcs3/Emu/RSX/rsx_methods.cpp +++ b/rpcs3/Emu/RSX/rsx_methods.cpp @@ -1768,8 +1768,26 @@ namespace rsx void flip_command(thread* rsx, u32, u32 arg) { ensure(rsx->isHLE); + + if (auto ptr = rsx->queue_handler) + { + rsx->intr_thread->cmd_list + ({ + { ppu_cmd::set_args, 1 }, u64{1}, + { ppu_cmd::lle_call, ptr }, + { ppu_cmd::sleep, 0 } + }); + + rsx->intr_thread->cmd_notify++; + rsx->intr_thread->cmd_notify.notify_one(); + } + rsx->reset(); + nv4097::set_zcull_render_enable(rsx, 0, 0x3); + nv4097::set_render_mode(rsx, 0, 0x0100'0000); + rsx->on_frame_end(arg); rsx->request_emu_flip(arg); + vm::_ref>(rsx->label_addr + 0x10).store(u128{}); } void user_command(thread* rsx, u32, u32 arg) @@ -1780,12 +1798,12 @@ namespace rsx return; } - if (rsx->user_handler) + if (auto ptr = rsx->user_handler) { rsx->intr_thread->cmd_list ({ { ppu_cmd::set_args, 1 }, u64{arg}, - { ppu_cmd::lle_call, rsx->user_handler }, + { ppu_cmd::lle_call, ptr }, { ppu_cmd::sleep, 0 } });