Merge pull request #1457 from PatrickvL/xqemu_port
Ported xqemu nv2a changes
This commit is contained in:
commit
81d52e50b6
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// *
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#ifdef DEBUG_NV2A_GL
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue