Merge pull request #1457 from PatrickvL/xqemu_port

Ported xqemu nv2a changes
This commit is contained in:
Luke Usher 2018-10-07 21:34:39 +01:00 committed by GitHub
commit 81d52e50b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 3327 additions and 3194 deletions

View File

@ -293,6 +293,7 @@
<ClInclude Include="..\..\src\devices\video\nv2a_debug.h" />
<ClInclude Include="..\..\src\devices\video\nv2a_int.h" />
<ClInclude Include="..\..\src\devices\video\nv2a_psh.h" />
<ClInclude Include="..\..\src\devices\video\nv2a_regs.h" />
<ClInclude Include="..\..\src\devices\video\nv2a_shaders.h" />
<ClInclude Include="..\..\src\devices\video\nv2a_shaders_common.h" />
<ClInclude Include="..\..\src\devices\video\nv2a_vsh.h" />

View File

@ -808,4 +808,9 @@
<Image Include="..\..\resource\Cxbx-R.ico" />
<Image Include="..\..\resource\Logo-License-CC4.bmp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\devices\video\nv2a_regs.h">
<Filter>Hardware\Video</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -2377,7 +2377,7 @@ static void EmuVerifyResourceIsRegistered(XTL::X_D3DResource *pResource, DWORD D
typedef struct {
DWORD Hash = 0;
DWORD IndexCount = 0;;
DWORD IndexCount = 0;
XTL::IDirect3DIndexBuffer* pHostIndexBuffer = nullptr;
} ConvertedIndexBuffer;
@ -4870,7 +4870,7 @@ void CreateHostResource(XTL::X_D3DResource *pResource, DWORD D3DUsage, int iText
// If, and ONLY if this is the default backbuffer, make sure the format matches the host backbuffer
if (pResource == g_XboxBackBufferSurface) {
PCFormat = g_EmuCDPD.HostPresentationParameters.BackBufferFormat;;
PCFormat = g_EmuCDPD.HostPresentationParameters.BackBufferFormat;
}
}
else {
@ -8541,7 +8541,7 @@ HRESULT WINAPI XTL::EMUPATCH(D3DDevice_SetDepthClipPlanes)
break;
default:
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "Unknown SetDepthClipPlanes Flags provided");;
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "Unknown SetDepthClipPlanes Flags provided");
}
// TODO

View File

@ -449,7 +449,8 @@ extern void XTL::EmuExecutePushBufferRaw
// Retrieve NV2AState via the (LLE) NV2A device :
NV2AState *d = g_NV2A->GetDeviceState();
d->pgraph.channel_valid = true; // avoid assert
d->pgraph.regs[NV_PGRAPH_CTX_CONTROL] |= NV_PGRAPH_CTX_CONTROL_CHID; // avoid assert in pgraph_handle_method()
// DMA Pusher state -- see https://envytools.readthedocs.io/en/latest/hw/fifo/dma-pusher.html#pusher-state
#if 0

View File

@ -905,7 +905,7 @@ static void VshWriteShader(VSH_XBOX_SHADER *pShader,
}
i++;
} while (i < RegVUsage.size());;
} while (i < RegVUsage.size());
}
for (int i = 0; i < pShader->IntermediateCount && (i < VSH_MAX_INSTRUCTION_COUNT || !Truncate); i++)

View File

@ -1839,7 +1839,7 @@ HRESULT WINAPI XTL::EMUPATCH(IDirectSound_CreateSoundStream)
LOG_FUNC_ARG(pUnknown)
LOG_FUNC_END;
return EMUPATCH(DirectSoundCreateStream)(pdssd, ppStream);;
return EMUPATCH(DirectSoundCreateStream)(pdssd, ppStream);
}
// ******************************************************************

View File

@ -864,11 +864,11 @@ void EmuSBCGetState(XTL::PX_SBC_GAMEPAD pSBCGamepad, XTL::PX_XINPUT_GAMEPAD pXIG
// Analog Sticks
pSBCGamepad->sAimingX = pXIGamepad->sThumbRX;;
pSBCGamepad->sAimingY = pXIGamepad->sThumbRY;;
pSBCGamepad->sAimingX = pXIGamepad->sThumbRX;
pSBCGamepad->sAimingY = pXIGamepad->sThumbRY;
pSBCGamepad->sRotationLever = 0;//(pXIGamepad->wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? 255 : 0;
pSBCGamepad->sSightChangeX = pXIGamepad->sThumbLX;;
pSBCGamepad->sSightChangeY = pXIGamepad->sThumbLY;;
pSBCGamepad->sSightChangeX = pXIGamepad->sThumbLX;
pSBCGamepad->sSightChangeY = pXIGamepad->sThumbLY;
pSBCGamepad->wLeftPedal = ((SHORT)(pXIGamepad->bAnalogButtons[X_XINPUT_GAMEPAD_LEFT_TRIGGER]))<<8;
pSBCGamepad->wMiddlePedal=0;// = (pXIGamepad->wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) ? 255 : 0;
pSBCGamepad->wRightPedal = (SHORT)(pXIGamepad->bAnalogButtons[X_XINPUT_GAMEPAD_RIGHT_TRIGGER])<<8;

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/tree/master/hw/xbox/nv2a/nv2a_debug.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *
@ -134,6 +137,8 @@ DEBUG_START(PFIFO)
DEBUG_CASE(NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW);
DEBUG_CASE(NV_PFIFO_CACHE1_DMA_RSVD_SHADOW);
DEBUG_CASE(NV_PFIFO_CACHE1_DMA_DATA_SHADOW);
DEBUG_CASE(NV_PFIFO_CACHE1_METHOD);
DEBUG_CASE(NV_PFIFO_CACHE1_DATA);
DEBUG_END(PFIFO)
// TODO: Remove disabled warning once case are add to PRMA switch.
@ -322,6 +327,12 @@ DEBUG_START(PGRAPH)
DEBUG_CASE(NV_PGRAPH_CTX_SWITCH2);
DEBUG_CASE(NV_PGRAPH_CTX_SWITCH3);
DEBUG_CASE(NV_PGRAPH_CTX_SWITCH4);
DEBUG_CASE(NV_PGRAPH_CTX_SWITCH5);
DEBUG_CASE(NV_PGRAPH_CTX_CACHE1);
DEBUG_CASE(NV_PGRAPH_CTX_CACHE2);
DEBUG_CASE(NV_PGRAPH_CTX_CACHE3);
DEBUG_CASE(NV_PGRAPH_CTX_CACHE4);
DEBUG_CASE(NV_PGRAPH_CTX_CACHE5);
DEBUG_CASE(NV_PGRAPH_STATUS);
DEBUG_CASE(NV_PGRAPH_TRAPPED_ADDR);
DEBUG_CASE(NV_PGRAPH_TRAPPED_DATA_LOW);
@ -378,6 +389,7 @@ DEBUG_START(PGRAPH)
DEBUG_CASE(NV_PGRAPH_ZCOMP_OFFSET);
DEBUG_CASE(NV_PGRAPH_FBCFG0);
DEBUG_CASE(NV_PGRAPH_FBCFG1);
DEBUG_CASE(NV_PGRAPH_PATT_COLOR0);
DEBUG_CASE(NV_PGRAPH_DEBUG_6);
DEBUG_CASE(NV_PGRAPH_DEBUG_7);
DEBUG_CASE(NV_PGRAPH_DEBUG_10);
@ -386,6 +398,7 @@ DEBUG_START(PGRAPH)
DEBUG_CASE(NV_PGRAPH_CSV1_B);
DEBUG_CASE(NV_PGRAPH_CSV1_A);
DEBUG_CASE(NV_PGRAPH_CHEOPS_OFFSET);
DEBUG_CASE(NV_PGRAPH_DMA_STATE);
DEBUG_CASE(NV_PGRAPH_BLEND);
DEBUG_CASE(NV_PGRAPH_BLENDCOLOR);
DEBUG_CASE(NV_PGRAPH_BORDERCOLOR0);
@ -407,6 +420,7 @@ DEBUG_START(PGRAPH)
DEBUG_CASE(NV_PGRAPH_COMBINESPECFOG0);
DEBUG_CASE(NV_PGRAPH_COMBINESPECFOG1);
DEBUG_CASE(NV_PGRAPH_CONTROL_0);
DEBUG_CASE(NV_PGRAPH_CONTROL_1);
DEBUG_CASE(NV_PGRAPH_CONTROL_2);
DEBUG_CASE(NV_PGRAPH_CONTROL_3);
DEBUG_CASE(NV_PGRAPH_FOGCOLOR);
@ -416,6 +430,7 @@ DEBUG_START(PGRAPH)
DEBUG_CASE(NV_PGRAPH_SHADERCLIPMODE);
DEBUG_CASE(NV_PGRAPH_SHADERCTL);
DEBUG_CASE(NV_PGRAPH_SHADERPROG);
DEBUG_CASE(NV_PGRAPH_SEMAPHOREOFFSET);
DEBUG_CASE(NV_PGRAPH_SHADOWZSLOPETHRESHOLD);
DEBUG_CASE(NV_PGRAPH_SPECFOGFACTOR0);
DEBUG_CASE(NV_PGRAPH_SPECFOGFACTOR1);
@ -453,6 +468,22 @@ DEBUG_START(PGRAPH)
DEBUG_CASE(NV_PGRAPH_TEXPALETTE1);
DEBUG_CASE(NV_PGRAPH_TEXPALETTE2);
DEBUG_CASE(NV_PGRAPH_TEXPALETTE3);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPX0);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPX1);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPX2);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPX3);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPX4);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPX5);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPX6);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPX7);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPY0);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPY1);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPY2);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPY3);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPY4);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPY5);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPY6);
DEBUG_CASE(NV_PGRAPH_WINDOWCLIPY7);
DEBUG_CASE(NV_PGRAPH_ZSTENCILCLEARVALUE);
DEBUG_CASE(NV_PGRAPH_ZCLIPMIN);
DEBUG_CASE(NV_PGRAPH_ZOFFSETBIAS);

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/tree/master/hw/xbox/nv2a/nv2a_pbus.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_stubs.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/tree/master/hw/xbox/nv2a/nv2a_pcrtc.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_pfb.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_pfifo.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *
@ -47,14 +50,13 @@ typedef struct RAMHTEntry {
bool valid;
} RAMHTEntry;
static void pfifo_run_pusher(NV2AState *d); // forward declaration
int pfifo_puller_thread(NV2AState *d);
static uint32_t ramht_hash(NV2AState *d, uint32_t handle);
static RAMHTEntry ramht_lookup(NV2AState *d, uint32_t handle); // forward declaration
/* PFIFO - MMIO and DMA FIFO submission to PGRAPH and VPE */
DEVICE_READ32(PFIFO)
{
qemu_mutex_lock(&d->pfifo.pfifo_lock);
DEVICE_READ32_SWITCH() {
case NV_PFIFO_RAMHT:
result = 0x03000100; // = NV_PFIFO_RAMHT_SIZE_4K | NV_PFIFO_RAMHT_BASE_ADDRESS(NumberOfPaddingBytes >> 12) | NV_PFIFO_RAMHT_SEARCH_128
@ -71,94 +73,19 @@ DEVICE_READ32(PFIFO)
case NV_PFIFO_RUNOUT_STATUS:
result = NV_PFIFO_RUNOUT_STATUS_LOW_MARK; /* low mark empty */
break;
case NV_PFIFO_CACHE1_PUSH0:
result = d->pfifo.cache1.push_enabled;
break;
case NV_PFIFO_CACHE1_PUSH1:
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: {
qemu_mutex_lock(&d->pfifo.cache1.cache_lock);
if (d->pfifo.cache1.cache.empty()) {
result |= NV_PFIFO_CACHE1_STATUS_LOW_MARK; /* low mark empty */
}
qemu_mutex_unlock(&d->pfifo.cache1.cache_lock);
break;
}
case NV_PFIFO_CACHE1_DMA_PUSH:
SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS,
d->pfifo.cache1.dma_push_enabled);
SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_STATUS,
d->pfifo.cache1.dma_push_suspended);
SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_BUFFER, 1); /* buffer emoty */
break;
case NV_PFIFO_CACHE1_DMA_STATE:
SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE,
d->pfifo.cache1.method_nonincreasing);
SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_METHOD,
d->pfifo.cache1.method >> 2);
SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL,
d->pfifo.cache1.subchannel);
SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT,
d->pfifo.cache1.method_count);
SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_ERROR,
d->pfifo.cache1.error);
break;
case NV_PFIFO_CACHE1_DMA_INSTANCE:
SET_MASK(result, NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MASK,
d->pfifo.cache1.dma_instance >> 4);
break;
case NV_PFIFO_CACHE1_DMA_PUT:
result = d->user.channel_control[d->pfifo.cache1.channel_id].dma_put;
break;
case NV_PFIFO_CACHE1_DMA_GET:
result = d->user.channel_control[d->pfifo.cache1.channel_id].dma_get;
break;
case NV_PFIFO_CACHE1_DMA_SUBROUTINE:
result = d->pfifo.cache1.subroutine_return
| (xbaddr)d->pfifo.cache1.subroutine_active;
break;
case NV_PFIFO_CACHE1_PULL0: {
qemu_mutex_lock(&d->pfifo.cache1.cache_lock);
result = d->pfifo.cache1.pull_enabled;
qemu_mutex_unlock(&d->pfifo.cache1.cache_lock);
break;
}
case NV_PFIFO_CACHE1_ENGINE: {
qemu_mutex_lock(&d->pfifo.cache1.cache_lock);
for (int i = 0; i < NV2A_NUM_SUBCHANNELS; i++) {
result |= d->pfifo.cache1.bound_engines[i] << (i * 2);
}
qemu_mutex_unlock(&d->pfifo.cache1.cache_lock);
break;
}
case NV_PFIFO_CACHE1_DMA_DCOUNT:
result = d->pfifo.cache1.dcount;
break;
case NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW:
result = d->pfifo.cache1.get_jmp_shadow;
break;
case NV_PFIFO_CACHE1_DMA_RSVD_SHADOW:
result = d->pfifo.cache1.rsvd_shadow;
break;
case NV_PFIFO_CACHE1_DMA_DATA_SHADOW:
result = d->pfifo.cache1.data_shadow;
break;
default:
DEVICE_READ32_REG(pfifo); // Was : DEBUG_READ32_UNHANDLED(PFIFO);
break;
}
qemu_mutex_unlock(&d->pfifo.pfifo_lock);
DEVICE_READ32_END(PFIFO);
}
DEVICE_WRITE32(PFIFO)
{
int i;
qemu_mutex_lock(&d->pfifo.pfifo_lock);
switch(addr) {
case NV_PFIFO_INTR_0:
@ -169,356 +96,383 @@ DEVICE_WRITE32(PFIFO)
d->pfifo.enabled_interrupts = value;
update_irq(d);
break;
case NV_PFIFO_CACHE1_PUSH0:
d->pfifo.cache1.push_enabled = value & NV_PFIFO_CACHE1_PUSH0_ACCESS;
break;
case NV_PFIFO_CACHE1_PUSH1:
d->pfifo.cache1.channel_id = GET_MASK(value, NV_PFIFO_CACHE1_PUSH1_CHID);
d->pfifo.cache1.mode = (FifoMode)GET_MASK(value, NV_PFIFO_CACHE1_PUSH1_MODE);
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_suspended = false;
pfifo_run_pusher(d);
}
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);
break;
case NV_PFIFO_CACHE1_DMA_INSTANCE:
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;
break;
case NV_PFIFO_CACHE1_DMA_GET:
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);
break;
case NV_PFIFO_CACHE1_PULL0: {
qemu_mutex_lock(&d->pfifo.cache1.cache_lock);
if ((value & NV_PFIFO_CACHE1_PULL0_ACCESS)
&& !d->pfifo.cache1.pull_enabled) {
d->pfifo.cache1.pull_enabled = true;
/* the puller thread should wake up */
qemu_cond_signal(&d->pfifo.cache1.cache_cond);
}
else if (!(value & NV_PFIFO_CACHE1_PULL0_ACCESS)
&& d->pfifo.cache1.pull_enabled) {
d->pfifo.cache1.pull_enabled = false;
}
qemu_mutex_unlock(&d->pfifo.cache1.cache_lock);
break;
}
case NV_PFIFO_CACHE1_ENGINE: {
qemu_mutex_lock(&d->pfifo.cache1.cache_lock);
for (i = 0; i < NV2A_NUM_SUBCHANNELS; i++) {
d->pfifo.cache1.bound_engines[i] = (FIFOEngine)((value >> (i * 2)) & 3);
}
qemu_mutex_unlock(&d->pfifo.cache1.cache_lock);
break;
}
case NV_PFIFO_CACHE1_DMA_DCOUNT:
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);
break;
case NV_PFIFO_CACHE1_DMA_RSVD_SHADOW:
d->pfifo.cache1.rsvd_shadow = value;
break;
case NV_PFIFO_CACHE1_DMA_DATA_SHADOW:
d->pfifo.cache1.data_shadow = value;
break;
default:
DEVICE_WRITE32_REG(pfifo); // Was : DEBUG_WRITE32_UNHANDLED(PFIFO);
break;
}
qemu_cond_broadcast(&d->pfifo.pusher_cond);
qemu_cond_broadcast(&d->pfifo.puller_cond);
qemu_mutex_unlock(&d->pfifo.pfifo_lock);
DEVICE_WRITE32_END(PFIFO);
}
static void pfifo_run_puller(NV2AState *d)
{
uint32_t *pull0 = &d->pfifo.regs[NV_PFIFO_CACHE1_PULL0];
uint32_t *pull1 = &d->pfifo.regs[NV_PFIFO_CACHE1_PULL1];
uint32_t *engine_reg = &d->pfifo.regs[NV_PFIFO_CACHE1_ENGINE];
/* pusher should be fine to run from a mimo handler
* whenever's it's convenient */
static void pfifo_run_pusher(NV2AState *d) {
uint8_t channel_id;
ChannelControl *control;
Cache1State *state;
CacheEntry *command;
uint8_t *dma;
xbaddr dma_len;
uint32_t word;
uint32_t *status = &d->pfifo.regs[NV_PFIFO_CACHE1_STATUS];
uint32_t *get_reg = &d->pfifo.regs[NV_PFIFO_CACHE1_GET];
uint32_t *put_reg = &d->pfifo.regs[NV_PFIFO_CACHE1_PUT];
/* TODO: How is cache1 selected? */
state = &d->pfifo.cache1;
channel_id = state->channel_id;
control = &d->user.channel_control[channel_id];
// TODO
// CacheEntry working_cache[NV2A_CACHE1_SIZE];
// int working_cache_size = 0;
// pull everything into our own queue
if (!state->push_enabled)
return;
// TODO think more about locking
while (true) {
if (!GET_MASK(*pull0, NV_PFIFO_CACHE1_PULL0_ACCESS)) return;
/* empty cache1 */
if (*status & NV_PFIFO_CACHE1_STATUS_LOW_MARK) break;
uint32_t get = *get_reg;
uint32_t put = *put_reg;
assert(get < 128*4 && (get % 4) == 0);
uint32_t method_entry = d->pfifo.regs[NV_PFIFO_CACHE1_METHOD + get*2];
uint32_t parameter = d->pfifo.regs[NV_PFIFO_CACHE1_DATA + get*2];
uint32_t new_get = (get+4) & 0x1fc;
*get_reg = new_get;
if (new_get == put) {
// set low mark
*status |= NV_PFIFO_CACHE1_STATUS_LOW_MARK;
}
if (*status & NV_PFIFO_CACHE1_STATUS_HIGH_MARK) {
// unset high mark
*status &= ~NV_PFIFO_CACHE1_STATUS_HIGH_MARK;
// signal pusher
qemu_cond_signal(&d->pfifo.pusher_cond);
}
/* only handling DMA for now... */
uint32_t method = method_entry & 0x1FFC;
uint32_t subchannel = GET_MASK(method_entry, NV_PFIFO_CACHE1_METHOD_SUBCHANNEL);
/* Channel running DMA */
uint32_t channel_modes = d->pfifo.regs[NV_PFIFO_MODE];
assert(channel_modes & (1 << channel_id));
assert(state->mode == FIFO_DMA);
// NV2A_DPRINTF("pull %d 0x%08X 0x%08X - subch %d\n", get/4, method_entry, parameter, subchannel);
if (!state->dma_push_enabled)
return;
if (state->dma_push_suspended)
return;
if (method == 0) {
RAMHTEntry entry = ramht_lookup(d, parameter);
assert(entry.valid);
/* We're running so there should be no pending errors... */
assert(state->error == NV_PFIFO_CACHE1_DMA_STATE_ERROR_NONE);
// assert(entry.channel_id == state->channel_id);
dma = (uint8_t*)nv_dma_map(d, state->dma_instance, &dma_len);
assert(entry.engine == ENGINE_GRAPHICS);
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) {
/* the engine is bound to the subchannel */
assert(subchannel < 8);
SET_MASK(*engine_reg, 3 << (4*subchannel), entry.engine);
SET_MASK(*pull1, NV_PFIFO_CACHE1_PULL1_ENGINE, entry.engine);
// NV2A_DPRINTF("engine_reg1 %d 0x%08X\n", subchannel, *engine_reg);
state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_PROTECTION;
break;
}
word = ldl_le_p((uint32_t*)(dma + control->dma_get));
control->dma_get += 4;
// TODO: this is fucked
qemu_mutex_lock(&d->pgraph.pgraph_lock);
//make pgraph busy
qemu_mutex_unlock(&d->pfifo.pfifo_lock);
if (state->method_count) {
/* data word of methods command */
state->data_shadow = word;
pgraph_switch_context(d, entry.channel_id);
pgraph_wait_fifo_access(d);
pgraph_handle_method(d, subchannel, 0, entry.instance);
command = (CacheEntry*)g_malloc0(sizeof(CacheEntry));
command->method = state->method;
command->subchannel = state->subchannel;
command->nonincreasing = state->method_nonincreasing;
command->parameter = word;
// make pgraph not busy
qemu_mutex_unlock(&d->pgraph.pgraph_lock);
qemu_mutex_lock(&d->pfifo.pfifo_lock);
qemu_mutex_lock(&state->cache_lock);
state->cache.push(command);
qemu_cond_signal(&state->cache_cond);
qemu_mutex_unlock(&state->cache_lock);
} else if (method >= 0x100) {
// method passed to engine
if (!state->method_nonincreasing) {
state->method += 4;
}
state->method_count--;
state->dcount++;
} else {
/* no command active - this is the first word of a new one */
state->rsvd_shadow = word;
/* match all forms */
if ((word & 0xe0000003) == 0x20000000) {
/* old jump */
state->get_jmp_shadow = control->dma_get;
control->dma_get = word & 0x1fffffff;
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;
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;
break;
}
state->subroutine_return = control->dma_get;
state->subroutine_active = true;
control->dma_get = word & 0xfffffffc;
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;
break;
}
control->dma_get = state->subroutine_return;
state->subroutine_active = false;
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;
state->method_count = (word >> 18) & 0x7ff;
state->method_nonincreasing = false;
state->dcount = 0;
} else if ((word & 0xe0030003) == 0x40000000) {
/* non-increasing methods */
state->method = word & 0x1fff;
state->subchannel = (word >> 13) & 7;
state->method_count = (word >> 18) & 0x7ff;
state->method_nonincreasing = true;
state->dcount = 0;
} 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;
}
}
}
/* methods that take objects.
* TODO: Check this range is correct for the nv2a */
if (method >= 0x180 && method < 0x200) {
//qemu_mutex_lock_iothread();
RAMHTEntry entry = ramht_lookup(d, parameter);
assert(entry.valid);
// assert(entry.channel_id == state->channel_id);
parameter = entry.instance;
//qemu_mutex_unlock_iothread();
}
NV2A_DPRINTF("DMA pusher done: max 0x%08X, 0x%08X - 0x%08X\n",
dma_len, control->dma_get, control->dma_put);
enum FIFOEngine engine = (enum FIFOEngine)GET_MASK(*engine_reg, 3 << (4*subchannel));
// NV2A_DPRINTF("engine_reg2 %d 0x%08X\n", subchannel, *engine_reg);
assert(engine == ENGINE_GRAPHICS);
SET_MASK(*pull1, NV_PFIFO_CACHE1_PULL1_ENGINE, engine);
if (state->error) {
NV2A_DPRINTF("pb error: %d\n", state->error);
assert(false);
// TODO: this is fucked
qemu_mutex_lock(&d->pgraph.pgraph_lock);
//make pgraph busy
qemu_mutex_unlock(&d->pfifo.pfifo_lock);
state->dma_push_suspended = true;
pgraph_wait_fifo_access(d);
pgraph_handle_method(d, subchannel, method, parameter);
d->pfifo.pending_interrupts |= NV_PFIFO_INTR_0_DMA_PUSHER;
update_irq(d);
}
// make pgraph not busy
qemu_mutex_unlock(&d->pgraph.pgraph_lock);
qemu_mutex_lock(&d->pfifo.pfifo_lock);
} else {
assert(false);
}
}
}
int pfifo_puller_thread(NV2AState *d)
{
CxbxSetThreadName("Cxbx NV2A FIFO");
CxbxSetThreadName("Cxbx NV2A FIFO puller");
Cache1State *state = &(d->pfifo.cache1);
glo_set_current(d->pgraph.gl_context);
glo_set_current(d->pgraph.gl_context);
qemu_mutex_lock(&d->pfifo.pfifo_lock);
while (true) {
pfifo_run_puller(d);
qemu_cond_wait(&d->pfifo.puller_cond, &d->pfifo.pfifo_lock);
while (true) {
qemu_mutex_lock(&state->cache_lock);
if (d->exiting) {
break;
}
}
qemu_mutex_unlock(&d->pfifo.pfifo_lock);
while (state->cache.empty() || !state->pull_enabled) {
qemu_cond_wait(&state->cache_cond, &state->cache_lock);
glo_set_current(NULL); // Cxbx addition
if (d->exiting) {
qemu_mutex_unlock(&state->cache_lock);
glo_set_current(NULL);
return 0;
}
}
return NULL;
}
// Copy cache to working_cache
while (!state->cache.empty()) {
state->working_cache.push(state->cache.front());
state->cache.pop();
}
static void pfifo_run_pusher(NV2AState *d)
{
uint32_t *push0 = &d->pfifo.regs[NV_PFIFO_CACHE1_PUSH0];
uint32_t *push1 = &d->pfifo.regs[NV_PFIFO_CACHE1_PUSH1];
uint32_t *dma_subroutine = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_SUBROUTINE];
uint32_t *dma_state = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_STATE];
uint32_t *dma_push = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_PUSH];
uint32_t *dma_get = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_GET];
uint32_t *dma_put = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_PUT];
uint32_t *dma_dcount = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_DCOUNT];
qemu_mutex_unlock(&state->cache_lock);
uint32_t *status = &d->pfifo.regs[NV_PFIFO_CACHE1_STATUS];
uint32_t *get_reg = &d->pfifo.regs[NV_PFIFO_CACHE1_GET];
uint32_t *put_reg = &d->pfifo.regs[NV_PFIFO_CACHE1_PUT];
qemu_mutex_lock(&d->pgraph.lock);
if (!GET_MASK(*push0, NV_PFIFO_CACHE1_PUSH0_ACCESS)) return;
if (!GET_MASK(*dma_push, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS)) return;
while (!state->working_cache.empty()) {
if (d->exiting) {
qemu_mutex_lock(&d->pgraph.lock);
glo_set_current(NULL);
return 0;
}
/* suspended */
if (GET_MASK(*dma_push, NV_PFIFO_CACHE1_DMA_PUSH_STATUS)) return;
CacheEntry* command = state->working_cache.front();
state->working_cache.pop();
// TODO: should we become busy here??
// NV_PFIFO_CACHE1_DMA_PUSH_STATE _BUSY
if (command->method == 0) {
// qemu_mutex_lock_iothread();
RAMHTEntry entry = ramht_lookup(d, command->parameter);
assert(entry.valid);
unsigned int channel_id = GET_MASK(*push1,
NV_PFIFO_CACHE1_PUSH1_CHID);
assert(entry.channel_id == state->channel_id);
// qemu_mutex_unlock_iothread();
switch (entry.engine) {
case ENGINE_GRAPHICS:
pgraph_switch_context(d, entry.channel_id);
pgraph_wait_fifo_access(d);
pgraph_handle_method(d, command->subchannel, 0, entry.instance);
break;
default:
assert(false);
break;
}
/* Channel running DMA */
uint32_t channel_modes = d->pfifo.regs[NV_PFIFO_MODE];
assert(channel_modes & (1 << channel_id));
/* the engine is bound to the subchannel */
qemu_mutex_lock(&state->cache_lock);
state->bound_engines[command->subchannel] = entry.engine;
state->last_engine = entry.engine;
qemu_mutex_unlock(&state->cache_lock);
}
else if (command->method >= 0x100) {
/* method passed to engine */
assert(GET_MASK(*push1, NV_PFIFO_CACHE1_PUSH1_MODE)
== NV_PFIFO_CACHE1_PUSH1_MODE_DMA);
uint32_t parameter = command->parameter;
/* We're running so there should be no pending errors... */
assert(GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR)
== NV_PFIFO_CACHE1_DMA_STATE_ERROR_NONE);
/* methods that take objects.
* TODO: Check this range is correct for the nv2a */
if (command->method >= 0x180 && command->method < 0x200) {
//qemu_mutex_lock_iothread();
RAMHTEntry entry = ramht_lookup(d, parameter);
assert(entry.valid);
assert(entry.channel_id == state->channel_id);
parameter = entry.instance;
//qemu_mutex_unlock_iothread();
}
hwaddr dma_instance =
GET_MASK(d->pfifo.regs[NV_PFIFO_CACHE1_DMA_INSTANCE],
NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MASK) << 4; // TODO : Use NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MOVE?
// state->cache_lock.lock();
enum FIFOEngine engine = state->bound_engines[command->subchannel];
// state->cache_lock.unlock();
hwaddr dma_len;
uint8_t *dma = (uint8_t*)nv_dma_map(d, dma_instance, &dma_len);
switch (engine) {
case ENGINE_GRAPHICS:
pgraph_wait_fifo_access(d);
pgraph_handle_method(d, command->subchannel,
command->method, parameter);
break;
default:
// TODO: FIx this
// assert(false);
break;
}
/* based on the convenient pseudocode in envytools */
while (true) {
uint32_t dma_get_v = *dma_get;
uint32_t dma_put_v = *dma_put;
if (dma_get_v == dma_put_v) break;
if (dma_get_v >= dma_len) {
assert(false);
SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR,
NV_PFIFO_CACHE1_DMA_STATE_ERROR_PROTECTION);
break;
}
// state->cache_lock.lock();
state->last_engine = state->bound_engines[command->subchannel];
// state->cache_lock.unlock();
}
else
NV2A_DPRINTF("FIFO: Unknown Method - 0x%08X\n",
command->method);
uint32_t word = ldl_le_p((uint32_t*)(dma + dma_get_v));
dma_get_v += 4;
g_free(command);
}
uint32_t method_type =
GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE);
uint32_t method_subchannel =
GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL);
uint32_t method =
GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD) << 2;
uint32_t method_count =
GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT);
qemu_mutex_unlock(&d->pgraph.lock);
}
uint32_t subroutine_state =
GET_MASK(*dma_subroutine, NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE);
if (method_count) {
/* full */
if (*status & NV_PFIFO_CACHE1_STATUS_HIGH_MARK) return;
/* data word of methods command */
d->pfifo.regs[NV_PFIFO_CACHE1_DMA_DATA_SHADOW] = word;
uint32_t put = *put_reg;
uint32_t get = *get_reg;
assert((method & 3) == 0);
uint32_t method_entry = 0;
SET_MASK(method_entry, NV_PFIFO_CACHE1_METHOD_ADDRESS, method >> 2);
SET_MASK(method_entry, NV_PFIFO_CACHE1_METHOD_TYPE, method_type);
SET_MASK(method_entry, NV_PFIFO_CACHE1_METHOD_SUBCHANNEL, method_subchannel);
// NV2A_DPRINTF("push %d 0x%08X 0x%08X - subch %d\n", put/4, method_entry, word, method_subchannel);
assert(put < 128*4 && (put%4) == 0);
d->pfifo.regs[NV_PFIFO_CACHE1_METHOD + put*2] = method_entry;
d->pfifo.regs[NV_PFIFO_CACHE1_DATA + put*2] = word;
uint32_t new_put = (put+4) & 0x1fc;
*put_reg = new_put;
if (new_put == get) {
// set high mark
*status |= NV_PFIFO_CACHE1_STATUS_HIGH_MARK;
}
if (*status & NV_PFIFO_CACHE1_STATUS_LOW_MARK) {
// unset low mark
*status &= ~NV_PFIFO_CACHE1_STATUS_LOW_MARK;
// signal puller
qemu_cond_signal(&d->pfifo.puller_cond);
}
if (method_type == NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE_INC) {
SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD,
(method + 4) >> 2);
}
SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT,
method_count - 1);
(*dma_dcount)++;
} else {
/* no command active - this is the first word of a new one */
d->pfifo.regs[NV_PFIFO_CACHE1_DMA_RSVD_SHADOW] = word;
/* match all forms */
if ((word & 0xe0000003) == 0x20000000) {
/* old jump */
d->pfifo.regs[NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW] =
dma_get_v;
dma_get_v = word & 0x1fffffff;
NV2A_DPRINTF("pb OLD_JMP 0x%08X\n", dma_get_v);
} else if ((word & 3) == 1) {
/* jump */
d->pfifo.regs[NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW] =
dma_get_v;
dma_get_v = word & 0xfffffffc;
NV2A_DPRINTF("pb JMP 0x%08X\n", dma_get_v);
} else if ((word & 3) == 2) {
/* call */
if (subroutine_state) {
SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR,
NV_PFIFO_CACHE1_DMA_STATE_ERROR_CALL);
break;
} else {
*dma_subroutine = dma_get_v;
SET_MASK(*dma_subroutine,
NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE, 1);
dma_get_v = word & 0xfffffffc;
NV2A_DPRINTF("pb CALL 0x%08X\n", dma_get_v);
}
} else if (word == 0x00020000) {
/* return */
if (!subroutine_state) {
SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR,
NV_PFIFO_CACHE1_DMA_STATE_ERROR_RETURN);
// break;
} else {
dma_get_v = *dma_subroutine & 0xfffffffc;
SET_MASK(*dma_subroutine,
NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE, 0);
NV2A_DPRINTF("pb RET 0x%08X\n", dma_get_v);
}
} else if ((word & 0xe0030003) == 0) {
/* increasing methods */
SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD,
(word & 0x1fff) >> 2 );
SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL,
(word >> 13) & 7);
SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT,
(word >> 18) & 0x7ff);
SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE,
NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE_INC);
*dma_dcount = 0;
} else if ((word & 0xe0030003) == 0x40000000) {
/* non-increasing methods */
SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD,
(word & 0x1fff) >> 2 );
SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL,
(word >> 13) & 7);
SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT,
(word >> 18) & 0x7ff);
SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE,
NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE_NON_INC);
*dma_dcount = 0;
} else {
NV2A_DPRINTF("pb reserved cmd 0x%08X - 0x%08X\n",
dma_get_v, word);
SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR,
NV_PFIFO_CACHE1_DMA_STATE_ERROR_RESERVED_CMD);
// break;
assert(false);
}
}
*dma_get = dma_get_v;
if (GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR)) {
break;
}
}
// NV2A_DPRINTF("DMA pusher done: max 0x%08X, 0x%08X - 0x%08X\n",
// dma_len, control->dma_get, control->dma_put);
uint32_t error = GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR);
if (error) {
NV2A_DPRINTF("pb error: %d\n", error);
assert(false);
SET_MASK(*dma_push, NV_PFIFO_CACHE1_DMA_PUSH_STATUS, 1); /* suspended */
// d->pfifo.pending_interrupts |= NV_PFIFO_INTR_0_DMA_PUSHER;
// update_irq(d);
}
}
int pfifo_pusher_thread(NV2AState *d)
{
CxbxSetThreadName("Cxbx NV2A FIFO pusher");
qemu_mutex_lock(&d->pfifo.pfifo_lock);
while (true) {
pfifo_run_pusher(d);
qemu_cond_wait(&d->pfifo.pusher_cond, &d->pfifo.pfifo_lock);
if (d->exiting) {
break;
}
}
qemu_mutex_unlock(&d->pfifo.pfifo_lock);
return 0;
}
@ -539,7 +493,10 @@ static uint32_t ramht_hash(NV2AState *d, uint32_t handle)
hash ^= (handle & ((1 << bits) - 1));
handle >>= bits;
}
hash ^= d->pfifo.cache1.channel_id << (bits - 4);
unsigned int channel_id = GET_MASK(d->pfifo.regs[NV_PFIFO_CACHE1_PUSH1],
NV_PFIFO_CACHE1_PUSH1_CHID);
hash ^= channel_id << (bits - 4);
return hash;
}
@ -549,7 +506,7 @@ static RAMHTEntry ramht_lookup(NV2AState *d, uint32_t handle)
uint32_t hash = ramht_hash(d, handle);
assert(hash * 8 < ramht_size(d));
uint32_t ramht_address =
xbaddr ramht_address =
GET_MASK(d->pfifo.regs[NV_PFIFO_RAMHT],
NV_PFIFO_RAMHT_BASE_ADDRESS_MASK) << 12;

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_pgraph.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *
@ -408,10 +411,9 @@ void (*pgraph_draw_inline_elements)(NV2AState *d);
void (*pgraph_draw_state_update)(NV2AState *d);
void (*pgraph_draw_clear)(NV2AState *d);
static void pgraph_switch_context(NV2AState *d, unsigned int channel_id);
static void pgraph_set_context_user(NV2AState *d, uint32_t value);
static void pgraph_wait_fifo_access(NV2AState *d);
//static void pgraph_set_context_user(NV2AState *d, uint32_t value);
void pgraph_handle_method(NV2AState *d, unsigned int subchannel, unsigned int method, uint32_t parameter);
static void pgraph_log_method(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_update_shader_constants(PGRAPHState *pg, ShaderBinding *binding, bool binding_changed, bool vertex_program, bool fixed_function);
@ -429,8 +431,6 @@ static void pgraph_update_memory_buffer(NV2AState *d, hwaddr addr, hwaddr size,
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);
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* convert_texture_data(const unsigned int color_format, const uint8_t *data, const uint8_t *palette_data, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int row_pitch, const unsigned int slice_pitch);
@ -446,14 +446,13 @@ static gboolean shader_equal(gconstpointer a, gconstpointer b);
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_log_method(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);
/* PGRAPH - accelerated 2d/3d drawing engine */
DEVICE_READ32(PGRAPH)
{
qemu_mutex_lock(&d->pgraph.lock);
qemu_mutex_lock(&d->pgraph.pgraph_lock);
DEVICE_READ32_SWITCH() {
case NV_PGRAPH_INTR:
@ -462,60 +461,22 @@ DEVICE_READ32(PGRAPH)
case NV_PGRAPH_INTR_EN:
result = d->pgraph.enabled_interrupts;
break;
case NV_PGRAPH_NSOURCE:
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_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_CHID, d->pgraph.channel_id);
break;
case NV_PGRAPH_TRAPPED_ADDR:
SET_MASK(result, NV_PGRAPH_TRAPPED_ADDR_CHID, d->pgraph.trapped_channel_id);
SET_MASK(result, NV_PGRAPH_TRAPPED_ADDR_SUBCH, d->pgraph.trapped_subchannel);
SET_MASK(result, NV_PGRAPH_TRAPPED_ADDR_MTHD, d->pgraph.trapped_method);
break;
case NV_PGRAPH_TRAPPED_DATA_LOW:
result = d->pgraph.trapped_data[0];
break;
case NV_PGRAPH_FIFO:
SET_MASK(result, NV_PGRAPH_FIFO_ACCESS, d->pgraph.fifo_access);
break;
case NV_PGRAPH_CHANNEL_CTX_TABLE:
result = d->pgraph.context_table >> 4;
break;
case NV_PGRAPH_CHANNEL_CTX_POINTER:
result = d->pgraph.context_address >> 4;
break;
default:
DEVICE_READ32_REG(pgraph); // Was : DEBUG_READ32_UNHANDLED(PGRAPH);
}
qemu_mutex_unlock(&d->pgraph.lock);
qemu_mutex_unlock(&d->pgraph.pgraph_lock);
// 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);
}
DEVICE_WRITE32(PGRAPH)
{
// reg_log_write(NV_PGRAPH, addr, val);
qemu_mutex_lock(&d->pgraph.lock);
qemu_mutex_lock(&d->pgraph.pgraph_lock);
switch (addr) {
case NV_PGRAPH_INTR:
@ -525,12 +486,6 @@ DEVICE_WRITE32(PGRAPH)
case NV_PGRAPH_INTR_EN:
d->pgraph.enabled_interrupts = value;
break;
case NV_PGRAPH_CTX_CONTROL:
d->pgraph.channel_valid = (value & NV_PGRAPH_CTX_CONTROL_CHID);
break;
case NV_PGRAPH_CTX_USER:
pgraph_set_context_user(d, value);
break;
case NV_PGRAPH_INCREMENT:
if (value & NV_PGRAPH_INCREMENT_READ_3D) {
SET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE],
@ -542,43 +497,44 @@ DEVICE_WRITE32(PGRAPH)
qemu_cond_broadcast(&d->pgraph.flip_3d);
}
break;
case NV_PGRAPH_FIFO:
d->pgraph.fifo_access = GET_MASK(value, NV_PGRAPH_FIFO_ACCESS);
qemu_cond_broadcast(&d->pgraph.fifo_access_cond);
break;
case NV_PGRAPH_CHANNEL_CTX_TABLE:
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:
case NV_PGRAPH_CHANNEL_CTX_TRIGGER: {
xbaddr context_address =
GET_MASK(d->pgraph.regs[NV_PGRAPH_CHANNEL_CTX_POINTER], NV_PGRAPH_CHANNEL_CTX_POINTER_INST) << 4;
if (value & NV_PGRAPH_CHANNEL_CTX_TRIGGER_READ_IN) {
NV2A_DPRINTF("PGRAPH: read channel %d context from %" HWADDR_PRIx "\n",
d->pgraph.channel_id, d->pgraph.context_address);
unsigned pgraph_channel_id =
GET_MASK(d->pgraph.regs[NV_PGRAPH_CTX_USER], NV_PGRAPH_CTX_USER_CHID);
uint8_t *context_ptr = d->pramin.ramin_ptr + d->pgraph.context_address;
NV2A_DPRINTF("PGRAPH: read channel %d context from %" HWADDR_PRIx "\n",
pgraph_channel_id, context_address);
uint8_t *context_ptr = d->pramin.ramin_ptr + context_address;
uint32_t context_user = ldl_le_p((uint32_t*)context_ptr);
NV2A_DPRINTF(" - CTX_USER = 0x%x\n", context_user);
NV2A_DPRINTF(" - CTX_USER = 0x%08X\n", context_user);
pgraph_set_context_user(d, context_user);
d->pgraph.regs[NV_PGRAPH_CTX_USER] = context_user;
// pgraph_set_context_user(d, context_user);
}
if (value & NV_PGRAPH_CHANNEL_CTX_TRIGGER_WRITE_OUT) {
/* do stuff ... */
}
break;
}
default:
DEVICE_WRITE32_REG(pgraph); // Was : DEBUG_WRITE32_UNHANDLED(PGRAPH);
break;
}
qemu_mutex_unlock(&d->pgraph.lock);
// events
switch (addr) {
case NV_PGRAPH_FIFO:
qemu_cond_broadcast(&d->pgraph.fifo_access_cond);
break;
}
qemu_mutex_unlock(&d->pgraph.pgraph_lock);
DEVICE_WRITE32_END(PGRAPH);
}
@ -895,8 +851,6 @@ void OpenGL_draw_clear(NV2AState *d)
assert(pg->opengl_enabled);
lockGL(pg);
NV2A_DPRINTF("---------PRE CLEAR ------\n");
GLbitfield gl_mask = 0;
@ -1064,6 +1018,8 @@ void OpenGL_draw_clear(NV2AState *d)
/* FIXME: Should this really be inverted instead of ymin? */
glScissor(scissor_x, scissor_y, scissor_width, scissor_height);
/* FIXME: Respect window clip?!?! */
NV2A_DPRINTF("------------------CLEAR 0x%x %d,%d - %d,%d %x---------------\n",
parameter, xmin, ymin, xmax, ymax, d->pgraph.regs[NV_PGRAPH_COLORCLEARVALUE]);
@ -1083,8 +1039,6 @@ void OpenGL_draw_clear(NV2AState *d)
}
pgraph_set_surface_dirty(pg, write_color, write_zeta);
unlockGL(pg);
}
void OpenGL_init_pgraph_plugins()
@ -1103,41 +1057,75 @@ void pgraph_handle_method(NV2AState *d,
uint32_t parameter)
{
unsigned int i;
GraphicsSubchannel *subchannel_data;
GraphicsObject *object;
unsigned int slot;
PGRAPHState *pg = &d->pgraph;
assert(pg->channel_valid);
subchannel_data = &pg->subchannel_data[subchannel];
object = &subchannel_data->object;
bool channel_valid =
d->pgraph.regs[NV_PGRAPH_CTX_CONTROL] & NV_PGRAPH_CTX_CONTROL_CHID;
assert(channel_valid);
ContextSurfaces2DState *context_surfaces_2d
= &object->data.context_surfaces_2d;
ImageBlitState *image_blit = &object->data.image_blit;
KelvinState *kelvin = &object->data.kelvin;
unsigned channel_id = GET_MASK(pg->regs[NV_PGRAPH_CTX_USER], NV_PGRAPH_CTX_USER_CHID);
ContextSurfaces2DState *context_surfaces_2d = &pg->context_surfaces_2d;
ImageBlitState *image_blit = &pg->image_blit;
KelvinState *kelvin = &pg->kelvin;
// Logging is slow.. disable for now..
//pgraph_log_method(subchannel, object->graphics_class, method, parameter);
assert(subchannel < 8);
if (method == NV_SET_OBJECT) {
subchannel_data->object_instance = parameter;
assert(parameter < d->pramin.ramin_size);
uint8_t *obj_ptr = d->pramin.ramin_ptr + parameter;
//qemu_mutex_lock_iothread();
load_graphics_object(d, parameter, object);
//qemu_mutex_unlock_iothread();
return;
}
uint32_t ctx_1 = ldl_le_p((uint32_t*)obj_ptr);
uint32_t ctx_2 = ldl_le_p((uint32_t*)(obj_ptr+4));
uint32_t ctx_3 = ldl_le_p((uint32_t*)(obj_ptr+8));
uint32_t ctx_4 = ldl_le_p((uint32_t*)(obj_ptr+12));
uint32_t ctx_5 = parameter;
pg->regs[NV_PGRAPH_CTX_CACHE1 + subchannel * 4] = ctx_1;
pg->regs[NV_PGRAPH_CTX_CACHE2 + subchannel * 4] = ctx_2;
pg->regs[NV_PGRAPH_CTX_CACHE3 + subchannel * 4] = ctx_3;
pg->regs[NV_PGRAPH_CTX_CACHE4 + subchannel * 4] = ctx_4;
pg->regs[NV_PGRAPH_CTX_CACHE5 + subchannel * 4] = ctx_5;
}
// is this right?
pg->regs[NV_PGRAPH_CTX_SWITCH1] = pg->regs[NV_PGRAPH_CTX_CACHE1 + subchannel * 4];
pg->regs[NV_PGRAPH_CTX_SWITCH2] = pg->regs[NV_PGRAPH_CTX_CACHE2 + subchannel * 4];
pg->regs[NV_PGRAPH_CTX_SWITCH3] = pg->regs[NV_PGRAPH_CTX_CACHE3 + subchannel * 4];
pg->regs[NV_PGRAPH_CTX_SWITCH4] = pg->regs[NV_PGRAPH_CTX_CACHE4 + subchannel * 4];
pg->regs[NV_PGRAPH_CTX_SWITCH5] = pg->regs[NV_PGRAPH_CTX_CACHE5 + subchannel * 4];
uint32_t graphics_class = GET_MASK(pg->regs[NV_PGRAPH_CTX_SWITCH1],
NV_PGRAPH_CTX_SWITCH1_GRCLASS);
// Logging is slow.. disable for now..
//pgraph_log_method(subchannel, graphics_class, method, parameter);
if (subchannel != 0) {
// catches context switching issues on xbox d3d
assert(graphics_class != 0x97);
}
/* ugly switch for now */
switch (object->graphics_class) {
switch (graphics_class) {
case NV_CONTEXT_SURFACES_2D: {
case NV_CONTEXT_PATTERN: {
switch (method) {
case NV044_SET_MONOCHROME_COLOR0:
pg->regs[NV_PGRAPH_PATT_COLOR0] = parameter;
break;
}
break;
}
case NV_CONTEXT_SURFACES_2D: {
switch (method) {
case NV062_SET_OBJECT:
context_surfaces_2d->object_instance = parameter;
break;
case NV062_SET_CONTEXT_DMA_IMAGE_SOURCE:
context_surfaces_2d->dma_image_source = parameter;
break;
@ -1158,7 +1146,7 @@ void pgraph_handle_method(NV2AState *d,
context_surfaces_2d->dest_offset = parameter & 0x07FFFFFF;
break;
default:
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "Unknown NV_CONTEXT_SURFACES_2D Method: 0x%08X\n", method);
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "Unknown NV_CONTEXT_SURFACES_2D Method: 0x%08X", method);
}
break;
@ -1166,6 +1154,9 @@ void pgraph_handle_method(NV2AState *d,
case NV_IMAGE_BLIT: {
switch (method) {
case NV09F_SET_OBJECT:
image_blit->object_instance = parameter;
break;
case NV09F_SET_CONTEXT_SURFACES:
image_blit->context_surfaces = parameter;
break;
@ -1189,14 +1180,9 @@ void pgraph_handle_method(NV2AState *d,
NV2A_GL_DPRINTF(true, "NV09F_SET_OPERATION_SRCCOPY");
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);
ContextSurfaces2DState *context_surfaces =
&context_surfaces_obj->data.context_surfaces_2d;
ContextSurfaces2DState *context_surfaces = context_surfaces_2d;
assert(context_surfaces->object_instance
== image_blit->context_surfaces);
unsigned int bytes_per_pixel;
switch (context_surfaces->color_format) {
@ -1251,13 +1237,17 @@ void pgraph_handle_method(NV2AState *d,
break;
default:
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "Unknown NV_IMAGE_BLIT Method: 0x%08X\n", method);
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "Unknown NV_IMAGE_BLIT Method: 0x%08X", method);
}
break;
}
case NV_KELVIN_PRIMITIVE: {
switch (method) {
case NV097_SET_OBJECT:
kelvin->object_instance = parameter;
break;
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.
@ -1268,31 +1258,30 @@ void pgraph_handle_method(NV2AState *d,
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 */
SET_MASK(pg->regs[NV_PGRAPH_TRAPPED_ADDR],
NV_PGRAPH_TRAPPED_ADDR_CHID, channel_id);
SET_MASK(pg->regs[NV_PGRAPH_TRAPPED_ADDR],
NV_PGRAPH_TRAPPED_ADDR_SUBCH, subchannel);
SET_MASK(pg->regs[NV_PGRAPH_TRAPPED_ADDR],
NV_PGRAPH_TRAPPED_ADDR_MTHD, method);
pg->regs[NV_PGRAPH_TRAPPED_DATA_LOW] = parameter;
pg->regs[NV_PGRAPH_NSOURCE] = NV_PGRAPH_NSOURCE_NOTIFICATION; /* TODO: check this */
pg->pending_interrupts |= NV_PGRAPH_INTR_ERROR;
qemu_mutex_unlock(&pg->lock);
qemu_mutex_unlock(&pg->pgraph_lock);
qemu_mutex_lock_iothread();
update_irq(d);
qemu_mutex_lock(&pg->lock);
qemu_mutex_lock(&pg->pgraph_lock);
qemu_mutex_unlock_iothread();
while (pg->pending_interrupts & NV_PGRAPH_INTR_ERROR) {
qemu_cond_wait(&pg->interrupt_cond, &pg->lock);
qemu_cond_wait(&pg->interrupt_cond, &pg->pgraph_lock);
}
}
break;
case NV097_WAIT_FOR_IDLE:
lockGL(pg);
pgraph_update_surface(d, false, true, true);
unlockGL(pg);
break;
@ -1309,7 +1298,6 @@ void pgraph_handle_method(NV2AState *d,
parameter);
break;
case NV097_FLIP_INCREMENT_WRITE: {
lockGL(pg);
NV2A_DPRINTF("flip increment write %d -> ",
GET_MASK(pg->regs[NV_PGRAPH_SURFACE],
NV_PGRAPH_SURFACE_WRITE_3D));
@ -1328,13 +1316,10 @@ void pgraph_handle_method(NV2AState *d,
glFrameTerminatorGREMEDY();
}
#endif // __APPLE__
unlockGL(pg);
break;
}
case NV097_FLIP_STALL:
lockGL(pg);
pgraph_update_surface(d, false, true, true);
unlockGL(pg);
// TODO: Fix this (why does it hang?)
@ -1359,7 +1344,7 @@ void pgraph_handle_method(NV2AState *d,
break;
case NV097_SET_CONTEXT_DMA_NOTIFIES:
kelvin->dma_notifies = parameter;
pg->dma_notifies = parameter;
break;
case NV097_SET_CONTEXT_DMA_A:
pg->dma_a = parameter;
@ -1368,13 +1353,11 @@ void pgraph_handle_method(NV2AState *d,
pg->dma_b = parameter;
break;
case NV097_SET_CONTEXT_DMA_STATE:
kelvin->dma_state = parameter;
pg->dma_state = parameter;
break;
case NV097_SET_CONTEXT_DMA_COLOR:
/* try to get any straggling draws in before the surface's changed :/ */
lockGL(pg);
pgraph_update_surface(d, false, true, true);
unlockGL(pg);
pg->dma_color = parameter;
break;
@ -1388,16 +1371,14 @@ void pgraph_handle_method(NV2AState *d,
pg->dma_vertex_b = parameter;
break;
case NV097_SET_CONTEXT_DMA_SEMAPHORE:
kelvin->dma_semaphore = parameter;
pg->dma_semaphore = parameter;
break;
case NV097_SET_CONTEXT_DMA_REPORT:
pg->dma_report = parameter;
break;
case NV097_SET_SURFACE_CLIP_HORIZONTAL:
lockGL(pg);
pgraph_update_surface(d, false, true, true);
unlockGL(pg);
pg->surface_shape.clip_x =
GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_X);
@ -1405,9 +1386,7 @@ void pgraph_handle_method(NV2AState *d,
GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_WIDTH);
break;
case NV097_SET_SURFACE_CLIP_VERTICAL:
lockGL(pg);
pgraph_update_surface(d, false, true, true);
unlockGL(pg);
pg->surface_shape.clip_y =
GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_Y);
@ -1415,9 +1394,7 @@ void pgraph_handle_method(NV2AState *d,
GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_HEIGHT);
break;
case NV097_SET_SURFACE_FORMAT:
lockGL(pg);
pgraph_update_surface(d, false, true, true);
unlockGL(pg);
pg->surface_shape.color_format =
GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_COLOR);
@ -1433,9 +1410,7 @@ void pgraph_handle_method(NV2AState *d,
GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_HEIGHT);
break;
case NV097_SET_SURFACE_PITCH:
lockGL(pg);
pgraph_update_surface(d, false, true, true);
unlockGL(pg);
pg->surface_color.pitch =
GET_MASK(parameter, NV097_SET_SURFACE_PITCH_COLOR);
@ -1443,16 +1418,12 @@ void pgraph_handle_method(NV2AState *d,
GET_MASK(parameter, NV097_SET_SURFACE_PITCH_ZETA);
break;
case NV097_SET_SURFACE_COLOR_OFFSET:
lockGL(pg);
pgraph_update_surface(d, false, true, true);
unlockGL(pg);
pg->surface_color.offset = parameter;
break;
case NV097_SET_SURFACE_ZETA_OFFSET:
lockGL(pg);
pgraph_update_surface(d, false, true, true);
unlockGL(pg);
pg->surface_zeta.offset = parameter;
break;
@ -1475,9 +1446,7 @@ void pgraph_handle_method(NV2AState *d,
pg->regs[NV_PGRAPH_TEXADDRESS0 + slot * 4] = parameter;
break;
case NV097_SET_CONTROL0: {
lockGL(pg);
pgraph_update_surface(d, false, true, true);
unlockGL(pg);
bool stencil_write_enable =
parameter & NV097_SET_CONTROL0_STENCIL_WRITE_ENABLE;
@ -1568,6 +1537,18 @@ void pgraph_handle_method(NV2AState *d,
SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_BLUE, blue);
break;
}
case NV097_SET_WINDOW_CLIP_TYPE:
SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER],
NV_PGRAPH_SETUPRASTER_WINDOWCLIPTYPE, parameter);
break;
CASE_8(NV097_SET_WINDOW_CLIP_HORIZONTAL, 4):
slot = (method - NV097_SET_WINDOW_CLIP_HORIZONTAL) / 4;
pg->regs[NV_PGRAPH_WINDOWCLIPX0 + slot * 4] = parameter;
break;
CASE_8(NV097_SET_WINDOW_CLIP_VERTICAL, 4):
slot = (method - NV097_SET_WINDOW_CLIP_VERTICAL) / 4;
pg->regs[NV_PGRAPH_WINDOWCLIPY0 + slot * 4] = parameter;
break;
case NV097_SET_ALPHA_TEST_ENABLE:
SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0],
NV_PGRAPH_CONTROL_0_ALPHATESTENABLE, parameter);
@ -2264,11 +2245,12 @@ void pgraph_handle_method(NV2AState *d,
slot = (method - NV097_SET_VERTEX_DATA_ARRAY_OFFSET) / 4;
VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot];
pg->vertex_attributes[slot].dma_select =
parameter & 0x80000000;
pg->vertex_attributes[slot].offset =
parameter & 0x7fffffff;
vertex_attribute->dma_select = parameter & 0x80000000;
vertex_attribute->offset = parameter & 0x7fffffff;
vertex_attribute->converted_elements = 0;
pg->vertex_attributes[slot].converted_elements = 0;
break;
}
@ -2284,7 +2266,6 @@ void pgraph_handle_method(NV2AState *d,
break;
case NV097_CLEAR_REPORT_VALUE:
lockGL(pg);
/* FIXME: Does this have a value in parameter? Also does this (also?) modify
* the report memory block?
@ -2298,7 +2279,6 @@ void pgraph_handle_method(NV2AState *d,
}
pg->zpass_pixel_count_result = 0;
unlockGL(pg);
break;
case NV097_SET_ZPASS_PIXEL_COUNT_ENABLE:
@ -2306,8 +2286,6 @@ void pgraph_handle_method(NV2AState *d,
break;
case NV097_GET_REPORT: {
lockGL(pg);
/* 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
@ -2349,7 +2327,6 @@ void pgraph_handle_method(NV2AState *d,
stl_le_p((uint32_t*)&report_data[12], done);
}
unlockGL(pg);
break;
}
@ -2360,8 +2337,6 @@ void pgraph_handle_method(NV2AState *d,
break;
case NV097_SET_BEGIN_END: {
lockGL(pg);
uint32_t control_0 = pg->regs[NV_PGRAPH_CONTROL_0];
uint32_t control_1 = pg->regs[NV_PGRAPH_CONTROL_1];
@ -2428,8 +2403,6 @@ void pgraph_handle_method(NV2AState *d,
}
pgraph_set_surface_dirty(pg, true, depth_test || stencil_test);
unlockGL(pg);
break;
}
CASE_4(NV097_SET_TEXTURE_OFFSET, 64):
@ -2669,21 +2642,21 @@ void pgraph_handle_method(NV2AState *d,
break;
}
case NV097_SET_SEMAPHORE_OFFSET:
kelvin->semaphore_offset = parameter;
pg->regs[NV_PGRAPH_SEMAPHOREOFFSET] = parameter;
break;
case NV097_BACK_END_WRITE_SEMAPHORE_RELEASE: {
lockGL(pg);
pgraph_update_surface(d, false, true, true);
unlockGL(pg);
//qemu_mutex_unlock(&pg->pgraph_lock);
//qemu_mutex_lock_iothread();
uint32_t semaphore_offset = pg->regs[NV_PGRAPH_SEMAPHOREOFFSET];
xbaddr semaphore_dma_len;
uint8_t *semaphore_data = (uint8_t*)nv_dma_map(d, kelvin->dma_semaphore,
uint8_t *semaphore_data = (uint8_t*)nv_dma_map(d, pg->dma_semaphore,
&semaphore_dma_len);
assert(kelvin->semaphore_offset < semaphore_dma_len);
semaphore_data += kelvin->semaphore_offset;
assert(semaphore_offset < semaphore_dma_len);
semaphore_data += semaphore_offset;
stl_le_p((uint32_t*)semaphore_data, parameter);
@ -2780,15 +2753,15 @@ void pgraph_handle_method(NV2AState *d,
break;
default:
NV2A_GL_DPRINTF(true, " unhandled (0x%02x 0x%08x)",
object->graphics_class, method);
graphics_class, method);
break;
}
break;
}
default:
NV2A_GL_DPRINTF(true, "Unknown Graphics Class/Method 0x%08X/0x%08X\n",
object->graphics_class, method);
NV2A_GL_DPRINTF(true, "Unknown Graphics Class/Method 0x%08X/0x%08X",
graphics_class, method);
break;
}
@ -2796,35 +2769,39 @@ void pgraph_handle_method(NV2AState *d,
static void pgraph_switch_context(NV2AState *d, unsigned int channel_id)
{
bool valid;
bool channel_valid =
d->pgraph.regs[NV_PGRAPH_CTX_CONTROL] & NV_PGRAPH_CTX_CONTROL_CHID;
unsigned pgraph_channel_id = GET_MASK(d->pgraph.regs[NV_PGRAPH_CTX_USER], NV_PGRAPH_CTX_USER_CHID);
// Cxbx Note : This isn't present in xqemu / OpenXbox : d->pgraph.pgraph_lock.lock();
valid = d->pgraph.channel_valid && d->pgraph.channel_id == channel_id;
bool valid = channel_valid && pgraph_channel_id == channel_id;
if (!valid) {
d->pgraph.trapped_channel_id = channel_id;
}
// Cxbx Note : This isn't present in xqemu / OpenXbox : d->pgraph.pgraph_lock.unlock();
SET_MASK(d->pgraph.regs[NV_PGRAPH_TRAPPED_ADDR],
NV_PGRAPH_TRAPPED_ADDR_CHID, channel_id);
if (!valid) {
NV2A_DPRINTF("puller needs to switch to ch %d\n", channel_id);
NV2A_DPRINTF("pgraph switching to ch %d\n", channel_id);
qemu_mutex_unlock(&d->pgraph.lock);
/* TODO: hardware context switching */
assert(!(d->pgraph.regs[NV_PGRAPH_DEBUG_3]
& NV_PGRAPH_DEBUG_3_HW_CONTEXT_SWITCH));
qemu_mutex_unlock(&d->pgraph.pgraph_lock);
qemu_mutex_lock_iothread();
d->pgraph.pending_interrupts |= NV_PGRAPH_INTR_CONTEXT_SWITCH;
d->pgraph.pending_interrupts |= NV_PGRAPH_INTR_CONTEXT_SWITCH; // TODO : Should this be done before unlocking pgraph_lock?
update_irq(d);
qemu_mutex_lock(&d->pgraph.lock);
qemu_mutex_lock(&d->pgraph.pgraph_lock);
qemu_mutex_unlock_iothread();
// wait for the interrupt to be serviced
while (d->pgraph.pending_interrupts & NV_PGRAPH_INTR_CONTEXT_SWITCH) {
qemu_cond_wait(&d->pgraph.interrupt_cond, &d->pgraph.lock);
qemu_cond_wait(&d->pgraph.interrupt_cond, &d->pgraph.pgraph_lock);
}
}
}
static void pgraph_wait_fifo_access(NV2AState *d) {
while (!d->pgraph.fifo_access) {
qemu_cond_wait(&d->pgraph.fifo_access_cond, &d->pgraph.lock);
while (!(d->pgraph.regs[NV_PGRAPH_FIFO] & NV_PGRAPH_FIFO_ACCESS)) {
qemu_cond_wait(&d->pgraph.fifo_access_cond, &d->pgraph.pgraph_lock);
}
}
@ -2842,29 +2819,28 @@ static void pgraph_log_method(unsigned int subchannel,
subchannel, last, method_name, count);
}
if (method != 0x1800) {
const char* method_name = NV2AMethodToString(method);
unsigned int nmethod = 0;
switch (graphics_class) {
case NV_KELVIN_PRIMITIVE:
nmethod = method | (0x5c << 16);
break;
case NV_CONTEXT_SURFACES_2D:
nmethod = method | (0x6d << 16);
break;
default:
break;
}
/*
if (nmethod != 0 && nmethod < ARRAY_SIZE(nv2a_method_names)) {
method_name = nv2a_method_names[nmethod];
}*/
if (method_name) {
NV2A_DPRINTF("d->pgraph method (%d): %s (0x%x)\n",
subchannel, method_name, parameter);
} else {
NV2A_DPRINTF("d->pgraph method (%d): 0x%x -> 0x%04x (0x%x)\n",
// const char* method_name = NV2AMethodToString(method);
// unsigned int nmethod = 0;
// switch (graphics_class) {
// case NV_KELVIN_PRIMITIVE:
// nmethod = method | (0x5c << 16);
// break;
// case NV_CONTEXT_SURFACES_2D:
// nmethod = method | (0x6d << 16);
// break;
// case NV_CONTEXT_PATTERN:
// nmethod = method | (0x68 << 16);
// break;
// default:
// break;
// }
// if (method_name) {
// NV2A_DPRINTF("d->pgraph method (%d): %s (0x%x)\n",
// subchannel, method_name, parameter);
// } else {
NV2A_DPRINTF("pgraph method (%d): 0x%08X -> 0x%04x (0x%x)\n",
subchannel, graphics_class, method, parameter);
}
// }
}
if (method == last) { count++; }
@ -2917,8 +2893,7 @@ void pgraph_init(NV2AState *d)
PGRAPHState *pg = &d->pgraph;
qemu_mutex_init(&pg->lock);
qemu_mutex_init(&pg->gl_lock);
qemu_mutex_init(&pg->pgraph_lock);
qemu_cond_init(&pg->interrupt_cond);
qemu_cond_init(&pg->fifo_access_cond);
qemu_cond_init(&pg->flip_3d);
@ -2931,8 +2906,6 @@ void pgraph_init(NV2AState *d)
/* fire up opengl */
lockGL(pg);
pg->gl_context = glo_context_create();
assert(pg->gl_context);
@ -3008,14 +2981,18 @@ void pgraph_init(NV2AState *d)
// assert(glGetError() == GL_NO_ERROR);
unlockGL(pg);
glo_set_current(NULL);
}
void pgraph_destroy(PGRAPHState *pg)
{
if (pg->opengl_enabled) {
lockGL(pg);
qemu_mutex_destroy(&pg->pgraph_lock);
qemu_cond_destroy(&pg->interrupt_cond);
qemu_cond_destroy(&pg->fifo_access_cond);
qemu_cond_destroy(&pg->flip_3d);
if (pg->opengl_enabled) {
glo_set_current(pg->gl_context);
if (pg->gl_color_buffer) {
@ -3032,15 +3009,7 @@ void pgraph_destroy(PGRAPHState *pg)
glo_set_current(NULL);
glo_context_destroy(pg->gl_context);
unlockGL(pg);
}
qemu_mutex_destroy(&pg->lock);
qemu_mutex_destroy(&pg->gl_lock);
qemu_cond_destroy(&pg->interrupt_cond);
qemu_cond_destroy(&pg->fifo_access_cond);
qemu_cond_destroy(&pg->flip_3d);
}
static void pgraph_update_shader_constants(PGRAPHState *pg,
@ -3256,6 +3225,8 @@ static void pgraph_bind_shaders(PGRAPHState *pg)
ShaderState state;
/* register combiner stuff */
state.psh.window_clip_exclusive = pg->regs[NV_PGRAPH_SETUPRASTER]
& NV_PGRAPH_SETUPRASTER_WINDOWCLIPTYPE,
state.psh.combiner_control = pg->regs[NV_PGRAPH_COMBINECTL];
state.psh.shader_stage_program = pg->regs[NV_PGRAPH_SHADERPROG];
state.psh.other_stage_input = pg->regs[NV_PGRAPH_SHADERCTL];
@ -3351,6 +3322,45 @@ static void pgraph_bind_shaders(PGRAPHState *pg)
}
}
/* Window clip
*
* Optimization note: very quickly check to ignore any repeated or zero-size
* clipping regions. Note that if region number 7 is valid, but the rest are
* not, we will still add all of them. Clip regions seem to be typically
* front-loaded (meaning the first one or two regions are populated, and the
* following are zeroed-out), so let's avoid adding any more complicated
* masking or copying logic here for now unless we discover a valid case.
*/
assert(!state.psh.window_clip_exclusive); /* FIXME: Untested */
state.psh.window_clip_count = 0;
uint32_t last_x = 0, last_y = 0;
for (i = 0; i < 8; i++) {
const uint32_t x = pg->regs[NV_PGRAPH_WINDOWCLIPX0 + i * 4];
const uint32_t y = pg->regs[NV_PGRAPH_WINDOWCLIPY0 + i * 4];
const uint32_t x_min = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMIN);
const uint32_t x_max = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMAX);
const uint32_t y_min = GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMIN);
const uint32_t y_max = GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMAX);
/* Check for zero width or height clipping region */
if ((x_min == x_max) || (y_min == y_max)) {
continue;
}
/* Check for in-order duplicate regions */
if ((x == last_x) && (y == last_y)) {
continue;
}
NV2A_DPRINTF("Clipping Region %d: min=(%d, %d) max=(%d, %d)\n",
i, x_min, y_min, x_max, y_max);
state.psh.window_clip_count = i + 1;
last_x = x;
last_y = y;
}
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];
@ -3402,6 +3412,33 @@ static void pgraph_bind_shaders(PGRAPHState *pg)
glUseProgram(pg->shader_binding->gl_program);
/* Clipping regions */
for (i = 0; i < state.psh.window_clip_count; i++) {
if (pg->shader_binding->clip_region_loc[i] == -1) {
continue;
}
uint32_t x = pg->regs[NV_PGRAPH_WINDOWCLIPX0 + i * 4];
GLuint x_min = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMIN);
GLuint x_max = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMAX);
/* Adjust y-coordinates for the OpenGL viewport: translate coordinates
* to have the origin at the bottom-left of the surface (as opposed to
* top-left), and flip y-min and y-max accordingly.
*/
uint32_t y = pg->regs[NV_PGRAPH_WINDOWCLIPY0 + i * 4];
GLuint y_min = (pg->surface_shape.clip_height - 1) -
GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMAX);
GLuint y_max = (pg->surface_shape.clip_height - 1) -
GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMIN);
pgraph_apply_anti_aliasing_factor(pg, &x_min, &y_min);
pgraph_apply_anti_aliasing_factor(pg, &x_max, &y_max);
glUniform4i(pg->shader_binding->clip_region_loc[i],
x_min, y_min, x_max, y_max);
}
pgraph_update_shader_constants(pg, pg->shader_binding, binding_changed,
vertex_program, fixed_function);
@ -3630,7 +3667,7 @@ static void pgraph_update_surface_part(NV2AState *d, bool upload, bool color) {
}
surface->buffer_dirty = false;
#ifdef DEBUG_NV2A
uint8_t *out = data + surface->offset + 64;
NV2A_DPRINTF("upload_surface %s 0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", "
"(0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", "
@ -3644,7 +3681,7 @@ static void pgraph_update_surface_part(NV2AState *d, bool upload, bool color) {
pg->surface_shape.clip_height,
surface->pitch,
out[0], out[1], out[2], out[3]);
#endif
}
if (!upload && surface->draw_dirty) {
@ -3680,6 +3717,7 @@ static void pgraph_update_surface_part(NV2AState *d, bool upload, bool color) {
surface->draw_dirty = false;
surface->write_enabled_cache = false;
#ifdef DEBUG_NV2A
uint8_t *out = data + surface->offset + 64;
NV2A_DPRINTF("read_surface %s 0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", "
"(0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", "
@ -3692,7 +3730,7 @@ static void pgraph_update_surface_part(NV2AState *d, bool upload, bool color) {
pg->surface_shape.clip_width, pg->surface_shape.clip_height,
surface->pitch,
out[0], out[1], out[2], out[3]);
#endif
}
if (swizzle) {
@ -3810,9 +3848,10 @@ static void pgraph_bind_textures(NV2AState *d)
unsigned int rect_height =
GET_MASK(pg->regs[NV_PGRAPH_TEXIMAGERECT0 + i*4],
NV_PGRAPH_TEXIMAGERECT0_HEIGHT);
#ifdef DEBUG_NV2A
unsigned int lod_bias =
GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MIPMAP_LOD_BIAS);
#endif
unsigned int min_filter = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MIN);
unsigned int mag_filter = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MAG);
@ -3863,9 +3902,9 @@ static void pgraph_bind_textures(NV2AState *d)
continue;
}
NV2A_DPRINTF(" texture %d is format 0x%x, (r %d, %d or %d, %d, %d; %d%s),"
NV2A_DPRINTF(" texture %d is format 0x%x, off 0x%x (r %d, %d or %d, %d, %d; %d%s),"
" filter %x %x, levels %d-%d %d bias %d\n",
i, color_format,
i, color_format, offset,
rect_width, rect_height,
1 << log_width, 1 << log_height, 1 << log_depth,
pitch,
@ -4307,44 +4346,6 @@ static unsigned int pgraph_bind_inline_array(NV2AState *d)
return index_count;
}
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);
switch2 = ldl_le_p((uint32_t*)(obj_ptr + 4));
switch3 = ldl_le_p((uint32_t*)(obj_ptr + 8));
obj->graphics_class = switch1 & NV_PGRAPH_CTX_SWITCH1_GRCLASS;
/* init graphics object */
switch (obj->graphics_class) {
case NV_KELVIN_PRIMITIVE:
// kelvin->vertex_attributes[NV2A_VERTEX_ATTR_DIFFUSE].inline_value = 0xFFFFFFF;
break;
default:
break;
}
}
static GraphicsObject* lookup_graphics_object(PGRAPHState *s,
hwaddr instance_address)
{
int i;
for (i = 0; i<NV2A_NUM_SUBCHANNELS; i++) {
if (s->subchannel_data[i].object_instance == instance_address) {
return &s->subchannel_data[i].object;
}
}
return NULL;
}
/* 16 bit to [0.0, F16_MAX = 511.9375] */
static float convert_f16_to_float(uint16_t f16) {
if (f16 == 0x0000) { return 0.0f; }
@ -4658,7 +4659,7 @@ static TextureBinding* generate_texture(const TextureShape s,
NV2A_GL_DLABEL(GL_TEXTURE, gl_texture,
"format: 0x%02X%s, %d dimensions%s, width: %d, height: %d, depth: %d",
s.color_format, {"", " (SZ)", " (DXT)"}[f.encoding],
s.color_format, (f.encoding == linear) ? "" : (f.encoding == swizzled) ? " (SZ)" : " (DXT)", // compressed
s.dimensionality, s.cubemap ? " (Cubemap)" : "",
s.width, s.height, s.depth);

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_pmc.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_pramdac.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_stubs.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_stubs.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_prmcio.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_stubs.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_stubs.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_prmvio.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_stubs.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_ptimer.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *
@ -50,7 +53,7 @@ static uint64_t ptimer_get_clock(NV2AState * d)
uint64_t time = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
return Muldiv64(Muldiv64(time,
d->pramdac.core_clock_freq,
d->pramdac.core_clock_freq, // TODO : Research how this can be updated to accept uint64_t
NANOSECONDS_PER_SECOND), // Was CLOCKS_PER_SEC
d->ptimer.denominator,
d->ptimer.numerator);

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_stubs.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_pvideo.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_stubs.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *

View File

@ -28,12 +28,15 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_user.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *
@ -45,31 +48,43 @@ DEVICE_READ32(USER)
unsigned int channel_id = addr >> 16;
assert(channel_id < NV2A_NUM_CHANNELS);
ChannelControl *control = &d->user.channel_control[channel_id];
qemu_mutex_lock(&d->pfifo.pfifo_lock);
uint32_t channel_modes = d->pfifo.regs[NV_PFIFO_MODE];
/* PIO Mode */
if (!channel_modes & (1 << channel_id)) {
uint32_t result = 0;
if (channel_modes & (1 << channel_id)) {
/* DMA Mode */
unsigned int cur_channel_id =
GET_MASK(d->pfifo.regs[NV_PFIFO_CACHE1_PUSH1],
NV_PFIFO_CACHE1_PUSH1_CHID);
if (channel_id == cur_channel_id) {
switch(addr & 0xFFFF) { // Was DEVICE_READ32_SWITCH()
case NV_USER_DMA_PUT:
result = d->pfifo.regs[NV_PFIFO_CACHE1_DMA_PUT];
break;
case NV_USER_DMA_GET:
result = d->pfifo.regs[NV_PFIFO_CACHE1_DMA_GET];
break;
case NV_USER_REF:
result = d->pfifo.regs[NV_PFIFO_CACHE1_REF];
break;
default:
DEBUG_READ32_UNHANDLED(USER);
break;
}
} else {
/* ramfc */
assert(false);
}
} else {
/* PIO Mode */
assert(false);
}
/* DMA Mode */
addr &= 0xFFFF;
DEVICE_READ32_SWITCH() {
case NV_USER_DMA_PUT:
result = control->dma_put;
break;
case NV_USER_DMA_GET:
result = control->dma_get;
break;
case NV_USER_REF:
result = control->ref;
break;
default:
DEBUG_READ32_UNHANDLED(USER);
break;
}
qemu_mutex_unlock(&d->pfifo.pfifo_lock);
DEVICE_READ32_END(USER);
}
@ -79,33 +94,44 @@ DEVICE_WRITE32(USER)
unsigned int channel_id = addr >> 16;
assert(channel_id < NV2A_NUM_CHANNELS);
ChannelControl *control = &d->user.channel_control[channel_id];
qemu_mutex_lock(&d->pfifo.pfifo_lock);
uint32_t channel_modes = d->pfifo.regs[NV_PFIFO_MODE];
if (channel_modes & (1 << channel_id)) {
/* DMA Mode */
switch (addr & 0xFFFF) {
case NV_USER_DMA_PUT:
control->dma_put = value;
unsigned int cur_channel_id =
GET_MASK(d->pfifo.regs[NV_PFIFO_CACHE1_PUSH1],
NV_PFIFO_CACHE1_PUSH1_CHID);
if (d->pfifo.cache1.push_enabled) {
pfifo_run_pusher(d);
if (channel_id == cur_channel_id) {
switch (addr & 0xFFFF) {
case NV_USER_DMA_PUT:
d->pfifo.regs[NV_PFIFO_CACHE1_DMA_PUT] = value;
break;
case NV_USER_DMA_GET:
d->pfifo.regs[NV_PFIFO_CACHE1_DMA_GET] = value;
break;
case NV_USER_REF:
d->pfifo.regs[NV_PFIFO_CACHE1_REF] = value;
break;
default:
assert(false);
break;
}
break;
case NV_USER_DMA_GET:
control->dma_get = value;
break;
case NV_USER_REF:
control->ref = value;
break;
default:
DEBUG_WRITE32_UNHANDLED(USER);
break;
// kick pfifo
qemu_cond_broadcast(&d->pfifo.pusher_cond);
qemu_cond_broadcast(&d->pfifo.puller_cond);
} else {
/* ramfc */
assert(false);
}
} else {
/* PIO Mode */
assert(false);
}
qemu_mutex_unlock(&d->pfifo.pfifo_lock);
DEVICE_WRITE32_END(USER);
}

View File

@ -30,13 +30,15 @@
// *
// * (c) 2002-2003 Aaron Robinson <caustik@caustik.com>
// *
// * nv2a.cpp is heavily based on code from XQEMU
// * Copyright(c) 2012 espes
// * Copyright(c) 2015 Jannik Vogel
// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * This file is heavily based on code from XQEMU
// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a.c
// * Copyright (c) 2012 espes
// * Copyright (c) 2015 Jannik Vogel
// * Copyright (c) 2018 Matt Borgerson
// *
// * (c) 2016-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2017-2018 Patrick van Logchem <pvanlogchem@gmail.com>
// * Contributions for Cxbx-Reloaded
// * Copyright (c) 2017-2018 Luke Usher <luke.usher@outlook.com>
// * Copyright (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *
@ -99,6 +101,9 @@ struct _GError
#include "CxbxKrnl/gloffscreen/glextensions.h" // for glextensions_init
GLuint create_gl_shader(GLenum gl_shader_type,
const char *code,
const char *name); // forward to nv2a_shaders.cpp
static void update_irq(NV2AState *d)
{
@ -1068,10 +1073,10 @@ void NV2ADevice::UpdateHostDisplay(NV2AState *d)
return;
}
lockGL(pg);
NV2A_GL_DGROUP_BEGIN("VGA Frame");
glo_set_current(pg->gl_context);
cxbx_gl_update_displaymode(d);
for (int v = 0; v < 2; v++) {
@ -1114,7 +1119,7 @@ void NV2ADevice::UpdateHostDisplay(NV2AState *d)
NV2A_GL_DGROUP_END();
unlockGL(pg);
// glo_set_current(NULL);
UpdateFPSCounter();
}
@ -1224,8 +1229,6 @@ void NV2ADevice::Init()
d->pramdac.video_clock_coeff = 0x0003C20D; /* 25182Khz...? */
// Setup the conditions/mutexes
qemu_mutex_init(&d->pfifo.cache1.cache_lock);
qemu_cond_init(&d->pfifo.cache1.cache_cond);
pgraph_init(d);
// Only spawn VBlank thread when LLE is enabled
@ -1235,7 +1238,16 @@ void NV2ADevice::Init()
vblank_thread = std::thread(nv2a_vblank_thread, d);
}
qemu_mutex_init(&d->pfifo.pfifo_lock);
qemu_cond_init(&d->pfifo.puller_cond);
qemu_cond_init(&d->pfifo.pusher_cond);
d->pfifo.regs[NV_PFIFO_CACHE1_STATUS] |= NV_PFIFO_CACHE1_STATUS_LOW_MARK;
/* fire up puller */
d->pfifo.puller_thread = std::thread(pfifo_puller_thread, d);
/* fire up pusher */
d->pfifo.pusher_thread = std::thread(pfifo_pusher_thread, d);
}
void NV2ADevice::Reset()
@ -1244,17 +1256,18 @@ void NV2ADevice::Reset()
if (!d) return;
d->exiting = true;
qemu_cond_signal(&d->pfifo.cache1.cache_cond);
d->pfifo.puller_thread.join(); // was qemu_thread_join(&d->pfifo.puller_thread);
qemu_cond_broadcast(&d->pfifo.puller_cond);
qemu_cond_broadcast(&d->pfifo.pusher_cond);
d->pfifo.puller_thread.join();
d->pfifo.pusher_thread.join();
qemu_mutex_destroy(&d->pfifo.pfifo_lock); // Cbxbx addition
if (d->pgraph.opengl_enabled) {
vblank_thread.join();
pvideo_destroy(d);
}
pgraph_destroy(&d->pgraph);
qemu_mutex_destroy(&d->pfifo.cache1.cache_lock);
qemu_cond_destroy(&d->pfifo.cache1.cache_cond);
}
uint32_t NV2ADevice::IORead(int barIndex, uint32_t port, unsigned size)

View File

@ -34,553 +34,9 @@
// ******************************************************************
#pragma once
#undef USE_SHADER_CACHE
#ifdef USE_SHADER_CACHE
#include "glib_compat.h" // For GHashTable, g_hash_table_new, g_hash_table_lookup, g_hash_table_insert
#endif
#include "Cxbx.h" // For xbaddr
#include "devices\PCIDevice.h" // For PCIDevice
#include <queue>
#include <thread>
#include <GL/glew.h>
#include "swizzle.h"
#include "nv2a_int.h"
#include "nv2a_debug.h" // For HWADDR_PRIx, NV2A_DPRINTF, NV2A_GL_DPRINTF, etc.
#include "CxbxKrnl/gloffscreen/gloffscreen.h"
#include "qemu-thread.h" // For qemu_mutex, etc
#include "nv2a_shaders.h" // For ShaderBinding
#define NV_PMC_SIZE 0x001000
#define _NV_PFIFO_SIZE 0x002000 // Underscore prefix to prevent clash with NV_PFIFO_SIZE
#define NV_PVIDEO_SIZE 0x001000
#define NV_PTIMER_SIZE 0x001000
#define NV_PFB_SIZE 0x001000
#define NV_PGRAPH_SIZE 0x002000
#define NV_PCRTC_SIZE 0x001000
#define NV_PRAMDAC_SIZE 0x001000
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)
#ifdef __cplusplus
template <size_t N> struct ArraySizeHelper { char _[N]; };
template <typename T, size_t N>
ArraySizeHelper<N> makeArraySizeHelper(T(&)[N]);
# define ARRAY_SIZE(a) sizeof(makeArraySizeHelper(a))
#else
// The expression ARRAY_SIZE(a) is a compile-time constant of type
// size_t which represents the number of elements of the given
// array. You should only use ARRAY_SIZE on statically allocated
// arrays.
#define ARRAY_SIZE(a) \
((sizeof(a) / sizeof(*(a))) / \
static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
#endif
#define VSH_TOKEN_SIZE 4 // Compatibility; TODO : Move this to nv2a_vsh.h
#define MAX(a,b) ((a)>(b) ? (a) : (b)) // Compatibility
#define MIN(a,b) ((a)<(b) ? (a) : (b)) // Compatibility
#define g_free(x) free(x) // Compatibility
#define g_malloc(x) malloc(x) // Compatibility
#define g_malloc0(x) calloc(1, x) // Compatibility
#define g_realloc(x, y) realloc(x, y) // Compatibility
#undef USE_TEXTURE_CACHE
#if __cplusplus >= 201402L
# define NV2A_CONSTEXPR constexpr
#else
# define NV2A_CONSTEXPR static
#endif
// GCC implementation of FFS
static int ffs(register int valu)
{
register int bit;
if (valu == 0)
return 0;
for (bit = 1; !(valu & 1); bit++)
valu >>= 1;
return bit;
}
#define GET_MASK(v, mask) (((v) & (mask)) >> (ffs(mask)-1))
#define SET_MASK(v, mask, val) \
do { \
(v) &= ~(mask); \
(v) |= ((val) << (ffs(mask)-1)) & (mask); \
} while (0)
// 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)
#define CASE_256(v, step) CASE_128(v, step) : CASE_128(v + (step) * 128, step)
// Non-power-of-two CASE statements
#define CASE_3(v, step) CASE_2(v, step) : CASE_1(v + (step) * 2, step)
#define NV2A_DEVICE(obj) \
OBJECT_CHECK(NV2AState, (obj), "nv2a")
//void reg_log_read(int block, hwaddr addr, uint64_t val);
//void reg_log_write(int block, hwaddr addr, uint64_t val);
enum FifoMode {
FIFO_PIO = 0,
FIFO_DMA = 1,
};
enum FIFOEngine {
ENGINE_SOFTWARE = 0,
ENGINE_GRAPHICS = 1,
ENGINE_DVD = 2,
};
typedef struct DMAObject {
unsigned int dma_class;
unsigned int dma_target;
xbaddr address;
xbaddr limit;
} DMAObject;
typedef struct VertexAttribute {
bool dma_select;
xbaddr offset;
/* inline arrays are packed in order?
* Need to pass the offset to converted attributes */
unsigned int inline_array_offset;
float inline_value[4];
unsigned int format;
unsigned int size; /* size of the data type */
unsigned int count; /* number of components */
uint32_t stride;
bool needs_conversion;
uint8_t *converted_buffer;
unsigned int converted_elements;
unsigned int converted_size;
unsigned int converted_count;
float *inline_buffer;
GLint gl_count;
GLenum gl_type;
GLboolean gl_normalize;
GLuint gl_converted_buffer;
GLuint gl_inline_buffer;
} VertexAttribute;
typedef struct Surface {
bool draw_dirty;
bool buffer_dirty;
bool write_enabled_cache;
unsigned int pitch;
xbaddr offset;
} Surface;
typedef struct SurfaceShape {
unsigned int z_format;
unsigned int color_format;
unsigned int zeta_format;
unsigned int log_width, log_height;
unsigned int clip_x, clip_y;
unsigned int clip_width, clip_height;
unsigned int anti_aliasing;
} SurfaceShape;
typedef struct TextureShape {
bool cubemap;
unsigned int dimensionality;
unsigned int color_format;
unsigned int levels;
unsigned int width, height, depth;
unsigned int min_mipmap_level, max_mipmap_level;
unsigned int pitch;
} TextureShape;
typedef struct TextureKey {
TextureShape state;
uint64_t data_hash;
uint8_t* texture_data;
uint8_t* palette_data;
} TextureKey;
typedef struct TextureBinding {
GLenum gl_target;
GLuint gl_texture;
unsigned int refcnt;
} TextureBinding;
typedef struct KelvinState {
xbaddr dma_notifies;
xbaddr dma_state;
xbaddr dma_semaphore;
unsigned int semaphore_offset;
} KelvinState;
typedef struct ContextSurfaces2DState {
xbaddr dma_image_source;
xbaddr dma_image_dest;
unsigned int color_format;
unsigned int source_pitch, dest_pitch;
xbaddr source_offset, dest_offset;
} ContextSurfaces2DState;
typedef struct ImageBlitState {
xbaddr context_surfaces;
unsigned int operation;
unsigned int in_x, in_y;
unsigned int out_x, out_y;
unsigned int width, height;
} ImageBlitState;
typedef struct GraphicsObject {
uint8_t graphics_class;
union {
ContextSurfaces2DState context_surfaces_2d;
ImageBlitState image_blit;
KelvinState kelvin;
} data;
} GraphicsObject;
typedef struct GraphicsSubchannel {
xbaddr object_instance;
GraphicsObject object;
uint32_t object_cache[5];
} GraphicsSubchannel;
typedef struct GraphicsContext {
bool channel_3d;
unsigned int subchannel;
} GraphicsContext;
typedef struct PGRAPHState {
bool opengl_enabled; // == bLLE_GPU
QemuMutex lock;
uint32_t pending_interrupts;
uint32_t enabled_interrupts;
QemuCond interrupt_cond;
xbaddr context_table;
xbaddr context_address;
unsigned int trapped_method;
unsigned int trapped_subchannel;
unsigned int trapped_channel_id;
uint32_t trapped_data[2];
uint32_t notify_source;
bool fifo_access;
QemuCond fifo_access_cond;
QemuCond flip_3d;
unsigned int channel_id;
bool channel_valid;
GraphicsContext context[NV2A_NUM_CHANNELS];
xbaddr dma_color, dma_zeta;
Surface surface_color, surface_zeta;
unsigned int surface_type;
SurfaceShape surface_shape;
SurfaceShape last_surface_shape;
xbaddr dma_a, dma_b;
#ifdef USE_TEXTURE_CACHE
GLruCache *texture_cache;
#endif
bool texture_dirty[NV2A_MAX_TEXTURES];
TextureBinding *texture_binding[NV2A_MAX_TEXTURES];
#ifdef USE_SHADER_CACHE
GHashTable *shader_cache;
#endif
ShaderBinding *shader_binding;
bool texture_matrix_enable[NV2A_MAX_TEXTURES];
/* FIXME: Move to NV_PGRAPH_BUMPMAT... */
float bump_env_matrix[NV2A_MAX_TEXTURES - 1][4]; /* 3 allowed stages with 2x2 matrix each */
GloContext *gl_context;
QemuMutex gl_lock;
GLuint gl_framebuffer;
GLuint gl_color_buffer, gl_zeta_buffer;
GraphicsSubchannel subchannel_data[NV2A_NUM_SUBCHANNELS];
xbaddr dma_report;
xbaddr report_offset;
bool zpass_pixel_count_enable;
unsigned int zpass_pixel_count_result;
unsigned int gl_zpass_pixel_count_query_count;
GLuint* gl_zpass_pixel_count_queries;
xbaddr dma_vertex_a, dma_vertex_b;
unsigned int primitive_mode;
unsigned int clear_surface;
bool enable_vertex_program_write;
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];
/* lighting constant arrays */
uint32_t ltctxa[NV2A_LTCTXA_COUNT][4];
bool ltctxa_dirty[NV2A_LTCTXA_COUNT];
uint32_t ltctxb[NV2A_LTCTXB_COUNT][4];
bool ltctxb_dirty[NV2A_LTCTXB_COUNT];
uint32_t ltc1[NV2A_LTC1_COUNT][4];
bool ltc1_dirty[NV2A_LTC1_COUNT];
// should figure out where these are in lighting context
float light_infinite_half_vector[NV2A_MAX_LIGHTS][3];
float light_infinite_direction[NV2A_MAX_LIGHTS][3];
float light_local_position[NV2A_MAX_LIGHTS][3];
float light_local_attenuation[NV2A_MAX_LIGHTS][3];
VertexAttribute vertex_attributes[NV2A_VERTEXSHADER_ATTRIBUTES];
unsigned int inline_array_length;
uint32_t inline_array[NV2A_MAX_BATCH_LENGTH];
GLuint gl_inline_array_buffer;
unsigned int inline_elements_length;
uint16_t inline_elements[NV2A_MAX_BATCH_LENGTH]; // Cxbx-Reloaded TODO : Restore uint32_t once HLE_draw_inline_elements can using that
unsigned int inline_buffer_length;
unsigned int draw_arrays_length;
unsigned int draw_arrays_max_count;
/* FIXME: Unknown size, possibly endless, 1000 will do for now */
GLint gl_draw_arrays_start[1000];
GLsizei gl_draw_arrays_count[1000];
GLuint gl_element_buffer;
GLuint gl_memory_buffer;
GLuint gl_vertex_array;
uint32_t regs[NV_PGRAPH_SIZE]; // TODO : union
} PGRAPHState;
#define lockGL(x) lockGL_(x, __LINE__)
static void lockGL_(PGRAPHState* pg, unsigned int line) {
//printf("Locking from line %d\n", line);
qemu_mutex_lock(&pg->gl_lock);
if (pg->opengl_enabled) {
glo_set_current(pg->gl_context);
}
}
#define unlockGL(x) unlockGL_(x, __LINE__)
static void unlockGL_(PGRAPHState* pg, unsigned int line) {
//printf("Unlocking from line %d\n", line);
if (pg->opengl_enabled) {
glo_set_current(NULL);
}
qemu_mutex_unlock(&pg->gl_lock);
}
typedef struct CacheEntry {
//QSIMPLEQ_ENTRY(CacheEntry) entry;
unsigned int method : 14;
unsigned int subchannel : 3;
bool nonincreasing;
uint32_t parameter;
} CacheEntry;
typedef struct Cache1State {
unsigned int channel_id;
FifoMode mode;
/* Pusher state */
bool push_enabled;
bool dma_push_enabled;
bool dma_push_suspended;
xbaddr dma_instance;
bool method_nonincreasing;
unsigned int method : 14;
unsigned int subchannel : 3;
unsigned int method_count : 24;
uint32_t dcount;
bool subroutine_active;
xbaddr subroutine_return;
xbaddr get_jmp_shadow;
uint32_t rsvd_shadow;
uint32_t data_shadow;
uint32_t error;
bool pull_enabled;
enum FIFOEngine bound_engines[NV2A_NUM_SUBCHANNELS];
enum FIFOEngine last_engine;
/* The actual command queue */
QemuMutex cache_lock;
QemuCond cache_cond;
std::queue<CacheEntry*> cache;
std::queue<CacheEntry*> working_cache;
} Cache1State;
typedef struct OverlayState {
bool video_buffer_use;
int pitch;
bool is_transparent;
#ifdef DEBUG
hwaddr base;
hwaddr limit;
#endif
hwaddr offset;
uint32_t in_height;
uint32_t in_width;
int out_x;
int out_y;
int out_width;
int out_height;
bool covers_framebuffer;
int old_in_width;
int old_in_height;
int old_pitch;
GLuint gl_texture;
} OverlayState;
typedef struct ChannelControl {
xbaddr dma_put;
xbaddr dma_get;
uint32_t ref;
} ChannelControl;
typedef struct NV2AState {
// PCIDevice dev;
// qemu_irq irq;
bool exiting;
bool enable_overlay = false;
// VGACommonState vga;
// GraphicHwOps hw_ops;
// QEMUTimer *vblank_timer;
// MemoryRegion *vram;
// MemoryRegion vram_pci;
uint8_t *vram_ptr;
size_t vram_size;
// MemoryRegion ramin;
struct {
uint8_t *ramin_ptr;
size_t ramin_size;
} pramin;
// MemoryRegion mmio;
// MemoryRegion block_mmio[NV_NUM_BLOCKS];
struct {
uint32_t pending_interrupts;
uint32_t enabled_interrupts;
uint32_t regs[NV_PMC_SIZE]; // Not in xqemu/openxbox? TODO : union
} pmc;
struct {
std::thread puller_thread;
uint32_t pending_interrupts;
uint32_t enabled_interrupts;
Cache1State cache1;
uint32_t regs[_NV_PFIFO_SIZE]; // TODO : union
} pfifo;
struct {
uint32_t pending_interrupts;
uint32_t enabled_interrupts;
//QemuCond interrupt_cond; // pvideo.interrupt_cond not used (yet)
OverlayState overlays[2]; // NV2A supports 2 video overlays
uint32_t regs[NV_PVIDEO_SIZE]; // TODO : union
} pvideo;
struct {
uint32_t pending_interrupts;
uint32_t enabled_interrupts;
uint32_t numerator;
uint32_t denominator;
uint32_t alarm_time;
uint32_t regs[NV_PTIMER_SIZE]; // Not in xqemu/openxbox? TODO : union
} ptimer;
struct {
uint32_t regs[NV_PFB_SIZE]; // TODO : union
} pfb;
struct PGRAPHState pgraph;
struct {
uint32_t pending_interrupts;
uint32_t enabled_interrupts;
hwaddr start;
uint32_t regs[NV_PCRTC_SIZE]; // Not in xqemu/openxbox? TODO : union
} pcrtc;
struct {
uint32_t core_clock_coeff;
uint64_t core_clock_freq;
uint32_t memory_clock_coeff;
uint32_t video_clock_coeff;
uint32_t regs[NV_PRAMDAC_SIZE]; // Not in xqemu/openxbox? TODO : union
} pramdac;
struct {
ChannelControl channel_control[NV2A_NUM_CHANNELS];
} user;
// PRMCIO (Actually the VGA controller)
struct {
uint8_t cr_index;
uint8_t cr[256]; /* CRT registers */
} prmcio; // Not in xqemu/openxbox?
} NV2AState;
typedef value_t(*read_func)(NV2AState *d, hwaddr addr); //, unsigned int size);
typedef void(*write_func)(NV2AState *d, hwaddr addr, value_t val); //, unsigned int size);
typedef struct {
read_func read;
write_func write;
} MemoryRegionOps;
#include "nv2a_int.h" // For NV2AState
typedef struct NV2ABlockInfo {
const char* name;
@ -591,69 +47,8 @@ typedef struct NV2ABlockInfo {
const NV2ABlockInfo* EmuNV2A_Block(xbaddr addr);
#if 0
// Valid after PCI init :
#define NV20_REG_BASE_KERNEL 0xFD000000
typedef volatile DWORD *PPUSH;
typedef struct {
DWORD Ignored[0x10];
PPUSH Put; // On Xbox1, this field is only written to by the CPU (the GPU uses this as a trigger to start executing from the given address)
PPUSH Get; // On Xbox1, this field is only read from by the CPU (the GPU reflects in here where it is/stopped executing)
PPUSH Reference; // TODO : xbaddr / void* / DWORD ?
DWORD Ignored2[0x7ED];
} Nv2AControlDma;
#define PUSH_TYPE_MASK 0x00000002 // 2 bits
#define PUSH_TYPE_SHIFT 0
#define PUSH_TYPE_METHOD 0 // method
#define PUSH_TYPE_JMP_FAR 1 // jump far
#define PUSH_TYPE_CALL_FAR 2 // call far
#define PUSH_TYPE_METHOD_UNUSED 3 // method (unused)
#define PUSH_METHOD_MASK 0x00001FFC // 12 bits
#define PUSH_METHOD_SHIFT 0 // Dxbx note : Not 2, because methods are actually DWORD offsets (and thus defined with increments of 4)
#define PUSH_SUBCH_MASK 0x0000E000 // 3 bits
#define PUSH_SUBCH_SHIFT 13
#define PUSH_COUNT_MASK 0x1FFC0000 // 11 bits
#define PUSH_COUNT_SHIFT 18
#define PUSH_INSTR_MASK 0xE0000000 // 3 bits
#define PUSH_INSTR_SHIFT 29
#define PUSH_INSTR_IMM_INCR 0 // immediate, increment
#define PUSH_INSTR_JMP_NEAR 1 // near jump
#define PUSH_INSTR_IMM_NOINC 2 // immediate, no-increment
#define PUSH_ADDR_FAR_MASK 0xFFFFFFFC // 30 bits
#define PUSH_ADDR_FAR_SHIFT 0
#define PUSH_ADDR_NEAR_MASK 0x1FFFFFFC // 27 bits
#define PUSH_ADDR_NEAR_SHIFT 0 // Cxbx note : Not 2, because methods are actually DWORD offsets (and thus defined with increments of 4)
#define PUSH_TYPE(dwPushCommand) ((dwPushCommand & PUSH_TYPE_MASK) >> PUSH_TYPE_SHIFT)
#define PUSH_METHOD(dwPushCommand) ((dwPushCommand & PUSH_METHOD_MASK) >> PUSH_METHOD_SHIFT)
#define PUSH_SUBCH(dwPushCommand) ((dwPushCommand & PUSH_SUBCH_MASK) >> PUSH_SUBCH_SHIFT)
#define PUSH_COUNT(dwPushCommand) ((dwPushCommand & PUSH_COUNT_MASK) >> PUSH_COUNT_SHIFT)
#define PUSH_INSTR(dwPushCommand) ((dwPushCommand & PUSH_INSTR_MASK) >> PUSH_INSTR_SHIFT)
#define PUSH_ADDR_FAR(dwPushCommand) ((dwPushCommand & PUSH_ADDR_FAR_MASK) >> PUSH_ADDR_FAR_SHIFT)
#define PUSH_ADDR_NEAR(dwPushCommand) ((dwPushCommand & PUSH_ADDR_NEAR_MASK) >> PUSH_ADDR_NEAR_SHIFT)
#define PUSH_METHOD_MAX ((PUSH_METHOD_MASK | 3) >> PUSH_METHOD_SHIFT) // = 8191
#define PUSH_SUBCH_MAX (PUSH_SUBCH_MASK >> PUSH_SUBCH_SHIFT) // = 7
#define PUSH_COUNT_MAX (PUSH_COUNT_MASK >> PUSH_COUNT_SHIFT) // = 2047
// Decode push buffer conmmand (inverse of D3DPUSH_ENCODE)
inline void D3DPUSH_DECODE(const DWORD dwPushCommand, DWORD &dwMethod, DWORD &dwSubCh, DWORD &dwCount)
{
dwMethod = PUSH_METHOD(dwPushCommand);
dwSubCh = PUSH_SUBCH(dwPushCommand);
dwCount = PUSH_COUNT(dwPushCommand);
}
#endif
void CxbxReserveNV2AMemory(NV2AState *d);
GLuint create_gl_shader(GLenum gl_shader_type,
const char *code,
const char *name); // forward to nv2a_shaders.cpp
class NV2ADevice : public PCIDevice {
public:
// constructor

View File

@ -23,6 +23,7 @@
#ifdef DEBUG_NV2A_GL
#include <stdio.h>
#include <stdbool.h>
#include <stdarg.h>
#include <assert.h>

View File

@ -25,9 +25,6 @@
// Enable for NV2A Debug logging (Warning: Slow!)
// #define DEBUG_NV2A
#include "CxbxKrnl/gloffscreen/gloffscreen.h"
#include "CxbxKrnl/gloffscreen/glextensions.h"
#ifdef DEBUG_NV2A
# define NV2A_DPRINTF(format, ...) printf("[0x????] NV2A: " format, ## __VA_ARGS__)
#else
@ -39,6 +36,9 @@
#ifdef DEBUG_NV2A_GL
#include <stdbool.h>
#include "CxbxKrnl/gloffscreen/gloffscreen.h"
#include "CxbxKrnl/gloffscreen/glextensions.h"
void gl_debug_message(bool cc, const char *fmt, ...);
void gl_debug_group_begin(const char *fmt, ...);
void gl_debug_group_end(void);

File diff suppressed because it is too large Load Diff

View File

@ -338,7 +338,7 @@ static QString* get_input_var(struct PixelShader *ps, struct InputInfo in, bool
switch (in.mod) {
case PS_INPUTMAPPING_SIGNED_IDENTITY:
case PS_INPUTMAPPING_UNSIGNED_IDENTITY:
QINCREF(reg);
qobject_ref(reg);
res = reg;
break;
case PS_INPUTMAPPING_UNSIGNED_INVERT:
@ -364,7 +364,7 @@ static QString* get_input_var(struct PixelShader *ps, struct InputInfo in, bool
break;
}
QDECREF(reg);
qobject_unref(reg);
return res;
}
@ -374,7 +374,7 @@ static QString* get_output(QString *reg, int mapping)
QString *res;
switch (mapping) {
case PS_COMBINEROUTPUT_IDENTITY:
QINCREF(reg);
qobject_ref(reg);
res = reg;
break;
case PS_COMBINEROUTPUT_BIAS:
@ -442,8 +442,8 @@ static void add_stage_code(struct PixelShader *ps,
qstring_append_fmt(ps->code, "%s.%s = %s(%s);\n",
qstring_get_str(ab_dest), write_mask, caster, qstring_get_str(ab_mapping));
} else {
QDECREF(ab_dest);
QINCREF(ab_mapping);
qobject_unref(ab_dest);
qobject_ref(ab_mapping);
ab_dest = ab_mapping;
}
@ -451,8 +451,8 @@ static void add_stage_code(struct PixelShader *ps,
qstring_append_fmt(ps->code, "%s.%s = %s(%s);\n",
qstring_get_str(cd_dest), write_mask, caster, qstring_get_str(cd_mapping));
} else {
QDECREF(cd_dest);
QINCREF(cd_mapping);
qobject_unref(cd_dest);
qobject_ref(cd_mapping);
cd_dest = cd_mapping;
}
@ -479,19 +479,19 @@ static void add_stage_code(struct PixelShader *ps,
qstring_get_str(sum_dest), write_mask, caster, qstring_get_str(sum_mapping));
}
QDECREF(a);
QDECREF(b);
QDECREF(c);
QDECREF(d);
QDECREF(ab);
QDECREF(cd);
QDECREF(ab_mapping);
QDECREF(cd_mapping);
QDECREF(ab_dest);
QDECREF(cd_dest);
QDECREF(sum_dest);
QDECREF(sum);
QDECREF(sum_mapping);
qobject_unref(a);
qobject_unref(b);
qobject_unref(c);
qobject_unref(d);
qobject_unref(ab);
qobject_unref(cd);
qobject_unref(ab_mapping);
qobject_unref(cd_mapping);
qobject_unref(ab_dest);
qobject_unref(cd_dest);
qobject_unref(sum_dest);
qobject_unref(sum);
qobject_unref(sum_mapping);
}
// Add code for the final combiner stage
@ -513,14 +513,14 @@ static void add_final_stage_code(struct PixelShader *ps, struct FCInputInfo fina
/* FIXME: Is .x correctly here? */
qstring_append_fmt(ps->code, "r0.a = vec3(%s).x;\n", qstring_get_str(g));
QDECREF(a);
QDECREF(b);
QDECREF(c);
QDECREF(d);
QDECREF(g);
qobject_unref(a);
qobject_unref(b);
qobject_unref(c);
qobject_unref(d);
qobject_unref(g);
QDECREF(ps->varE);
QDECREF(ps->varF);
qobject_unref(ps->varE);
qobject_unref(ps->varF);
ps->varE = ps->varF = NULL;
}
@ -539,6 +539,42 @@ static QString* psh_convert(struct PixelShader *ps)
qstring_append(preflight, "\n");
qstring_append(preflight, "uniform vec4 fogColor;\n");
/* Window Clipping */
QString *clip = qstring_new();
if (ps->state.window_clip_count != 0) {
qstring_append_fmt(preflight, "uniform ivec4 clipRegion[%d];\n",
ps->state.window_clip_count);
qstring_append_fmt(clip, "/* Window-clip (%s) */\n",
ps->state.window_clip_exclusive ?
"Exclusive" : "Inclusive");
if (!ps->state.window_clip_exclusive) {
qstring_append(clip, "bool clipContained = false;\n");
}
qstring_append_fmt(clip, "for (int i = 0; i < %d; i++) {\n",
ps->state.window_clip_count);
qstring_append(clip, " bvec4 clipTest = bvec4(lessThan(gl_FragCoord.xy, clipRegion[i].xy),\n"
" greaterThan(gl_FragCoord.xy, clipRegion[i].zw));\n"
" if (!any(clipTest)) {\n");
if (ps->state.window_clip_exclusive) {
/* Pixel in clip region = exclude by discarding */
qstring_append(clip, " discard;\n");
assert(false); /* Untested */
} else {
/* Pixel in clip region = mark pixel as contained and leave */
qstring_append(clip, " clipContained = true;\n"
" break;\n");
}
qstring_append(clip, " }\n"
"}\n");
/* Check for inclusive window clip */
if (!ps->state.window_clip_exclusive) {
qstring_append(clip, "if (!clipContained) { discard; }\n");
}
} else if (ps->state.window_clip_exclusive) {
/* Clip everything */
qstring_append(clip, "discard;\n");
}
/* calculate perspective-correct inputs */
QString *vars = qstring_new();
qstring_append(vars, "vec4 pD0 = vtx.D0 / vtx.inv_w;\n");
@ -746,14 +782,15 @@ static QString* psh_convert(struct PixelShader *ps)
qstring_append(final, "#version 330\n\n");
qstring_append(final, qstring_get_str(preflight));
qstring_append(final, "void main() {\n");
qstring_append(final, qstring_get_str(clip));
qstring_append(final, qstring_get_str(vars));
qstring_append(final, qstring_get_str(ps->code));
qstring_append(final, "fragColor = r0;\n");
qstring_append(final, "}\n");
QDECREF(preflight);
QDECREF(vars);
QDECREF(ps->code);
qobject_unref(preflight);
qobject_unref(vars);
qobject_unref(ps->code);
return final;
}

View File

@ -52,6 +52,9 @@ typedef struct PshState {
bool alpha_test;
enum PshAlphaFunc alpha_func;
bool window_clip_exclusive;
unsigned int window_clip_count;
} PshState;
QString *psh_translate(const PshState state);

File diff suppressed because it is too large Load Diff

View File

@ -759,7 +759,7 @@ STRUCT_VERTEX_DATA);
/* Return combined header + source */
qstring_append(header, qstring_get_str(body));
QDECREF(body);
qobject_unref(body);
return header;
}
@ -824,7 +824,7 @@ ShaderBinding* generate_shaders(const ShaderState state)
"geometry shader");
glAttachShader(program, geometry_shader);
QDECREF(geometry_shader_code);
qobject_unref(geometry_shader_code);
vtx_prefix = 'v';
} else {
@ -838,7 +838,7 @@ ShaderBinding* generate_shaders(const ShaderState state)
qstring_get_str(vertex_shader_code),
"vertex shader");
glAttachShader(program, vertex_shader);
QDECREF(vertex_shader_code);
qobject_unref(vertex_shader_code);
/* Bind attributes for vertices */
@ -859,7 +859,7 @@ ShaderBinding* generate_shaders(const ShaderState state)
"fragment shader");
glAttachShader(program, fragment_shader);
QDECREF(fragment_shader_code);
qobject_unref(fragment_shader_code);
/* link the program */
@ -952,6 +952,10 @@ ShaderBinding* generate_shaders(const ShaderState state)
snprintf(tmp, sizeof(tmp), "lightLocalAttenuation%d", i);
ret->light_local_attenuation_loc[i] = glGetUniformLocation(program, tmp);
}
for (i = 0; i < 8; i++) {
snprintf(tmp, sizeof(tmp), "clipRegion[%d]", i);
ret->clip_region_loc[i] = glGetUniformLocation(program, tmp);
}
return ret;
}

View File

@ -22,12 +22,11 @@
#define HW_NV2A_SHADERS_H
#include "qstring.h"
#include "CxbxKrnl/gloffscreen/gloffscreen.h"
#include "CxbxKrnl/gloffscreen/gloffscreen.h" // For GLenum, etc
#include "nv2a_vsh.h"
#include "nv2a_psh.h"
#include "nv2a_int.h"
#include "nv2a_regs.h"
enum ShaderPrimitiveMode {
PRIM_TYPE_NONE,
@ -108,6 +107,7 @@ typedef struct ShaderBinding {
GLint light_local_position_loc[NV2A_MAX_LIGHTS];
GLint light_local_attenuation_loc[NV2A_MAX_LIGHTS];
GLint clip_region_loc[8];
} ShaderBinding;
ShaderBinding* generate_shaders(const ShaderState state);

View File

@ -373,7 +373,7 @@ static QString* decode_opcode_input(const uint32_t *shader_token,
/* swizzle bits are next to the neg bit */
QString *swizzle_str = decode_swizzle(shader_token, (VshFieldName)((int)neg_field+1));
qstring_append(ret_str, qstring_get_str(swizzle_str));
QDECREF(swizzle_str);
qobject_unref(swizzle_str);
}
return ret_str;
@ -463,7 +463,7 @@ static QString* decode_token(const uint32_t *shader_token)
vsh_get_field(shader_token, FLD_A_R));
qstring_append(inputs_mac, ", ");
qstring_append(inputs_mac, qstring_get_str(input_a));
QDECREF(input_a);
qobject_unref(input_a);
}
if (mac_opcode_params[mac].B) {
QString *input_b =
@ -473,7 +473,7 @@ static QString* decode_token(const uint32_t *shader_token)
vsh_get_field(shader_token, FLD_B_R));
qstring_append(inputs_mac, ", ");
qstring_append(inputs_mac, qstring_get_str(input_b));
QDECREF(input_b);
qobject_unref(input_b);
}
if (mac_opcode_params[mac].C) {
qstring_append(inputs_mac, ", ");
@ -486,7 +486,7 @@ static QString* decode_token(const uint32_t *shader_token)
vsh_get_field(shader_token, FLD_OUT_MAC_MASK),
mac_opcode[mac],
qstring_get_str(inputs_mac));
QDECREF(inputs_mac);
qobject_unref(inputs_mac);
} else {
ret = qstring_new();
}
@ -507,11 +507,11 @@ static QString* decode_token(const uint32_t *shader_token)
qstring_append(ret, qstring_get_str(ilu_op));
QDECREF(inputs_c);
QDECREF(ilu_op);
qobject_unref(inputs_c);
qobject_unref(ilu_op);
}
QDECREF(input_c);
qobject_unref(input_c);
return ret;
}
@ -625,7 +625,16 @@ static const char* vsh_header =
"#define ARL(dest, src) dest = _ARL(_in(src).x)\n"
"int _ARL(float src)\n"
"{\n"
" return int(floor(src));\n"
" /* Xbox GPU does specify rounding, OpenGL doesn't; so we need a bias.\n"
" * Example: We probably want to floor 16.99.. to 17, not 16.\n"
" * Source of error (why we get 16.99.. instead of 17.0) is typically\n"
" * vertex-attributes being normalized from a byte value to float:\n"
" * 17 / 255 = 0.06666.. so is this 0.06667 (ceil) or 0.06666 (floor)?\n"
" * Which value we get depends on the host GPU.\n"
" * If we multiply these rounded values by 255 later, we get:\n"
" * 17.00 (ARL result = 17) or 16.99 (ARL result = 16).\n"
" * We assume the intend was to get 17, so we add our bias to fix it. */\n"
" return int(floor(src + 0.001));\n"
"}\n"
"\n"
"#define SGE(dest, mask, src0, src1) dest.mask = _SGE(_in(src0), _in(src1)).mask\n"
@ -663,13 +672,25 @@ static const char* vsh_header =
"#define EXP(dest, mask, src) dest.mask = _EXP(_in(src).x).mask\n"
"vec4 _EXP(float src)\n"
"{\n"
" return vec4(exp2(src));\n"
" vec4 result;\n"
" result.x = exp2(floor(src));\n"
" result.y = src - floor(src);\n"
" result.z = exp2(src);\n"
" result.w = 1.0;\n"
" return result;\n"
"}\n"
"\n"
"#define LOG(dest, mask, src) dest.mask = _LOG(_in(src).x).mask\n"
"vec4 _LOG(float src)\n"
"{\n"
" return vec4(log2(src));\n"
" float tmp = abs(src);\n"
" if (tmp == 0.0) { return vec4(-INFINITY, 1.0f, -INFINITY, 1.0f); }\n"
" vec4 result;\n"
" result.x = floor(log2(tmp));\n"
" result.y = tmp / exp2(floor(log2(tmp)));\n"
" result.z = log2(tmp);\n"
" result.w = 1.0;\n"
" return result;\n"
"}\n"
"\n"
"#define LIT(dest, mask, src) dest.mask = _LIT(_in(src)).mask\n"
@ -711,7 +732,7 @@ void vsh_translate(uint16_t version,
qstring_append(body, "\n");
qstring_append(body, qstring_get_str(token_str));
qstring_append(body, "\n");
QDECREF(token_str);
qobject_unref(token_str);
if (vsh_get_field(cur_token, FLD_FINAL)) {
has_final = true;

View File

@ -37,7 +37,7 @@ static QString* qstring_from_fmt(std::string fmt, ...) {
#define qstring_append_fmt(gs, fmt, ...) gs->append(*(std::string*)(qstring_from_fmt(fmt, ##__VA_ARGS__)))
#define qstring_get_length(gs) gs->size()
#define QDECREF(X) // FIXME: Mostly free, but needs to be reviewed case-by-case
#define QINCREF(X) // FIXME: Tricky!
#define qobject_unref(X) // FIXME: Mostly free, but needs to be reviewed case-by-case
#define qobject_ref(X) // FIXME: Tricky!
#endif