diff --git a/src/devices/video/EmuNV2A.cpp b/src/devices/video/EmuNV2A.cpp index 68ac4a7cc..e20e48f80 100644 --- a/src/devices/video/EmuNV2A.cpp +++ b/src/devices/video/EmuNV2A.cpp @@ -642,6 +642,10 @@ static void nv2a_vblank_thread(NV2AState *d) } } +// See NV2ABlockInfo regions[] PRAMIN +#define NV_PRAMIN_ADDR 0x00700000 +#define NV_PRAMIN_SIZE 0x100000 + void CxbxReserveNV2AMemory(NV2AState *d) { // Reserve NV2A memory : @@ -673,7 +677,7 @@ void CxbxReserveNV2AMemory(NV2AState *d) } printf("[0x%.4X] INIT: Allocated %d MiB of Xbox NV2A PRAMIN memory at 0x%.8X to 0x%.8X\n", - GetCurrentThreadId(), NV_PRAMIN_SIZE / ONE_MB, NV2A_ADDR + NV_PRAMIN_ADDR, NV2A_ADDR + NV_PRAMIN_ADDR + NV_PRAMIN_SIZE - 1); + GetCurrentThreadId(), d->pramin.ramin_size / ONE_MB, d->pramin.ramin_ptr, d->pramin.ramin_ptr + d->pramin.ramin_size - 1); } void EmuNV2A_Init() @@ -682,6 +686,7 @@ void EmuNV2A_Init() d->pcrtc.start = 0; + d->vram_ptr = (uint8_t*)MM_SYSTEM_PHYSICAL_MAP; d->vram_size = (g_bIsChihiro || g_bIsDebug) ? CONTIGUOUS_MEMORY_CHIHIRO_SIZE : CONTIGUOUS_MEMORY_XBOX_SIZE; d->pramdac.core_clock_coeff = 0x00011c01; /* 189MHz...? */ diff --git a/src/devices/video/EmuNV2A_PFIFO.cpp b/src/devices/video/EmuNV2A_PFIFO.cpp index dfaad892e..5f2738d8d 100644 --- a/src/devices/video/EmuNV2A_PFIFO.cpp +++ b/src/devices/video/EmuNV2A_PFIFO.cpp @@ -36,13 +36,13 @@ DEVICE_READ32(PFIFO) SET_MASK(result, NV_PFIFO_CACHE1_PUSH1_CHID, d->pfifo.cache1.channel_id); SET_MASK(result, NV_PFIFO_CACHE1_PUSH1_MODE, d->pfifo.cache1.mode); break; - case NV_PFIFO_CACHE1_STATUS: { + case NV_PFIFO_CACHE1_STATUS: 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; + break; case NV_PFIFO_CACHE1_DMA_PUSH: SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS, d->pfifo.cache1.dma_push_enabled); @@ -76,18 +76,18 @@ DEVICE_READ32(PFIFO) result = d->pfifo.cache1.subroutine_return | d->pfifo.cache1.subroutine_active; break; - case NV_PFIFO_CACHE1_PULL0: { + case NV_PFIFO_CACHE1_PULL0: d->pfifo.cache1.cache_lock.lock(); result = d->pfifo.cache1.pull_enabled; d->pfifo.cache1.cache_lock.unlock(); - } break; - case NV_PFIFO_CACHE1_ENGINE: { + break; + case NV_PFIFO_CACHE1_ENGINE: 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; + break; case NV_PFIFO_CACHE1_DMA_DCOUNT: result = d->pfifo.cache1.dcount; break; @@ -110,6 +110,8 @@ DEVICE_READ32(PFIFO) DEVICE_WRITE32(PFIFO) { + int i; + switch(addr) { case NV_PFIFO_INTR_0: d->pfifo.pending_interrupts &= ~value; @@ -128,22 +130,31 @@ DEVICE_WRITE32(PFIFO) assert(d->pfifo.cache1.channel_id < NV2A_NUM_CHANNELS); break; case NV_PFIFO_CACHE1_DMA_PUSH: - d->pfifo.cache1.dma_push_enabled = GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS); - if (d->pfifo.cache1.dma_push_suspended && !GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_STATUS)) { + d->pfifo.cache1.dma_push_enabled = + GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS); + if (d->pfifo.cache1.dma_push_suspended + && !GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_STATUS)) { d->pfifo.cache1.dma_push_suspended = false; pfifo_run_pusher(d); } - d->pfifo.cache1.dma_push_suspended = GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_STATUS); + d->pfifo.cache1.dma_push_suspended = + GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_STATUS); break; case NV_PFIFO_CACHE1_DMA_STATE: - d->pfifo.cache1.method_nonincreasing = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE); - d->pfifo.cache1.method = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD) << 2; - d->pfifo.cache1.subchannel = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL); - d->pfifo.cache1.method_count = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT); - d->pfifo.cache1.error = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_ERROR); + d->pfifo.cache1.method_nonincreasing = + GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE); + d->pfifo.cache1.method = + GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD) << 2; + d->pfifo.cache1.subchannel = + GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL); + d->pfifo.cache1.method_count = + GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT); + d->pfifo.cache1.error = + GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_ERROR); break; case NV_PFIFO_CACHE1_DMA_INSTANCE: - d->pfifo.cache1.dma_instance = GET_MASK(value, NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MASK) << 4; + d->pfifo.cache1.dma_instance = + GET_MASK(value, NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MASK) << 4; break; case NV_PFIFO_CACHE1_DMA_PUT: d->user.channel_control[d->pfifo.cache1.channel_id].dma_put = value; @@ -152,12 +163,13 @@ DEVICE_WRITE32(PFIFO) d->user.channel_control[d->pfifo.cache1.channel_id].dma_get = value; break; case NV_PFIFO_CACHE1_DMA_SUBROUTINE: - d->pfifo.cache1.subroutine_return = (value & NV_PFIFO_CACHE1_DMA_SUBROUTINE_RETURN_OFFSET); - d->pfifo.cache1.subroutine_active = (value & NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE); + d->pfifo.cache1.subroutine_return = + (value & NV_PFIFO_CACHE1_DMA_SUBROUTINE_RETURN_OFFSET); + d->pfifo.cache1.subroutine_active = + (value & NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE); break; - case NV_PFIFO_CACHE1_PULL0: { + case NV_PFIFO_CACHE1_PULL0: d->pfifo.cache1.cache_lock.lock(); - if ((value & NV_PFIFO_CACHE1_PULL0_ACCESS) && !d->pfifo.cache1.pull_enabled) { d->pfifo.cache1.pull_enabled = true; @@ -169,19 +181,21 @@ DEVICE_WRITE32(PFIFO) d->pfifo.cache1.pull_enabled = false; } d->pfifo.cache1.cache_lock.unlock(); - } break; - case NV_PFIFO_CACHE1_ENGINE: { + break; + case NV_PFIFO_CACHE1_ENGINE: d->pfifo.cache1.cache_lock.lock(); - for (int i = 0; i < NV2A_NUM_SUBCHANNELS; i++) { + for (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; + break; case NV_PFIFO_CACHE1_DMA_DCOUNT: - d->pfifo.cache1.dcount = (value & NV_PFIFO_CACHE1_DMA_DCOUNT_VALUE); + d->pfifo.cache1.dcount = + (value & NV_PFIFO_CACHE1_DMA_DCOUNT_VALUE); break; case NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW: - d->pfifo.cache1.get_jmp_shadow = (value & NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW_OFFSET); + d->pfifo.cache1.get_jmp_shadow = + (value & NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW_OFFSET); break; case NV_PFIFO_CACHE1_DMA_RSVD_SHADOW: d->pfifo.cache1.rsvd_shadow = value; @@ -197,6 +211,7 @@ DEVICE_WRITE32(PFIFO) DEVICE_WRITE32_END(PFIFO); } + /* pusher should be fine to run from a mimo handler * whenever's it's convenient */ static void pfifo_run_pusher(NV2AState *d) { @@ -215,6 +230,7 @@ static void pfifo_run_pusher(NV2AState *d) { if (!state->push_enabled) return; + /* only handling DMA for now... */ /* Channel running DMA */ @@ -230,12 +246,13 @@ static void pfifo_run_pusher(NV2AState *d) { dma = (uint8_t*)nv_dma_map(d, state->dma_instance, &dma_len); - printf("DMA pusher: max 0x%08X, 0x%08X - 0x%08X\n", + NV2A_DPRINTF("DMA pusher: max 0x%08X, 0x%08X - 0x%08X\n", dma_len, control->dma_get, control->dma_put); /* based on the convenient pseudocode in envytools */ while (control->dma_get != control->dma_put) { if (control->dma_get >= dma_len) { + state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_PROTECTION; break; } @@ -247,7 +264,7 @@ static void pfifo_run_pusher(NV2AState *d) { /* data word of methods command */ state->data_shadow = word; - command = (CacheEntry*)calloc(1, sizeof(CacheEntry)); + command = (CacheEntry*)malloc(sizeof(CacheEntry)); command->method = state->method; command->subchannel = state->subchannel; command->nonincreasing = state->method_nonincreasing; @@ -271,15 +288,13 @@ static void pfifo_run_pusher(NV2AState *d) { /* old jump */ state->get_jmp_shadow = control->dma_get; control->dma_get = word & 0x1fffffff; - printf("pb OLD_JMP 0x%08X\n", control->dma_get); - } - else if ((word & 3) == 1) { + NV2A_DPRINTF("pb OLD_JMP 0x%08X\n", control->dma_get); + } else if ((word & 3) == 1) { /* jump */ state->get_jmp_shadow = control->dma_get; control->dma_get = word & 0xfffffffc; - printf("pb JMP 0x%08X\n", control->dma_get); - } - else if ((word & 3) == 2) { + NV2A_DPRINTF("pb JMP 0x%08X\n", control->dma_get); + } else if ((word & 3) == 2) { /* call */ if (state->subroutine_active) { state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_CALL; @@ -288,9 +303,8 @@ static void pfifo_run_pusher(NV2AState *d) { state->subroutine_return = control->dma_get; state->subroutine_active = true; control->dma_get = word & 0xfffffffc; - printf("pb CALL 0x%08X\n", control->dma_get); - } - else if (word == 0x00020000) { + NV2A_DPRINTF("pb CALL 0x%08X\n", control->dma_get); + } else if (word == 0x00020000) { /* return */ if (!state->subroutine_active) { state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_RETURN; @@ -298,9 +312,8 @@ static void pfifo_run_pusher(NV2AState *d) { } control->dma_get = state->subroutine_return; state->subroutine_active = false; - printf("pb RET 0x%08X\n", control->dma_get); - } - else if ((word & 0xe0030003) == 0) { + NV2A_DPRINTF("pb RET 0x%08X\n", control->dma_get); + } else if ((word & 0xe0030003) == 0) { /* increasing methods */ state->method = word & 0x1fff; state->subchannel = (word >> 13) & 7; @@ -315,9 +328,8 @@ static void pfifo_run_pusher(NV2AState *d) { state->method_count = (word >> 18) & 0x7ff; state->method_nonincreasing = true; state->dcount = 0; - } - else { - printf("pb reserved cmd 0x%08X - 0x%08X\n", + } else { + NV2A_DPRINTF("pb reserved cmd 0x%08X - 0x%08X\n", control->dma_get, word); state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_RESERVED_CMD; break; @@ -325,11 +337,11 @@ static void pfifo_run_pusher(NV2AState *d) { } } - printf("DMA pusher done: max 0x%08X, 0x%08X - 0x%08X\n", + NV2A_DPRINTF("DMA pusher done: max 0x%08X, 0x%08X - 0x%08X\n", dma_len, control->dma_get, control->dma_put); if (state->error) { - printf("pb error: %d\n", state->error); + NV2A_DPRINTF("pb error: %d\n", state->error); assert(false); state->dma_push_suspended = true; @@ -345,6 +357,8 @@ int pfifo_puller_thread(NV2AState *d) Cache1State *state = &d->pfifo.cache1; + // glo_set_current(d->pgraph.gl_context); + while (true) { state->cache_lock.lock(); while (state->cache.empty() || !state->pull_enabled) { @@ -411,7 +425,8 @@ int pfifo_puller_thread(NV2AState *d) switch (engine) { case ENGINE_GRAPHICS: pgraph_wait_fifo_access(d); - pgraph_method(d, command->subchannel, command->method, parameter); + pgraph_method(d, command->subchannel, + command->method, parameter); break; default: assert(false); @@ -425,17 +440,17 @@ int pfifo_puller_thread(NV2AState *d) free(command); } + + d->pgraph.lock.unlock(); } - d->pgraph.lock.unlock(); - - return 0; } static uint32_t ramht_hash(NV2AState *d, uint32_t handle) { - unsigned int ramht_size = 1 << (GET_MASK(d->pfifo.regs[NV_PFIFO_RAMHT], NV_PFIFO_RAMHT_SIZE_MASK) + 12); + unsigned int ramht_size = + 1 << (GET_MASK(d->pfifo.regs[NV_PFIFO_RAMHT], NV_PFIFO_RAMHT_SIZE_MASK) + 12); /* XXX: Think this is different to what nouveau calculates... */ unsigned int bits = ffs(ramht_size) - 2; @@ -452,7 +467,8 @@ static uint32_t ramht_hash(NV2AState *d, uint32_t handle) static RAMHTEntry ramht_lookup(NV2AState *d, uint32_t handle) { - unsigned int ramht_size = 1 << (GET_MASK(d->pfifo.regs[NV_PFIFO_RAMHT], NV_PFIFO_RAMHT_SIZE_MASK) + 12); + unsigned int ramht_size = + 1 << (GET_MASK(d->pfifo.regs[NV_PFIFO_RAMHT], NV_PFIFO_RAMHT_SIZE_MASK) + 12); uint32_t hash = ramht_hash(d, handle); assert(hash * 8 < ramht_size); @@ -475,4 +491,3 @@ static RAMHTEntry ramht_lookup(NV2AState *d, uint32_t handle) return entry; } - diff --git a/src/devices/video/EmuNV2A_PGRAPH.cpp b/src/devices/video/EmuNV2A_PGRAPH.cpp index d17f0ddb7..794097c62 100644 --- a/src/devices/video/EmuNV2A_PGRAPH.cpp +++ b/src/devices/video/EmuNV2A_PGRAPH.cpp @@ -1,7 +1,21 @@ -// TODO : Replace these functions by something equivalent +#undef COMPILE_OPENGL // define this to include all OpenGL calls + +// FIXME +#if 1 +#define qemu_mutex_lock_iothread(...) do { \ + d->io_lock.lock(); \ +} while (0) +#define qemu_mutex_unlock_iothread(...) do { \ + d->io_lock.unlock(); \ +} while (0) +#else #define qemu_mutex_lock_iothread() #define qemu_mutex_unlock_iothread() -#define NV2A_DPRINTF printf +#endif + +// Xbox uses 4 KiB pages +#define TARGET_PAGE_MASK 0xfff +#define TARGET_PAGE_ALIGN(x) (((x) + 0xfff) & ~0xfff) static const GLenum pgraph_texture_min_filter_map[] = { 0, @@ -129,6 +143,163 @@ typedef struct ColorFormatInfo { GLenum gl_swizzle_mask[4]; } ColorFormatInfo; +// Note : Avoid designated initializers to facilitate C++ builds +static const ColorFormatInfo kelvin_color_format_map[256] = { + //0x00 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_Y8] = + {1, false, GL_R8, GL_RED, GL_UNSIGNED_BYTE, + {GL_RED, GL_RED, GL_RED, GL_ONE}}, + //0x01 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_AY8] = + {1, false, GL_R8, GL_RED, GL_UNSIGNED_BYTE, + {GL_RED, GL_RED, GL_RED, GL_RED}}, + //0x02 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A1R5G5B5] = + {2, false, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, + //0x03 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_X1R5G5B5] = + {2, false, GL_RGB5, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, + //0x04 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A4R4G4B4] = + {2, false, GL_RGBA4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, + //0x05 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R5G6B5] = + {2, false, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, + //0x06 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8R8G8B8] = + {4, false, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, + //0x07 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_X8R8G8B8] = + {4, false, GL_RGB8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, + //0x08 [?] = + {}, + //0x09 [?] = + {}, + //0x0A [?] = + {}, + + /* paletted texture */ + //0x0B [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_I8_A8R8G8B8] = + {1, false, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, + + //0x0C [NV097_SET_TEXTURE_FORMAT_COLOR_L_DXT1_A1R5G5B5] = + {4, false, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, GL_RGBA}, + //0x0D [?] = + {}, + //0x0E [NV097_SET_TEXTURE_FORMAT_COLOR_L_DXT23_A8R8G8B8] = + {4, false, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, GL_RGBA}, + //0x0F [NV097_SET_TEXTURE_FORMAT_COLOR_L_DXT45_A8R8G8B8] = + {4, false, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, GL_RGBA}, + //0x10 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A1R5G5B5] = + {2, true, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, + //0x11 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R5G6B5] = + {2, true, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, + //0x12 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8R8G8B8] = + {4, true, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, + //0x13 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_Y8] = + {1, true, GL_R8, GL_RED, GL_UNSIGNED_BYTE, + {GL_RED, GL_RED, GL_RED, GL_ONE}}, + //0x14 [?] = + {}, + //0x15 [?] = + {}, + //0x16 [?] = + {}, + //0x17 [?] = + {}, + //0x18 [?] = + {}, + + //0x19 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8] = + {1, false, GL_R8, GL_RED, GL_UNSIGNED_BYTE, + {GL_ONE, GL_ONE, GL_ONE, GL_RED}}, + //0x1A [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8Y8] = + {2, false, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, + {GL_GREEN, GL_GREEN, GL_GREEN, GL_RED}}, + //0x1B [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_AY8] = + {1, true, GL_R8, GL_RED, GL_UNSIGNED_BYTE, + {GL_RED, GL_RED, GL_RED, GL_RED}}, + //0x1C [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X1R5G5B5] = + {2, true, GL_RGB5, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, + //0x1D [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A4R4G4B4] = + {2, false, GL_RGBA4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, + //0x1E [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X8R8G8B8] = + {4, true, GL_RGB8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, + //0x1F [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8] = + {1, true, GL_R8, GL_RED, GL_UNSIGNED_BYTE, + {GL_ONE, GL_ONE, GL_ONE, GL_RED}}, + //0x20 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8Y8] = + {2, true, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, + {GL_GREEN, GL_GREEN, GL_GREEN, GL_RED}}, + //0x21 [?] = + {}, + //0x22 [?] = + {}, + //0x23 [?] = + {}, + //0x24 [NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_CR8YB8CB8YA8] = + { 2, true, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV }, // TODO: format conversion + //0x25 [?] = + {}, + //0x26 [?] = + {}, + + //0x27 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R6G5B5] = + {2, false, GL_RGB8_SNORM, GL_RGB, GL_BYTE}, /* FIXME: This might be signed */ + //0x28 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_G8B8] = + {2, false, GL_RG8_SNORM, GL_RG, GL_BYTE, /* FIXME: This might be signed */ + {GL_ZERO, GL_RED, GL_GREEN, GL_ONE}}, + //0x29 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R8B8] = + {2, false, GL_RG8_SNORM, GL_RG, GL_BYTE, /* FIXME: This might be signed */ + {GL_RED, GL_ZERO, GL_GREEN, GL_ONE}}, + //0x2A [?] = + {}, + //0x2B [?] = + {}, + //0x2C [?] = + {}, + //0x2D [?] = + {}, + + + /* TODO: format conversion */ + //0x2E [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_X8_Y24_FIXED] = + {4, true, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, + //0x2F [?] = + {}, + //0x30 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_Y16_FIXED] = + {2, true, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, + //0x31 [?] = + {}, + //0x32 [?] = + {}, + //0x33 [?] = + {}, + //0x34 [?] = + {}, + //0x35 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_Y16] = + {2, true, GL_R16, GL_RED, GL_UNSIGNED_SHORT, + {GL_RED, GL_RED, GL_RED, GL_ONE}}, + //0x36 [?] = + {}, + //0x37 [?] = + {}, + //0x38 [?] = + {}, + //0x39 [?] = + {}, + //0x3A [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8B8G8R8] = + {4, false, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, + //0x3B [?] = + {}, + + //0x3C [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R8G8B8A8] = + {4, false, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8}, + //0x3D [?] = + {}, + //0x3E [?] = + {}, + + //0x3F [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8B8G8R8] = + {4, true, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, + //0x40 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_B8G8R8A8] = + {4, true, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8}, + //0x41 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R8G8B8A8] = + {4, true, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8}, +}; + typedef struct SurfaceColorFormatInfo { unsigned int bytes_per_pixel; GLint gl_internal_format; @@ -136,49 +307,96 @@ typedef struct SurfaceColorFormatInfo { GLenum gl_type; } SurfaceColorFormatInfo; +// Note : Avoid designated initializers to facilitate C++ builds +static const SurfaceColorFormatInfo kelvin_surface_color_format_map[16] = { + //0x00 [?] = + {}, + //0x01 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_Z1R5G5B5] = + {2, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, + //0x02 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_O1R5G5B5] = + {}, + //0x03 [NV097_SET_SURFACE_FORMAT_COLOR_LE_R5G6B5] = + {2, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, + //0x04 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_Z8R8G8B8] = + {4, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, + //0x05 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_O8R8G8B8] = + {}, + //0x06 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8] = + {}, + //0x07 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8] = + {}, + //0x08 [NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8] = + {4, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, + //0x09 [NV097_SET_SURFACE_FORMAT_COLOR_LE_B8] = + {}, // TODO : {1, GL_R8, GL_R8, GL_UNSIGNED_BYTE}, // PatrickvL guesstimate + //0x0A [NV097_SET_SURFACE_FORMAT_COLOR_LE_G8B8] = + {}, + //0x0B [?] = + {}, + //0x0C [?] = + {}, + //0x0D [?] = + {}, + //0x0E [?] = + {}, + //0x0F [?] = + {} +}; + static void pgraph_context_switch(NV2AState *d, unsigned int channel_id); -static void pgraph_set_context_user(NV2AState *d, uint32_t val); -//static void pgraph_wait_fifo_access(NV2AState *d); -static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int graphics_class, unsigned int method, uint32_t parameter); -//static void pgraph_allocate_inline_buffer_vertices(PGRAPHState *pg, unsigned int attr); -//static void pgraph_finish_inline_buffer_vertex(PGRAPHState *pg); -//static void pgraph_shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, bool binding_changed, bool vertex_program, bool fixed_function); -//static void pgraph_bind_shaders(PGRAPHState *pg); -//static bool pgraph_framebuffer_dirty(PGRAPHState *pg); +static void pgraph_set_context_user(NV2AState *d, uint32_t value); +static void pgraph_wait_fifo_access(NV2AState *d); +static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int method, uint32_t parameter); +static void pgraph_allocate_inline_buffer_vertices(PGRAPHState *pg, unsigned int attr); +static void pgraph_finish_inline_buffer_vertex(PGRAPHState *pg); +#ifdef COMPILE_OPENGL +static void pgraph_shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, bool binding_changed, bool vertex_program, bool fixed_function); +static void pgraph_bind_shaders(PGRAPHState *pg); +#endif // COMPILE_OPENGL +static bool pgraph_framebuffer_dirty(PGRAPHState *pg); static bool pgraph_color_write_enabled(PGRAPHState *pg); static bool pgraph_zeta_write_enabled(PGRAPHState *pg); -//static void pgraph_set_surface_dirty(PGRAPHState *pg, bool color, bool zeta); -//static void pgraph_update_surface_part(NV2AState *d, bool upload, bool color); +static void pgraph_set_surface_dirty(PGRAPHState *pg, bool color, bool zeta); +#ifdef COMPILE_OPENGL +static void pgraph_update_surface_part(NV2AState *d, bool upload, bool color); +#endif // COMPILE_OPENGL static void pgraph_update_surface(NV2AState *d, bool upload, bool color_write, bool zeta_write); -//static void pgraph_bind_textures(NV2AState *d); -//static void pgraph_apply_anti_aliasing_factor(PGRAPHState *pg, unsigned int *width, unsigned int *height); -//static void pgraph_get_surface_dimensions(PGRAPHState *pg, unsigned int *width, unsigned int *height); -//static void pgraph_update_memory_buffer(NV2AState *d, hwaddr addr, hwaddr size, bool f); -//static void pgraph_bind_vertex_attributes(NV2AState *d, unsigned int num_elements, bool inline_data, unsigned int inline_stride); -//static unsigned int pgraph_bind_inline_array(NV2AState *d); +#ifdef COMPILE_OPENGL +static void pgraph_bind_textures(NV2AState *d); +#endif // COMPILE_OPENGL +static void pgraph_apply_anti_aliasing_factor(PGRAPHState *pg, unsigned int *width, unsigned int *height); +static void pgraph_get_surface_dimensions(PGRAPHState *pg, unsigned int *width, unsigned int *height); +#ifdef COMPILE_OPENGL +static void pgraph_update_memory_buffer(NV2AState *d, hwaddr addr, hwaddr size, bool f); +static void pgraph_bind_vertex_attributes(NV2AState *d, unsigned int num_elements, bool inline_data, unsigned int inline_stride); +static unsigned int pgraph_bind_inline_array(NV2AState *d); +#endif // COMPILE_OPENGL static void load_graphics_object(NV2AState *d, hwaddr instance_address, GraphicsObject *obj); static GraphicsObject* lookup_graphics_object(PGRAPHState *s, hwaddr instance_address); -//static float convert_f16_to_float(uint16_t f16); -//static float convert_f24_to_float(uint32_t f24); -//static uint8_t cliptobyte(int x); -//static void convert_yuy2_to_rgb(const uint8_t *line, unsigned int ix, uint8_t *r, uint8_t *g, uint8_t* b); -//static uint8_t* convert_texture_data(const TextureShape s, const uint8_t *data, const uint8_t *palette_data, unsigned int width, unsigned int height, unsigned int depth, unsigned int row_pitch, unsigned int slice_pitch); -//static void upload_gl_texture(GLenum gl_target, const TextureShape s, const uint8_t *texture_data, const uint8_t *palette_data); -//static TextureBinding* generate_texture(const TextureShape s, const uint8_t *texture_data, const uint8_t *palette_data); -//static guint texture_key_hash(gconstpointer key); -//static gboolean texture_key_equal(gconstpointer a, gconstpointer b); -//static gpointer texture_key_retrieve(gpointer key, gpointer user_data, GError **error); -//static void texture_key_destroy(gpointer data); -//static void texture_binding_destroy(gpointer data); -//static guint shader_hash(gconstpointer key); -//static gboolean shader_equal(gconstpointer a, gconstpointer b); +static float convert_f16_to_float(uint16_t f16); +static float convert_f24_to_float(uint32_t f24); +static uint8_t cliptobyte(int x); +static void convert_yuy2_to_rgb(const uint8_t *line, unsigned int ix, uint8_t *r, uint8_t *g, uint8_t* b); +static uint8_t* convert_texture_data(const TextureShape s, const uint8_t *data, const uint8_t *palette_data, unsigned int width, unsigned int height, unsigned int depth, unsigned int row_pitch, unsigned int slice_pitch); +#ifdef COMPILE_OPENGL +static void upload_gl_texture(GLenum gl_target, const TextureShape s, const uint8_t *texture_data, const uint8_t *palette_data); +static TextureBinding* generate_texture(const TextureShape s, const uint8_t *texture_data, const uint8_t *palette_data); +static guint texture_key_hash(gconstpointer key); +static gboolean texture_key_equal(gconstpointer a, gconstpointer b); +static gpointer texture_key_retrieve(gpointer key, gpointer user_data, GError **error); +static void texture_key_destroy(gpointer data); +static void texture_binding_destroy(gpointer data); +static guint shader_hash(gconstpointer key); +static gboolean shader_equal(gconstpointer a, gconstpointer b); +#endif // COMPILE_OPENGL static unsigned int kelvin_map_stencil_op(uint32_t parameter); static unsigned int kelvin_map_polygon_mode(uint32_t parameter); static unsigned int kelvin_map_texgen(uint32_t parameter, unsigned int channel); static void pgraph_method_log(unsigned int subchannel, unsigned int graphics_class, unsigned int method, uint32_t parameter); -//static uint64_t fnv_hash(const uint8_t *data, size_t len); -//static uint64_t fast_hash(const uint8_t *data, size_t len, unsigned int samples); +static uint64_t fnv_hash(const uint8_t *data, size_t len); +static uint64_t fast_hash(const uint8_t *data, size_t len, unsigned int samples); +/* PGRAPH - accelerated 2d/3d drawing engine */ DEVICE_READ32(PGRAPH) { d->pgraph.lock.lock(); @@ -194,9 +412,11 @@ DEVICE_READ32(PGRAPH) result = d->pgraph.notify_source; break; case NV_PGRAPH_CTX_USER: - SET_MASK(result, NV_PGRAPH_CTX_USER_CHANNEL_3D, d->pgraph.context[d->pgraph.channel_id].channel_3d); + SET_MASK(result, NV_PGRAPH_CTX_USER_CHANNEL_3D, + d->pgraph.context[d->pgraph.channel_id].channel_3d); SET_MASK(result, NV_PGRAPH_CTX_USER_CHANNEL_3D_VALID, 1); - SET_MASK(result, NV_PGRAPH_CTX_USER_SUBCH, d->pgraph.context[d->pgraph.channel_id].subchannel << 13); + SET_MASK(result, NV_PGRAPH_CTX_USER_SUBCH, + d->pgraph.context[d->pgraph.channel_id].subchannel << 13); SET_MASK(result, NV_PGRAPH_CTX_USER_CHID, d->pgraph.channel_id); break; case NV_PGRAPH_TRAPPED_ADDR: @@ -222,18 +442,25 @@ DEVICE_READ32(PGRAPH) d->pgraph.lock.unlock(); +// reg_log_read(NV_PGRAPH, addr, r); + DEVICE_READ32_END(PGRAPH); } static void pgraph_set_context_user(NV2AState *d, uint32_t value) { d->pgraph.channel_id = (value & NV_PGRAPH_CTX_USER_CHID) >> 24; - d->pgraph.context[d->pgraph.channel_id].channel_3d = GET_MASK(value, NV_PGRAPH_CTX_USER_CHANNEL_3D); - d->pgraph.context[d->pgraph.channel_id].subchannel = GET_MASK(value, NV_PGRAPH_CTX_USER_SUBCH); + + d->pgraph.context[d->pgraph.channel_id].channel_3d = + GET_MASK(value, NV_PGRAPH_CTX_USER_CHANNEL_3D); + d->pgraph.context[d->pgraph.channel_id].subchannel = + GET_MASK(value, NV_PGRAPH_CTX_USER_SUBCH); } DEVICE_WRITE32(PGRAPH) { +// reg_log_write(NV_PGRAPH, addr, val); + d->pgraph.lock.lock(); switch (addr) { @@ -258,7 +485,6 @@ DEVICE_WRITE32(PGRAPH) NV_PGRAPH_SURFACE_READ_3D) + 1) % GET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_MODULO_3D)); - d->pgraph.flip_3d.notify_all(); } break; @@ -267,21 +493,23 @@ DEVICE_WRITE32(PGRAPH) d->pgraph.fifo_access_cond.notify_all(); break; case NV_PGRAPH_CHANNEL_CTX_TABLE: - d->pgraph.context_table = (value & NV_PGRAPH_CHANNEL_CTX_TABLE_INST) << 4; + d->pgraph.context_table = + (value & NV_PGRAPH_CHANNEL_CTX_TABLE_INST) << 4; break; case NV_PGRAPH_CHANNEL_CTX_POINTER: d->pgraph.context_address = (value & NV_PGRAPH_CHANNEL_CTX_POINTER_INST) << 4; break; case NV_PGRAPH_CHANNEL_CTX_TRIGGER: + if (value & NV_PGRAPH_CHANNEL_CTX_TRIGGER_READ_IN) { - printf("PGRAPH: read channel %d context from %0x08X\n", + NV2A_DPRINTF("PGRAPH: read channel %d context from %0x08X\n", d->pgraph.channel_id, d->pgraph.context_address); uint8_t *context_ptr = d->pramin.ramin_ptr + d->pgraph.context_address; uint32_t context_user = ldl_le_p((uint32_t*)context_ptr); - printf(" - CTX_USER = 0x%x\n", context_user); + NV2A_DPRINTF(" - CTX_USER = 0x%x\n", context_user); pgraph_set_context_user(d, context_user); @@ -301,7 +529,10 @@ DEVICE_WRITE32(PGRAPH) DEVICE_WRITE32_END(PGRAPH); } -static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int method, uint32_t parameter) +static void pgraph_method(NV2AState *d, + unsigned int subchannel, + unsigned int method, + uint32_t parameter) { // int i; GraphicsSubchannel *subchannel_data; @@ -315,22 +546,27 @@ static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int me subchannel_data = &pg->subchannel_data[subchannel]; object = &subchannel_data->object; - ContextSurfaces2DState *context_surfaces_2d = &object->data.context_surfaces_2d; + ContextSurfaces2DState *context_surfaces_2d + = &object->data.context_surfaces_2d; ImageBlitState *image_blit = &object->data.image_blit; KelvinState *kelvin = &object->data.kelvin; + + pgraph_method_log(subchannel, object->graphics_class, method, parameter); 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; } + /* ugly switch for now */ switch (object->graphics_class) { + case NV_CONTEXT_SURFACES_2D: { switch (method) { case NV062_SET_CONTEXT_DMA_IMAGE_SOURCE: @@ -382,13 +618,16 @@ static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int me /* I guess this kicks it off? */ if (image_blit->operation == NV09F_SET_OPERATION_SRCCOPY) { - printf("NV09F_SET_OPERATION_SRCCOPY\n"); + NV2A_GL_DPRINTF("NV09F_SET_OPERATION_SRCCOPY\n"); - GraphicsObject *context_surfaces_obj = lookup_graphics_object(pg, image_blit->context_surfaces); + GraphicsObject *context_surfaces_obj = + lookup_graphics_object(pg, image_blit->context_surfaces); assert(context_surfaces_obj); - assert(context_surfaces_obj->graphics_class == NV_CONTEXT_SURFACES_2D); + assert(context_surfaces_obj->graphics_class + == NV_CONTEXT_SURFACES_2D); - ContextSurfaces2DState *context_surfaces = &context_surfaces_obj->data.context_surfaces_2d; + ContextSurfaces2DState *context_surfaces = + &context_surfaces_obj->data.context_surfaces_2d; unsigned int bytes_per_pixel; switch (context_surfaces->color_format) { @@ -410,15 +649,18 @@ static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int me xbaddr source_dma_len, dest_dma_len; uint8_t *source, *dest; - source = (uint8_t*)nv_dma_map(d, context_surfaces->dma_image_source, &source_dma_len); + source = (uint8_t*)nv_dma_map(d, context_surfaces->dma_image_source, + &source_dma_len); assert(context_surfaces->source_offset < source_dma_len); source += context_surfaces->source_offset; - dest = (uint8_t*)nv_dma_map(d, context_surfaces->dma_image_dest, &dest_dma_len); + dest = (uint8_t*)nv_dma_map(d, context_surfaces->dma_image_dest, + &dest_dma_len); assert(context_surfaces->dest_offset < dest_dma_len); dest += context_surfaces->dest_offset; - printf(" - 0x%tx -> 0x%tx\n", source - MM_SYSTEM_PHYSICAL_MAP,dest - MM_SYSTEM_PHYSICAL_MAP); + NV2A_DPRINTF(" - 0x%tx -> 0x%tx\n", source - d->vram_ptr, + dest - d->vram_ptr); unsigned int y; for (y = 0; yheight; y++) { @@ -433,8 +675,8 @@ static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int me memmove(dest_row, source_row, image_blit->width * bytes_per_pixel); } - } - else { + + } else { assert(false); } @@ -447,73 +689,106 @@ 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)); + 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->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(); + 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; + while (pg->pending_interrupts & NV_PGRAPH_INTR_ERROR) { + pg->interrupt_cond.wait(pg->lock); + } + } + break; - case NV097_WAIT_FOR_IDLE: - pgraph_update_surface(d, false, true, true); - 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)); + 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(); -// } +#ifdef COMPILE_OPENGL + if (glFrameTerminatorGREMEDY) { + glFrameTerminatorGREMEDY(); + } +#endif // COMPILE_OPENGL + break; + } + case NV097_FLIP_STALL: +#ifdef COMPILE_OPENGL + // HACK HACK HACK + glBindFramebuffer(GL_READ_FRAMEBUFFER, pg->gl_framebuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBlitFramebuffer(0, 0, 640, 480, 0, 0, 640, 480, GL_COLOR_BUFFER_BIT, GL_NEAREST); + SDL_GL_SwapWindow(d->sdl_window); // ugh + assert(glGetError() == GL_NO_ERROR); + glBindFramebuffer(GL_READ_FRAMEBUFFER, pg->gl_framebuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, pg->gl_framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, pg->gl_framebuffer); + // HACK HACK HACK +#endif // COMPILE_OPENGL + + pgraph_update_surface(d, false, true, true); + + while (true) { + NV2A_DPRINTF("flip stall read: %d, write: %d, modulo: %d\n", + GET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_READ_3D), + GET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_WRITE_3D), + GET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_MODULO_3D)); + + uint32_t s = pg->regs[NV_PGRAPH_SURFACE]; + if (GET_MASK(s, NV_PGRAPH_SURFACE_READ_3D) + != GET_MASK(s, NV_PGRAPH_SURFACE_WRITE_3D)) { + break; + } + pg->flip_3d.wait(pg->lock); + } + NV2A_DPRINTF("flip stall done\n"); + break; - break; - } case NV097_SET_CONTEXT_DMA_NOTIFIES: kelvin->dma_notifies = parameter; break; @@ -547,6 +822,7 @@ static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int me case NV097_SET_CONTEXT_DMA_REPORT: pg->dma_report = parameter; break; + case NV097_SET_SURFACE_CLIP_HORIZONTAL: pgraph_update_surface(d, false, true, true); @@ -597,12 +873,20 @@ static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int me pg->surface_zeta.offset = parameter; break; + + CASE_8(NV097_SET_COMBINER_ALPHA_ICW, 4) : + slot = (method - NV097_SET_COMBINER_ALPHA_ICW) / 4; + pg->regs[NV_PGRAPH_COMBINEALPHAI0 + slot * 4] = parameter; + break; + case NV097_SET_COMBINER_SPECULAR_FOG_CW0: pg->regs[NV_PGRAPH_COMBINESPECFOG0] = parameter; break; + case NV097_SET_COMBINER_SPECULAR_FOG_CW1: pg->regs[NV_PGRAPH_COMBINESPECFOG1] = parameter; break; + CASE_4(NV097_SET_TEXTURE_ADDRESS, 64): slot = (method - NV097_SET_TEXTURE_ADDRESS) / 64; pg->regs[NV_PGRAPH_TEXADDRESS0 + slot * 4] = parameter; @@ -1028,12 +1312,981 @@ static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int me } CASE_4(NV097_SET_TEXTURE_MATRIX_ENABLE, 4) : slot = (method - NV097_SET_TEXTURE_MATRIX_ENABLE) / 4; - pg->texture_matrix_enable[slot] = parameter; - break; + pg->texture_matrix_enable[slot] = parameter; + break; + + CASE_16(NV097_SET_PROJECTION_MATRIX, 4) : { + slot = (method - NV097_SET_PROJECTION_MATRIX) / 4; + // pg->projection_matrix[slot] = *(float*)¶meter; + unsigned int row = NV_IGRAPH_XF_XFCTX_PMAT0 + slot / 4; + pg->vsh_constants[row][slot % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + CASE_64(NV097_SET_MODEL_VIEW_MATRIX, 4) : { + slot = (method - NV097_SET_MODEL_VIEW_MATRIX) / 4; + unsigned int matnum = slot / 16; + unsigned int entry = slot % 16; + unsigned int row = NV_IGRAPH_XF_XFCTX_MMAT0 + matnum * 8 + entry / 4; + pg->vsh_constants[row][entry % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + CASE_64(NV097_SET_INVERSE_MODEL_VIEW_MATRIX, 4) : { + slot = (method - NV097_SET_INVERSE_MODEL_VIEW_MATRIX) / 4; + unsigned int matnum = slot / 16; + unsigned int entry = slot % 16; + unsigned int row = NV_IGRAPH_XF_XFCTX_IMMAT0 + matnum * 8 + entry / 4; + pg->vsh_constants[row][entry % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + CASE_16(NV097_SET_COMPOSITE_MATRIX, 4) : { + slot = (method - NV097_SET_COMPOSITE_MATRIX) / 4; + unsigned int row = NV_IGRAPH_XF_XFCTX_CMAT0 + slot / 4; + pg->vsh_constants[row][slot % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + CASE_64(NV097_SET_TEXTURE_MATRIX, 4) : { + slot = (method - NV097_SET_TEXTURE_MATRIX) / 4; + unsigned int tex = slot / 16; + unsigned int entry = slot % 16; + unsigned int row = NV_IGRAPH_XF_XFCTX_T0MAT + tex * 8 + entry / 4; + pg->vsh_constants[row][entry % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + CASE_3(NV097_SET_FOG_PARAMS, 4) : + slot = (method - NV097_SET_FOG_PARAMS) / 4; + pg->regs[NV_PGRAPH_FOGPARAM0 + slot * 4] = parameter; + /* Cxbx note: slot = 2 is right after slot = 1 */ + pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FOG_K][slot] = parameter; + pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FOG_K] = true; + break; + + /* Handles NV097_SET_TEXGEN_PLANE_S,T,R,Q */ + CASE_64(NV097_SET_TEXGEN_PLANE_S, 4) : { + slot = (method - NV097_SET_TEXGEN_PLANE_S) / 4; + unsigned int tex = slot / 16; + unsigned int entry = slot % 16; + unsigned int row = NV_IGRAPH_XF_XFCTX_TG0MAT + tex * 8 + entry / 4; + pg->vsh_constants[row][entry % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } case NV097_SET_TEXGEN_VIEW_MODEL: - SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_TEXGEN_REF, parameter); + SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_TEXGEN_REF, + parameter); break; + + CASE_4(NV097_SET_FOG_PLANE, 4): + slot = (method - NV097_SET_FOG_PLANE) / 4; + pg->vsh_constants[NV_IGRAPH_XF_XFCTX_FOG][slot] = parameter; + pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_FOG] = true; + break; + + CASE_3(NV097_SET_SCENE_AMBIENT_COLOR, 4): + slot = (method - NV097_SET_SCENE_AMBIENT_COLOR) / 4; + // ?? + pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FR_AMB][slot] = parameter; + pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FR_AMB] = true; + break; + + CASE_4(NV097_SET_VIEWPORT_OFFSET, 4): + slot = (method - NV097_SET_VIEWPORT_OFFSET) / 4; + pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPOFF][slot] = parameter; + pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPOFF] = true; + break; + + CASE_4(NV097_SET_EYE_POSITION, 4): + slot = (method - NV097_SET_EYE_POSITION) / 4; + pg->vsh_constants[NV_IGRAPH_XF_XFCTX_EYEP][slot] = parameter; + pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_EYEP] = true; + break; + + CASE_8(NV097_SET_COMBINER_FACTOR0, 4): + slot = (method - NV097_SET_COMBINER_FACTOR0) / 4; + pg->regs[NV_PGRAPH_COMBINEFACTOR0 + slot * 4] = parameter; + break; + + CASE_8(NV097_SET_COMBINER_FACTOR1, 4): + slot = (method - NV097_SET_COMBINER_FACTOR1) / 4; + pg->regs[NV_PGRAPH_COMBINEFACTOR1 + slot * 4] = parameter; + break; + + CASE_8(NV097_SET_COMBINER_ALPHA_OCW, 4): + slot = (method - NV097_SET_COMBINER_ALPHA_OCW) / 4; + pg->regs[NV_PGRAPH_COMBINEALPHAO0 + slot * 4] = parameter; + break; + + CASE_8(NV097_SET_COMBINER_COLOR_ICW, 4): + slot = (method - NV097_SET_COMBINER_COLOR_ICW) / 4; + pg->regs[NV_PGRAPH_COMBINECOLORI0 + slot * 4] = parameter; + break; + + CASE_4(NV097_SET_VIEWPORT_SCALE, 4): + slot = (method - NV097_SET_VIEWPORT_SCALE) / 4; + pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPSCL][slot] = parameter; + pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPSCL] = true; + break; + + CASE_32(NV097_SET_TRANSFORM_PROGRAM, 4) : { + + slot = (method - NV097_SET_TRANSFORM_PROGRAM) / 4; + + int program_load = GET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], + NV_PGRAPH_CHEOPS_OFFSET_PROG_LD_PTR); + + assert(program_load < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH); + pg->program_data[program_load][slot % 4] = parameter; + + if (slot % 4 == 3) { + SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], + NV_PGRAPH_CHEOPS_OFFSET_PROG_LD_PTR, program_load + 1); + } + + break; + } + + CASE_32(NV097_SET_TRANSFORM_CONSTANT, 4): { + + slot = (method - NV097_SET_TRANSFORM_CONSTANT) / 4; + + int const_load = GET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], + NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR); + + assert(const_load < NV2A_VERTEXSHADER_CONSTANTS); + // VertexShaderConstant *constant = &pg->constants[const_load]; + pg->vsh_constants_dirty[const_load] |= + (parameter != pg->vsh_constants[const_load][slot%4]); + pg->vsh_constants[const_load][slot%4] = parameter; + + if (slot % 4 == 3) { + SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], + NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR, const_load+1); + } + break; + } + + CASE_3(NV097_SET_VERTEX3F, 4) : { + slot = (method - NV097_SET_VERTEX3F) / 4; + VertexAttribute *attribute = + &pg->vertex_attributes[NV2A_VERTEX_ATTR_POSITION]; + pgraph_allocate_inline_buffer_vertices(pg, NV2A_VERTEX_ATTR_POSITION); + attribute->inline_value[slot] = *(float*)¶meter; + attribute->inline_value[3] = 1.0f; + if (slot == 2) { + pgraph_finish_inline_buffer_vertex(pg); + } + break; + } + + /* Handles NV097_SET_BACK_LIGHT_* */ + CASE_78(NV097_SET_BACK_LIGHT_AMBIENT_COLOR, 4): { + // NV097_SET_BACK_LIGHT_SPECULAR_COLOR 0x00000C18 - 0x00000C00 + 0x1C8 = 0x1E0; /4= 78d + slot = (method - NV097_SET_BACK_LIGHT_AMBIENT_COLOR) / 4; + unsigned int part = NV097_SET_BACK_LIGHT_AMBIENT_COLOR / 4 + slot % 16; + slot /= 16; /* [Light index] */ + assert(slot < 8); + switch(part * 4) { + CASE_3(NV097_SET_BACK_LIGHT_AMBIENT_COLOR, 4): + part -= NV097_SET_BACK_LIGHT_AMBIENT_COLOR / 4; + pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_BAMB + slot*6][part] = parameter; + pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_BAMB + slot*6] = true; + break; + CASE_3(NV097_SET_BACK_LIGHT_DIFFUSE_COLOR, 4): + part -= NV097_SET_BACK_LIGHT_DIFFUSE_COLOR / 4; + pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_BDIF + slot*6][part] = parameter; + pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_BDIF + slot*6] = true; + break; + CASE_3(NV097_SET_BACK_LIGHT_SPECULAR_COLOR, 4): + part -= NV097_SET_BACK_LIGHT_SPECULAR_COLOR / 4; + pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_BSPC + slot*6][part] = parameter; + pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_BSPC + slot*6] = true; + break; + default: + assert(false); + break; + } + break; + } + /* Handles all the light source props except for NV097_SET_BACK_LIGHT_* */ + CASE_253(NV097_SET_LIGHT_AMBIENT_COLOR, 4): { + // NV097_SET_LIGHT_LOCAL_ATTENUATION 0x00001068 - 0x00001000 + 0x38C = 0x3F4; /4= 253d + slot = (method - NV097_SET_LIGHT_AMBIENT_COLOR) / 4; + unsigned int part = NV097_SET_LIGHT_AMBIENT_COLOR / 4 + slot % 32; + slot /= 32; /* [Light index] */ + assert(slot < 8); + switch(part * 4) { + CASE_3(NV097_SET_LIGHT_AMBIENT_COLOR, 4): + part -= NV097_SET_LIGHT_AMBIENT_COLOR / 4; + pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_AMB + slot*6][part] = parameter; + pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_AMB + slot*6] = true; + break; + CASE_3(NV097_SET_LIGHT_DIFFUSE_COLOR, 4): + part -= NV097_SET_LIGHT_DIFFUSE_COLOR / 4; + pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_DIF + slot*6][part] = parameter; + pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_DIF + slot*6] = true; + break; + CASE_3(NV097_SET_LIGHT_SPECULAR_COLOR, 4): + part -= NV097_SET_LIGHT_SPECULAR_COLOR / 4; + pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_SPC + slot*6][part] = parameter; + pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_SPC + slot*6] = true; + break; + case NV097_SET_LIGHT_LOCAL_RANGE: + pg->ltc1[NV_IGRAPH_XF_LTC1_r0 + slot][0] = parameter; + pg->ltc1_dirty[NV_IGRAPH_XF_LTC1_r0 + slot] = true; + break; + CASE_3(NV097_SET_LIGHT_INFINITE_HALF_VECTOR, 4): + part -= NV097_SET_LIGHT_INFINITE_HALF_VECTOR / 4; + pg->light_infinite_half_vector[slot][part] = *(float*)¶meter; + break; + CASE_3(NV097_SET_LIGHT_INFINITE_DIRECTION, 4): + part -= NV097_SET_LIGHT_INFINITE_DIRECTION / 4; + pg->light_infinite_direction[slot][part] = *(float*)¶meter; + break; + CASE_3(NV097_SET_LIGHT_SPOT_FALLOFF, 4): + part -= NV097_SET_LIGHT_SPOT_FALLOFF / 4; + pg->ltctxa[NV_IGRAPH_XF_LTCTXA_L0_K + slot*2][part] = parameter; + pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_L0_K + slot*2] = true; + break; + CASE_4(NV097_SET_LIGHT_SPOT_DIRECTION, 4): + part -= NV097_SET_LIGHT_SPOT_DIRECTION / 4; + pg->ltctxa[NV_IGRAPH_XF_LTCTXA_L0_SPT + slot*2][part] = parameter; + pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_L0_SPT + slot*2] = true; + break; + CASE_3(NV097_SET_LIGHT_LOCAL_POSITION, 4): + part -= NV097_SET_LIGHT_LOCAL_POSITION / 4; + pg->light_local_position[slot][part] = *(float*)¶meter; + break; + CASE_3(NV097_SET_LIGHT_LOCAL_ATTENUATION, 4): + part -= NV097_SET_LIGHT_LOCAL_ATTENUATION / 4; + pg->light_local_attenuation[slot][part] = *(float*)¶meter; + break; + default: + assert(false); + break; + } + break; + } + + CASE_4(NV097_SET_VERTEX4F, 4): { + slot = (method - NV097_SET_VERTEX4F) / 4; + VertexAttribute *attribute = + &pg->vertex_attributes[NV2A_VERTEX_ATTR_POSITION]; + pgraph_allocate_inline_buffer_vertices(pg, NV2A_VERTEX_ATTR_POSITION); + attribute->inline_value[slot] = *(float*)¶meter; + if (slot == 3) { + pgraph_finish_inline_buffer_vertex(pg); + } + break; + } + + CASE_16(NV097_SET_VERTEX_DATA_ARRAY_FORMAT, 4): { + + slot = (method - NV097_SET_VERTEX_DATA_ARRAY_FORMAT) / 4; + VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot]; + + vertex_attribute->format = + GET_MASK(parameter, NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE); + vertex_attribute->count = + GET_MASK(parameter, NV097_SET_VERTEX_DATA_ARRAY_FORMAT_SIZE); + vertex_attribute->stride = + GET_MASK(parameter, NV097_SET_VERTEX_DATA_ARRAY_FORMAT_STRIDE); + + NV2A_DPRINTF("vertex data array format=%d, count=%d, stride=%d\n", + vertex_attribute->format, + vertex_attribute->count, + vertex_attribute->stride); + + vertex_attribute->gl_count = vertex_attribute->count; + + switch (vertex_attribute->format) { + case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_D3D: + vertex_attribute->gl_type = GL_UNSIGNED_BYTE; + vertex_attribute->gl_normalize = GL_TRUE; + vertex_attribute->size = 1; + assert(vertex_attribute->count == 4); + // http://www.opengl.org/registry/specs/ARB/vertex_array_bgra.txt + vertex_attribute->gl_count = GL_BGRA; + vertex_attribute->needs_conversion = false; + break; + case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_OGL: + vertex_attribute->gl_type = GL_UNSIGNED_BYTE; + vertex_attribute->gl_normalize = GL_TRUE; + vertex_attribute->size = 1; + vertex_attribute->needs_conversion = false; + break; + case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_S1: + vertex_attribute->gl_type = GL_SHORT; + vertex_attribute->gl_normalize = GL_TRUE; + vertex_attribute->size = 2; + vertex_attribute->needs_conversion = false; + break; + case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_F: + vertex_attribute->gl_type = GL_FLOAT; + vertex_attribute->gl_normalize = GL_FALSE; + vertex_attribute->size = 4; + vertex_attribute->needs_conversion = false; + break; + case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_S32K: + vertex_attribute->gl_type = GL_SHORT; + vertex_attribute->gl_normalize = GL_FALSE; + vertex_attribute->size = 2; + vertex_attribute->needs_conversion = false; + break; + case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_CMP: + /* 3 signed, normalized components packed in 32-bits. (11,11,10) */ + vertex_attribute->size = 4; + vertex_attribute->gl_type = GL_FLOAT; + vertex_attribute->gl_normalize = GL_FALSE; + vertex_attribute->needs_conversion = true; + vertex_attribute->converted_size = sizeof(float); + vertex_attribute->converted_count = 3 * vertex_attribute->count; + break; + default: + fprintf(stderr, "Unknown vertex type: 0x%x\n", vertex_attribute->format); + assert(false); + break; + } + + if (vertex_attribute->needs_conversion) { + vertex_attribute->converted_elements = 0; + } else { + if (vertex_attribute->converted_buffer) { + free(vertex_attribute->converted_buffer); + vertex_attribute->converted_buffer = NULL; + } + } + + break; + } + + CASE_16(NV097_SET_VERTEX_DATA_ARRAY_OFFSET, 4): + + slot = (method - NV097_SET_VERTEX_DATA_ARRAY_OFFSET) / 4; + + pg->vertex_attributes[slot].dma_select = + parameter & 0x80000000; + pg->vertex_attributes[slot].offset = + parameter & 0x7fffffff; + + pg->vertex_attributes[slot].converted_elements = 0; + + break; + + case NV097_SET_LOGIC_OP_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_BLEND], + NV_PGRAPH_BLEND_LOGICOP_ENABLE, parameter); + break; + + case NV097_SET_LOGIC_OP: + SET_MASK(pg->regs[NV_PGRAPH_BLEND], + NV_PGRAPH_BLEND_LOGICOP, parameter & 0xF); + break; + + case NV097_CLEAR_REPORT_VALUE: + /* FIXME: Does this have a value in parameter? Also does this (also?) modify + * the report memory block? + */ + if (pg->gl_zpass_pixel_count_query_count) { + glDeleteQueries(pg->gl_zpass_pixel_count_query_count, + pg->gl_zpass_pixel_count_queries); + pg->gl_zpass_pixel_count_query_count = 0; + } + pg->zpass_pixel_count_result = 0; + break; + + case NV097_SET_ZPASS_PIXEL_COUNT_ENABLE: + pg->zpass_pixel_count_enable = parameter; + break; + + case NV097_GET_REPORT: { + /* FIXME: This was first intended to be watchpoint-based. However, + * qemu / kvm only supports virtual-address watchpoints. + * This'll do for now, but accuracy and performance with other + * approaches could be better + */ + uint8_t type = GET_MASK(parameter, NV097_GET_REPORT_TYPE); + assert(type == NV097_GET_REPORT_TYPE_ZPASS_PIXEL_CNT); + hwaddr offset = GET_MASK(parameter, NV097_GET_REPORT_OFFSET); + + uint64_t timestamp = 0x0011223344556677; /* FIXME: Update timestamp?! */ + uint32_t done = 0; + +#ifdef COMPILE_OPENGL + /* FIXME: Multisampling affects this (both: OGL and Xbox GPU), + * not sure if CLEARs also count + */ + /* FIXME: What about clipping regions etc? */ + for(i = 0; i < pg->gl_zpass_pixel_count_query_count; i++) { + GLuint gl_query_result; + glGetQueryObjectuiv(pg->gl_zpass_pixel_count_queries[i], + GL_QUERY_RESULT, + &gl_query_result); + pg->zpass_pixel_count_result += gl_query_result; + } + if (pg->gl_zpass_pixel_count_query_count) { + glDeleteQueries(pg->gl_zpass_pixel_count_query_count, + pg->gl_zpass_pixel_count_queries); + } + pg->gl_zpass_pixel_count_query_count = 0; + + hwaddr report_dma_len; + uint8_t *report_data = (uint8_t*)nv_dma_map(d, pg->dma_report, + &report_dma_len); + assert(offset < report_dma_len); + report_data += offset; + + stq_le_p((uint64_t*)&report_data[0], timestamp); + stl_le_p((uint32_t*)&report_data[8], pg->zpass_pixel_count_result); + stl_le_p((uint32_t*)&report_data[12], done); +#endif // COMPILE_OPENGL + + break; + } + + CASE_3(NV097_SET_EYE_DIRECTION, 4): + slot = (method - NV097_SET_EYE_DIRECTION) / 4; + pg->ltctxa[NV_IGRAPH_XF_LTCTXA_EYED][slot] = parameter; + pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_EYED] = true; + break; + + case NV097_SET_BEGIN_END: { + bool depth_test = + pg->regs[NV_PGRAPH_CONTROL_0] & NV_PGRAPH_CONTROL_0_ZENABLE; + bool stencil_test = pg->regs[NV_PGRAPH_CONTROL_1] + & NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE; + +#ifdef COMPILE_OPENGL + if (parameter == NV097_SET_BEGIN_END_OP_END) { + + assert(pg->shader_binding); + + if (pg->draw_arrays_length) { + + NV2A_GL_DPRINTF(false, "Draw Arrays"); + + assert(pg->inline_buffer_length == 0); + assert(pg->inline_array_length == 0); + assert(pg->inline_elements_length == 0); + + pgraph_bind_vertex_attributes(d, pg->draw_arrays_max_count, + false, 0); + glMultiDrawArrays(pg->shader_binding->gl_primitive_mode, + pg->gl_draw_arrays_start, + pg->gl_draw_arrays_count, + pg->draw_arrays_length); + } else if (pg->inline_buffer_length) { + + NV2A_GL_DPRINTF(false, "Inline Buffer"); + + assert(pg->draw_arrays_length == 0); + assert(pg->inline_array_length == 0); + assert(pg->inline_elements_length == 0); + + for (i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) { + VertexAttribute *attribute = &pg->vertex_attributes[i]; + + if (attribute->inline_buffer) { + + glBindBuffer(GL_ARRAY_BUFFER, + attribute->gl_inline_buffer); + glBufferData(GL_ARRAY_BUFFER, + pg->inline_buffer_length + * sizeof(float) * 4, + attribute->inline_buffer, + GL_DYNAMIC_DRAW); + + /* Clear buffer for next batch */ + g_free(attribute->inline_buffer); + attribute->inline_buffer = NULL; + + glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(i); + } else { + glDisableVertexAttribArray(i); + + glVertexAttrib4fv(i, attribute->inline_value); + } + + } + + glDrawArrays(pg->shader_binding->gl_primitive_mode, + 0, pg->inline_buffer_length); + } else if (pg->inline_array_length) { + + NV2A_GL_DPRINTF(false, "Inline Array"); + + assert(pg->draw_arrays_length == 0); + assert(pg->inline_buffer_length == 0); + assert(pg->inline_elements_length == 0); + + unsigned int index_count = pgraph_bind_inline_array(d); + glDrawArrays(pg->shader_binding->gl_primitive_mode, + 0, index_count); + } else if (pg->inline_elements_length) { + + NV2A_GL_DPRINTF(false, "Inline Elements"); + + assert(pg->draw_arrays_length == 0); + assert(pg->inline_buffer_length == 0); + assert(pg->inline_array_length == 0); + + uint32_t max_element = 0; + uint32_t min_element = (uint32_t)-1; + for (i=0; iinline_elements_length; i++) { + max_element = MAX(pg->inline_elements[i], max_element); + min_element = MIN(pg->inline_elements[i], min_element); + } + + pgraph_bind_vertex_attributes(d, max_element+1, false, 0); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pg->gl_element_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + pg->inline_elements_length*4, + pg->inline_elements, + GL_DYNAMIC_DRAW); + + glDrawRangeElements(pg->shader_binding->gl_primitive_mode, + min_element, max_element, + pg->inline_elements_length, + GL_UNSIGNED_INT, + (void*)0); + + } else { + NV2A_GL_DPRINTF(true, "EMPTY NV097_SET_BEGIN_END"); + assert(false); + } + + /* End of visibility testing */ + if (pg->zpass_pixel_count_enable) { + glEndQuery(GL_SAMPLES_PASSED); + } + + NV2A_GL_DGROUP_END(); + } else { + NV2A_GL_DGROUP_BEGIN("NV097_SET_BEGIN_END: 0x%x", parameter); + assert(parameter <= NV097_SET_BEGIN_END_OP_POLYGON); + + pgraph_update_surface(d, true, true, depth_test || stencil_test); + + pg->primitive_mode = parameter; + + uint32_t control_0 = pg->regs[NV_PGRAPH_CONTROL_0]; + + bool alpha = control_0 & NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE; + bool red = control_0 & NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE; + bool green = control_0 & NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE; + bool blue = control_0 & NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE; + glColorMask(red, green, blue, alpha); + glDepthMask(!!(control_0 & NV_PGRAPH_CONTROL_0_ZWRITEENABLE)); + glStencilMask(GET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], + NV_PGRAPH_CONTROL_1_STENCIL_MASK_WRITE)); + + if (pg->regs[NV_PGRAPH_BLEND] & NV_PGRAPH_BLEND_EN) { + glEnable(GL_BLEND); + uint32_t sfactor = GET_MASK(pg->regs[NV_PGRAPH_BLEND], + NV_PGRAPH_BLEND_SFACTOR); + uint32_t dfactor = GET_MASK(pg->regs[NV_PGRAPH_BLEND], + NV_PGRAPH_BLEND_DFACTOR); + assert(sfactor < ARRAY_SIZE(pgraph_blend_factor_map)); + assert(dfactor < ARRAY_SIZE(pgraph_blend_factor_map)); + glBlendFunc(pgraph_blend_factor_map[sfactor], + pgraph_blend_factor_map[dfactor]); + + uint32_t equation = GET_MASK(pg->regs[NV_PGRAPH_BLEND], + NV_PGRAPH_BLEND_EQN); + assert(equation < ARRAY_SIZE(pgraph_blend_equation_map)); + glBlendEquation(pgraph_blend_equation_map[equation]); + + uint32_t blend_color = pg->regs[NV_PGRAPH_BLENDCOLOR]; + glBlendColor( ((blend_color >> 16) & 0xFF) / 255.0f, /* red */ + ((blend_color >> 8) & 0xFF) / 255.0f, /* green */ + (blend_color & 0xFF) / 255.0f, /* blue */ + ((blend_color >> 24) & 0xFF) / 255.0f);/* alpha */ + } else { + glDisable(GL_BLEND); + } + + /* Face culling */ + if (pg->regs[NV_PGRAPH_SETUPRASTER] + & NV_PGRAPH_SETUPRASTER_CULLENABLE) { + uint32_t cull_face = GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_CULLCTRL); + assert(cull_face < ARRAY_SIZE(pgraph_cull_face_map)); + glCullFace(pgraph_cull_face_map[cull_face]); + glEnable(GL_CULL_FACE); + } else { + glDisable(GL_CULL_FACE); + } + + /* Front-face select */ + glFrontFace(pg->regs[NV_PGRAPH_SETUPRASTER] + & NV_PGRAPH_SETUPRASTER_FRONTFACE + ? GL_CCW : GL_CW); + + /* Polygon offset */ + /* FIXME: GL implementation-specific, maybe do this in VS? */ + if (pg->regs[NV_PGRAPH_SETUPRASTER] & + NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE) { + glEnable(GL_POLYGON_OFFSET_FILL); + } else { + glDisable(GL_POLYGON_OFFSET_FILL); + } + if (pg->regs[NV_PGRAPH_SETUPRASTER] & + NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE) { + glEnable(GL_POLYGON_OFFSET_LINE); + } else { + glDisable(GL_POLYGON_OFFSET_LINE); + } + if (pg->regs[NV_PGRAPH_SETUPRASTER] & + NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE) { + glEnable(GL_POLYGON_OFFSET_POINT); + } else { + glDisable(GL_POLYGON_OFFSET_POINT); + } + if (pg->regs[NV_PGRAPH_SETUPRASTER] & + (NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE | + NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE | + NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE)) { + GLfloat zfactor = *(float*)&pg->regs[NV_PGRAPH_ZOFFSETFACTOR]; + GLfloat zbias = *(float*)&pg->regs[NV_PGRAPH_ZOFFSETBIAS]; + glPolygonOffset(zfactor, zbias); + } + + /* Depth testing */ + if (depth_test) { + glEnable(GL_DEPTH_TEST); + + uint32_t depth_func = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_ZFUNC); + assert(depth_func < ARRAY_SIZE(pgraph_depth_func_map)); + glDepthFunc(pgraph_depth_func_map[depth_func]); + } else { + glDisable(GL_DEPTH_TEST); + } + + if (stencil_test) { + glEnable(GL_STENCIL_TEST); + + uint32_t stencil_func = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], + NV_PGRAPH_CONTROL_1_STENCIL_FUNC); + uint32_t stencil_ref = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], + NV_PGRAPH_CONTROL_1_STENCIL_REF); + uint32_t func_mask = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], + NV_PGRAPH_CONTROL_1_STENCIL_MASK_READ); + uint32_t op_fail = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_2], + NV_PGRAPH_CONTROL_2_STENCIL_OP_FAIL); + uint32_t op_zfail = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_2], + NV_PGRAPH_CONTROL_2_STENCIL_OP_ZFAIL); + uint32_t op_zpass = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_2], + NV_PGRAPH_CONTROL_2_STENCIL_OP_ZPASS); + + assert(stencil_func < ARRAY_SIZE(pgraph_stencil_func_map)); + assert(op_fail < ARRAY_SIZE(pgraph_stencil_op_map)); + assert(op_zfail < ARRAY_SIZE(pgraph_stencil_op_map)); + assert(op_zpass < ARRAY_SIZE(pgraph_stencil_op_map)); + + glStencilFunc( + pgraph_stencil_func_map[stencil_func], + stencil_ref, + func_mask); + + glStencilOp( + pgraph_stencil_op_map[op_fail], + pgraph_stencil_op_map[op_zfail], + pgraph_stencil_op_map[op_zpass]); + + } else { + glDisable(GL_STENCIL_TEST); + } + + /* Dither */ + /* FIXME: GL implementation dependent */ + if (pg->regs[NV_PGRAPH_CONTROL_0] & + NV_PGRAPH_CONTROL_0_DITHERENABLE) { + glEnable(GL_DITHER); + } else { + glDisable(GL_DITHER); + } + + pgraph_bind_shaders(pg); + pgraph_bind_textures(d); + + //glDisableVertexAttribArray(NV2A_VERTEX_ATTR_DIFFUSE); + //glVertexAttrib4f(NV2A_VERTEX_ATTR_DIFFUSE, 1.0f, 1.0f, 1.0f, 1.0f); + + + unsigned int width, height; + pgraph_get_surface_dimensions(pg, &width, &height); + pgraph_apply_anti_aliasing_factor(pg, &width, &height); + glViewport(0, 0, width, height); + + pg->inline_elements_length = 0; + pg->inline_array_length = 0; + pg->inline_buffer_length = 0; + pg->draw_arrays_length = 0; + pg->draw_arrays_max_count = 0; + + /* Visibility testing */ + if (pg->zpass_pixel_count_enable) { + GLuint gl_query; + glGenQueries(1, &gl_query); + pg->gl_zpass_pixel_count_query_count++; + pg->gl_zpass_pixel_count_queries = (GLuint*)g_realloc( + pg->gl_zpass_pixel_count_queries, + sizeof(GLuint) * pg->gl_zpass_pixel_count_query_count); + pg->gl_zpass_pixel_count_queries[ + pg->gl_zpass_pixel_count_query_count - 1] = gl_query; + glBeginQuery(GL_SAMPLES_PASSED, gl_query); + } + + } + + pgraph_set_surface_dirty(pg, true, depth_test || stencil_test); +#endif // COMPILE_OPENGL + break; + } + CASE_4(NV097_SET_TEXTURE_OFFSET, 64): + slot = (method - NV097_SET_TEXTURE_OFFSET) / 64; + pg->regs[NV_PGRAPH_TEXOFFSET0 + slot * 4] = parameter; + pg->texture_dirty[slot] = true; + break; + CASE_4(NV097_SET_TEXTURE_FORMAT, 64): { + slot = (method - NV097_SET_TEXTURE_FORMAT) / 64; + + bool dma_select = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_CONTEXT_DMA) == 2; + bool cubemap = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_CUBEMAP_ENABLE); + unsigned int border_source = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BORDER_SOURCE); + unsigned int dimensionality = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_DIMENSIONALITY); + unsigned int color_format = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_COLOR); + unsigned int levels = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_MIPMAP_LEVELS); + unsigned int log_width = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BASE_SIZE_U); + unsigned int log_height = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BASE_SIZE_V); + unsigned int log_depth = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BASE_SIZE_P); + + uint32_t *reg = &pg->regs[NV_PGRAPH_TEXFMT0 + slot * 4]; + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_CONTEXT_DMA, dma_select); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_CUBEMAPENABLE, cubemap); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BORDER_SOURCE, border_source); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_DIMENSIONALITY, dimensionality); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_COLOR, color_format); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_MIPMAP_LEVELS, levels); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BASE_SIZE_U, log_width); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BASE_SIZE_V, log_height); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BASE_SIZE_P, log_depth); + + pg->texture_dirty[slot] = true; + break; + } + CASE_4(NV097_SET_TEXTURE_CONTROL0, 64): + slot = (method - NV097_SET_TEXTURE_CONTROL0) / 64; + pg->regs[NV_PGRAPH_TEXCTL0_0 + slot*4] = parameter; + break; + CASE_4(NV097_SET_TEXTURE_CONTROL1, 64): + slot = (method - NV097_SET_TEXTURE_CONTROL1) / 64; + pg->regs[NV_PGRAPH_TEXCTL1_0 + slot*4] = parameter; + break; + CASE_4(NV097_SET_TEXTURE_FILTER, 64): + slot = (method - NV097_SET_TEXTURE_FILTER) / 64; + pg->regs[NV_PGRAPH_TEXFILTER0 + slot * 4] = parameter; + break; + CASE_4(NV097_SET_TEXTURE_IMAGE_RECT, 64): + slot = (method - NV097_SET_TEXTURE_IMAGE_RECT) / 64; + pg->regs[NV_PGRAPH_TEXIMAGERECT0 + slot * 4] = parameter; + pg->texture_dirty[slot] = true; + break; + CASE_4(NV097_SET_TEXTURE_PALETTE, 64): { + slot = (method - NV097_SET_TEXTURE_PALETTE) / 64; + + bool dma_select = + GET_MASK(parameter, NV097_SET_TEXTURE_PALETTE_CONTEXT_DMA) == 1; + unsigned int length = + GET_MASK(parameter, NV097_SET_TEXTURE_PALETTE_LENGTH); + unsigned int offset = + GET_MASK(parameter, NV097_SET_TEXTURE_PALETTE_OFFSET); + + uint32_t *reg = &pg->regs[NV_PGRAPH_TEXPALETTE0 + slot * 4]; + SET_MASK(*reg, NV_PGRAPH_TEXPALETTE0_CONTEXT_DMA, dma_select); + SET_MASK(*reg, NV_PGRAPH_TEXPALETTE0_LENGTH, length); + SET_MASK(*reg, NV_PGRAPH_TEXPALETTE0_OFFSET, offset); + + pg->texture_dirty[slot] = true; + break; + } + + CASE_4(NV097_SET_TEXTURE_BORDER_COLOR, 64): + slot = (method - NV097_SET_TEXTURE_BORDER_COLOR) / 64; + pg->regs[NV_PGRAPH_BORDERCOLOR0 + slot * 4] = parameter; + break; + CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0x0, 64): + CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0x4, 64): + CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0x8, 64): + CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0xc, 64): + slot = (method - NV097_SET_TEXTURE_SET_BUMP_ENV_MAT) / 4; + assert((slot / 16) > 0); + slot -= 16; + pg->bump_env_matrix[slot / 16][slot % 4] = *(float*)¶meter; + break; + + CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_SCALE, 64): + slot = (method - NV097_SET_TEXTURE_SET_BUMP_ENV_SCALE) / 64; + assert(slot > 0); + slot--; + pg->regs[NV_PGRAPH_BUMPSCALE1 + slot * 4] = parameter; + break; + CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_OFFSET, 64): + slot = (method - NV097_SET_TEXTURE_SET_BUMP_ENV_OFFSET) / 64; + assert(slot > 0); + slot--; + pg->regs[NV_PGRAPH_BUMPOFFSET1 + slot * 4] = parameter; + break; + + case NV097_ARRAY_ELEMENT16: + assert(pg->inline_elements_length < NV2A_MAX_BATCH_LENGTH); + pg->inline_elements[ + pg->inline_elements_length++] = parameter & 0xFFFF; + pg->inline_elements[ + pg->inline_elements_length++] = parameter >> 16; + break; + case NV097_ARRAY_ELEMENT32: + assert(pg->inline_elements_length < NV2A_MAX_BATCH_LENGTH); + pg->inline_elements[ + pg->inline_elements_length++] = parameter; + break; + case NV097_DRAW_ARRAYS: { + + unsigned int start = GET_MASK(parameter, NV097_DRAW_ARRAYS_START_INDEX); + unsigned int count = GET_MASK(parameter, NV097_DRAW_ARRAYS_COUNT)+1; + +#ifdef COMPILE_OPENGL + pg->draw_arrays_max_count = MAX(pg->draw_arrays_max_count, start + count); + + assert(pg->draw_arrays_length < ARRAY_SIZE(pg->gl_draw_arrays_start)); +#endif // COMPILE_OPENGL + + /* Attempt to connect primitives */ + if (pg->draw_arrays_length > 0) { + unsigned int last_start = + pg->gl_draw_arrays_start[pg->draw_arrays_length - 1]; + GLsizei* last_count = + &pg->gl_draw_arrays_count[pg->draw_arrays_length - 1]; + if (start == (last_start + *last_count)) { + *last_count += count; + break; + } + } + + pg->gl_draw_arrays_start[pg->draw_arrays_length] = start; + pg->gl_draw_arrays_count[pg->draw_arrays_length] = count; + pg->draw_arrays_length++; + break; + } + case NV097_INLINE_ARRAY: + assert(pg->inline_array_length < NV2A_MAX_BATCH_LENGTH); + pg->inline_array[ + pg->inline_array_length++] = parameter; + break; + CASE_3(NV097_SET_EYE_VECTOR, 4): + slot = (method - NV097_SET_EYE_VECTOR) / 4; + pg->regs[NV_PGRAPH_EYEVEC0 + slot * 4] = parameter; + break; + + CASE_32(NV097_SET_VERTEX_DATA2F_M, 4): { + slot = (method - NV097_SET_VERTEX_DATA2F_M) / 4; + unsigned int part = slot % 2; + slot /= 2; + VertexAttribute *attribute = &pg->vertex_attributes[slot]; + pgraph_allocate_inline_buffer_vertices(pg, slot); + attribute->inline_value[part] = *(float*)¶meter; + /* FIXME: Should these really be set to 0.0 and 1.0 ? Conditions? */ + attribute->inline_value[2] = 0.0f; + attribute->inline_value[3] = 1.0f; + if ((slot == 0) && (part == 1)) { + pgraph_finish_inline_buffer_vertex(pg); + } + break; + } + CASE_64(NV097_SET_VERTEX_DATA4F_M, 4): { + slot = (method - NV097_SET_VERTEX_DATA4F_M) / 4; + unsigned int part = slot % 4; + slot /= 4; + VertexAttribute *attribute = &pg->vertex_attributes[slot]; + pgraph_allocate_inline_buffer_vertices(pg, slot); + attribute->inline_value[part] = *(float*)¶meter; + if ((slot == 0) && (part == 3)) { + pgraph_finish_inline_buffer_vertex(pg); + } + break; + } + CASE_16(NV097_SET_VERTEX_DATA2S, 4): { + slot = (method - NV097_SET_VERTEX_DATA2S) / 4; + assert(false); /* FIXME: Untested! */ + VertexAttribute *attribute = &pg->vertex_attributes[slot]; + pgraph_allocate_inline_buffer_vertices(pg, slot); + /* FIXME: Is mapping to [-1,+1] correct? */ + attribute->inline_value[0] = ((int16_t)(parameter & 0xFFFF) * 2.0f + 1) + / 65535.0f; + attribute->inline_value[1] = ((int16_t)(parameter >> 16) * 2.0f + 1) + / 65535.0f; + /* FIXME: Should these really be set to 0.0 and 1.0 ? Conditions? */ + attribute->inline_value[2] = 0.0f; + attribute->inline_value[3] = 1.0f; + if (slot == 0) { + pgraph_finish_inline_buffer_vertex(pg); + assert(false); /* FIXME: Untested */ + } + break; + } + CASE_16(NV097_SET_VERTEX_DATA4UB, 4) : { + slot = (method - NV097_SET_VERTEX_DATA4UB) / 4; + VertexAttribute *attribute = &pg->vertex_attributes[slot]; + pgraph_allocate_inline_buffer_vertices(pg, slot); + attribute->inline_value[0] = (parameter & 0xFF) / 255.0f; + attribute->inline_value[1] = ((parameter >> 8) & 0xFF) / 255.0f; + attribute->inline_value[2] = ((parameter >> 16) & 0xFF) / 255.0f; + attribute->inline_value[3] = ((parameter >> 24) & 0xFF) / 255.0f; + if (slot == 0) { + pgraph_finish_inline_buffer_vertex(pg); + assert(false); /* FIXME: Untested */ + } + break; + } + CASE_32(NV097_SET_VERTEX_DATA4S_M, 4) : { + slot = (method - NV097_SET_VERTEX_DATA4S_M) / 4; + unsigned int part = slot % 2; + slot /= 2; + assert(false); /* FIXME: Untested! */ + VertexAttribute *attribute = &pg->vertex_attributes[slot]; + pgraph_allocate_inline_buffer_vertices(pg, slot); + /* FIXME: Is mapping to [-1,+1] correct? */ + attribute->inline_value[part * 2 + 0] = ((int16_t)(parameter & 0xFFFF) + * 2.0f + 1) / 65535.0f; + attribute->inline_value[part * 2 + 1] = ((int16_t)(parameter >> 16) + * 2.0f + 1) / 65535.0f; + if ((slot == 0) && (part == 1)) { + pgraph_finish_inline_buffer_vertex(pg); + assert(false); /* FIXME: Untested */ + } + break; + } case NV097_SET_SEMAPHORE_OFFSET: kelvin->semaphore_offset = parameter; break; @@ -1042,7 +2295,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, @@ -1053,156 +2306,271 @@ 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; } - default: - if (method >= NV097_SET_COMBINER_ALPHA_ICW && method <= NV097_SET_COMBINER_ALPHA_ICW + 28) { - slot = (method - NV097_SET_COMBINER_ALPHA_ICW) / 4; - pg->regs[NV_PGRAPH_COMBINEALPHAI0 + slot * 4] = parameter; - break; - } + case NV097_SET_ZSTENCIL_CLEAR_VALUE: + pg->regs[NV_PGRAPH_ZSTENCILCLEARVALUE] = parameter; + break; - if (method >= NV097_SET_PROJECTION_MATRIX && method <= NV097_SET_PROJECTION_MATRIX + 0x3c) { - slot = (method - NV097_SET_PROJECTION_MATRIX) / 4; - // pg->projection_matrix[slot] = *(float*)¶meter; - unsigned int row = NV_IGRAPH_XF_XFCTX_PMAT0 + slot / 4; - pg->vsh_constants[row][slot % 4] = parameter; - pg->vsh_constants_dirty[row] = true; - break; - } + case NV097_SET_COLOR_CLEAR_VALUE: + pg->regs[NV_PGRAPH_COLORCLEARVALUE] = parameter; + break; - if (method >= NV097_SET_MODEL_VIEW_MATRIX && method <= NV097_SET_MODEL_VIEW_MATRIX + 0xfc) { - slot = (method - NV097_SET_MODEL_VIEW_MATRIX) / 4; - unsigned int matnum = slot / 16; - unsigned int entry = slot % 16; - unsigned int row = NV_IGRAPH_XF_XFCTX_MMAT0 + matnum * 8 + entry / 4; - pg->vsh_constants[row][entry % 4] = parameter; - pg->vsh_constants_dirty[row] = true; - break; - } + case NV097_CLEAR_SURFACE: { + NV2A_DPRINTF("---------PRE CLEAR ------\n"); + GLbitfield gl_mask = 0; - if (method >= NV097_SET_INVERSE_MODEL_VIEW_MATRIX && method <= NV097_SET_INVERSE_MODEL_VIEW_MATRIX + 0xfc) { - slot = (method - NV097_SET_INVERSE_MODEL_VIEW_MATRIX) / 4; - unsigned int matnum = slot / 16; - unsigned int entry = slot % 16; - unsigned int row = NV_IGRAPH_XF_XFCTX_IMMAT0 + matnum * 8 + entry / 4; - pg->vsh_constants[row][entry % 4] = parameter; - pg->vsh_constants_dirty[row] = true; - break; - } + bool write_color = (parameter & NV097_CLEAR_SURFACE_COLOR); + bool write_zeta = + (parameter & (NV097_CLEAR_SURFACE_Z | NV097_CLEAR_SURFACE_STENCIL)); +#ifdef COMPILE_OPENGL - if (method >= NV097_SET_COMPOSITE_MATRIX && method <= NV097_SET_COMPOSITE_MATRIX + 0x3c) { - slot = (method - NV097_SET_COMPOSITE_MATRIX) / 4; - unsigned int row = NV_IGRAPH_XF_XFCTX_CMAT0 + slot / 4; - pg->vsh_constants[row][slot % 4] = parameter; - pg->vsh_constants_dirty[row] = true; - break; - } + if (write_zeta) { + uint32_t clear_zstencil = + d->pgraph.regs[NV_PGRAPH_ZSTENCILCLEARVALUE]; + GLint gl_clear_stencil; + GLfloat gl_clear_depth; - if (method >= NV097_SET_TEXTURE_MATRIX && method <= NV097_SET_TEXTURE_MATRIX + 0xfc) { - slot = (method - NV097_SET_TEXTURE_MATRIX) / 4; - unsigned int tex = slot / 16; - unsigned int entry = slot % 16; - unsigned int row = NV_IGRAPH_XF_XFCTX_T0MAT + tex * 8 + entry / 4; - pg->vsh_constants[row][entry % 4] = parameter; - pg->vsh_constants_dirty[row] = true; - break; - } + /* FIXME: Put these in some lookup table */ + const float f16_max = 511.9375f; + /* FIXME: 7 bits of mantissa unused. maybe use full buffer? */ + const float f24_max = 3.4027977E38; - if (method >= NV097_SET_FOG_PARAMS && method <= NV097_SET_FOG_PARAMS + 8) { - slot = (method - NV097_SET_FOG_PARAMS) / 4; - if (slot < 2) { - pg->regs[NV_PGRAPH_FOGPARAM0 + slot * 4] = parameter; + switch(pg->surface_shape.zeta_format) { + case NV097_SET_SURFACE_FORMAT_ZETA_Z16: { + uint16_t z = clear_zstencil & 0xFFFF; + /* FIXME: Remove bit for stencil clear? */ + if (pg->surface_shape.z_format) { + gl_clear_depth = convert_f16_to_float(z) / f16_max; + assert(false); /* FIXME: Untested */ + } else { + gl_clear_depth = z / (float)0xFFFF; + } + break; } - else { - /* FIXME: No idea where slot = 2 is */ + case NV097_SET_SURFACE_FORMAT_ZETA_Z24S8: { + gl_clear_stencil = clear_zstencil & 0xFF; + uint32_t z = clear_zstencil >> 8; + if (pg->surface_shape.z_format) { + gl_clear_depth = convert_f24_to_float(z) / f24_max; + assert(false); /* FIXME: Untested */ + } else { + gl_clear_depth = z / (float)0xFFFFFF; + } + break; + } + default: + fprintf(stderr, "Unknown zeta surface format: 0x%x\n", pg->surface_shape.zeta_format); + assert(false); + break; + } + if (parameter & NV097_CLEAR_SURFACE_Z) { + gl_mask |= GL_DEPTH_BUFFER_BIT; + glDepthMask(GL_TRUE); + glClearDepth(gl_clear_depth); + } + if (parameter & NV097_CLEAR_SURFACE_STENCIL) { + gl_mask |= GL_STENCIL_BUFFER_BIT; + glStencilMask(0xff); + glClearStencil(gl_clear_stencil); + } + } + if (write_color) { + gl_mask |= GL_COLOR_BUFFER_BIT; + glColorMask((parameter & NV097_CLEAR_SURFACE_R) + ? GL_TRUE : GL_FALSE, + (parameter & NV097_CLEAR_SURFACE_G) + ? GL_TRUE : GL_FALSE, + (parameter & NV097_CLEAR_SURFACE_B) + ? GL_TRUE : GL_FALSE, + (parameter & NV097_CLEAR_SURFACE_A) + ? GL_TRUE : GL_FALSE); + uint32_t clear_color = d->pgraph.regs[NV_PGRAPH_COLORCLEARVALUE]; + + /* Handle RGB */ + GLfloat red, green, blue; + switch(pg->surface_shape.color_format) { + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_Z1R5G5B5: + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_O1R5G5B5: + red = ((clear_color >> 10) & 0x1F) / 31.0f; + green = ((clear_color >> 5) & 0x1F) / 31.0f; + blue = (clear_color & 0x1F) / 31.0f; + assert(false); /* Untested */ + break; + case NV097_SET_SURFACE_FORMAT_COLOR_LE_R5G6B5: + red = ((clear_color >> 11) & 0x1F) / 31.0f; + green = ((clear_color >> 5) & 0x3F) / 63.0f; + blue = (clear_color & 0x1F) / 31.0f; + break; + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_Z8R8G8B8: + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_O8R8G8B8: + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8: + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8: + case NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8: + red = ((clear_color >> 16) & 0xFF) / 255.0f; + green = ((clear_color >> 8) & 0xFF) / 255.0f; + blue = (clear_color & 0xFF) / 255.0f; + break; + case NV097_SET_SURFACE_FORMAT_COLOR_LE_B8: + case NV097_SET_SURFACE_FORMAT_COLOR_LE_G8B8: + /* Xbox D3D doesn't support clearing those */ + default: + red = 1.0f; + green = 0.0f; + blue = 1.0f; + fprintf(stderr, "CLEAR_SURFACE for color_format 0x%x unsupported", + pg->surface_shape.color_format); + assert(false); + break; } - pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FOG_K][slot] = parameter; - pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FOG_K] = true; - break; + /* Handle alpha */ + GLfloat alpha; + switch(pg->surface_shape.color_format) { + /* FIXME: CLEAR_SURFACE seems to work like memset, so maybe we + * also have to clear non-alpha bits with alpha value? + * As GL doesn't own those pixels we'd have to do this on + * our own in xbox memory. + */ + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8: + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8: + alpha = ((clear_color >> 24) & 0x7F) / 127.0f; + assert(false); /* Untested */ + break; + case NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8: + alpha = ((clear_color >> 24) & 0xFF) / 255.0f; + break; + default: + alpha = 1.0f; + break; + } + + glClearColor(red, green, blue, alpha); } - - /* Handles NV097_SET_TEXGEN_PLANE_S,T,R,Q */ - if (method >= NV097_SET_TEXGEN_PLANE_S && method <= NV097_SET_TEXGEN_PLANE_S + 0xfc) { - slot = (method - NV097_SET_TEXGEN_PLANE_S) / 4; - unsigned int tex = slot / 16; - unsigned int entry = slot % 16; - unsigned int row = NV_IGRAPH_XF_XFCTX_TG0MAT + tex * 8 + entry / 4; - pg->vsh_constants[row][entry % 4] = parameter; - pg->vsh_constants_dirty[row] = true; - break; + pgraph_update_surface(d, true, write_color, write_zeta); + + glEnable(GL_SCISSOR_TEST); + + unsigned int xmin = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTX], + NV_PGRAPH_CLEARRECTX_XMIN); + unsigned int xmax = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTX], + NV_PGRAPH_CLEARRECTX_XMAX); + unsigned int ymin = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTY], + NV_PGRAPH_CLEARRECTY_YMIN); + unsigned int ymax = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTY], + NV_PGRAPH_CLEARRECTY_YMAX); + + unsigned int scissor_x = xmin; + unsigned int scissor_y = pg->surface_shape.clip_height - ymax - 1; + + unsigned int scissor_width = xmax - xmin + 1; + unsigned int scissor_height = ymax - ymin + 1; + + pgraph_apply_anti_aliasing_factor(pg, &scissor_x, &scissor_y); + pgraph_apply_anti_aliasing_factor(pg, &scissor_width, &scissor_height); + + /* FIXME: Should this really be inverted instead of ymin? */ + glScissor(scissor_x, scissor_y, scissor_width, scissor_height); + + NV2A_DPRINTF("------------------CLEAR 0x%x %d,%d - %d,%d %x---------------\n", + parameter, xmin, ymin, xmax, ymax, d->pgraph.regs[NV_PGRAPH_COLORCLEARVALUE]); + + /* Dither */ + /* FIXME: Maybe also disable it here? + GL implementation dependent */ + if (pg->regs[NV_PGRAPH_CONTROL_0] & + NV_PGRAPH_CONTROL_0_DITHERENABLE) { + glEnable(GL_DITHER); + } else { + glDisable(GL_DITHER); } - if (method >= NV097_SET_FOG_PLANE && method <= NV097_SET_FOG_PLANE + 12) { - slot = (method - NV097_SET_FOG_PLANE) / 4; - pg->vsh_constants[NV_IGRAPH_XF_XFCTX_FOG][slot] = parameter; - pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_FOG] = true; - break; - } + glClear(gl_mask); - if (method >= NV097_SET_SCENE_AMBIENT_COLOR && method <= NV097_SET_SCENE_AMBIENT_COLOR + 8) { - slot = (method - NV097_SET_SCENE_AMBIENT_COLOR) / 4; - // ?? - pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FR_AMB][slot] = parameter; - pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FR_AMB] = true; - break; - } + glDisable(GL_SCISSOR_TEST); +#endif // COMPILE_OPENGL - if (method >= NV097_SET_VIEWPORT_OFFSET && method <= NV097_SET_VIEWPORT_OFFSET + 12) { - slot = (method - NV097_SET_VIEWPORT_OFFSET) / 4; - pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPOFF][slot] = parameter; - pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPOFF] = true; - break; - } - - if (method >= NV097_SET_EYE_POSITION && method <= NV097_SET_EYE_POSITION + 12) { - slot = (method - NV097_SET_EYE_POSITION) / 4; - pg->vsh_constants[NV_IGRAPH_XF_XFCTX_EYEP][slot] = parameter; - pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_EYEP] = true; - break; - } - - if (method >= NV097_SET_COMBINER_FACTOR0 && method <= NV097_SET_COMBINER_FACTOR0 + 28) { - slot = (method - NV097_SET_COMBINER_FACTOR0) / 4; - pg->regs[NV_PGRAPH_COMBINEFACTOR0 + slot * 4] = parameter; - break; - } - - if (method >= NV097_SET_COMBINER_FACTOR1 && method <= NV097_SET_COMBINER_FACTOR1 + 28) { - slot = (method - NV097_SET_COMBINER_FACTOR1) / 4; - pg->regs[NV_PGRAPH_COMBINEFACTOR1 + slot * 4] = parameter; - break; - } - - if (method >= NV097_SET_COMBINER_ALPHA_OCW && method <= NV097_SET_COMBINER_ALPHA_OCW + 28) { - slot = (method - NV097_SET_COMBINER_ALPHA_OCW) / 4; - pg->regs[NV_PGRAPH_COMBINEALPHAO0 + slot * 4] = parameter; - break; - } - - if (method >= NV097_SET_COMBINER_COLOR_ICW && method <= NV097_SET_COMBINER_COLOR_ICW + 28) { - slot = (method - NV097_SET_COMBINER_COLOR_ICW) / 4; - pg->regs[NV_PGRAPH_COMBINECOLORI0 + slot * 4] = parameter; - break; - } - - if (method >= NV097_SET_VIEWPORT_SCALE && method <= NV097_SET_VIEWPORT_SCALE + 12) { - slot = (method - NV097_SET_VIEWPORT_SCALE) / 4; - pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPSCL][slot] = parameter; - pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPSCL] = true; - break; - } - - EmuWarning("EmuNV2A: Unknown NV_KELVIN_PRIMITIVE Method: 0x%08X\n", method); + pgraph_set_surface_dirty(pg, write_color, write_zeta); + break; + } + + case NV097_SET_CLEAR_RECT_HORIZONTAL: + pg->regs[NV_PGRAPH_CLEARRECTX] = parameter; + break; + case NV097_SET_CLEAR_RECT_VERTICAL: + pg->regs[NV_PGRAPH_CLEARRECTY] = parameter; + break; + + CASE_2(NV097_SET_SPECULAR_FOG_FACTOR, 4) : + slot = (method - NV097_SET_SPECULAR_FOG_FACTOR) / 4; + pg->regs[NV_PGRAPH_SPECFOGFACTOR0 + slot * 4] = parameter; + break; + + case NV097_SET_SHADER_CLIP_PLANE_MODE: + pg->regs[NV_PGRAPH_SHADERCLIPMODE] = parameter; + break; + + CASE_8(NV097_SET_COMBINER_COLOR_OCW, 4) : + slot = (method - NV097_SET_COMBINER_COLOR_OCW) / 4; + pg->regs[NV_PGRAPH_COMBINECOLORO0 + slot * 4] = parameter; + break; + + case NV097_SET_COMBINER_CONTROL: + pg->regs[NV_PGRAPH_COMBINECTL] = parameter; + break; + + case NV097_SET_SHADOW_ZSLOPE_THRESHOLD: + pg->regs[NV_PGRAPH_SHADOWZSLOPETHRESHOLD] = parameter; + assert(parameter == 0x7F800000); /* FIXME: Unimplemented */ + break; + + case NV097_SET_SHADER_STAGE_PROGRAM: + pg->regs[NV_PGRAPH_SHADERPROG] = parameter; + break; + + case NV097_SET_SHADER_OTHER_STAGE_INPUT: + pg->regs[NV_PGRAPH_SHADERCTL] = parameter; + break; + + case NV097_SET_TRANSFORM_EXECUTION_MODE: + SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_MODE, + GET_MASK(parameter, + NV097_SET_TRANSFORM_EXECUTION_MODE_MODE)); + SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_RANGE_MODE, + GET_MASK(parameter, + NV097_SET_TRANSFORM_EXECUTION_MODE_RANGE_MODE)); + break; + case NV097_SET_TRANSFORM_PROGRAM_CXT_WRITE_EN: + pg->enable_vertex_program_write = parameter; + break; + case NV097_SET_TRANSFORM_PROGRAM_LOAD: + assert(parameter < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH); + SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], + NV_PGRAPH_CHEOPS_OFFSET_PROG_LD_PTR, parameter); + break; + case NV097_SET_TRANSFORM_PROGRAM_START: + assert(parameter < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH); + SET_MASK(pg->regs[NV_PGRAPH_CSV0_C], + NV_PGRAPH_CSV0_C_CHEOPS_PROGRAM_START, parameter); + break; + case NV097_SET_TRANSFORM_CONSTANT_LOAD: + assert(parameter < NV2A_VERTEXSHADER_CONSTANTS); + SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], + NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR, parameter); + NV2A_DPRINTF("load to %d\n", parameter); + break; + + default: + NV2A_GL_DPRINTF("EmuNV2A: Unknown NV_KELVIN_PRIMITIVE Method: 0x%08X\n", + method); + break; } - break; } default: - EmuWarning("EmuNV2A: Unknown Graphics Class/Method 0x%08X/0x%08X\n", object->graphics_class, method); + NV2A_GL_DPRINTF("EmuNV2A: Unknown Graphics Class/Method 0x%08X/0x%08X\n", + object->graphics_class, method); break; } @@ -1210,21 +2578,19 @@ static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int me static void pgraph_context_switch(NV2AState *d, unsigned int channel_id) { - bool valid = false; + bool valid; 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; } - d->pgraph.lock.unlock(); // TODO : This isn't in xqemu / OpenXbox? if (!valid) { - printf("puller needs to switch to ch %d\n", channel_id); + NV2A_DPRINTF("puller needs to switch to ch %d\n", channel_id); - d->pgraph.lock.unlock() + d->pgraph.lock.unlock(); qemu_mutex_lock_iothread(); d->pgraph.pending_interrupts |= NV_PGRAPH_INTR_CONTEXT_SWITCH; update_irq(d); @@ -1244,12 +2610,15 @@ static void pgraph_wait_fifo_access(NV2AState *d) { } } -static void pgraph_method_log(unsigned int subchannel, unsigned int graphics_class, unsigned int method, uint32_t parameter) { +static void pgraph_method_log(unsigned int subchannel, + unsigned int graphics_class, + unsigned int method, uint32_t parameter) { static unsigned int last = 0; static unsigned int count = 0; if (last == 0x1800 && method != last) { - printf("d->pgraph method (%d) 0x%08X * %d", subchannel, last, count); + NV2A_GL_DPRINTF("d->pgraph method (%d) 0x%08X * %d", + subchannel, last, count); } if (method != 0x1800) { const char* method_name = NULL; @@ -1269,12 +2638,11 @@ static void pgraph_method_log(unsigned int subchannel, unsigned int graphics_cla method_name = nv2a_method_names[nmethod]; } if (method_name) { - printf("d->pgraph method (%d): %s (0x%x)\n", + NV2A_DPRINTF("d->pgraph method (%d): %s (0x%x)\n", subchannel, method_name, parameter); - } - else { + } else { */ - printf("d->pgraph method (%d): 0x%x -> 0x%04x (0x%x)\n", + NV2A_DPRINTF("d->pgraph method (%d): 0x%x -> 0x%04x (0x%x)\n", subchannel, graphics_class, method, parameter); //} @@ -1287,7 +2655,7 @@ static void pgraph_method_log(unsigned int subchannel, unsigned int graphics_cla static void pgraph_allocate_inline_buffer_vertices(PGRAPHState *pg, unsigned int attr) { - int i; + unsigned int i; VertexAttribute *attribute = &pg->vertex_attributes[attr]; if (attribute->inline_buffer || pg->inline_buffer_length == 0) { @@ -1295,7 +2663,7 @@ static void pgraph_allocate_inline_buffer_vertices(PGRAPHState *pg, } /* Now upload the previous attribute value */ - attribute->inline_buffer = (float*)calloc(1, NV2A_MAX_BATCH_LENGTH + attribute->inline_buffer = (float*)malloc(NV2A_MAX_BATCH_LENGTH * sizeof(float) * 4); for (i = 0; i < pg->inline_buffer_length; i++) { memcpy(&attribute->inline_buffer[i * 4], @@ -1306,7 +2674,7 @@ static void pgraph_allocate_inline_buffer_vertices(PGRAPHState *pg, static void pgraph_finish_inline_buffer_vertex(PGRAPHState *pg) { - int i; + unsigned int i; assert(pg->inline_buffer_length < NV2A_MAX_BATCH_LENGTH); @@ -1323,6 +2691,477 @@ static void pgraph_finish_inline_buffer_vertex(PGRAPHState *pg) pg->inline_buffer_length++; } +#ifdef COMPILE_OPENGL +void pgraph_init(NV2AState *d) +{ + int i; + + PGRAPHState *pg = &d->pgraph; + + pg->lock = SDL_CreateMutex(); + pg->interrupt_cond = SDL_CreateCond(); + pg->fifo_access_cond = SDL_CreateCond(); + pg->flip_3d = SDL_CreateCond(); + + /* fire up opengl */ + + // pg->gl_context = glo_context_create(); + // assert(pg->gl_context); + +#ifdef DEBUG_NV2A_GL + glEnable(GL_DEBUG_OUTPUT); +#endif + + glextensions_init(); + + /* DXT textures */ + // assert(glo_check_extension("GL_EXT_texture_compression_s3tc")); + /* Internal RGB565 texture format */ + // assert(glo_check_extension("GL_ARB_ES2_compatibility")); + + GLint max_vertex_attributes; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attributes); + assert(max_vertex_attributes >= NV2A_VERTEXSHADER_ATTRIBUTES); + + + glGenFramebuffers(1, &pg->gl_framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, pg->gl_framebuffer); + + /* need a valid framebuffer to start with */ + glGenTextures(1, &pg->gl_color_buffer); + glBindTexture(GL_TEXTURE_2D, pg->gl_color_buffer); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 640, 480, + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, pg->gl_color_buffer, 0); + + assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) + == GL_FRAMEBUFFER_COMPLETE); + + //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + + pg->texture_cache = g_lru_cache_new_full( + 0, + NULL, + texture_key_destroy, + 0, + NULL, + texture_binding_destroy, + texture_key_hash, + texture_key_equal, + texture_key_retrieve, + NULL, + NULL + ); + + g_lru_cache_set_max_size(pg->texture_cache, 512); + + pg->shader_cache = g_hash_table_new(shader_hash, shader_equal); + + + for (i=0; ivertex_attributes[i].gl_converted_buffer); + glGenBuffers(1, &pg->vertex_attributes[i].gl_inline_buffer); + } + glGenBuffers(1, &pg->gl_inline_array_buffer); + glGenBuffers(1, &pg->gl_element_buffer); + + glGenBuffers(1, &pg->gl_memory_buffer); + glBindBuffer(GL_ARRAY_BUFFER, pg->gl_memory_buffer); + glBufferData(GL_ARRAY_BUFFER, + d->vram_size, + NULL, + GL_DYNAMIC_DRAW); + + glGenVertexArrays(1, &pg->gl_vertex_array); + glBindVertexArray(pg->gl_vertex_array); + + assert(glGetError() == GL_NO_ERROR); + + // glo_set_current(NULL); +} + +void pgraph_destroy(PGRAPHState *pg) +{ + SDL_DestroyMutex(pg->lock); + SDL_DestroyCond(pg->interrupt_cond); + SDL_DestroyCond(pg->fifo_access_cond); + SDL_DestroyCond(pg->flip_3d); + + // glo_set_current(pg->gl_context); + + if (pg->gl_color_buffer) { + glDeleteTextures(1, &pg->gl_color_buffer); + } + if (pg->gl_zeta_buffer) { + glDeleteTextures(1, &pg->gl_zeta_buffer); + } + glDeleteFramebuffers(1, &pg->gl_framebuffer); + + // TODO: clear out shader cached + // TODO: clear out texture cache + + // glo_set_current(NULL); + + // glo_context_destroy(pg->gl_context); +} + +static void pgraph_shader_update_constants(PGRAPHState *pg, + ShaderBinding *binding, + bool binding_changed, + bool vertex_program, + bool fixed_function) +{ + int i, j; + + /* update combiner constants */ + for (i = 0; i<= 8; i++) { + uint32_t constant[2]; + if (i == 8) { + /* final combiner */ + constant[0] = pg->regs[NV_PGRAPH_SPECFOGFACTOR0]; + constant[1] = pg->regs[NV_PGRAPH_SPECFOGFACTOR1]; + } else { + constant[0] = pg->regs[NV_PGRAPH_COMBINEFACTOR0 + i * 4]; + constant[1] = pg->regs[NV_PGRAPH_COMBINEFACTOR1 + i * 4]; + } + + for (j = 0; j < 2; j++) { + GLint loc = binding->psh_constant_loc[i][j]; + if (loc != -1) { + float value[4]; + value[0] = (float) ((constant[j] >> 16) & 0xFF) / 255.0f; + value[1] = (float) ((constant[j] >> 8) & 0xFF) / 255.0f; + value[2] = (float) (constant[j] & 0xFF) / 255.0f; + value[3] = (float) ((constant[j] >> 24) & 0xFF) / 255.0f; + + glUniform4fv(loc, 1, value); + } + } + } + if (binding->alpha_ref_loc != -1) { + float alpha_ref = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_ALPHAREF) / 255.0f; + glUniform1f(binding->alpha_ref_loc, alpha_ref); + } + + + /* For each texture stage */ + for (i = 0; i < NV2A_MAX_TEXTURES; i++) { + // char name[32]; + GLint loc; + + /* Bump luminance only during stages 1 - 3 */ + if (i > 0) { + loc = binding->bump_mat_loc[i]; + if (loc != -1) { + glUniformMatrix2fv(loc, 1, GL_FALSE, pg->bump_env_matrix[i - 1]); + } + loc = binding->bump_scale_loc[i]; + if (loc != -1) { + glUniform1f(loc, *(float*)&pg->regs[ + NV_PGRAPH_BUMPSCALE1 + (i - 1) * 4]); + } + loc = binding->bump_offset_loc[i]; + if (loc != -1) { + glUniform1f(loc, *(float*)&pg->regs[ + NV_PGRAPH_BUMPOFFSET1 + (i - 1) * 4]); + } + } + + } + + if (binding->fog_color_loc != -1) { + uint32_t fog_color = pg->regs[NV_PGRAPH_FOGCOLOR]; + glUniform4f(binding->fog_color_loc, + GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_RED) / 255.0f, + GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_GREEN) / 255.0f, + GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_BLUE) / 255.0f, + GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_ALPHA) / 255.0f); + } + if (binding->fog_param_loc[0] != -1) { + glUniform1f(binding->fog_param_loc[0], + *(float*)&pg->regs[NV_PGRAPH_FOGPARAM0]); + } + if (binding->fog_param_loc[1] != -1) { + glUniform1f(binding->fog_param_loc[1], + *(float*)&pg->regs[NV_PGRAPH_FOGPARAM1]); + } + + + float zclip_max = *(float*)&pg->regs[NV_PGRAPH_ZCLIPMAX]; + float zclip_min = *(float*)&pg->regs[NV_PGRAPH_ZCLIPMIN]; + + if (fixed_function) { + /* update lighting constants */ + struct { + uint32_t* v; + bool* dirty; + GLint* locs; + size_t len; + } lighting_arrays[] = { + {&pg->ltctxa[0][0], &pg->ltctxa_dirty[0], binding->ltctxa_loc, NV2A_LTCTXA_COUNT}, + {&pg->ltctxb[0][0], &pg->ltctxb_dirty[0], binding->ltctxb_loc, NV2A_LTCTXB_COUNT}, + {&pg->ltc1[0][0], &pg->ltc1_dirty[0], binding->ltc1_loc, NV2A_LTC1_COUNT}, + }; + + for (i=0; ilight_infinite_half_vector_loc[i]; + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_infinite_half_vector[i]); + } + loc = binding->light_infinite_direction_loc[i]; + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_infinite_direction[i]); + } + + loc = binding->light_local_position_loc[i]; + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_local_position[i]); + } + loc = binding->light_local_attenuation_loc[i]; + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_local_attenuation[i]); + } + } + + /* estimate the viewport by assuming it matches the surface ... */ + //FIXME: Get surface dimensions? + float m11 = 0.5 * pg->surface_shape.clip_width; + float m22 = -0.5 * pg->surface_shape.clip_height; + float m33 = zclip_max - zclip_min; + //float m41 = m11; + //float m42 = -m22; + float m43 = zclip_min; + //float m44 = 1.0f; + + if (m33 == 0.0f) { + m33 = 1.0f; + } + float invViewport[16] = { + 1.0f/m11, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f/m22, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f/m33, 0.0f, + -1.0f, 1.0f, -m43/m33, 1.0f + }; + + if (binding->inv_viewport_loc != -1) { + glUniformMatrix4fv(binding->inv_viewport_loc, + 1, GL_FALSE, &invViewport[0]); + } + + } + + /* update vertex program constants */ + for (i=0; ivsh_constants_dirty[i] && !binding_changed) continue; + + GLint loc = binding->vsh_constant_loc[i]; + //assert(loc != -1); + if (loc != -1) { + glUniform4fv(loc, 1, (const GLfloat*)pg->vsh_constants[i]); + } + pg->vsh_constants_dirty[i] = false; + } + + if (binding->surface_size_loc != -1) { + glUniform2f(binding->surface_size_loc, pg->surface_shape.clip_width, + pg->surface_shape.clip_height); + } + + if (binding->clip_range_loc != -1) { + glUniform2f(binding->clip_range_loc, zclip_min, zclip_max); + } + +} + +static void pgraph_bind_shaders(PGRAPHState *pg) +{ + int i, j; + + bool vertex_program = GET_MASK(pg->regs[NV_PGRAPH_CSV0_D], + NV_PGRAPH_CSV0_D_MODE) == 2; + + bool fixed_function = GET_MASK(pg->regs[NV_PGRAPH_CSV0_D], + NV_PGRAPH_CSV0_D_MODE) == 0; + + int program_start = GET_MASK(pg->regs[NV_PGRAPH_CSV0_C], + NV_PGRAPH_CSV0_C_CHEOPS_PROGRAM_START); + + NV2A_GL_DGROUP_BEGIN("%s (VP: %s FFP: %s)", __func__, + vertex_program ? "yes" : "no", + fixed_function ? "yes" : "no"); + + ShaderBinding* old_binding = pg->shader_binding; + + ShaderState state = { + .psh = (PshState){ + /* register combier stuff */ + .combiner_control = pg->regs[NV_PGRAPH_COMBINECTL], + .shader_stage_program = pg->regs[NV_PGRAPH_SHADERPROG], + .other_stage_input = pg->regs[NV_PGRAPH_SHADERCTL], + .final_inputs_0 = pg->regs[NV_PGRAPH_COMBINESPECFOG0], + .final_inputs_1 = pg->regs[NV_PGRAPH_COMBINESPECFOG1], + + .alpha_test = pg->regs[NV_PGRAPH_CONTROL_0] + & NV_PGRAPH_CONTROL_0_ALPHATESTENABLE, + .alpha_func = (enum PshAlphaFunc)GET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_ALPHAFUNC), + }, + + /* fixed function stuff */ + .skinning = (enum VshSkinning)GET_MASK(pg->regs[NV_PGRAPH_CSV0_D], + NV_PGRAPH_CSV0_D_SKIN), + .lighting = GET_MASK(pg->regs[NV_PGRAPH_CSV0_C], + NV_PGRAPH_CSV0_C_LIGHTING), + .normalization = pg->regs[NV_PGRAPH_CSV0_C] + & NV_PGRAPH_CSV0_C_NORMALIZATION_ENABLE, + + .fixed_function = fixed_function, + + /* vertex program stuff */ + .vertex_program = vertex_program, + .z_perspective = pg->regs[NV_PGRAPH_CONTROL_0] + & NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE, + + /* geometry shader stuff */ + .primitive_mode = (enum ShaderPrimitiveMode)pg->primitive_mode, + .polygon_front_mode = (enum ShaderPolygonMode)GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_FRONTFACEMODE), + .polygon_back_mode = (enum ShaderPolygonMode)GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_BACKFACEMODE), + }; + + state.program_length = 0; + memset(state.program_data, 0, sizeof(state.program_data)); + + if (vertex_program) { + // copy in vertex program tokens + for (i = program_start; i < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH; i++) { + uint32_t *cur_token = (uint32_t*)&pg->program_data[i]; + memcpy(&state.program_data[state.program_length], + cur_token, + VSH_TOKEN_SIZE * sizeof(uint32_t)); + state.program_length++; + + if (vsh_get_field(cur_token, FLD_FINAL)) { + break; + } + } + } + + /* Texgen */ + for (i = 0; i < 4; i++) { + unsigned int reg = (i < 2) ? NV_PGRAPH_CSV1_A : NV_PGRAPH_CSV1_B; + for (j = 0; j < 4; j++) { + unsigned int masks[] = { + (i % 2) ? NV_PGRAPH_CSV1_A_T1_S : NV_PGRAPH_CSV1_A_T0_S, + (i % 2) ? NV_PGRAPH_CSV1_A_T1_T : NV_PGRAPH_CSV1_A_T0_T, + (i % 2) ? NV_PGRAPH_CSV1_A_T1_R : NV_PGRAPH_CSV1_A_T0_R, + (i % 2) ? NV_PGRAPH_CSV1_A_T1_Q : NV_PGRAPH_CSV1_A_T0_Q + }; + state.texgen[i][j] = (enum VshTexgen)GET_MASK(pg->regs[reg], masks[j]); + } + } + + /* Fog */ + state.fog_enable = pg->regs[NV_PGRAPH_CONTROL_3] + & NV_PGRAPH_CONTROL_3_FOGENABLE; + if (state.fog_enable) { + /*FIXME: Use CSV0_D? */ + state.fog_mode = (enum VshFogMode)GET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], + NV_PGRAPH_CONTROL_3_FOG_MODE); + state.foggen = (enum VshFoggen)GET_MASK(pg->regs[NV_PGRAPH_CSV0_D], + NV_PGRAPH_CSV0_D_FOGGENMODE); + } else { + /* FIXME: Do we still pass the fogmode? */ + state.fog_mode = (enum VshFogMode)0; + state.foggen = (enum VshFoggen)0; + } + + /* Texture matrices */ + for (i = 0; i < 4; i++) { + state.texture_matrix_enable[i] = pg->texture_matrix_enable[i]; + } + + /* Lighting */ + if (state.lighting) { + for (i = 0; i < NV2A_MAX_LIGHTS; i++) { + state.light[i] = (enum VshLight)GET_MASK(pg->regs[NV_PGRAPH_CSV0_D], + NV_PGRAPH_CSV0_D_LIGHT0 << (i * 2)); + } + } + + for (i = 0; i < 8; i++) { + state.psh.rgb_inputs[i] = pg->regs[NV_PGRAPH_COMBINECOLORI0 + i * 4]; + state.psh.rgb_outputs[i] = pg->regs[NV_PGRAPH_COMBINECOLORO0 + i * 4]; + state.psh.alpha_inputs[i] = pg->regs[NV_PGRAPH_COMBINEALPHAI0 + i * 4]; + state.psh.alpha_outputs[i] = pg->regs[NV_PGRAPH_COMBINEALPHAO0 + i * 4]; + //constant_0[i] = pg->regs[NV_PGRAPH_COMBINEFACTOR0 + i * 4]; + //constant_1[i] = pg->regs[NV_PGRAPH_COMBINEFACTOR1 + i * 4]; + } + + for (i = 0; i < 4; i++) { + state.psh.rect_tex[i] = false; + bool enabled = pg->regs[NV_PGRAPH_TEXCTL0_0 + i*4] + & NV_PGRAPH_TEXCTL0_0_ENABLE; + unsigned int color_format = + GET_MASK(pg->regs[NV_PGRAPH_TEXFMT0 + i*4], + NV_PGRAPH_TEXFMT0_COLOR); + + if (enabled && kelvin_color_format_map[color_format].linear) { + state.psh.rect_tex[i] = true; + } + + for (j = 0; j < 4; j++) { + state.psh.compare_mode[i][j] = + (pg->regs[NV_PGRAPH_SHADERCLIPMODE] >> (4 * i + j)) & 1; + } + state.psh.alphakill[i] = pg->regs[NV_PGRAPH_TEXCTL0_0 + i*4] + & NV_PGRAPH_TEXCTL0_0_ALPHAKILLEN; + } + + ShaderBinding* cached_shader = (ShaderBinding*)g_hash_table_lookup(pg->shader_cache, &state); + if (cached_shader) { + pg->shader_binding = cached_shader; + } else { + pg->shader_binding = generate_shaders(state); + + /* cache it */ + ShaderState *cache_state = (ShaderState *)g_malloc(sizeof(*cache_state)); + memcpy(cache_state, &state, sizeof(*cache_state)); + g_hash_table_insert(pg->shader_cache, cache_state, + (gpointer)pg->shader_binding); + } + + bool binding_changed = (pg->shader_binding != old_binding); + + glUseProgram(pg->shader_binding->gl_program); + + pgraph_shader_update_constants(pg, pg->shader_binding, binding_changed, + vertex_program, fixed_function); + + NV2A_GL_DGROUP_END(); +} +#endif // COMPILE_OPENGL + static bool pgraph_framebuffer_dirty(PGRAPHState *pg) { bool shape_changed = memcmp(&pg->surface_shape, &pg->last_surface_shape, @@ -1350,18 +3189,876 @@ static bool pgraph_zeta_write_enabled(PGRAPHState *pg) | NV_PGRAPH_CONTROL_0_STENCIL_WRITE_ENABLE); } +static void pgraph_set_surface_dirty(PGRAPHState *pg, bool color, bool zeta) +{ + NV2A_DPRINTF("pgraph_set_surface_dirty(%d, %d) -- %d %d\n", + color, zeta, + pgraph_color_write_enabled(pg), pgraph_zeta_write_enabled(pg)); + /* FIXME: Does this apply to CLEARs too? */ + color = color && pgraph_color_write_enabled(pg); + zeta = zeta && pgraph_zeta_write_enabled(pg); + pg->surface_color.draw_dirty |= color; + pg->surface_zeta.draw_dirty |= zeta; +} + +#ifdef COMPILE_OPENGL +static void pgraph_update_surface_part(NV2AState *d, bool upload, bool color) { + PGRAPHState *pg = &d->pgraph; + + unsigned int width, height; + pgraph_get_surface_dimensions(pg, &width, &height); + pgraph_apply_anti_aliasing_factor(pg, &width, &height); + + Surface *surface; + hwaddr dma_address; + GLuint *gl_buffer; + unsigned int bytes_per_pixel; + GLenum gl_internal_format, gl_format, gl_type, gl_attachment; + + if (color) { + surface = &pg->surface_color; + dma_address = pg->dma_color; + gl_buffer = &pg->gl_color_buffer; + + assert(pg->surface_shape.color_format != 0); + assert(pg->surface_shape.color_format + < ARRAY_SIZE(kelvin_surface_color_format_map)); + SurfaceColorFormatInfo f = + kelvin_surface_color_format_map[pg->surface_shape.color_format]; + if (f.bytes_per_pixel == 0) { + fprintf(stderr, "nv2a: unimplemented color surface format 0x%x\n", + pg->surface_shape.color_format); + abort(); + } + + bytes_per_pixel = f.bytes_per_pixel; + gl_internal_format = f.gl_internal_format; + gl_format = f.gl_format; + gl_type = f.gl_type; + gl_attachment = GL_COLOR_ATTACHMENT0; + + } else { + surface = &pg->surface_zeta; + dma_address = pg->dma_zeta; + gl_buffer = &pg->gl_zeta_buffer; + + assert(pg->surface_shape.zeta_format != 0); + switch (pg->surface_shape.zeta_format) { + case NV097_SET_SURFACE_FORMAT_ZETA_Z16: + bytes_per_pixel = 2; + gl_format = GL_DEPTH_COMPONENT; + gl_attachment = GL_DEPTH_ATTACHMENT; + if (pg->surface_shape.z_format) { + gl_type = GL_HALF_FLOAT; + gl_internal_format = GL_DEPTH_COMPONENT32F; + } else { + gl_type = GL_UNSIGNED_SHORT; + gl_internal_format = GL_DEPTH_COMPONENT16; + } + break; + case NV097_SET_SURFACE_FORMAT_ZETA_Z24S8: + bytes_per_pixel = 4; + gl_format = GL_DEPTH_STENCIL; + gl_attachment = GL_DEPTH_STENCIL_ATTACHMENT; + if (pg->surface_shape.z_format) { + assert(false); + gl_type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV; + gl_internal_format = GL_DEPTH32F_STENCIL8; + } else { + gl_type = GL_UNSIGNED_INT_24_8; + gl_internal_format = GL_DEPTH24_STENCIL8; + } + break; + default: + assert(false); + break; + } + } + + + DMAObject dma = nv_dma_load(d, dma_address); + /* There's a bunch of bugs that could cause us to hit this function + * at the wrong time and get a invalid dma object. + * Check that it's sane. */ + assert(dma.dma_class == NV_DMA_IN_MEMORY_CLASS); + + assert(dma.address + surface->offset != 0); + assert(surface->offset <= dma.limit); + assert(surface->offset + surface->pitch * height <= dma.limit + 1); + + hwaddr data_len; + uint8_t *data = (uint8_t*)nv_dma_map(d, dma_address, &data_len); + + /* TODO */ + // assert(pg->surface_clip_x == 0 && pg->surface_clip_y == 0); + + bool swizzle = (pg->surface_type == NV097_SET_SURFACE_FORMAT_TYPE_SWIZZLE); + + uint8_t *buf = data + surface->offset; + if (swizzle) { + buf = (uint8_t*)g_malloc(height * surface->pitch); + } + + bool dirty = surface->buffer_dirty; + if (color) { +#if 1 + dirty |= 1; +#else + dirty |= memory_region_test_and_clear_dirty(d->vram, + dma.address + surface->offset, + surface->pitch * height, + DIRTY_MEMORY_NV2A); +#endif + } + if (upload && dirty) { + /* surface modified (or moved) by the cpu. + * copy it into the opengl renderbuffer */ + assert(!surface->draw_dirty); + + assert(surface->pitch % bytes_per_pixel == 0); + + if (swizzle) { + unswizzle_rect(data + surface->offset, + width, height, + buf, + surface->pitch, + bytes_per_pixel); + } + + if (!color) { + /* need to clear the depth_stencil and depth attachment for zeta */ + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, + 0, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, + 0, 0); + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, + gl_attachment, + GL_TEXTURE_2D, + 0, 0); + + if (*gl_buffer) { + glDeleteTextures(1, gl_buffer); + *gl_buffer = 0; + } + + glGenTextures(1, gl_buffer); + glBindTexture(GL_TEXTURE_2D, *gl_buffer); + + /* This is VRAM so we can't do this inplace! */ + uint8_t *flipped_buf = (uint8_t*)g_malloc(width * height * bytes_per_pixel); + unsigned int irow; + for (irow = 0; irow < height; irow++) { + memcpy(&flipped_buf[width * (height - irow - 1) + * bytes_per_pixel], + &buf[surface->pitch * irow], + width * bytes_per_pixel); + } + + glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, + width, height, 0, + gl_format, gl_type, + flipped_buf); + + g_free(flipped_buf); + + glFramebufferTexture2D(GL_FRAMEBUFFER, + gl_attachment, + GL_TEXTURE_2D, + *gl_buffer, 0); + + assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) + == GL_FRAMEBUFFER_COMPLETE); + + if (color) { + pgraph_update_memory_buffer(d, dma.address + surface->offset, + surface->pitch * height, true); + } + surface->buffer_dirty = false; + + + uint8_t *out = data + surface->offset + 64; + NV2A_DPRINTF("upload_surface %s 0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", " + "(0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", " + "%d %d, %d %d, %d) - %x %x %x %x\n", + color ? "color" : "zeta", + dma.address, dma.address + dma.limit, + dma.address + surface->offset, + dma.address + surface->pitch * height, + pg->surface_shape.clip_x, pg->surface_shape.clip_y, + pg->surface_shape.clip_width, + pg->surface_shape.clip_height, + surface->pitch, + out[0], out[1], out[2], out[3]); + + } + + if (!upload && surface->draw_dirty) { + /* read the opengl framebuffer into the surface */ + + glo_readpixels(gl_format, gl_type, + bytes_per_pixel, surface->pitch, + width, height, + buf); + assert(glGetError() == GL_NO_ERROR); + + if (swizzle) { + swizzle_rect(buf, + width, height, + data + surface->offset, + surface->pitch, + bytes_per_pixel); + } + + // memory_region_set_client_dirty(d->vram, + // dma.address + surface->offset, + // surface->pitch * height, + // DIRTY_MEMORY_VGA); + + if (color) { + pgraph_update_memory_buffer(d, dma.address + surface->offset, + surface->pitch * height, true); + } + + surface->draw_dirty = false; + surface->write_enabled_cache = false; + + uint8_t *out = data + surface->offset + 64; + NV2A_DPRINTF("read_surface %s 0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", " + "(0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", " + "%d %d, %d %d, %d) - %x %x %x %x\n", + color ? "color" : "zeta", + dma.address, dma.address + dma.limit, + dma.address + surface->offset, + dma.address + surface->pitch * pg->surface_shape.clip_height, + pg->surface_shape.clip_x, pg->surface_shape.clip_y, + pg->surface_shape.clip_width, pg->surface_shape.clip_height, + surface->pitch, + out[0], out[1], out[2], out[3]); + + } + + if (swizzle) { + g_free(buf); + } +} +#endif // COMPILE_OPENGL + static void pgraph_update_surface(NV2AState *d, bool upload, bool color_write, bool zeta_write) { +#ifdef COMPILE_OPENGL + PGRAPHState *pg = &d->pgraph; + + pg->surface_shape.z_format = GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_Z_FORMAT); + + /* FIXME: Does this apply to CLEARs too? */ + color_write = color_write && pgraph_color_write_enabled(pg); + zeta_write = zeta_write && pgraph_zeta_write_enabled(pg); + + if (upload && pgraph_framebuffer_dirty(pg)) { + assert(!pg->surface_color.draw_dirty); + assert(!pg->surface_zeta.draw_dirty); + + pg->surface_color.buffer_dirty = true; + pg->surface_zeta.buffer_dirty = true; + + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + 0, 0); + + if (pg->gl_color_buffer) { + glDeleteTextures(1, &pg->gl_color_buffer); + pg->gl_color_buffer = 0; + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, + 0, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, + 0, 0); + + if (pg->gl_zeta_buffer) { + glDeleteTextures(1, &pg->gl_zeta_buffer); + pg->gl_zeta_buffer = 0; + } + + memcpy(&pg->last_surface_shape, &pg->surface_shape, + sizeof(SurfaceShape)); + } + + if ((color_write || (!upload && pg->surface_color.write_enabled_cache)) + && (upload || pg->surface_color.draw_dirty)) { + pgraph_update_surface_part(d, upload, true); + } + + + if ((zeta_write || (!upload && pg->surface_zeta.write_enabled_cache)) + && (upload || pg->surface_zeta.draw_dirty)) { + pgraph_update_surface_part(d, upload, false); + } +#else // COMPILE_OPENGL printf("TODO: pgraph_update_surface\n"); +#endif // COMPILE_OPENGL + } -static void load_graphics_object(NV2AState *d, xbaddr instance_address, GraphicsObject *obj) +#ifdef COMPILE_OPENGL +static void pgraph_bind_textures(NV2AState *d) +{ + int i; + PGRAPHState *pg = &d->pgraph; + + NV2A_GL_DGROUP_BEGIN("%s", __func__); + + for (i=0; iregs[NV_PGRAPH_TEXCTL0_0 + i*4]; + uint32_t ctl_1 = pg->regs[NV_PGRAPH_TEXCTL1_0 + i*4]; + uint32_t fmt = pg->regs[NV_PGRAPH_TEXFMT0 + i*4]; + uint32_t filter = pg->regs[NV_PGRAPH_TEXFILTER0 + i*4]; + uint32_t address = pg->regs[NV_PGRAPH_TEXADDRESS0 + i*4]; + uint32_t palette = pg->regs[NV_PGRAPH_TEXPALETTE0 + i*4]; + + bool enabled = GET_MASK(ctl_0, NV_PGRAPH_TEXCTL0_0_ENABLE); + unsigned int min_mipmap_level = + GET_MASK(ctl_0, NV_PGRAPH_TEXCTL0_0_MIN_LOD_CLAMP); + unsigned int max_mipmap_level = + GET_MASK(ctl_0, NV_PGRAPH_TEXCTL0_0_MAX_LOD_CLAMP); + + unsigned int pitch = + GET_MASK(ctl_1, NV_PGRAPH_TEXCTL1_0_IMAGE_PITCH); + + unsigned int dma_select = + GET_MASK(fmt, NV_PGRAPH_TEXFMT0_CONTEXT_DMA); + bool cubemap = + GET_MASK(fmt, NV_PGRAPH_TEXFMT0_CUBEMAPENABLE); + unsigned int dimensionality = + GET_MASK(fmt, NV_PGRAPH_TEXFMT0_DIMENSIONALITY); + unsigned int color_format = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_COLOR); + unsigned int levels = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_MIPMAP_LEVELS); + unsigned int log_width = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_BASE_SIZE_U); + unsigned int log_height = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_BASE_SIZE_V); + unsigned int log_depth = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_BASE_SIZE_P); + + unsigned int rect_width = + GET_MASK(pg->regs[NV_PGRAPH_TEXIMAGERECT0 + i*4], + NV_PGRAPH_TEXIMAGERECT0_WIDTH); + unsigned int rect_height = + GET_MASK(pg->regs[NV_PGRAPH_TEXIMAGERECT0 + i*4], + NV_PGRAPH_TEXIMAGERECT0_HEIGHT); + + unsigned int lod_bias = + GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MIPMAP_LOD_BIAS); + unsigned int min_filter = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MIN); + unsigned int mag_filter = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MAG); + + unsigned int addru = GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRU); + unsigned int addrv = GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRV); + unsigned int addrp = GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRP); + + unsigned int border_source = GET_MASK(fmt, + NV_PGRAPH_TEXFMT0_BORDER_SOURCE); + uint32_t border_color = pg->regs[NV_PGRAPH_BORDERCOLOR0 + i*4]; + + unsigned int offset = pg->regs[NV_PGRAPH_TEXOFFSET0 + i*4]; + + bool palette_dma_select = + GET_MASK(palette, NV_PGRAPH_TEXPALETTE0_CONTEXT_DMA); + unsigned int palette_length_index = + GET_MASK(palette, NV_PGRAPH_TEXPALETTE0_LENGTH); + unsigned int palette_offset = + palette & NV_PGRAPH_TEXPALETTE0_OFFSET; + + unsigned int palette_length = 0; + switch (palette_length_index) { + case NV_PGRAPH_TEXPALETTE0_LENGTH_256: palette_length = 256; break; + case NV_PGRAPH_TEXPALETTE0_LENGTH_128: palette_length = 128; break; + case NV_PGRAPH_TEXPALETTE0_LENGTH_64: palette_length = 64; break; + case NV_PGRAPH_TEXPALETTE0_LENGTH_32: palette_length = 32; break; + default: assert(false); break; + } + + /* Check for unsupported features */ + assert(!(filter & NV_PGRAPH_TEXFILTER0_ASIGNED)); + assert(!(filter & NV_PGRAPH_TEXFILTER0_RSIGNED)); + assert(!(filter & NV_PGRAPH_TEXFILTER0_GSIGNED)); + assert(!(filter & NV_PGRAPH_TEXFILTER0_BSIGNED)); + + glActiveTexture(GL_TEXTURE0 + i); + if (!enabled) { + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + glBindTexture(GL_TEXTURE_RECTANGLE, 0); + glBindTexture(GL_TEXTURE_1D, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(GL_TEXTURE_3D, 0); + continue; + } + + if (!pg->texture_dirty[i] && pg->texture_binding[i]) { + glBindTexture(pg->texture_binding[i]->gl_target, + pg->texture_binding[i]->gl_texture); + continue; + } + + NV2A_DPRINTF(" texture %d is format 0x%x, (r %d, %d or %d, %d, %d; %d%s)," + " filter %x %x, levels %d-%d %d bias %d\n", + i, color_format, + rect_width, rect_height, + 1 << log_width, 1 << log_height, 1 << log_depth, + pitch, + cubemap ? "; cubemap" : "", + min_filter, mag_filter, + min_mipmap_level, max_mipmap_level, levels, + lod_bias); + + assert(color_format < ARRAY_SIZE(kelvin_color_format_map)); + ColorFormatInfo f = kelvin_color_format_map[color_format]; + if (f.bytes_per_pixel == 0) { + fprintf(stderr, "nv2a: unimplemented texture color format 0x%x\n", + color_format); + abort(); + } + + unsigned int width, height, depth; + if (f.linear) { + assert(dimensionality == 2); + width = rect_width; + height = rect_height; + depth = 1; + } else { + width = 1 << log_width; + height = 1 << log_height; + depth = 1 << log_depth; + + /* FIXME: What about 3D mipmaps? */ + levels = MIN(levels, max_mipmap_level + 1); + if (f.gl_format != 0) { + /* Discard mipmap levels that would be smaller than 1x1. + * FIXME: Is this actually needed? + * + * >> Level 0: 32 x 4 + * Level 1: 16 x 2 + * Level 2: 8 x 1 + * Level 3: 4 x 1 + * Level 4: 2 x 1 + * Level 5: 1 x 1 + */ + levels = MIN(levels, MAX(log_width, log_height) + 1); + } else { + /* OpenGL requires DXT textures to always have a width and + * height a multiple of 4. The Xbox and DirectX handles DXT + * textures smaller than 4 by padding the reset of the block. + * + * See: + * https://msdn.microsoft.com/en-us/library/windows/desktop/bb204843(v=vs.85).aspx + * https://msdn.microsoft.com/en-us/library/windows/desktop/bb694531%28v=vs.85%29.aspx#Virtual_Size + * + * Work around this for now by discarding mipmap levels that + * would result in too-small textures. A correct solution + * will be to decompress these levels manually, or add texture + * sampling logic. + * + * >> Level 0: 64 x 8 + * Level 1: 32 x 4 + * Level 2: 16 x 2 << Ignored + * >> Level 0: 16 x 16 + * Level 1: 8 x 8 + * Level 2: 4 x 4 << OK! + */ + if (log_width < 2 || log_height < 2) { + /* Base level is smaller than 4x4... */ + levels = 1; + } else { + levels = MIN(levels, MIN(log_width, log_height) - 1); + } + } + assert(levels > 0); + } + + hwaddr dma_len; + uint8_t *texture_data; + if (dma_select) { + texture_data = (uint8_t*)nv_dma_map(d, pg->dma_b, &dma_len); + } else { + texture_data = (uint8_t*)nv_dma_map(d, pg->dma_a, &dma_len); + } + assert(offset < dma_len); + texture_data += offset; + + hwaddr palette_dma_len; + uint8_t *palette_data; + if (palette_dma_select) { + palette_data = (uint8_t*)nv_dma_map(d, pg->dma_b, &palette_dma_len); + } else { + palette_data = (uint8_t*)nv_dma_map(d, pg->dma_a, &palette_dma_len); + } + assert(palette_offset < palette_dma_len); + palette_data += palette_offset; + + NV2A_DPRINTF(" - 0x%tx\n", texture_data - d->vram_ptr); + + size_t length = 0; + if (f.linear) { + assert(cubemap == false); + assert(dimensionality == 2); + length = height * pitch; + } else { + if (dimensionality >= 2) { + unsigned int w = width, h = height; + int level; + if (f.gl_format != 0) { + for (level = 0; level < levels; level++) { + w = MAX(w, 1); h = MAX(h, 1); + length += w * h * f.bytes_per_pixel; + w /= 2; + h /= 2; + } + } else { + /* Compressed textures are a bit different */ + unsigned int block_size; + if (f.gl_internal_format == + GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) { + block_size = 8; + } else { + block_size = 16; + } + + for (level = 0; level < levels; level++) { + w = MAX(w, 4); h = MAX(h, 4); + length += w/4 * h/4 * block_size; + w /= 2; h /= 2; + } + } + if (cubemap) { + assert(dimensionality == 2); + length *= 6; + } + if (dimensionality >= 3) { + length *= depth; + } + } + } + + TextureShape state = { + .cubemap = cubemap, + .dimensionality = dimensionality, + .color_format = color_format, + .levels = levels, + .width = width, + .height = height, + .depth = depth, + .min_mipmap_level = min_mipmap_level, + .max_mipmap_level = max_mipmap_level, + .pitch = pitch, + }; + +#ifdef USE_TEXTURE_CACHE + TextureKey key = { + .state = state, + .data_hash = fast_hash(texture_data, length, 5003) + ^ fnv_hash(palette_data, palette_length), + .texture_data = texture_data, + .palette_data = palette_data, + }; + + gpointer cache_key = g_malloc(sizeof(TextureKey)); + memcpy(cache_key, &key, sizeof(TextureKey)); + + GError *err; + TextureBinding *binding = (TextureBinding *)g_lru_cache_get(pg->texture_cache, cache_key, &err); + assert(binding); + binding->refcnt++; +#else + TextureBinding *binding = generate_texture(state, + texture_data, palette_data); +#endif + + glBindTexture(binding->gl_target, binding->gl_texture); + + + if (f.linear) { + /* somtimes games try to set mipmap min filters on linear textures. + * this could indicate a bug... */ + switch (min_filter) { + case NV_PGRAPH_TEXFILTER0_MIN_BOX_NEARESTLOD: + case NV_PGRAPH_TEXFILTER0_MIN_BOX_TENT_LOD: + min_filter = NV_PGRAPH_TEXFILTER0_MIN_BOX_LOD0; + break; + case NV_PGRAPH_TEXFILTER0_MIN_TENT_NEARESTLOD: + case NV_PGRAPH_TEXFILTER0_MIN_TENT_TENT_LOD: + min_filter = NV_PGRAPH_TEXFILTER0_MIN_TENT_LOD0; + break; + } + } + + glTexParameteri(binding->gl_target, GL_TEXTURE_MIN_FILTER, + pgraph_texture_min_filter_map[min_filter]); + glTexParameteri(binding->gl_target, GL_TEXTURE_MAG_FILTER, + pgraph_texture_mag_filter_map[mag_filter]); + + /* Texture wrapping */ + assert(addru < ARRAY_SIZE(pgraph_texture_addr_map)); + glTexParameteri(binding->gl_target, GL_TEXTURE_WRAP_S, + pgraph_texture_addr_map[addru]); + if (dimensionality > 1) { + assert(addrv < ARRAY_SIZE(pgraph_texture_addr_map)); + glTexParameteri(binding->gl_target, GL_TEXTURE_WRAP_T, + pgraph_texture_addr_map[addrv]); + } + if (dimensionality > 2) { + assert(addrp < ARRAY_SIZE(pgraph_texture_addr_map)); + glTexParameteri(binding->gl_target, GL_TEXTURE_WRAP_R, + pgraph_texture_addr_map[addrp]); + } + + /* FIXME: Only upload if necessary? [s, t or r = GL_CLAMP_TO_BORDER] */ + if (border_source == NV_PGRAPH_TEXFMT0_BORDER_SOURCE_COLOR) { + GLfloat gl_border_color[] = { + /* FIXME: Color channels might be wrong order */ + ((border_color >> 16) & 0xFF) / 255.0f, /* red */ + ((border_color >> 8) & 0xFF) / 255.0f, /* green */ + (border_color & 0xFF) / 255.0f, /* blue */ + ((border_color >> 24) & 0xFF) / 255.0f /* alpha */ + }; + glTexParameterfv(binding->gl_target, GL_TEXTURE_BORDER_COLOR, + gl_border_color); + } + + if (pg->texture_binding[i]) { + texture_binding_destroy(pg->texture_binding[i]); + } + pg->texture_binding[i] = binding; + pg->texture_dirty[i] = false; + } + NV2A_GL_DGROUP_END(); +} +#endif // COMPILE_OPENGL + +static void pgraph_apply_anti_aliasing_factor(PGRAPHState *pg, + unsigned int *width, + unsigned int *height) +{ + switch (pg->surface_shape.anti_aliasing) { + case NV097_SET_SURFACE_FORMAT_ANTI_ALIASING_CENTER_1: + break; + case NV097_SET_SURFACE_FORMAT_ANTI_ALIASING_CENTER_CORNER_2: + if (width) { *width *= 2; } + break; + case NV097_SET_SURFACE_FORMAT_ANTI_ALIASING_SQUARE_OFFSET_4: + if (width) { *width *= 2; } + if (height) { *height *= 2; } + break; + default: + assert(false); + break; + } +} + +static void pgraph_get_surface_dimensions(PGRAPHState *pg, + unsigned int *width, + unsigned int *height) +{ + bool swizzle = (pg->surface_type == NV097_SET_SURFACE_FORMAT_TYPE_SWIZZLE); + if (swizzle) { + *width = 1 << pg->surface_shape.log_width; + *height = 1 << pg->surface_shape.log_height; + } else { + *width = pg->surface_shape.clip_width; + *height = pg->surface_shape.clip_height; + } +} + +#ifdef COMPILE_OPENGL +static void pgraph_update_memory_buffer(NV2AState *d, hwaddr addr, hwaddr size, + bool f) +{ + glBindBuffer(GL_ARRAY_BUFFER, d->pgraph.gl_memory_buffer); + + hwaddr end = TARGET_PAGE_ALIGN(addr + size); + addr &= TARGET_PAGE_MASK; + assert(end < d->vram_size); + // if (f || memory_region_test_and_clear_dirty(d->vram, + // addr, + // end - addr, + // DIRTY_MEMORY_NV2A)) { + glBufferSubData(GL_ARRAY_BUFFER, addr, end - addr, d->vram_ptr + addr); + // } +} + +static void pgraph_bind_vertex_attributes(NV2AState *d, + unsigned int num_elements, + bool inline_data, + unsigned int inline_stride) +{ + int i, j; + PGRAPHState *pg = &d->pgraph; + + if (inline_data) { + NV2A_GL_DGROUP_BEGIN("%s (num_elements: %d inline stride: %d)", + __func__, num_elements, inline_stride); + } else { + NV2A_GL_DGROUP_BEGIN("%s (num_elements: %d)", __func__, num_elements); + } + + for (i=0; ivertex_attributes[i]; + if (attribute->count) { + uint8_t *data; + unsigned int in_stride; + if (inline_data && attribute->needs_conversion) { + data = (uint8_t*)pg->inline_array + + attribute->inline_array_offset; + in_stride = inline_stride; + } else { + hwaddr dma_len; + if (attribute->dma_select) { + data = (uint8_t*)nv_dma_map(d, pg->dma_vertex_b, &dma_len); + } else { + data = (uint8_t*)nv_dma_map(d, pg->dma_vertex_a, &dma_len); + } + + assert(attribute->offset < dma_len); + data += attribute->offset; + + in_stride = attribute->stride; + } + + if (attribute->needs_conversion) { + NV2A_DPRINTF("converted %d\n", i); + + unsigned int out_stride = attribute->converted_size + * attribute->converted_count; + + if (num_elements > attribute->converted_elements) { + attribute->converted_buffer = (uint8_t*)g_realloc( + attribute->converted_buffer, + num_elements * out_stride); + } + + for (j=attribute->converted_elements; jconverted_buffer + j * out_stride; + + switch (attribute->format) { + case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_CMP: { + uint32_t p = ldl_le_p((uint32_t*)in); + float *xyz = (float*)out; + xyz[0] = ((int32_t)(((p >> 0) & 0x7FF) << 21) >> 21) + / 1023.0f; + xyz[1] = ((int32_t)(((p >> 11) & 0x7FF) << 21) >> 21) + / 1023.0f; + xyz[2] = ((int32_t)(((p >> 22) & 0x3FF) << 22) >> 22) + / 511.0f; + break; + } + default: + assert(false); + break; + } + } + + + glBindBuffer(GL_ARRAY_BUFFER, attribute->gl_converted_buffer); + if (num_elements != attribute->converted_elements) { + glBufferData(GL_ARRAY_BUFFER, + num_elements * out_stride, + attribute->converted_buffer, + GL_DYNAMIC_DRAW); + attribute->converted_elements = num_elements; + } + + + glVertexAttribPointer(i, + attribute->converted_count, + attribute->gl_type, + attribute->gl_normalize, + out_stride, + 0); + } else if (inline_data) { + glBindBuffer(GL_ARRAY_BUFFER, pg->gl_inline_array_buffer); + glVertexAttribPointer(i, + attribute->gl_count, + attribute->gl_type, + attribute->gl_normalize, + inline_stride, + (void*)(uintptr_t)attribute->inline_array_offset); + } else { + hwaddr addr = data - d->vram_ptr; + pgraph_update_memory_buffer(d, addr, + num_elements * attribute->stride, + false); + glVertexAttribPointer(i, + attribute->gl_count, + attribute->gl_type, + attribute->gl_normalize, + attribute->stride, + (void*)(uint64_t)addr); + } + glEnableVertexAttribArray(i); + } else { + glDisableVertexAttribArray(i); + + glVertexAttrib4fv(i, attribute->inline_value); + } + } + NV2A_GL_DGROUP_END(); +} + +static unsigned int pgraph_bind_inline_array(NV2AState *d) +{ + int i; + + PGRAPHState *pg = &d->pgraph; + + unsigned int offset = 0; + for (i=0; ivertex_attributes[i]; + if (attribute->count) { + attribute->inline_array_offset = offset; + + NV2A_DPRINTF("bind inline attribute %d size=%d, count=%d\n", + i, attribute->size, attribute->count); + offset += attribute->size * attribute->count; + assert(offset % 4 == 0); + } + } + + unsigned int vertex_size = offset; + + + unsigned int index_count = pg->inline_array_length*4 / vertex_size; + + NV2A_DPRINTF("draw inline array %d, %d\n", vertex_size, index_count); + + glBindBuffer(GL_ARRAY_BUFFER, pg->gl_inline_array_buffer); + glBufferData(GL_ARRAY_BUFFER, pg->inline_array_length*4, pg->inline_array, + GL_DYNAMIC_DRAW); + + pgraph_bind_vertex_attributes(d, index_count, true, vertex_size); + + return index_count; +} +#endif // COMPILE_OPENGL + +static void load_graphics_object(NV2AState *d, hwaddr instance_address, + GraphicsObject *obj) { uint8_t *obj_ptr; uint32_t switch1, switch2, switch3; assert(instance_address < d->pramin.ramin_size); + obj_ptr = d->pramin.ramin_ptr + instance_address; switch1 = ldl_le_p((uint32_t*)obj_ptr); @@ -1380,7 +4077,8 @@ static void load_graphics_object(NV2AState *d, xbaddr instance_address, Graphics } } -static GraphicsObject* lookup_graphics_object(PGRAPHState *s,xbaddr instance_address) +static GraphicsObject* lookup_graphics_object(PGRAPHState *s, + hwaddr instance_address) { int i; for (i = 0; i> 24)); f24 &= 0xFFFFFF; - if (f24 == 0x000000) { return 0.0; } + if (f24 == 0x000000) { return 0.0f; } uint32_t i = f24 << 7; return *(float*)&i; } @@ -1428,6 +4126,356 @@ static void convert_yuy2_to_rgb(const uint8_t *line, unsigned int ix, *b = cliptobyte((298 * c + 516 * d + 128) >> 8); } +static uint8_t* convert_texture_data(const TextureShape s, + const uint8_t *data, + const uint8_t *palette_data, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int row_pitch, + unsigned int slice_pitch) +{ + if (s.color_format == NV097_SET_TEXTURE_FORMAT_COLOR_SZ_I8_A8R8G8B8) { + assert(depth == 1); /* FIXME */ + uint8_t* converted_data = (uint8_t*)malloc(width * height * 4); + unsigned int x, y; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + uint8_t index = data[y * row_pitch + x]; + uint32_t color = *(uint32_t*)(palette_data + index * 4); + *(uint32_t*)(converted_data + y * width * 4 + x * 4) = color; + } + } + return converted_data; + } else if (s.color_format + == NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_CR8YB8CB8YA8) { + assert(depth == 1); /* FIXME */ + uint8_t* converted_data = (uint8_t*)malloc(width * height * 4); + unsigned int x, y; + for (y = 0; y < height; y++) { + const uint8_t* line = &data[y * s.width * 2]; + for (x = 0; x < width; x++) { + uint8_t* pixel = &converted_data[(y * s.width + x) * 4]; + /* FIXME: Actually needs uyvy? */ + convert_yuy2_to_rgb(line, x, &pixel[0], &pixel[1], &pixel[2]); + pixel[3] = 255; + } + } + return converted_data; + } else if (s.color_format + == NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R6G5B5) { + assert(depth == 1); /* FIXME */ + uint8_t *converted_data = (uint8_t*)malloc(width * height * 3); + unsigned int x, y; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + uint16_t rgb655 = *(uint16_t*)(data + y * row_pitch + x * 2); + int8_t *pixel = (int8_t*)&converted_data[(y * width + x) * 3]; + /* Maps 5 bit G and B signed value range to 8 bit + * signed values. R is probably unsigned. + */ + rgb655 ^= (1 << 9) | (1 << 4); + pixel[0] = ((rgb655 & 0xFC00) >> 10) * 0x7F / 0x3F; + pixel[1] = ((rgb655 & 0x03E0) >> 5) * 0xFF / 0x1F - 0x80; + pixel[2] = (rgb655 & 0x001F) * 0xFF / 0x1F - 0x80; + } + } + return converted_data; + } else { + return NULL; + } +} + +#ifdef COMPILE_OPENGL +static void upload_gl_texture(GLenum gl_target, + const TextureShape s, + const uint8_t *texture_data, + const uint8_t *palette_data) +{ + ColorFormatInfo f = kelvin_color_format_map[s.color_format]; + + switch(gl_target) { + case GL_TEXTURE_1D: + assert(false); + break; + case GL_TEXTURE_RECTANGLE: { + /* Can't handle strides unaligned to pixels */ + assert(s.pitch % f.bytes_per_pixel == 0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, + s.pitch / f.bytes_per_pixel); + + uint8_t *converted = convert_texture_data(s, texture_data, + palette_data, + s.width, s.height, 1, + s.pitch, 0); + + glTexImage2D(gl_target, 0, f.gl_internal_format, + s.width, s.height, 0, + f.gl_format, f.gl_type, + converted ? converted : texture_data); + + if (converted) { + g_free(converted); + } + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + break; + } + case GL_TEXTURE_2D: + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: { + + unsigned int width = s.width, height = s.height; + + int level; + for (level = 0; level < s.levels; level++) { + if (f.gl_format == 0) { /* compressed */ + + width = MAX(width, 4); height = MAX(height, 4); + + unsigned int block_size; + if (f.gl_internal_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) { + block_size = 8; + } else { + block_size = 16; + } + + glCompressedTexImage2D(gl_target, level, f.gl_internal_format, + width, height, 0, + width/4 * height/4 * block_size, + texture_data); + + texture_data += width/4 * height/4 * block_size; + } else { + + width = MAX(width, 1); height = MAX(height, 1); + + unsigned int pitch = width * f.bytes_per_pixel; + uint8_t *unswizzled = (uint8_t*)g_malloc(height * pitch); + unswizzle_rect(texture_data, width, height, + unswizzled, pitch, f.bytes_per_pixel); + + uint8_t *converted = convert_texture_data(s, unswizzled, + palette_data, + width, height, 1, + pitch, 0); + + glTexImage2D(gl_target, level, f.gl_internal_format, + width, height, 0, + f.gl_format, f.gl_type, + converted ? converted : unswizzled); + + if (converted) { + g_free(converted); + } + g_free(unswizzled); + + texture_data += width * height * f.bytes_per_pixel; + } + + width /= 2; + height /= 2; + } + + break; + } + case GL_TEXTURE_3D: { + + unsigned int width = s.width, height = s.height, depth = s.depth; + + assert(f.gl_format != 0); /* FIXME: compressed not supported yet */ + assert(f.linear == false); + + int level; + for (level = 0; level < s.levels; level++) { + + unsigned int row_pitch = width * f.bytes_per_pixel; + unsigned int slice_pitch = row_pitch * height; + uint8_t *unswizzled = (uint8_t*)g_malloc(slice_pitch * depth); + unswizzle_box(texture_data, width, height, depth, unswizzled, + row_pitch, slice_pitch, f.bytes_per_pixel); + + uint8_t *converted = convert_texture_data(s, unswizzled, + palette_data, + width, height, depth, + row_pitch, slice_pitch); + + glTexImage3D(gl_target, level, f.gl_internal_format, + width, height, depth, 0, + f.gl_format, f.gl_type, + converted ? converted : unswizzled); + + if (converted) { + g_free(converted); + } + g_free(unswizzled); + + texture_data += width * height * depth * f.bytes_per_pixel; + + width /= 2; + height /= 2; + depth /= 2; + } + break; + } + default: + assert(false); + break; + } +} + +static TextureBinding* generate_texture(const TextureShape s, + const uint8_t *texture_data, + const uint8_t *palette_data) +{ + ColorFormatInfo f = kelvin_color_format_map[s.color_format]; + + /* Create a new opengl texture */ + GLuint gl_texture; + glGenTextures(1, &gl_texture); + + GLenum gl_target; + if (s.cubemap) { + assert(f.linear == false); + assert(s.dimensionality == 2); + gl_target = GL_TEXTURE_CUBE_MAP; + } else { + if (f.linear) { + /* linear textures use unnormalised texcoords. + * GL_TEXTURE_RECTANGLE_ARB conveniently also does, but + * does not allow repeat and mirror wrap modes. + * (or mipmapping, but xbox d3d says 'Non swizzled and non + * compressed textures cannot be mip mapped.') + * Not sure if that'll be an issue. */ + + /* FIXME: GLSL 330 provides us with textureSize()! Use that? */ + gl_target = GL_TEXTURE_RECTANGLE; + assert(s.dimensionality == 2); + } else { + switch(s.dimensionality) { + case 1: gl_target = GL_TEXTURE_1D; break; + case 2: gl_target = GL_TEXTURE_2D; break; + case 3: gl_target = GL_TEXTURE_3D; break; + default: + assert(false); + break; + } + } + } + + glBindTexture(gl_target, gl_texture); + + NV2A_GL_DLABEL(GL_TEXTURE, gl_texture, + "format: 0x%02X%s, %d dimensions%s, width: %d, height: %d, depth: %d", + s.color_format, f.linear ? "" : " (SZ)", + s.dimensionality, s.cubemap ? " (Cubemap)" : "", + s.width, s.height, s.depth); + + if (gl_target == GL_TEXTURE_CUBE_MAP) { + + size_t length = 0; + unsigned int w = s.width, h = s.height; + int level; + for (level = 0; level < s.levels; level++) { + /* FIXME: This is wrong for compressed textures and textures with 1x? non-square mipmaps */ + length += w * h * f.bytes_per_pixel; + w /= 2; + h /= 2; + } + + upload_gl_texture(GL_TEXTURE_CUBE_MAP_POSITIVE_X, + s, texture_data + 0 * length, palette_data); + upload_gl_texture(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + s, texture_data + 1 * length, palette_data); + upload_gl_texture(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + s, texture_data + 2 * length, palette_data); + upload_gl_texture(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + s, texture_data + 3 * length, palette_data); + upload_gl_texture(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + s, texture_data + 4 * length, palette_data); + upload_gl_texture(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + s, texture_data + 5 * length, palette_data); + } else { + upload_gl_texture(gl_target, s, texture_data, palette_data); + } + + /* Linear textures don't support mipmapping */ + if (!f.linear) { + glTexParameteri(gl_target, GL_TEXTURE_BASE_LEVEL, + s.min_mipmap_level); + glTexParameteri(gl_target, GL_TEXTURE_MAX_LEVEL, + s.levels - 1); + } + + if (f.gl_swizzle_mask[0] != 0 || f.gl_swizzle_mask[1] != 0 + || f.gl_swizzle_mask[2] != 0 || f.gl_swizzle_mask[3] != 0) { + glTexParameteriv(gl_target, GL_TEXTURE_SWIZZLE_RGBA, + (const GLint *)f.gl_swizzle_mask); + } + + TextureBinding* ret = (TextureBinding *)g_malloc(sizeof(TextureBinding)); + ret->gl_target = gl_target; + ret->gl_texture = gl_texture; + ret->refcnt = 1; + return ret; +} + +/* functions for texture LRU cache */ +static guint texture_key_hash(gconstpointer key) +{ + const TextureKey *k = (const TextureKey *)key; + uint64_t state_hash = fnv_hash( + (const uint8_t*)&k->state, sizeof(TextureShape)); + return state_hash ^ k->data_hash; +} +static gboolean texture_key_equal(gconstpointer a, gconstpointer b) +{ + const TextureKey *ak = (const TextureKey *)a, *bk = (const TextureKey *)b; + return memcmp(&ak->state, &bk->state, sizeof(TextureShape)) == 0 + && ak->data_hash == bk->data_hash; +} +static gpointer texture_key_retrieve(gpointer key, gpointer user_data, GError **error) +{ + const TextureKey *k = (const TextureKey *)key; + TextureBinding *v = generate_texture(k->state, + k->texture_data, + k->palette_data); + if (error != NULL) { + *error = NULL; + } + return v; +} +static void texture_key_destroy(gpointer data) +{ + g_free(data); +} +static void texture_binding_destroy(gpointer data) +{ + TextureBinding *binding = (TextureBinding *)data; + assert(binding->refcnt > 0); + binding->refcnt--; + if (binding->refcnt == 0) { + glDeleteTextures(1, &binding->gl_texture); + g_free(binding); + } +} + +/* hash and equality for shader cache hash table */ +static guint shader_hash(gconstpointer key) +{ + return fnv_hash((const uint8_t *)key, sizeof(ShaderState)); +} +static gboolean shader_equal(gconstpointer a, gconstpointer b) +{ + const ShaderState *as = (const ShaderState *)a, *bs = (const ShaderState *)b; + return memcmp(as, bs, sizeof(ShaderState)) == 0; +} +#endif // COMPILE_OPENGL + static unsigned int kelvin_map_stencil_op(uint32_t parameter) { unsigned int op; diff --git a/src/devices/video/EmuNV2A_PRAMDAC.cpp b/src/devices/video/EmuNV2A_PRAMDAC.cpp index 34ec5c60b..2cd190d6c 100644 --- a/src/devices/video/EmuNV2A_PRAMDAC.cpp +++ b/src/devices/video/EmuNV2A_PRAMDAC.cpp @@ -44,8 +44,7 @@ DEVICE_WRITE32(PRAMDAC) if (m == 0) { d->pramdac.core_clock_freq = 0; - } - else { + } else { d->pramdac.core_clock_freq = (NV2A_CRYSTAL_FREQ * n) / (1 << p) / m; } diff --git a/src/devices/video/EmuNV2A_PRAMIN.cpp b/src/devices/video/EmuNV2A_PRAMIN.cpp index 389ec2735..533be20ec 100644 --- a/src/devices/video/EmuNV2A_PRAMIN.cpp +++ b/src/devices/video/EmuNV2A_PRAMIN.cpp @@ -1,21 +1,13 @@ DEVICE_READ32(PRAMIN) { - DEVICE_READ32_SWITCH() { - default: - DEVICE_READ32_REG(pramin); - break; - } + uint32_t result = *((uint32_t*)(d->pramin.ramin_ptr + addr)); DEVICE_READ32_END(PRAMIN); } DEVICE_WRITE32(PRAMIN) { - switch (addr) { - default: - DEVICE_WRITE32_REG(pramin); - break; - } + *((uint32_t*)(d->pramin.ramin_ptr + addr)) = value; DEVICE_WRITE32_END(PRAMIN); } diff --git a/src/devices/video/EmuNV2A_PVIDEO.cpp b/src/devices/video/EmuNV2A_PVIDEO.cpp index 8121a8907..250b77866 100644 --- a/src/devices/video/EmuNV2A_PVIDEO.cpp +++ b/src/devices/video/EmuNV2A_PVIDEO.cpp @@ -1,15 +1,13 @@ -/* static void pvideo_vga_invalidate(NV2AState *d) { - int y1 = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_OUT], + int y1 = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_OUT(0)], NV_PVIDEO_POINT_OUT_Y); - int y2 = y1 + GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT], + int y2 = y1 + GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT(0)], NV_PVIDEO_SIZE_OUT_HEIGHT); NV2A_DPRINTF("pvideo_vga_invalidate %d %d\n", y1, y2); - vga_invalidate_scanlines(&d->vga, y1, y2); + // TODO : vga_invalidate_scanlines(&d->vga, y1, y2); } -*/ DEVICE_READ32(PVIDEO) { DEVICE_READ32_SWITCH() { @@ -30,13 +28,13 @@ DEVICE_WRITE32(PVIDEO) switch (addr) { case NV_PVIDEO_BUFFER: d->pvideo.regs[addr] = value; - // TODO: vga.enable_overlay = true; - // pvideo_vga_invalidate(d); + // TODO : d->vga.enable_overlay = true; + pvideo_vga_invalidate(d); break; case NV_PVIDEO_STOP: d->pvideo.regs[NV_PVIDEO_BUFFER] = 0; - // TODO: vga.enable_overlay = false; - //pvideo_vga_invalidate(d); + // TODO : d->vga.enable_overlay = false; + pvideo_vga_invalidate(d); break; default: DEVICE_WRITE32_REG(pvideo); diff --git a/src/devices/video/EmuNV2A_USER.cpp b/src/devices/video/EmuNV2A_USER.cpp index 731a73141..4aae56fc4 100644 --- a/src/devices/video/EmuNV2A_USER.cpp +++ b/src/devices/video/EmuNV2A_USER.cpp @@ -60,8 +60,7 @@ DEVICE_WRITE32(USER) DEBUG_WRITE32_UNHANDLED(USER); break; } - } - else { + } else { /* PIO Mode */ assert(false); } diff --git a/src/devices/video/nv2a.h b/src/devices/video/nv2a.h index f4e9260eb..b358d6fd3 100644 --- a/src/devices/video/nv2a.h +++ b/src/devices/video/nv2a.h @@ -57,12 +57,13 @@ #define NV_PGRAPH_SIZE 0x002000 #define NV_PCRTC_SIZE 0x001000 #define NV_PRAMDAC_SIZE 0x001000 -#define NV_PRAMIN_ADDR 0x00700000 -#define NV_PRAMIN_SIZE 0x100000 typedef xbaddr hwaddr; // Compatibility; Cxbx uses xbaddr, xqemu and OpenXbox use hwaddr typedef uint32_t value_t; // Compatibility; Cxbx values are uint32_t (xqemu and OpenXbox use uint64_t) - +#define NV2A_DPRINTF printf // Compatibility; TODO : Replace this by something equivalent +#define NV2A_GL_DPRINTF EmuWarning // Compatibility; TODO : Replace this by something equivalent +#define VSH_TOKEN_SIZE 4 // Compatibility; TODO : Move this to nv2a_vsh.h +//#define MAX(a,b) ((a)>(b) ? (a) : (b)) // Compatibility #define USE_TEXTURE_CACHE @@ -123,12 +124,27 @@ inline int SET_MASK(int v, int mask, int val) { return (v) |= ((__val) << (ffs(__mask)-1)) & (__mask); } -#define CASE_4(v, step) \ - case (v): \ - case (v)+(step) : \ - case (v)+(step) * 2: \ - case (v)+(step) * 3 +// Power-of-two CASE statements +#define CASE_1(v, step) case (v) +#define CASE_2(v, step) CASE_1(v, step) : CASE_1(v + (step) * 1, step) +#define CASE_4(v, step) CASE_2(v, step) : CASE_2(v + (step) * 2, step) +#define CASE_8(v, step) CASE_4(v, step) : CASE_4(v + (step) * 4, step) +#define CASE_16(v, step) CASE_8(v, step) : CASE_8(v + (step) * 8, step) +#define CASE_32(v, step) CASE_16(v, step) : CASE_16(v + (step) * 16, step) +#define CASE_64(v, step) CASE_32(v, step) : CASE_32(v + (step) * 32, step) +#define CASE_128(v, step) CASE_64(v, step) : CASE_64(v + (step) * 64, step) +// Non-power-of-two CASE statements +#define CASE_3(v, step) CASE_2(v, step) : CASE_1(v + (step) * 2, step) +#define CASE_12(v, step) CASE_8(v, step) : CASE_4(v + (step) * 8, step) +#define CASE_13(v, step) CASE_8(v, step) : CASE_3(v + (step) * 8, step) +#define CASE_28(v, step) CASE_16(v, step) : CASE_12(v + (step) * 16, step) +#define CASE_29(v, step) CASE_16(v, step) : CASE_13(v + (step) * 16, step) +#define CASE_61(v, step) CASE_32(v, step) : CASE_29(v + (step) * 32, step) +#define CASE_78(v, step) CASE_64(v, step) : CASE_12(v + (step) * 64, step) +#define CASE_125(v, step) CASE_64(v, step) : CASE_61(v + (step) * 64, step) +#define CASE_132(v, step) CASE_128(v, step) : CASE_4(v + (step) * 128, step) +#define CASE_253(v, step) CASE_128(v, step) : CASE_125(v + (step) * 128, step) #define NV2A_DEVICE(obj) \ OBJECT_CHECK(NV2AState, (obj), "nv2a") @@ -339,7 +355,7 @@ typedef struct PGRAPHState { bool enable_vertex_program_write; - //uint32_t program_data[NV2A_MAX_TRANSFORM_PROGRAM_LENGTH][VSH_TOKEN_SIZE]; + uint32_t program_data[NV2A_MAX_TRANSFORM_PROGRAM_LENGTH][VSH_TOKEN_SIZE]; uint32_t vsh_constants[NV2A_VERTEXSHADER_CONSTANTS][4]; bool vsh_constants_dirty[NV2A_VERTEXSHADER_CONSTANTS]; @@ -449,7 +465,6 @@ typedef struct NV2AState { struct { uint8_t *ramin_ptr; size_t ramin_size; - uint32_t regs[NV_PRAMIN_SIZE]; // TODO : union } pramin; // MemoryRegion mmio; @@ -458,7 +473,7 @@ typedef struct NV2AState { struct { uint32_t pending_interrupts; uint32_t enabled_interrupts; - uint32_t regs[NV_PMC_SIZE]; // TODO : union + uint32_t regs[NV_PMC_SIZE]; // Not in xqemu/openxbox? TODO : union } pmc; struct { @@ -479,7 +494,7 @@ typedef struct NV2AState { uint32_t numerator; uint32_t denominator; uint32_t alarm_time; - uint32_t regs[NV_PTIMER_SIZE]; // TODO : union + uint32_t regs[NV_PTIMER_SIZE]; // Not in xqemu/openxbox? TODO : union } ptimer; struct { @@ -492,7 +507,7 @@ typedef struct NV2AState { uint32_t pending_interrupts; uint32_t enabled_interrupts; hwaddr start; - uint32_t regs[NV_PCRTC_SIZE]; // TODO : union + uint32_t regs[NV_PCRTC_SIZE]; // Not in xqemu/openxbox? TODO : union } pcrtc; struct { @@ -500,7 +515,7 @@ typedef struct NV2AState { uint64_t core_clock_freq; uint32_t memory_clock_coeff; uint32_t video_clock_coeff; - uint32_t regs[NV_PRAMDAC_SIZE]; // TODO : union + uint32_t regs[NV_PRAMDAC_SIZE]; // Not in xqemu/openxbox? TODO : union } pramdac; struct { @@ -511,9 +526,9 @@ typedef struct NV2AState { struct { uint8_t cr_index; uint8_t cr[256]; /* CRT registers */ - } prmcio; + } prmcio; // Not in xqemu/openxbox? - std::mutex io_lock; + std::mutex io_lock; // TODO ? std::unique_lock //SDL_Window *sdl_window; } NV2AState;