NV2A : Updated locks
This commit is contained in:
parent
11b0e080be
commit
0ac96332a7
|
@ -7,7 +7,8 @@ typedef struct RAMHTEntry {
|
|||
} RAMHTEntry;
|
||||
|
||||
static void pfifo_run_pusher(NV2AState *d); // forward declaration
|
||||
int pfifo_puller_thread(void *opaque);
|
||||
int pfifo_puller_thread(NV2AState *d);
|
||||
static uint32_t ramht_hash(NV2AState *d, uint32_t handle);
|
||||
static RAMHTEntry ramht_lookup(NV2AState *d, uint32_t handle); // forward declaration
|
||||
|
||||
DEVICE_READ32(PFIFO)
|
||||
|
@ -36,12 +37,11 @@ DEVICE_READ32(PFIFO)
|
|||
SET_MASK(result, NV_PFIFO_CACHE1_PUSH1_MODE, d->pfifo.cache1.mode);
|
||||
break;
|
||||
case NV_PFIFO_CACHE1_STATUS: {
|
||||
std::lock_guard<std::mutex> lk(d->pfifo.cache1.mutex);
|
||||
|
||||
d->pfifo.cache1.cache_lock.lock();
|
||||
if (d->pfifo.cache1.cache.empty()) {
|
||||
result |= NV_PFIFO_CACHE1_STATUS_LOW_MARK; /* low mark empty */
|
||||
}
|
||||
|
||||
d->pfifo.cache1.cache_lock.unlock();
|
||||
} break;
|
||||
case NV_PFIFO_CACHE1_DMA_PUSH:
|
||||
SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS,
|
||||
|
@ -77,15 +77,16 @@ DEVICE_READ32(PFIFO)
|
|||
| d->pfifo.cache1.subroutine_active;
|
||||
break;
|
||||
case NV_PFIFO_CACHE1_PULL0: {
|
||||
std::lock_guard<std::mutex> lk(d->pfifo.cache1.mutex);
|
||||
d->pfifo.cache1.cache_lock.lock();
|
||||
result = d->pfifo.cache1.pull_enabled;
|
||||
d->pfifo.cache1.cache_lock.unlock();
|
||||
} break;
|
||||
case NV_PFIFO_CACHE1_ENGINE: {
|
||||
std::lock_guard<std::mutex> lk(d->pfifo.cache1.mutex);
|
||||
d->pfifo.cache1.cache_lock.lock();
|
||||
for (int i = 0; i < NV2A_NUM_SUBCHANNELS; i++) {
|
||||
result |= d->pfifo.cache1.bound_engines[i] << (i * 2);
|
||||
}
|
||||
|
||||
d->pfifo.cache1.cache_lock.unlock();
|
||||
} break;
|
||||
case NV_PFIFO_CACHE1_DMA_DCOUNT:
|
||||
result = d->pfifo.cache1.dcount;
|
||||
|
@ -155,7 +156,7 @@ DEVICE_WRITE32(PFIFO)
|
|||
d->pfifo.cache1.subroutine_active = (value & NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE);
|
||||
break;
|
||||
case NV_PFIFO_CACHE1_PULL0: {
|
||||
std::lock_guard<std::mutex> lk(d->pfifo.cache1.mutex);
|
||||
d->pfifo.cache1.cache_lock.lock();
|
||||
|
||||
if ((value & NV_PFIFO_CACHE1_PULL0_ACCESS)
|
||||
&& !d->pfifo.cache1.pull_enabled) {
|
||||
|
@ -167,14 +168,14 @@ DEVICE_WRITE32(PFIFO)
|
|||
&& d->pfifo.cache1.pull_enabled) {
|
||||
d->pfifo.cache1.pull_enabled = false;
|
||||
}
|
||||
d->pfifo.cache1.cache_lock.unlock();
|
||||
} break;
|
||||
case NV_PFIFO_CACHE1_ENGINE: {
|
||||
std::lock_guard<std::mutex> lk(d->pfifo.cache1.mutex);
|
||||
|
||||
d->pfifo.cache1.cache_lock.lock();
|
||||
for (int i = 0; i < NV2A_NUM_SUBCHANNELS; i++) {
|
||||
d->pfifo.cache1.bound_engines[i] = (FIFOEngine)((value >> (i * 2)) & 3);
|
||||
}
|
||||
|
||||
d->pfifo.cache1.cache_lock.unlock();
|
||||
} break;
|
||||
case NV_PFIFO_CACHE1_DMA_DCOUNT:
|
||||
d->pfifo.cache1.dcount = (value & NV_PFIFO_CACHE1_DMA_DCOUNT_VALUE);
|
||||
|
@ -251,10 +252,10 @@ static void pfifo_run_pusher(NV2AState *d) {
|
|||
command->subchannel = state->subchannel;
|
||||
command->nonincreasing = state->method_nonincreasing;
|
||||
command->parameter = word;
|
||||
|
||||
std::lock_guard<std::mutex> lk(state->mutex);
|
||||
state->cache_lock.lock();
|
||||
state->cache.push(command);
|
||||
state->cache_cond.notify_all();
|
||||
state->cache_lock.unlock();
|
||||
|
||||
if (!state->method_nonincreasing) {
|
||||
state->method += 4;
|
||||
|
@ -338,29 +339,27 @@ static void pfifo_run_pusher(NV2AState *d) {
|
|||
}
|
||||
}
|
||||
|
||||
int pfifo_puller_thread(void *opaque)
|
||||
int pfifo_puller_thread(NV2AState *d)
|
||||
{
|
||||
CxbxSetThreadName("Cxbx NV2A FIFO");
|
||||
|
||||
NV2AState *d = (NV2AState *)opaque;
|
||||
Cache1State *state = &d->pfifo.cache1;
|
||||
|
||||
while (true) {
|
||||
// Scope the lock so that it automatically unlocks at tne end of this block
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(state->mutex);
|
||||
|
||||
while (state->cache.empty() || !state->pull_enabled) {
|
||||
state->cache_cond.wait(lk);
|
||||
}
|
||||
|
||||
// Copy cache to working_cache
|
||||
while (!state->cache.empty()) {
|
||||
state->working_cache.push(state->cache.front());
|
||||
state->cache.pop();
|
||||
}
|
||||
state->cache_lock.lock();
|
||||
while (state->cache.empty() || !state->pull_enabled) {
|
||||
state->cache_cond.wait(state->cache_lock);
|
||||
}
|
||||
|
||||
// Copy cache to working_cache
|
||||
while (!state->cache.empty()) {
|
||||
state->working_cache.push(state->cache.front());
|
||||
state->cache.pop();
|
||||
}
|
||||
state->cache_lock.unlock();
|
||||
|
||||
d->pgraph.lock.lock();
|
||||
|
||||
while (!state->working_cache.empty()) {
|
||||
CacheEntry* command = state->working_cache.front();
|
||||
state->working_cache.pop();
|
||||
|
@ -385,9 +384,10 @@ int pfifo_puller_thread(void *opaque)
|
|||
}
|
||||
|
||||
/* the engine is bound to the subchannel */
|
||||
std::lock_guard<std::mutex> lk(state->mutex);
|
||||
state->cache_lock.lock();
|
||||
state->bound_engines[command->subchannel] = entry.engine;
|
||||
state->last_engine = entry.engine;
|
||||
state->cache_lock.unlock();
|
||||
} else if (command->method >= 0x100) {
|
||||
/* method passed to engine */
|
||||
|
||||
|
@ -427,6 +427,9 @@ int pfifo_puller_thread(void *opaque)
|
|||
}
|
||||
}
|
||||
|
||||
d->pgraph.lock.unlock();
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
// TODO : Replace these functions by something equivalent
|
||||
#define qemu_mutex_lock_iothread()
|
||||
#define qemu_mutex_unlock_iothread()
|
||||
#define NV2A_DPRINTF printf
|
||||
|
||||
static const GLenum pgraph_texture_min_filter_map[] = {
|
||||
0,
|
||||
GL_NEAREST,
|
||||
|
@ -176,7 +181,7 @@ static void pgraph_method_log(unsigned int subchannel, unsigned int graphics_cla
|
|||
|
||||
DEVICE_READ32(PGRAPH)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(d->pgraph.mutex);
|
||||
d->pgraph.lock.lock();
|
||||
|
||||
DEVICE_READ32_SWITCH() {
|
||||
case NV_PGRAPH_INTR:
|
||||
|
@ -215,6 +220,8 @@ DEVICE_READ32(PGRAPH)
|
|||
DEVICE_READ32_REG(pgraph); // Was : DEBUG_READ32_UNHANDLED(PGRAPH);
|
||||
}
|
||||
|
||||
d->pgraph.lock.unlock();
|
||||
|
||||
DEVICE_READ32_END(PGRAPH);
|
||||
}
|
||||
|
||||
|
@ -227,7 +234,7 @@ static void pgraph_set_context_user(NV2AState *d, uint32_t value)
|
|||
|
||||
DEVICE_WRITE32(PGRAPH)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(d->pgraph.mutex);
|
||||
d->pgraph.lock.lock();
|
||||
|
||||
switch (addr) {
|
||||
case NV_PGRAPH_INTR:
|
||||
|
@ -289,13 +296,13 @@ DEVICE_WRITE32(PGRAPH)
|
|||
break;
|
||||
}
|
||||
|
||||
d->pgraph.lock.unlock();
|
||||
|
||||
DEVICE_WRITE32_END(PGRAPH);
|
||||
}
|
||||
|
||||
static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int method, uint32_t parameter)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(d->pgraph.mutex);
|
||||
|
||||
// int i;
|
||||
GraphicsSubchannel *subchannel_data;
|
||||
GraphicsObject *object;
|
||||
|
@ -317,9 +324,9 @@ static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int me
|
|||
if (method == NV_SET_OBJECT) {
|
||||
subchannel_data->object_instance = parameter;
|
||||
|
||||
//qemu_mutex_lock_iothread();
|
||||
qemu_mutex_lock_iothread();
|
||||
load_graphics_object(d, parameter, object);
|
||||
//qemu_mutex_unlock_iothread();
|
||||
qemu_mutex_unlock_iothread();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -440,6 +447,73 @@ static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int me
|
|||
|
||||
case NV_KELVIN_PRIMITIVE: {
|
||||
switch (method) {
|
||||
case NV097_NO_OPERATION:
|
||||
/* The bios uses nop as a software method call -
|
||||
* it seems to expect a notify interrupt if the parameter isn't 0.
|
||||
* According to a nouveau guy it should still be a nop regardless
|
||||
* of the parameter. It's possible a debug register enables this,
|
||||
* but nothing obvious sticks out. Weird.
|
||||
*/
|
||||
if (parameter != 0) {
|
||||
assert(!(pg->pending_interrupts & NV_PGRAPH_INTR_ERROR));
|
||||
|
||||
|
||||
pg->trapped_channel_id = pg->channel_id;
|
||||
pg->trapped_subchannel = subchannel;
|
||||
pg->trapped_method = method;
|
||||
pg->trapped_data[0] = parameter;
|
||||
pg->notify_source = NV_PGRAPH_NSOURCE_NOTIFICATION; /* TODO: check this */
|
||||
pg->pending_interrupts |= NV_PGRAPH_INTR_ERROR;
|
||||
|
||||
pg->lock.unlock();
|
||||
qemu_mutex_lock_iothread();
|
||||
update_irq(d);
|
||||
pg->lock.lock();
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
while (pg->pending_interrupts & NV_PGRAPH_INTR_ERROR) {
|
||||
d->pgraph.interrupt_cond.wait(pg->lock);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NV097_WAIT_FOR_IDLE:
|
||||
pgraph_update_surface(d, false, true, true);
|
||||
break;
|
||||
|
||||
|
||||
case NV097_SET_FLIP_READ:
|
||||
SET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_READ_3D,
|
||||
parameter);
|
||||
break;
|
||||
case NV097_SET_FLIP_WRITE:
|
||||
SET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_WRITE_3D,
|
||||
parameter);
|
||||
break;
|
||||
case NV097_SET_FLIP_MODULO:
|
||||
SET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_MODULO_3D,
|
||||
parameter);
|
||||
break;
|
||||
case NV097_FLIP_INCREMENT_WRITE: {
|
||||
NV2A_DPRINTF("flip increment write %d -> ",
|
||||
GET_MASK(pg->regs[NV_PGRAPH_SURFACE],
|
||||
NV_PGRAPH_SURFACE_WRITE_3D));
|
||||
SET_MASK(pg->regs[NV_PGRAPH_SURFACE],
|
||||
NV_PGRAPH_SURFACE_WRITE_3D,
|
||||
(GET_MASK(pg->regs[NV_PGRAPH_SURFACE],
|
||||
NV_PGRAPH_SURFACE_WRITE_3D)+1)
|
||||
% GET_MASK(pg->regs[NV_PGRAPH_SURFACE],
|
||||
NV_PGRAPH_SURFACE_MODULO_3D) );
|
||||
NV2A_DPRINTF("%d\n",
|
||||
GET_MASK(pg->regs[NV_PGRAPH_SURFACE],
|
||||
NV_PGRAPH_SURFACE_WRITE_3D));
|
||||
|
||||
// if (glFrameTerminatorGREMEDY) {
|
||||
// glFrameTerminatorGREMEDY();
|
||||
// }
|
||||
|
||||
break;
|
||||
}
|
||||
case NV097_SET_CONTEXT_DMA_NOTIFIES:
|
||||
kelvin->dma_notifies = parameter;
|
||||
break;
|
||||
|
@ -968,7 +1042,7 @@ static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int me
|
|||
pgraph_update_surface(d, false, true, true);
|
||||
|
||||
//qemu_mutex_unlock(&d->pg->lock);
|
||||
//qemu_mutex_lock_iothread();
|
||||
qemu_mutex_lock_iothread();
|
||||
|
||||
xbaddr semaphore_dma_len;
|
||||
uint8_t *semaphore_data = (uint8_t*)nv_dma_map(d, kelvin->dma_semaphore,
|
||||
|
@ -979,7 +1053,7 @@ static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int me
|
|||
stl_le_p((uint32_t*)semaphore_data, parameter);
|
||||
|
||||
//qemu_mutex_lock(&d->pg->lock);
|
||||
//qemu_mutex_unlock_iothread();
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -1131,43 +1205,42 @@ static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int me
|
|||
EmuWarning("EmuNV2A: Unknown Graphics Class/Method 0x%08X/0x%08X\n", object->graphics_class, method);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void pgraph_context_switch(NV2AState *d, unsigned int channel_id)
|
||||
{
|
||||
bool valid = false;
|
||||
|
||||
// Scope the lock so that it gets unlocked at end of this block
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(d->pgraph.mutex);
|
||||
d->pgraph.lock.lock(); // TODO : This isn't in xqemu / OpenXbox?
|
||||
|
||||
valid = d->pgraph.channel_valid && d->pgraph.channel_id == channel_id;
|
||||
if (!valid) {
|
||||
d->pgraph.trapped_channel_id = channel_id;
|
||||
}
|
||||
valid = d->pgraph.channel_valid && d->pgraph.channel_id == channel_id;
|
||||
if (!valid) {
|
||||
d->pgraph.trapped_channel_id = channel_id;
|
||||
}
|
||||
|
||||
d->pgraph.lock.unlock(); // TODO : This isn't in xqemu / OpenXbox?
|
||||
|
||||
if (!valid) {
|
||||
printf("puller needs to switch to ch %d\n", channel_id);
|
||||
|
||||
//qemu_mutex_lock_iothread();
|
||||
d->pgraph.lock.unlock()
|
||||
qemu_mutex_lock_iothread();
|
||||
d->pgraph.pending_interrupts |= NV_PGRAPH_INTR_CONTEXT_SWITCH;
|
||||
update_irq(d);
|
||||
|
||||
std::unique_lock<std::mutex> lk(d->pgraph.mutex);
|
||||
//qemu_mutex_unlock_iothread();
|
||||
d->pgraph.lock.lock();
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
while (d->pgraph.pending_interrupts & NV_PGRAPH_INTR_CONTEXT_SWITCH) {
|
||||
d->pgraph.interrupt_cond.wait(lk);
|
||||
d->pgraph.interrupt_cond.wait(d->pgraph.lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pgraph_wait_fifo_access(NV2AState *d) {
|
||||
std::unique_lock<std::mutex> lk(d->pgraph.mutex);
|
||||
|
||||
while (!d->pgraph.fifo_access) {
|
||||
d->pgraph.fifo_access_cond.wait(lk);
|
||||
d->pgraph.fifo_access_cond.wait(d->pgraph.lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -277,7 +277,7 @@ typedef struct GraphicsContext {
|
|||
|
||||
|
||||
typedef struct PGRAPHState {
|
||||
std::mutex mutex;
|
||||
std::unique_lock<std::mutex> lock;
|
||||
|
||||
uint32_t pending_interrupts;
|
||||
uint32_t enabled_interrupts;
|
||||
|
@ -295,6 +295,7 @@ typedef struct PGRAPHState {
|
|||
|
||||
bool fifo_access;
|
||||
std::condition_variable fifo_access_cond;
|
||||
|
||||
std::condition_variable flip_3d;
|
||||
|
||||
unsigned int channel_id;
|
||||
|
@ -419,7 +420,7 @@ typedef struct Cache1State {
|
|||
enum FIFOEngine last_engine;
|
||||
|
||||
/* The actual command queue */
|
||||
std::mutex mutex;
|
||||
std::unique_lock<std::mutex> cache_lock;
|
||||
std::condition_variable cache_cond;
|
||||
std::queue<CacheEntry*> cache;
|
||||
std::queue<CacheEntry*> working_cache;
|
||||
|
|
Loading…
Reference in New Issue