diff --git a/Utilities/SSemaphore.cpp b/Utilities/SSemaphore.cpp deleted file mode 100644 index fd1aad9fbb..0000000000 --- a/Utilities/SSemaphore.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "stdafx.h" -#include "Utilities/SSemaphore.h" -#include "Emu/System.h" - -void SSemaphore::wait() -{ - u32 order; - { - std::lock_guard lock(m_mutex); - if (m_count && m_out_order == m_in_order) - { - m_count--; - return; - } - order = m_in_order++; - } - - std::unique_lock cv_lock(m_cv_mutex); - - while (true) - { - CHECK_EMU_STATUS; - - m_cond.wait_for(cv_lock, std::chrono::milliseconds(1)); - - { - std::lock_guard lock(m_mutex); - if (m_count) - { - if (m_out_order == order) - { - m_count--; - m_out_order++; - return; - } - else - { - m_cond.notify_one(); - } - } - } - } -} - -bool SSemaphore::try_wait() -{ - std::lock_guard lock(m_mutex); - - if (m_count && m_in_order == m_out_order) - { - m_count--; - return true; - } - else - { - return false; - } -} - -void SSemaphore::post() -{ - std::lock_guard lock(m_mutex); - - if (m_count < m_max) - { - m_count++; - } - else - { - return; - } - - m_cond.notify_one(); -} - -bool SSemaphore::post_and_wait() -{ - // TODO: merge these functions? Probably has a race condition. - if (try_wait()) return false; - - post(); - wait(); - - return true; -} diff --git a/Utilities/SSemaphore.h b/Utilities/SSemaphore.h deleted file mode 100644 index 2e61262959..0000000000 --- a/Utilities/SSemaphore.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -class SSemaphore -{ - const u32 m_max; - u32 m_count; - u32 m_in_order; - u32 m_out_order; - std::mutex m_cv_mutex; - std::mutex m_mutex; - std::condition_variable m_cond; - -public: - SSemaphore(u32 value, u32 max = 1) - : m_max(max > 0 ? max : 0xffffffff) - , m_count(value > m_max ? m_max : value) - , m_in_order(0) - , m_out_order(0) - { - } - - SSemaphore() - : m_max(0xffffffff) - , m_count(0) - , m_in_order(0) - , m_out_order(0) - { - } - - ~SSemaphore() - { - } - - void wait(); - - bool try_wait(); - - void post(); - - bool post_and_wait(); -}; \ No newline at end of file diff --git a/Utilities/Semaphore.cpp b/Utilities/Semaphore.cpp new file mode 100644 index 0000000000..ed3d419e12 --- /dev/null +++ b/Utilities/Semaphore.cpp @@ -0,0 +1,120 @@ +#include "stdafx.h" +#include "Utilities/Semaphore.h" + +bool semaphore_t::try_wait() +{ + // check m_value without interlocked op + if (m_var.load().value == 0) + { + return false; + } + + // try to decrement m_value atomically + const auto old = m_var.atomic_op([](sync_var_t& var) + { + if (var.value) + { + var.value--; + } + }); + + // recheck atomic result + if (old.value == 0) + { + return false; + } + + return true; +} + +bool semaphore_t::try_post() +{ + // check m_value without interlocked op + if (m_var.load().value >= max_value) + { + return false; + } + + // try to increment m_value atomically + const auto old = m_var.atomic_op([&](sync_var_t& var) + { + if (var.value < max_value) + { + var.value++; + } + }); + + // recheck atomic result + if (old.value >= max_value) + { + return false; + } + + if (old.waiters) + { + // notify waiting thread + std::lock_guard lock(m_mutex); + + m_cv.notify_one(); + } + + return true; +} + +void semaphore_t::wait() +{ + if (m_var.atomic_op([](sync_var_t& var) -> bool + { + if (var.value) + { + var.value--; + + return true; + } + else + { + //var.waiters++; + + return false; + } + })) + { + return; + } + + std::unique_lock lock(m_mutex); + + m_var.atomic_op([](sync_var_t& var) + { + var.waiters++; + }); + + while (!m_var.atomic_op([](sync_var_t& var) -> bool + { + if (var.value) + { + var.value--; + var.waiters--; + + return true; + } + else + { + return false; + } + })) + { + m_cv.wait(lock); + } +} + +bool semaphore_t::post_and_wait() +{ + // TODO: merge these functions? Probably has a race condition. + if (try_wait()) return false; + + try_post(); + wait(); + + return true; +} diff --git a/Utilities/Semaphore.h b/Utilities/Semaphore.h new file mode 100644 index 0000000000..03d3a77168 --- /dev/null +++ b/Utilities/Semaphore.h @@ -0,0 +1,37 @@ +#pragma once + +class semaphore_t +{ + // semaphore mutex + std::mutex m_mutex; + + // semaphore condition variable + std::condition_variable m_cv; + + struct sync_var_t + { + u32 value; // current semaphore value + u32 waiters; // current amount of waiters + }; + + // current semaphore value + atomic_t m_var; + +public: + // max semaphore value + const u32 max_value; + + semaphore_t(u32 max_value = 1, u32 value = 0) + : m_var({ value, 0 }) + , max_value(max_value) + { + } + + bool try_wait(); + + bool try_post(); + + void wait(); + + bool post_and_wait(); +}; \ No newline at end of file diff --git a/rpcs3/Emu/RSX/GCM.h b/rpcs3/Emu/RSX/GCM.h index 375788fdb7..71aa10ee82 100644 --- a/rpcs3/Emu/RSX/GCM.h +++ b/rpcs3/Emu/RSX/GCM.h @@ -224,9 +224,9 @@ typedef s32(CellGcmContextCallback)(vm::ptr, u32); struct CellGcmContextData { - be_t begin; - be_t end; - be_t current; + vm::bptr begin; + vm::bptr end; + vm::bptr current; vm::bptr callback; }; diff --git a/rpcs3/Emu/RSX/GSRender.cpp b/rpcs3/Emu/RSX/GSRender.cpp index 371db32257..cebc2d08a8 100644 --- a/rpcs3/Emu/RSX/GSRender.cpp +++ b/rpcs3/Emu/RSX/GSRender.cpp @@ -12,7 +12,6 @@ GSLock::GSLock(GSRender& renderer, GSLockType type) switch (m_type) { case GS_LOCK_NOT_WAIT: m_renderer.m_cs_main.lock(); break; - case GS_LOCK_WAIT_FLUSH: m_renderer.m_sem_flush.wait(); break; case GS_LOCK_WAIT_FLIP: m_renderer.m_sem_flip.wait(); break; } } @@ -22,8 +21,7 @@ GSLock::~GSLock() switch (m_type) { case GS_LOCK_NOT_WAIT: m_renderer.m_cs_main.unlock(); break; - case GS_LOCK_WAIT_FLUSH: m_renderer.m_sem_flush.post(); break; - case GS_LOCK_WAIT_FLIP: m_renderer.m_sem_flip.post(); break; + case GS_LOCK_WAIT_FLIP: m_renderer.m_sem_flip.try_post(); break; } } diff --git a/rpcs3/Emu/RSX/GSRender.h b/rpcs3/Emu/RSX/GSRender.h index 4b376e5187..d0755e5659 100644 --- a/rpcs3/Emu/RSX/GSRender.h +++ b/rpcs3/Emu/RSX/GSRender.h @@ -17,7 +17,6 @@ struct GSRender : public RSXThread enum GSLockType { GS_LOCK_NOT_WAIT, - GS_LOCK_WAIT_FLUSH, GS_LOCK_WAIT_FLIP, }; diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 8363adc6a5..3fb31f9c95 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -258,6 +258,8 @@ void RSXThread::DoCmd(const u32 fcmd, const u32 cmd, const u32 args_addr, const }); } + m_sem_flip.post_and_wait(); + auto sync = [&]() { double limit; @@ -2499,14 +2501,6 @@ void RSXThread::Task() if (put == get || !Emu.IsRunning()) { - if (put == get) - { - if (m_flip_status == 0) - m_sem_flip.post_and_wait(); - - m_sem_flush.post_and_wait(); - } - std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack continue; } diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index ce0d92c819..6cacabc967 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -5,7 +5,7 @@ #include "RSXFragmentProgram.h" #include -#include "Utilities/SSemaphore.h" +#include "Utilities/Semaphore.h" #include "Utilities/Thread.h" #include "Utilities/Timer.h" @@ -155,8 +155,7 @@ public: public: std::mutex m_cs_main; - SSemaphore m_sem_flush; - SSemaphore m_sem_flip; + semaphore_t m_sem_flip; u64 m_last_flip_time; vm::ptr m_flip_handler; vm::ptr m_user_handler; diff --git a/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp b/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp index 3c3d292c91..e4b4cf1ffc 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp @@ -320,9 +320,9 @@ void _cellGcmFunc15(vm::ptr context) u32 g_defaultCommandBufferBegin, g_defaultCommandBufferFragmentCount; // Called by cellGcmInit -s32 _cellGcmInitBody(vm::ptr context, u32 cmdSize, u32 ioSize, u32 ioAddress) +s32 _cellGcmInitBody(vm::pptr context, u32 cmdSize, u32 ioSize, u32 ioAddress) { - cellGcmSys.Warning("_cellGcmInitBody(context=*0x%x, cmdSize=0x%x, ioSize=0x%x, ioAddress=0x%x)", context, cmdSize, ioSize, ioAddress); + cellGcmSys.Warning("_cellGcmInitBody(context=**0x%x, cmdSize=0x%x, ioSize=0x%x, ioAddress=0x%x)", context, cmdSize, ioSize, ioAddress); if(!local_size && !local_addr) { @@ -365,8 +365,8 @@ s32 _cellGcmInitBody(vm::ptr context, u32 cmdSize, u32 ioSiz g_defaultCommandBufferBegin = ioAddress; g_defaultCommandBufferFragmentCount = cmdSize / (32 * 1024); - current_context.begin = g_defaultCommandBufferBegin + 4096; // 4 kb reserved at the beginning - current_context.end = g_defaultCommandBufferBegin + 32 * 1024 - 4; // 4b at the end for jump + current_context.begin.set(g_defaultCommandBufferBegin + 4096); // 4 kb reserved at the beginning + current_context.end.set(g_defaultCommandBufferBegin + 32 * 1024 - 4); // 4b at the end for jump current_context.current = current_context.begin; current_context.callback.set(Emu.GetRSXCallback() - 4); @@ -376,7 +376,7 @@ s32 _cellGcmInitBody(vm::ptr context, u32 cmdSize, u32 ioSiz gcm_info.label_addr = vm::alloc(0x1000, vm::main); // ??? vm::get_ref(gcm_info.context_addr) = current_context; - vm::write32(context.addr(), gcm_info.context_addr); + context->set(gcm_info.context_addr); auto& ctrl = vm::get_ref(gcm_info.control_addr); ctrl.put.store(0); @@ -480,59 +480,46 @@ void cellGcmSetFlipStatus() Emu.GetGSManager().GetRender().m_flip_status = 0; } -s32 cellGcmSetPrepareFlip(PPUThread& CPU, vm::ptr ctxt, u32 id) +s32 cellGcmSetPrepareFlip(PPUThread& ppu, vm::ptr ctxt, u32 id) { - cellGcmSys.Log("cellGcmSetPrepareFlip(ctx=0x%x, id=0x%x)", ctxt.addr(), id); + cellGcmSys.Log("cellGcmSetPrepareFlip(ctx=*0x%x, id=0x%x)", ctxt, id); - if(id > 7) + if (id > 7) { cellGcmSys.Error("cellGcmSetPrepareFlip : CELL_GCM_ERROR_FAILURE"); return CELL_GCM_ERROR_FAILURE; } - GSLockCurrent gslock(GS_LOCK_WAIT_FLUSH); - - u32 current = ctxt->current; - - if (current + 8 == ctxt->begin) + if (ctxt->current + 2 >= ctxt->end) { - cellGcmSys.Error("cellGcmSetPrepareFlip : queue is full"); - return CELL_GCM_ERROR_FAILURE; - } - - if (current + 8 >= ctxt->end) - { - cellGcmSys.Error("Bad flip!"); - if (s32 res = ctxt->callback(CPU, ctxt, 8 /* ??? */)) + if (s32 res = ctxt->callback(ppu, ctxt, 8 /* ??? */)) { cellGcmSys.Error("cellGcmSetPrepareFlip : callback failed (0x%08x)", res); return res; } } - current = ctxt->current; - vm::write32(current, 0x3fead | (1 << 18)); - vm::write32(current + 4, id); - ctxt->current += 8; + *ctxt->current++ = 0x3fead | (1 << 18); + *ctxt->current++ = id; - if(ctxt.addr() == gcm_info.context_addr) + if (ctxt.addr() == gcm_info.context_addr) { - auto& ctrl = vm::get_ref(gcm_info.control_addr); - ctrl.put.atomic_op([](be_t& value) - { - value += 8; - }); + vm::get_ref(gcm_info.control_addr).put += 8; } return id; } -s32 cellGcmSetFlip(PPUThread& CPU, vm::ptr ctxt, u32 id) +s32 cellGcmSetFlip(PPUThread& ppu, vm::ptr ctxt, u32 id) { - cellGcmSys.Log("cellGcmSetFlip(ctx=0x%x, id=0x%x)", ctxt.addr(), id); + cellGcmSys.Log("cellGcmSetFlip(ctxt=*0x%x, id=0x%x)", ctxt, id); - s32 res = cellGcmSetPrepareFlip(CPU, ctxt, id); - return res < 0 ? CELL_GCM_ERROR_FAILURE : CELL_OK; + if (s32 res = cellGcmSetPrepareFlip(ppu, ctxt, id)) + { + if (res < 0) return CELL_GCM_ERROR_FAILURE; + } + + return CELL_OK; } s32 cellGcmSetSecondVFrequency(u32 freq) @@ -610,9 +597,10 @@ void cellGcmSetVBlankHandler(vm::ptr handler) s32 cellGcmSetWaitFlip(vm::ptr ctxt) { - cellGcmSys.Log("cellGcmSetWaitFlip(ctx=*0x%x)", ctxt); + cellGcmSys.Warning("cellGcmSetWaitFlip(ctx=*0x%x)", ctxt); + + // TODO: emit RSX command for "wait flip" operation - GSLockCurrent lock(GS_LOCK_WAIT_FLIP); return CELL_OK; } @@ -1101,18 +1089,18 @@ void cellGcmSetDefaultCommandBuffer() // Other //------------------------------------------------------------------------ -s32 _cellGcmSetFlipCommand(PPUThread& CPU, vm::ptr ctx, u32 id) +s32 _cellGcmSetFlipCommand(PPUThread& ppu, vm::ptr ctx, u32 id) { cellGcmSys.Log("cellGcmSetFlipCommand(ctx=*0x%x, id=0x%x)", ctx, id); - return cellGcmSetPrepareFlip(CPU, ctx, id); + return cellGcmSetPrepareFlip(ppu, ctx, id); } -s32 _cellGcmSetFlipCommandWithWaitLabel(PPUThread& CPU, vm::ptr ctx, u32 id, u32 label_index, u32 label_value) +s32 _cellGcmSetFlipCommandWithWaitLabel(PPUThread& ppu, vm::ptr ctx, u32 id, u32 label_index, u32 label_value) { cellGcmSys.Log("cellGcmSetFlipCommandWithWaitLabel(ctx=*0x%x, id=0x%x, label_index=0x%x, label_value=0x%x)", ctx, id, label_index, label_value); - s32 res = cellGcmSetPrepareFlip(CPU, ctx, id); + s32 res = cellGcmSetPrepareFlip(ppu, ctx, id); vm::write32(gcm_info.label_addr + 0x10 * label_index, label_value); return res < 0 ? CELL_GCM_ERROR_FAILURE : CELL_OK; } @@ -1200,9 +1188,7 @@ static bool isInCommandBufferExcept(u32 getPos, u32 bufferBegin, u32 bufferEnd) return true; } -// TODO: This function was originally located in lv2/SC_GCM and appears in RPCS3 as a lv2 syscall with id 1023, -// which according to lv2 dumps isn't the case. So, is this a proper place for this function? - +// TODO: Avoid using syscall 1023 for calling this function s32 cellGcmCallback(vm::ptr context, u32 count) { cellGcmSys.Log("cellGcmCallback(context=*0x%x, count=0x%x)", context, count); @@ -1210,16 +1196,16 @@ s32 cellGcmCallback(vm::ptr context, u32 count) auto& ctrl = vm::get_ref(gcm_info.control_addr); const std::chrono::time_point enterWait = std::chrono::system_clock::now(); // Flush command buffer (ie allow RSX to read up to context->current) - ctrl.put.exchange(getOffsetFromAddress(context->current)); + ctrl.put.exchange(getOffsetFromAddress(context->current.addr())); - std::pair newCommandBuffer = getNextCommandBufferBeginEnd(context->current); + std::pair newCommandBuffer = getNextCommandBufferBeginEnd(context->current.addr()); u32 offset = getOffsetFromAddress(newCommandBuffer.first); // Write jump instruction - vm::write32(context->current, CELL_GCM_METHOD_FLAG_JUMP | offset); + *context->current = CELL_GCM_METHOD_FLAG_JUMP | offset; // Update current command buffer - context->begin = newCommandBuffer.first; - context->current = newCommandBuffer.first; - context->end = newCommandBuffer.second; + context->begin.set(newCommandBuffer.first); + context->current.set(newCommandBuffer.first); + context->end.set(newCommandBuffer.second); // Wait for rsx to "release" the new command buffer while (!Emu.IsStopped()) @@ -1235,55 +1221,6 @@ s32 cellGcmCallback(vm::ptr context, u32 count) } return CELL_OK; - - //if (0) - //{ - // auto& ctrl = vm::get_ref(gcm_info.control_addr); - // be_t res = context->current - context->begin - ctrl.put.load(); - - // if (res != 0) - // { - // GSLockCurrent gslock(GS_LOCK_WAIT_FLUSH); - // } - - // memmove(vm::get_ptr(context->begin), vm::get_ptr(context->current - res), res); - - // context->current = context->begin + res; - // ctrl.put.store(res); - // ctrl.get.store(0); - - // return CELL_OK; - //} - - //auto& ctrl = vm::get_ref(gcm_info.control_addr); - - // preparations for changing the place (for optimized FIFO mode) - //auto cmd = vm::ptr::make(context->current); - //cmd[0] = 0x41D6C; - //cmd[1] = 0x20; - //cmd[2] = 0x41D74; - //cmd[3] = 0; // some incrementing by module value - //context->current += 0x10; - - //if (0) - //{ - // const u32 address = context->begin; - // const u32 upper = offsetTable.ioAddress[address >> 20]; // 12 bits - // assert(upper != 0xFFFF); - // const u32 offset = (upper << 20) | (address & 0xFFFFF); - // vm::write32(context->current, CELL_GCM_METHOD_FLAG_JUMP | offset); // set JUMP cmd - - // auto& ctrl = vm::get_ref(gcm_info.control_addr); - // ctrl.put.exchange(offset); - //} - //else - //{ - // vm::write32(context->current, CELL_GCM_METHOD_FLAG_JUMP | CELL_GCM_METHOD_FLAG_NON_INCREMENT | (0)); - //} - - //context->current = context->begin; // rewind to the beginning - // TODO: something is missing - return CELL_OK; } //---------------------------------------------------------------------------- diff --git a/rpcs3/Emu/SysCalls/Modules/cellResc.cpp b/rpcs3/Emu/SysCalls/Modules/cellResc.cpp index ac2b403f04..ab1c7bffd2 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellResc.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellResc.cpp @@ -17,7 +17,7 @@ extern void cellGcmSetFlipHandler(vm::ptr handler); extern void cellGcmSetVBlankHandler(vm::ptr handler); extern s32 cellGcmAddressToOffset(u32 address, vm::ptr offset); extern s32 cellGcmSetDisplayBuffer(u32 id, u32 offset, u32 pitch, u32 width, u32 height); -extern s32 cellGcmSetPrepareFlip(PPUThread& CPU, vm::ptr ctx, u32 id); +extern s32 cellGcmSetPrepareFlip(PPUThread& ppu, vm::ptr ctx, u32 id); extern s32 cellGcmSetSecondVFrequency(u32 freq); extern u32 cellGcmGetLabelAddress(u8 index); extern u32 cellGcmGetTiledPitchSize(u32 size); @@ -467,7 +467,6 @@ void InitMembers() void SetupRsxRenderingStates(vm::ptr& cntxt) { //TODO: use cntxt - GSLockCurrent lock(GS_LOCK_WAIT_FLUSH); GSRender& r = Emu.GetGSManager().GetRender(); r.m_set_color_mask = true; r.m_color_mask_a = r.m_color_mask_r = r.m_color_mask_g = r.m_color_mask_b = true; r.m_set_depth_mask = true; r.m_depth_mask = 0; @@ -514,7 +513,6 @@ void SetupRsxRenderingStates(vm::ptr& cntxt) void SetupVertexArrays(vm::ptr& cntxt) { - GSLockCurrent lock(GS_LOCK_WAIT_FLUSH); GSRender& r = Emu.GetGSManager().GetRender(); //TODO @@ -538,7 +536,6 @@ void SetupSurfaces(vm::ptr& cntxt) dstOffset1 = s_rescInternalInstance->m_dstOffsets[s_rescInternalInstance->m_bufIdPalMidNow]; } - GSLockCurrent lock(GS_LOCK_WAIT_FLUSH); GSRender& r = Emu.GetGSManager().GetRender(); r.m_surface_type = CELL_GCM_SURFACE_PITCH; @@ -1023,8 +1020,9 @@ s32 cellRescSetConvertAndFlip(PPUThread& CPU, vm::ptr cntxt, s32 cellRescSetWaitFlip() { - cellResc.Log("cellRescSetWaitFlip()"); - GSLockCurrent lock(GS_LOCK_WAIT_FLIP); + cellResc.Warning("cellRescSetWaitFlip()"); + + // TODO: emit RSX command for "wait flip" operation return CELL_OK; } diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index b0b9837289..2542c6a4e7 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -30,7 +30,7 @@ - + @@ -318,9 +318,9 @@ + - diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index f69dd07f16..f00fc35ec6 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -476,9 +476,6 @@ Emu\SysCalls\currently_unused - - Utilities - Emu\SysCalls\lv2 @@ -884,6 +881,9 @@ Emu\SysCalls\Modules + + Utilities + @@ -1267,9 +1267,6 @@ Emu\SysCalls - - Utilities - Emu\SysCalls\lv2 @@ -1738,5 +1735,8 @@ Emu\SysCalls\Modules + + Utilities + \ No newline at end of file