From 0ac96332a7f4f61dc58f34905d8ffd9acd03ef17 Mon Sep 17 00:00:00 2001 From: PatrickvL Date: Thu, 1 Feb 2018 18:14:22 +0100 Subject: [PATCH] NV2A : Updated locks --- src/devices/video/EmuNV2A_PFIFO.cpp | 61 +++++++------- src/devices/video/EmuNV2A_PGRAPH.cpp | 117 ++++++++++++++++++++++----- src/devices/video/nv2a.h | 5 +- 3 files changed, 130 insertions(+), 53 deletions(-) diff --git a/src/devices/video/EmuNV2A_PFIFO.cpp b/src/devices/video/EmuNV2A_PFIFO.cpp index 6d8a3aef1..dfaad892e 100644 --- a/src/devices/video/EmuNV2A_PFIFO.cpp +++ b/src/devices/video/EmuNV2A_PFIFO.cpp @@ -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 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 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 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 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 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 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 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 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; } diff --git a/src/devices/video/EmuNV2A_PGRAPH.cpp b/src/devices/video/EmuNV2A_PGRAPH.cpp index eb5c35efa..d17f0ddb7 100644 --- a/src/devices/video/EmuNV2A_PGRAPH.cpp +++ b/src/devices/video/EmuNV2A_PGRAPH.cpp @@ -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 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 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 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 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 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 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); } } diff --git a/src/devices/video/nv2a.h b/src/devices/video/nv2a.h index 611ce58d0..f4e9260eb 100644 --- a/src/devices/video/nv2a.h +++ b/src/devices/video/nv2a.h @@ -277,7 +277,7 @@ typedef struct GraphicsContext { typedef struct PGRAPHState { - std::mutex mutex; + std::unique_lock 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 cache_lock; std::condition_variable cache_cond; std::queue cache; std::queue working_cache;