mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 11:36:13 +00:00
Some bicycle for PSV
This commit is contained in:
parent
8587ae5883
commit
78c37ff8b6
12 changed files with 300 additions and 110 deletions
|
@ -6,19 +6,10 @@ struct psv_cond_t
|
|||
u32 attr;
|
||||
s32 mutexId;
|
||||
|
||||
private:
|
||||
psv_cond_t() = delete;
|
||||
psv_cond_t(const psv_cond_t&) = delete;
|
||||
psv_cond_t(psv_cond_t&&) = delete;
|
||||
|
||||
psv_cond_t& operator =(const psv_cond_t&) = delete;
|
||||
psv_cond_t& operator =(psv_cond_t&&) = delete;
|
||||
|
||||
public:
|
||||
psv_cond_t(const char* name, u32 attr, s32 mutexId);
|
||||
void on_init(s32 id) {}
|
||||
void on_stop() {}
|
||||
|
||||
};
|
||||
|
||||
extern psv_object_list_t<psv_cond_t, SCE_KERNEL_THREADMGR_UID_CLASS_COND> g_psv_cond_list;
|
||||
typedef psv_object_list_t<psv_cond_t, SCE_KERNEL_THREADMGR_UID_CLASS_COND> psv_cond_list_t;
|
||||
|
||||
extern psv_cond_list_t g_psv_cond_list;
|
||||
|
|
|
@ -6,19 +6,10 @@ struct psv_event_flag_t
|
|||
u32 attr;
|
||||
u32 pattern;
|
||||
|
||||
private:
|
||||
psv_event_flag_t() = delete;
|
||||
psv_event_flag_t(const psv_event_flag_t&) = delete;
|
||||
psv_event_flag_t(psv_event_flag_t&&) = delete;
|
||||
|
||||
psv_event_flag_t& operator =(const psv_event_flag_t&) = delete;
|
||||
psv_event_flag_t& operator =(psv_event_flag_t&&) = delete;
|
||||
|
||||
public:
|
||||
psv_event_flag_t(const char* name, u32 attr, u32 pattern);
|
||||
void on_init(s32 id) {}
|
||||
void on_stop() {}
|
||||
|
||||
};
|
||||
|
||||
extern psv_object_list_t<psv_event_flag_t, SCE_KERNEL_THREADMGR_UID_CLASS_EVENT_FLAG> g_psv_ef_list;
|
||||
typedef psv_object_list_t<psv_event_flag_t, SCE_KERNEL_THREADMGR_UID_CLASS_EVENT_FLAG> psv_ef_list_t;
|
||||
|
||||
extern psv_ef_list_t g_psv_ef_list;
|
||||
|
|
|
@ -6,19 +6,10 @@ struct psv_mutex_t
|
|||
u32 attr;
|
||||
s32 count;
|
||||
|
||||
private:
|
||||
psv_mutex_t() = delete;
|
||||
psv_mutex_t(const psv_mutex_t&) = delete;
|
||||
psv_mutex_t(psv_mutex_t&&) = delete;
|
||||
|
||||
psv_mutex_t& operator =(const psv_mutex_t&) = delete;
|
||||
psv_mutex_t& operator =(psv_mutex_t&&) = delete;
|
||||
|
||||
public:
|
||||
psv_mutex_t(const char* name, u32 attr, s32 count);
|
||||
void on_init(s32 id) {}
|
||||
void on_stop() {}
|
||||
|
||||
};
|
||||
|
||||
extern psv_object_list_t<psv_mutex_t, SCE_KERNEL_THREADMGR_UID_CLASS_MUTEX> g_psv_mutex_list;
|
||||
typedef psv_object_list_t<psv_mutex_t, SCE_KERNEL_THREADMGR_UID_CLASS_MUTEX> psv_mutex_list_t;
|
||||
|
||||
extern psv_mutex_list_t g_psv_mutex_list;
|
||||
|
|
|
@ -7,19 +7,10 @@ struct psv_sema_t
|
|||
s32 value;
|
||||
s32 max;
|
||||
|
||||
private:
|
||||
psv_sema_t() = delete;
|
||||
psv_sema_t(const psv_sema_t&) = delete;
|
||||
psv_sema_t(psv_sema_t&&) = delete;
|
||||
|
||||
psv_sema_t& operator =(const psv_sema_t&) = delete;
|
||||
psv_sema_t& operator =(psv_sema_t&&) = delete;
|
||||
|
||||
public:
|
||||
psv_sema_t(const char* name, u32 attr, s32 init_value, s32 max_value);
|
||||
void on_init(s32 id) {}
|
||||
void on_stop() {}
|
||||
|
||||
};
|
||||
|
||||
extern psv_object_list_t<psv_sema_t, SCE_KERNEL_THREADMGR_UID_CLASS_SEMA> g_psv_sema_list;
|
||||
typedef psv_object_list_t<psv_sema_t, SCE_KERNEL_THREADMGR_UID_CLASS_SEMA> psv_sema_list_t;
|
||||
|
||||
extern psv_sema_list_t g_psv_sema_list;
|
||||
|
|
|
@ -398,11 +398,12 @@ s32 sceKernelCreateEventFlag(vm::psv::ptr<const char> pName, u32 attr, u32 initP
|
|||
{
|
||||
sceLibKernel.Error("sceKernelCreateEventFlag(pName=0x%x, attr=0x%x, initPattern=0x%x, pOptParam=0x%x)", pName, attr, initPattern, pOptParam);
|
||||
|
||||
std::shared_ptr<psv_event_flag_t> ef(new psv_event_flag_t(pName.get_ptr(), attr, initPattern));
|
||||
if (s32 id = g_psv_ef_list.add(new psv_event_flag_t(pName.get_ptr(), attr, initPattern), 0))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
const s32 id = g_psv_ef_list.add(ef);
|
||||
|
||||
return id;
|
||||
RETURN_ERROR(SCE_KERNEL_ERROR_ERROR);
|
||||
}
|
||||
|
||||
s32 sceKernelDeleteEventFlag(s32 evfId)
|
||||
|
@ -461,23 +462,31 @@ s32 sceKernelCreateSema(vm::psv::ptr<const char> pName, u32 attr, s32 initCount,
|
|||
{
|
||||
sceLibKernel.Error("sceKernelCreateSema(pName=0x%x, attr=0x%x, initCount=%d, maxCount=%d, pOptParam=0x%x)", pName, attr, initCount, maxCount, pOptParam);
|
||||
|
||||
std::shared_ptr<psv_sema_t> sema(new psv_sema_t(pName.get_ptr(), attr, initCount, maxCount));
|
||||
if (s32 id = g_psv_sema_list.add(new psv_sema_t(pName.get_ptr(), attr, initCount, maxCount), 0))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
const s32 id = g_psv_sema_list.add(sema);
|
||||
|
||||
return id;
|
||||
RETURN_ERROR(SCE_KERNEL_ERROR_ERROR);
|
||||
}
|
||||
|
||||
s32 sceKernelDeleteSema(s32 semaId)
|
||||
{
|
||||
sceLibKernel.Error("sceKernelDeleteSema(semaId=0x%x)", semaId);
|
||||
|
||||
ref_t<psv_sema_t> sema = g_psv_sema_list.get(semaId);
|
||||
|
||||
if (!sema)
|
||||
{
|
||||
RETURN_ERROR(SCE_KERNEL_ERROR_INVALID_UID);
|
||||
}
|
||||
|
||||
if (!g_psv_sema_list.remove(semaId))
|
||||
{
|
||||
RETURN_ERROR(SCE_KERNEL_ERROR_INVALID_UID);
|
||||
}
|
||||
|
||||
throw SCE_OK;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
s32 sceKernelOpenSema(vm::psv::ptr<const char> pName)
|
||||
|
@ -492,7 +501,18 @@ s32 sceKernelCloseSema(s32 semaId)
|
|||
|
||||
s32 sceKernelWaitSema(s32 semaId, s32 needCount, vm::psv::ptr<u32> pTimeout)
|
||||
{
|
||||
throw __FUNCTION__;
|
||||
sceLibKernel.Error("sceKernelWaitSema(semaId=0x%x, needCount=%d, pTimeout=0x%x)", semaId, needCount, pTimeout);
|
||||
|
||||
ref_t<psv_sema_t> sema = g_psv_sema_list.get(semaId);
|
||||
|
||||
if (!sema)
|
||||
{
|
||||
RETURN_ERROR(SCE_KERNEL_ERROR_INVALID_UID);
|
||||
}
|
||||
|
||||
sceLibKernel.Error("*** name = %s", sema->name);
|
||||
Emu.Pause();
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
s32 sceKernelWaitSemaCB(s32 semaId, s32 needCount, vm::psv::ptr<u32> pTimeout)
|
||||
|
@ -526,11 +546,12 @@ s32 sceKernelCreateMutex(vm::psv::ptr<const char> pName, u32 attr, s32 initCount
|
|||
{
|
||||
sceLibKernel.Error("sceKernelCreateMutex(pName=0x%x, attr=0x%x, initCount=%d, pOptParam=0x%x)", pName, attr, initCount, pOptParam);
|
||||
|
||||
std::shared_ptr<psv_mutex_t> mutex(new psv_mutex_t(pName.get_ptr(), attr, initCount));
|
||||
if (s32 id = g_psv_mutex_list.add(new psv_mutex_t(pName.get_ptr(), attr, initCount), 0))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
const s32 id = g_psv_mutex_list.add(mutex);
|
||||
|
||||
return id;
|
||||
RETURN_ERROR(SCE_KERNEL_ERROR_ERROR);
|
||||
}
|
||||
|
||||
s32 sceKernelDeleteMutex(s32 mutexId)
|
||||
|
@ -626,11 +647,12 @@ s32 sceKernelCreateCond(vm::psv::ptr<const char> pName, u32 attr, s32 mutexId, v
|
|||
{
|
||||
sceLibKernel.Error("sceKernelCreateCond(pName=0x%x, attr=0x%x, mutexId=0x%x, pOptParam=0x%x)", pName, attr, mutexId, pOptParam);
|
||||
|
||||
std::shared_ptr<psv_cond_t> cond(new psv_cond_t(pName.get_ptr(), attr, mutexId));
|
||||
if (s32 id = g_psv_cond_list.add(new psv_cond_t(pName.get_ptr(), attr, mutexId), 0))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
const s32 id = g_psv_cond_list.add(cond);
|
||||
|
||||
return id;
|
||||
RETURN_ERROR(SCE_KERNEL_ERROR_ERROR);
|
||||
}
|
||||
|
||||
s32 sceKernelDeleteCond(s32 condId)
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
#include "Modules/psv_mutex.h"
|
||||
#include "Modules/psv_cond.h"
|
||||
|
||||
psv_object_list_t<psv_sema_t, SCE_KERNEL_THREADMGR_UID_CLASS_SEMA> g_psv_sema_list;
|
||||
psv_object_list_t<psv_event_flag_t, SCE_KERNEL_THREADMGR_UID_CLASS_EVENT_FLAG> g_psv_ef_list;
|
||||
psv_object_list_t<psv_mutex_t, SCE_KERNEL_THREADMGR_UID_CLASS_MUTEX> g_psv_mutex_list;
|
||||
psv_object_list_t<psv_cond_t, SCE_KERNEL_THREADMGR_UID_CLASS_COND> g_psv_cond_list;
|
||||
psv_sema_list_t g_psv_sema_list;
|
||||
psv_ef_list_t g_psv_ef_list;
|
||||
psv_mutex_list_t g_psv_mutex_list;
|
||||
psv_cond_list_t g_psv_cond_list;
|
||||
|
||||
void clear_all_psv_objects()
|
||||
{
|
||||
|
|
|
@ -24,12 +24,26 @@ union psv_uid_t
|
|||
template<typename T, u32 type>
|
||||
class psv_object_list_t // Class for managing object data
|
||||
{
|
||||
std::array<std::shared_ptr<T>, 0x8000> m_data;
|
||||
public:
|
||||
typedef refcounter_t<T> rc_type;
|
||||
typedef ref_t<T> ref_type;
|
||||
|
||||
static const u32 max = 0x8000;
|
||||
|
||||
private:
|
||||
std::array<rc_type, max> m_data;
|
||||
std::atomic<u32> m_hint; // guessing next free position
|
||||
std::mutex m_mutex; // TODO: remove it when shared_ptr atomic ops are fully available
|
||||
|
||||
void error(s32 uid)
|
||||
{
|
||||
throw fmt::format("Invalid UID requested (type=0x%x, uid=0x%x)", type, uid);
|
||||
}
|
||||
|
||||
public:
|
||||
psv_object_list_t() : m_hint(0) {}
|
||||
psv_object_list_t()
|
||||
: m_hint(0)
|
||||
{
|
||||
}
|
||||
|
||||
psv_object_list_t(const psv_object_list_t&) = delete;
|
||||
psv_object_list_t(psv_object_list_t&&) = delete;
|
||||
|
@ -40,7 +54,7 @@ public:
|
|||
public:
|
||||
static const u32 uid_class = type;
|
||||
|
||||
// check if UID is potentially valid (will return true if the object doesn't exist)
|
||||
// check if UID is potentially valid (will return true even if the object doesn't exist)
|
||||
bool check(s32 uid)
|
||||
{
|
||||
const psv_uid_t id = psv_uid_t::make(uid);
|
||||
|
@ -49,76 +63,65 @@ public:
|
|||
return !id.sign && id.type == uid_class && id.oddness == 1;
|
||||
}
|
||||
|
||||
// share object with UID specified (will return empty pointer if the object doesn't exist or the UID is invalid)
|
||||
std::shared_ptr<T> find(s32 uid)
|
||||
// share object with UID specified
|
||||
ref_type get(s32 uid)
|
||||
{
|
||||
if (!check(uid))
|
||||
{
|
||||
return nullptr;
|
||||
return ref_type();
|
||||
}
|
||||
|
||||
return m_data[psv_uid_t::make(uid).number];
|
||||
return &m_data[psv_uid_t::make(uid).number];
|
||||
}
|
||||
|
||||
std::shared_ptr<T> operator [](s32 uid)
|
||||
ref_type operator [](s32 uid)
|
||||
{
|
||||
return find(uid);
|
||||
}
|
||||
|
||||
// generate UID for newly created object (will return zero if the limit exceeded)
|
||||
s32 add(std::shared_ptr<T>& data)
|
||||
s32 add(T* data, s32 error_code)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
for (u32 i = 0, j = m_hint % m_data.size(); i < m_data.size(); i++, j = (j + 1) % m_data.size())
|
||||
for (u32 i = 0, j = m_hint; i < m_data.size(); i++, j = (j + 1) % m_data.size())
|
||||
{
|
||||
// find an empty position and copy the pointer
|
||||
if (!m_data[j])
|
||||
if (m_data[j].try_set(data))
|
||||
{
|
||||
m_data[j] = data;
|
||||
m_hint = j + 1; // guess next position
|
||||
psv_uid_t id = psv_uid_t::make(1); // odd number
|
||||
id.type = uid_class; // set type
|
||||
id.number = j; // set position
|
||||
data->on_init(id.uid); // save UID
|
||||
m_hint = (j + 1) % m_data.size(); // guess next position
|
||||
|
||||
psv_uid_t id = psv_uid_t::make(1); // make UID
|
||||
id.type = uid_class;
|
||||
id.number = j;
|
||||
|
||||
return id.uid; // return UID
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
delete data;
|
||||
return error_code;
|
||||
}
|
||||
|
||||
// remove object with UID specified and share it for the last time (will return empty pointer if the object doesn't exists or the UID is invalid)
|
||||
std::shared_ptr<T> remove(s32 uid)
|
||||
// remove object with specified UID
|
||||
bool remove(s32 uid)
|
||||
{
|
||||
if (!check(uid))
|
||||
{
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 pos = psv_uid_t::make(uid).number;
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_hint = std::min<u32>(pos, m_hint);
|
||||
|
||||
std::shared_ptr<T> old_ptr = nullptr;
|
||||
m_data[pos].swap(old_ptr);
|
||||
m_hint = pos;
|
||||
return old_ptr;
|
||||
return m_data[pos].try_remove();
|
||||
}
|
||||
|
||||
// remove all objects
|
||||
void clear()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
for (auto& object : m_data)
|
||||
for (auto& v : m_data)
|
||||
{
|
||||
if (object)
|
||||
{
|
||||
object->on_stop();
|
||||
}
|
||||
|
||||
object = nullptr;
|
||||
v.try_remove();
|
||||
}
|
||||
|
||||
m_hint = 0;
|
||||
|
|
|
@ -832,7 +832,7 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
|
|||
if (mfc_queue[i].second.tag == value)
|
||||
{
|
||||
do_dma_list_cmd(mfc_queue[i].first, mfc_queue[i].second);
|
||||
mfc_queue[i].second.tag = ~0;
|
||||
mfc_queue[i].second.tag = 0xdead;
|
||||
processed++;
|
||||
}
|
||||
}
|
||||
|
@ -841,7 +841,7 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
|
|||
{
|
||||
for (size_t i = 0; i < mfc_queue.size(); i++)
|
||||
{
|
||||
if (mfc_queue[i].second.tag == ~0)
|
||||
if (mfc_queue[i].second.tag == 0xdead)
|
||||
{
|
||||
mfc_queue.erase(mfc_queue.begin() + i);
|
||||
processed--;
|
||||
|
|
196
rpcs3/Emu/Memory/refcnt.h
Normal file
196
rpcs3/Emu/Memory/refcnt.h
Normal file
|
@ -0,0 +1,196 @@
|
|||
#pragma once
|
||||
#include "atomic.h"
|
||||
|
||||
// run endless loop for debugging
|
||||
__forceinline static void deadlock()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class ref_t;
|
||||
|
||||
template<typename T>
|
||||
class refcounter_t // non-relocateable "smart" pointer with ref counter
|
||||
{
|
||||
public:
|
||||
typedef T type, * p_type;
|
||||
typedef refcounter_t<T> rc_type;
|
||||
|
||||
// counter > 0, ptr != nullptr : object exists and shared
|
||||
// counter > 0, ptr == nullptr : object exists and shared, but not owned by refcounter_t
|
||||
// counter == 0, ptr != nullptr : object exists and not shared
|
||||
// counter == 0, ptr == nullptr : object doesn't exist
|
||||
// counter < 0 : bad state, used to provoke error for debugging
|
||||
|
||||
struct sync_var_t
|
||||
{
|
||||
s64 counter;
|
||||
p_type ptr;
|
||||
};
|
||||
|
||||
private:
|
||||
atomic_le_t<sync_var_t> m_var;
|
||||
|
||||
friend class ref_t<T>;
|
||||
|
||||
// try to share object (increment counter), returns nullptr if doesn't exist or cannot be shared
|
||||
__forceinline p_type ref_inc()
|
||||
{
|
||||
p_type out_ptr;
|
||||
|
||||
m_var.atomic_op([&out_ptr](sync_var_t& v)
|
||||
{
|
||||
assert(v.counter >= 0);
|
||||
|
||||
if ((out_ptr = v.ptr))
|
||||
{
|
||||
v.counter++;
|
||||
}
|
||||
});
|
||||
|
||||
return out_ptr;
|
||||
}
|
||||
|
||||
// try to release previously shared object (decrement counter), returns true if should be deleted
|
||||
__forceinline bool ref_dec()
|
||||
{
|
||||
bool do_delete;
|
||||
|
||||
m_var.atomic_op([&do_delete](sync_var_t& v)
|
||||
{
|
||||
assert(v.counter > 0);
|
||||
|
||||
do_delete = !--v.counter && !v.ptr;
|
||||
});
|
||||
|
||||
return do_delete;
|
||||
}
|
||||
|
||||
public:
|
||||
refcounter_t()
|
||||
{
|
||||
// initialize ref counter
|
||||
m_var.write_relaxed({ 0, nullptr });
|
||||
}
|
||||
|
||||
~refcounter_t()
|
||||
{
|
||||
// set bad state
|
||||
auto ref = m_var.exchange({ -1, nullptr });
|
||||
|
||||
// finalize
|
||||
if (ref.counter)
|
||||
{
|
||||
deadlock();
|
||||
}
|
||||
else if (ref.ptr)
|
||||
{
|
||||
delete ref.ptr;
|
||||
}
|
||||
}
|
||||
|
||||
refcounter_t(const rc_type& right) = delete;
|
||||
refcounter_t(rc_type&& right_rv) = delete;
|
||||
|
||||
rc_type& operator =(const rc_type& right) = delete;
|
||||
rc_type& operator =(rc_type&& right_rv) = delete;
|
||||
|
||||
public:
|
||||
// try to set new object (if it doesn't exist)
|
||||
bool try_set(p_type ptr)
|
||||
{
|
||||
return m_var.compare_and_swap_test({ 0, nullptr }, { 0, ptr });
|
||||
}
|
||||
|
||||
// try to remove object (if exists)
|
||||
bool try_remove()
|
||||
{
|
||||
bool out_res;
|
||||
p_type out_ptr;
|
||||
|
||||
m_var.atomic_op([&out_res, &out_ptr](sync_var_t& v)
|
||||
{
|
||||
out_res = (out_ptr = v.ptr);
|
||||
|
||||
if (v.counter)
|
||||
{
|
||||
out_ptr = nullptr;
|
||||
}
|
||||
|
||||
v.ptr = nullptr;
|
||||
});
|
||||
|
||||
if (out_ptr)
|
||||
{
|
||||
delete out_ptr;
|
||||
}
|
||||
|
||||
return out_res;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class ref_t
|
||||
{
|
||||
public:
|
||||
typedef T type, * p_type;
|
||||
typedef refcounter_t<T> * rc_type;
|
||||
|
||||
private:
|
||||
rc_type m_rc;
|
||||
p_type m_ptr;
|
||||
|
||||
public:
|
||||
ref_t()
|
||||
: m_rc(nullptr)
|
||||
, m_ptr(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
ref_t(rc_type rc)
|
||||
: m_rc(rc)
|
||||
, m_ptr(rc->ref_inc())
|
||||
{
|
||||
}
|
||||
|
||||
~ref_t()
|
||||
{
|
||||
if (m_ptr && m_rc->ref_dec())
|
||||
{
|
||||
delete m_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
ref_t(const ref_t& right) = delete;
|
||||
|
||||
ref_t(ref_t&& right_rv)
|
||||
: m_rc(right_rv.m_rc)
|
||||
, m_ptr(right_rv.m_ptr)
|
||||
{
|
||||
right_rv.m_rc = nullptr;
|
||||
right_rv.m_ptr = nullptr;
|
||||
}
|
||||
|
||||
ref_t& operator =(const ref_t& right) = delete;
|
||||
ref_t& operator =(ref_t&& right_rv) = delete;
|
||||
|
||||
public:
|
||||
T& operator *() const
|
||||
{
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
T* operator ->() const
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
};
|
|
@ -411,6 +411,7 @@
|
|||
<ClInclude Include="Emu\Memory\Memory.h" />
|
||||
<ClInclude Include="Emu\Memory\MemoryBlock.h" />
|
||||
<ClInclude Include="Emu\Memory\atomic.h" />
|
||||
<ClInclude Include="Emu\Memory\refcnt.h" />
|
||||
<ClInclude Include="Emu\RSX\CgBinaryProgram.h" />
|
||||
<ClInclude Include="Emu\RSX\GCM.h" />
|
||||
<ClInclude Include="Emu\RSX\GL\GLBuffers.h" />
|
||||
|
|
|
@ -1540,5 +1540,8 @@
|
|||
<ClInclude Include="Emu\Memory\atomic.h">
|
||||
<Filter>Emu\Memory</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Memory\refcnt.h">
|
||||
<Filter>Emu\Memory</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -63,6 +63,7 @@ template<typename T> __forceinline T align(const T addr, int align)
|
|||
#include "Utilities/StrFmt.h"
|
||||
|
||||
#include "Emu/Memory/atomic.h"
|
||||
#include "Emu/Memory/refcnt.h"
|
||||
|
||||
#define _PRGNAME_ "RPCS3"
|
||||
#define _PRGVER_ "0.0.0.5"
|
||||
|
|
Loading…
Add table
Reference in a new issue