2021-03-03 04:42:55 +00:00
# include "ppsspp_config.h"
2022-04-14 22:35:22 +00:00
# if defined(_M_SSE)
# include <emmintrin.h>
# endif
# if PPSSPP_ARCH(ARM_NEON)
# if defined(_MSC_VER) && PPSSPP_ARCH(ARM64)
# include <arm64_neon.h>
# else
# include <arm_neon.h>
# endif
# endif
2013-04-02 06:54:19 +00:00
# include <algorithm>
2015-10-11 12:38:26 +00:00
# include <type_traits>
2017-02-27 20:57:46 +00:00
# include <mutex>
2015-10-11 12:38:26 +00:00
2020-10-04 08:04:01 +00:00
# include "Common/Profiler/Profiler.h"
2017-08-18 11:39:42 +00:00
2021-05-01 14:15:04 +00:00
# include "Common/Data/Convert/ColorConv.h"
2020-03-01 06:27:00 +00:00
# include "Common/GraphicsContext.h"
2020-08-10 07:12:51 +00:00
# include "Common/Serialize/Serializer.h"
# include "Common/Serialize/SerializeFuncs.h"
# include "Common/Serialize/SerializeList.h"
2020-08-15 18:53:08 +00:00
# include "Common/TimeUtil.h"
2017-01-24 09:44:02 +00:00
# include "Core/Reporting.h"
2015-05-23 17:33:24 +00:00
# include "GPU/GeDisasm.h"
2015-07-26 20:38:40 +00:00
# include "GPU/GPU.h"
2015-05-23 17:33:24 +00:00
# include "GPU/GPUCommon.h"
# include "GPU/GPUState.h"
2013-04-21 22:16:45 +00:00
# include "Core/Config.h"
2013-04-03 15:10:35 +00:00
# include "Core/CoreTiming.h"
2021-02-02 08:08:05 +00:00
# include "Core/Debugger/MemBlockInfo.h"
2013-04-01 06:02:46 +00:00
# include "Core/MemMap.h"
2013-02-10 15:36:06 +00:00
# include "Core/Host.h"
2013-04-05 08:13:54 +00:00
# include "Core/Reporting.h"
2014-06-14 15:42:18 +00:00
# include "Core/HLE/HLE.h"
2013-04-04 07:35:38 +00:00
# include "Core/HLE/sceKernelMemory.h"
2013-08-29 06:15:13 +00:00
# include "Core/HLE/sceKernelInterrupt.h"
2014-03-30 00:02:41 +00:00
# include "Core/HLE/sceKernelThread.h"
2013-04-01 06:02:46 +00:00
# include "Core/HLE/sceGe.h"
2022-01-30 18:46:50 +00:00
# include "Core/HW/Display.h"
2016-12-21 17:26:06 +00:00
# include "Core/MemMapHelpers.h"
2020-12-20 11:11:02 +00:00
# include "Core/Util/PPGeDraw.h"
2018-06-29 02:47:33 +00:00
# include "GPU/Common/DrawEngineCommon.h"
2020-08-03 21:17:22 +00:00
# include "GPU/Common/FramebufferManagerCommon.h"
2018-06-29 02:47:33 +00:00
# include "GPU/Common/SplineCommon.h"
2016-12-21 17:07:17 +00:00
# include "GPU/Common/TextureCacheCommon.h"
2018-09-01 15:32:03 +00:00
# include "GPU/Debugger/Debugger.h"
2017-06-03 04:53:20 +00:00
# include "GPU/Debugger/Record.h"
2012-12-28 20:58:00 +00:00
2017-03-14 12:21:24 +00:00
const CommonCommandTableEntry commonCommandTable [ ] = {
2017-03-14 12:35:09 +00:00
// From Common. No flushing but definitely need execute.
{ GE_CMD_OFFSETADDR , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_OffsetAddr } ,
{ GE_CMD_ORIGIN , FLAG_EXECUTE | FLAG_READS_PC , 0 , & GPUCommon : : Execute_Origin } ,
{ GE_CMD_JUMP , FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC , 0 , & GPUCommon : : Execute_Jump } ,
{ GE_CMD_CALL , FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC , 0 , & GPUCommon : : Execute_Call } ,
{ GE_CMD_RET , FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC , 0 , & GPUCommon : : Execute_Ret } ,
2022-01-31 22:57:56 +00:00
{ GE_CMD_END , FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC , 0 , & GPUCommon : : Execute_End } ,
2017-03-14 12:35:09 +00:00
{ GE_CMD_VADDR , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Vaddr } ,
{ GE_CMD_IADDR , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Iaddr } ,
{ GE_CMD_BJUMP , FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC , 0 , & GPUCommon : : Execute_BJump } , // EXECUTE
2022-01-23 06:41:41 +00:00
{ GE_CMD_BOUNDINGBOX , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_BoundingBox } , // Shouldn't need to FLUSHBEFORE.
2017-03-14 12:35:09 +00:00
2018-02-26 10:18:52 +00:00
{ GE_CMD_PRIM , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Prim } ,
2022-01-31 22:57:56 +00:00
{ GE_CMD_BEZIER , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Bezier } ,
{ GE_CMD_SPLINE , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Spline } ,
2018-02-26 10:18:52 +00:00
2018-04-10 10:22:02 +00:00
// Changing the vertex type requires us to flush.
{ GE_CMD_VERTEXTYPE , FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE , 0 , & GPUCommon : : Execute_VertexType } ,
2018-02-26 10:18:52 +00:00
2018-02-26 10:26:52 +00:00
{ GE_CMD_LOADCLUT , FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE , 0 , & GPUCommon : : Execute_LoadClut } ,
2022-01-31 22:57:56 +00:00
// These two are actually processed in CMD_END.
{ GE_CMD_SIGNAL } ,
{ GE_CMD_FINISH } ,
2017-03-14 14:44:39 +00:00
2017-03-14 12:21:24 +00:00
// Changes that dirty the framebuffer
{ GE_CMD_FRAMEBUFPTR , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS } ,
2018-09-22 04:55:11 +00:00
{ GE_CMD_FRAMEBUFWIDTH , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE } ,
2017-09-20 15:30:34 +00:00
{ GE_CMD_FRAMEBUFPIXFORMAT , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
2017-03-14 12:21:24 +00:00
{ GE_CMD_ZBUFPTR , FLAG_FLUSHBEFOREONCHANGE } ,
{ GE_CMD_ZBUFWIDTH , FLAG_FLUSHBEFOREONCHANGE } ,
{ GE_CMD_FOGCOLOR , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FOGCOLOR } ,
{ GE_CMD_FOG1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FOGCOEF } ,
{ GE_CMD_FOG2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FOGCOEF } ,
2017-03-14 12:47:34 +00:00
// These affect the fragment shader so need flushing.
2018-09-22 04:55:11 +00:00
{ GE_CMD_CLEARMODE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
2017-03-19 10:32:29 +00:00
{ GE_CMD_TEXTUREMAPENABLE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
2022-09-18 04:08:26 +00:00
{ GE_CMD_FOGENABLE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
2017-03-19 10:32:29 +00:00
{ GE_CMD_TEXMODE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS | DIRTY_FRAGMENTSHADER_STATE } ,
2017-01-30 13:04:20 +00:00
{ GE_CMD_TEXSHADELS , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE } ,
2018-04-28 23:31:39 +00:00
// Raster state for Direct3D 9, uncommon.
{ GE_CMD_SHADEMODE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE } ,
2017-03-19 10:32:29 +00:00
{ GE_CMD_TEXFUNC , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAGMENTSHADER_STATE } ,
{ GE_CMD_COLORTEST , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAGMENTSHADER_STATE } ,
{ GE_CMD_ALPHATESTENABLE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAGMENTSHADER_STATE } ,
{ GE_CMD_COLORTESTENABLE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAGMENTSHADER_STATE } ,
{ GE_CMD_COLORTESTMASK , FLAG_FLUSHBEFOREONCHANGE , DIRTY_ALPHACOLORMASK | DIRTY_FRAGMENTSHADER_STATE } ,
2017-03-14 12:47:34 +00:00
2017-03-14 12:44:18 +00:00
// These change the vertex shader so need flushing.
2017-01-30 13:04:20 +00:00
{ GE_CMD_REVERSENORMAL , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE } ,
2017-08-20 18:14:41 +00:00
{ GE_CMD_LIGHTINGENABLE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
2017-01-30 13:04:20 +00:00
{ GE_CMD_LIGHTENABLE0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE } ,
{ GE_CMD_LIGHTENABLE1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE } ,
{ GE_CMD_LIGHTENABLE2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE } ,
{ GE_CMD_LIGHTENABLE3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE } ,
2018-03-24 23:19:53 +00:00
{ GE_CMD_LIGHTTYPE0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE | DIRTY_LIGHT0 } ,
{ GE_CMD_LIGHTTYPE1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE | DIRTY_LIGHT1 } ,
{ GE_CMD_LIGHTTYPE2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE | DIRTY_LIGHT2 } ,
{ GE_CMD_LIGHTTYPE3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE | DIRTY_LIGHT3 } ,
2022-05-02 22:05:39 +00:00
{ GE_CMD_MATERIALUPDATE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE } , // TODO: This should not need to dirty the fragment shader?
2017-03-14 12:44:18 +00:00
// These change both shaders so need flushing.
2017-03-19 10:32:29 +00:00
{ GE_CMD_LIGHTMODE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
2017-03-14 12:44:18 +00:00
{ GE_CMD_TEXFILTER , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
2017-03-19 10:32:29 +00:00
{ GE_CMD_TEXWRAP , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS | DIRTY_FRAGMENTSHADER_STATE } ,
2017-03-14 12:44:18 +00:00
2017-03-19 10:32:29 +00:00
// Uniform changes. though the fragmentshader optimizes based on these sometimes.
{ GE_CMD_ALPHATEST , FLAG_FLUSHBEFOREONCHANGE , DIRTY_ALPHACOLORREF | DIRTY_ALPHACOLORMASK | DIRTY_FRAGMENTSHADER_STATE } ,
{ GE_CMD_COLORREF , FLAG_FLUSHBEFOREONCHANGE , DIRTY_ALPHACOLORREF | DIRTY_FRAGMENTSHADER_STATE } ,
2017-03-14 12:47:34 +00:00
{ GE_CMD_TEXENVCOLOR , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXENV } ,
// Simple render state changes. Handled in StateMapping.cpp.
2017-01-30 11:07:26 +00:00
{ GE_CMD_CULL , FLAG_FLUSHBEFOREONCHANGE , DIRTY_RASTER_STATE } ,
{ GE_CMD_CULLFACEENABLE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_RASTER_STATE } ,
{ GE_CMD_DITHERENABLE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_RASTER_STATE } ,
2017-03-19 10:32:29 +00:00
{ GE_CMD_STENCILOP , FLAG_FLUSHBEFOREONCHANGE , DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
2017-12-07 20:08:34 +00:00
{ GE_CMD_STENCILTEST , FLAG_FLUSHBEFOREONCHANGE , DIRTY_STENCILREPLACEVALUE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE } ,
2017-03-19 10:32:29 +00:00
{ GE_CMD_STENCILTESTENABLE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
{ GE_CMD_ALPHABLENDENABLE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
{ GE_CMD_BLENDMODE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
{ GE_CMD_BLENDFIXEDA , FLAG_FLUSHBEFOREONCHANGE , DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
{ GE_CMD_BLENDFIXEDB , FLAG_FLUSHBEFOREONCHANGE , DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
2022-08-10 01:45:27 +00:00
{ GE_CMD_MASKRGB , FLAG_FLUSHBEFOREONCHANGE , DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_COLORWRITEMASK } ,
2020-11-08 22:17:06 +00:00
{ GE_CMD_MASKALPHA , FLAG_FLUSHBEFOREONCHANGE , DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_COLORWRITEMASK } ,
2017-01-30 11:02:14 +00:00
{ GE_CMD_ZTEST , FLAG_FLUSHBEFOREONCHANGE , DIRTY_DEPTHSTENCIL_STATE } ,
2018-11-07 05:16:51 +00:00
{ GE_CMD_ZTESTENABLE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_DEPTHSTENCIL_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
{ GE_CMD_ZWRITEDISABLE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_DEPTHSTENCIL_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
2017-03-19 10:32:29 +00:00
{ GE_CMD_LOGICOP , FLAG_FLUSHBEFOREONCHANGE , DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
{ GE_CMD_LOGICOPENABLE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
2017-03-14 12:42:48 +00:00
2017-03-19 10:32:29 +00:00
{ GE_CMD_TEXMAPMODE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE } ,
2017-08-17 11:53:13 +00:00
// These are read on every SubmitPrim, no need for dirtying or flushing.
{ GE_CMD_TEXSCALEU } ,
{ GE_CMD_TEXSCALEV } ,
{ GE_CMD_TEXOFFSETU } ,
{ GE_CMD_TEXOFFSETV } ,
2017-03-14 12:39:32 +00:00
2018-02-26 10:18:52 +00:00
{ GE_CMD_TEXSIZE0 , FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE , 0 , & GPUCommon : : Execute_TexSize0 } ,
2017-03-14 12:39:32 +00:00
{ GE_CMD_TEXSIZE1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXSIZE2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXSIZE3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXSIZE4 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXSIZE5 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXSIZE6 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXSIZE7 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXFORMAT , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_IMAGE } ,
{ GE_CMD_TEXLEVEL , FLAG_EXECUTEONCHANGE , DIRTY_TEXTURE_PARAMS , & GPUCommon : : Execute_TexLevel } ,
2017-05-13 03:05:35 +00:00
{ GE_CMD_TEXLODSLOPE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
2017-03-14 12:39:32 +00:00
{ GE_CMD_TEXADDR0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_IMAGE | DIRTY_UVSCALEOFFSET } ,
{ GE_CMD_TEXADDR1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXADDR2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXADDR3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXADDR4 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXADDR5 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXADDR6 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXADDR7 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXBUFWIDTH0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_IMAGE } ,
{ GE_CMD_TEXBUFWIDTH1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXBUFWIDTH2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXBUFWIDTH3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXBUFWIDTH4 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXBUFWIDTH5 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXBUFWIDTH6 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
{ GE_CMD_TEXBUFWIDTH7 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS } ,
// These must flush on change, so that LoadClut doesn't have to always flush.
{ GE_CMD_CLUTADDR , FLAG_FLUSHBEFOREONCHANGE } ,
{ GE_CMD_CLUTADDRUPPER , FLAG_FLUSHBEFOREONCHANGE } ,
2018-04-13 10:45:10 +00:00
{ GE_CMD_CLUTFORMAT , FLAG_FLUSHBEFOREONCHANGE , DIRTY_TEXTURE_PARAMS | DIRTY_DEPAL } ,
2017-03-14 12:21:24 +00:00
2017-03-14 12:30:03 +00:00
// Morph weights. TODO: Remove precomputation?
{ GE_CMD_MORPHWEIGHT0 , FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE , 0 , & GPUCommon : : Execute_MorphWeight } ,
{ GE_CMD_MORPHWEIGHT1 , FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE , 0 , & GPUCommon : : Execute_MorphWeight } ,
{ GE_CMD_MORPHWEIGHT2 , FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE , 0 , & GPUCommon : : Execute_MorphWeight } ,
{ GE_CMD_MORPHWEIGHT3 , FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE , 0 , & GPUCommon : : Execute_MorphWeight } ,
{ GE_CMD_MORPHWEIGHT4 , FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE , 0 , & GPUCommon : : Execute_MorphWeight } ,
{ GE_CMD_MORPHWEIGHT5 , FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE , 0 , & GPUCommon : : Execute_MorphWeight } ,
{ GE_CMD_MORPHWEIGHT6 , FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE , 0 , & GPUCommon : : Execute_MorphWeight } ,
{ GE_CMD_MORPHWEIGHT7 , FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE , 0 , & GPUCommon : : Execute_MorphWeight } ,
// Control spline/bezier patches. Don't really require flushing as such, but meh.
{ GE_CMD_PATCHDIVISION , FLAG_FLUSHBEFOREONCHANGE } ,
{ GE_CMD_PATCHPRIMITIVE , FLAG_FLUSHBEFOREONCHANGE } ,
2017-01-30 13:04:20 +00:00
{ GE_CMD_PATCHFACING , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VERTEXSHADER_STATE } ,
2017-03-14 12:30:03 +00:00
{ GE_CMD_PATCHCULLENABLE , FLAG_FLUSHBEFOREONCHANGE } ,
2017-03-14 12:37:12 +00:00
// Can probably ignore this one as we don't support AA lines.
{ GE_CMD_ANTIALIASENABLE , FLAG_FLUSHBEFOREONCHANGE } ,
2017-03-14 12:28:28 +00:00
// Viewport.
2018-09-22 04:55:11 +00:00
{ GE_CMD_OFFSETX , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE } ,
{ GE_CMD_OFFSETY , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE } ,
2019-06-23 23:47:44 +00:00
{ GE_CMD_VIEWPORTXSCALE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE } ,
{ GE_CMD_VIEWPORTYSCALE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE } ,
{ GE_CMD_VIEWPORTXCENTER , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE } ,
{ GE_CMD_VIEWPORTYCENTER , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE } ,
2018-09-17 05:52:43 +00:00
{ GE_CMD_VIEWPORTZSCALE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_DEPTHRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE } ,
{ GE_CMD_VIEWPORTZCENTER , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_DEPTHRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE } ,
{ GE_CMD_DEPTHCLAMPENABLE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_RASTER_STATE } ,
2017-03-14 12:28:28 +00:00
2017-03-14 12:41:13 +00:00
// Z clip
2018-09-22 04:55:11 +00:00
{ GE_CMD_MINZ , FLAG_FLUSHBEFOREONCHANGE , DIRTY_DEPTHRANGE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE } ,
{ GE_CMD_MAXZ , FLAG_FLUSHBEFOREONCHANGE , DIRTY_DEPTHRANGE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE } ,
2017-03-14 12:41:13 +00:00
2017-03-14 12:28:28 +00:00
// Region
2018-09-22 04:55:11 +00:00
{ GE_CMD_REGION1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE } ,
{ GE_CMD_REGION2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE } ,
2017-03-14 12:28:28 +00:00
// Scissor
2018-09-22 04:55:11 +00:00
{ GE_CMD_SCISSOR1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE } ,
{ GE_CMD_SCISSOR2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE } ,
2017-03-14 12:28:28 +00:00
2017-03-14 12:26:53 +00:00
// Lighting base colors
{ GE_CMD_AMBIENTCOLOR , FLAG_FLUSHBEFOREONCHANGE , DIRTY_AMBIENT } ,
{ GE_CMD_AMBIENTALPHA , FLAG_FLUSHBEFOREONCHANGE , DIRTY_AMBIENT } ,
{ GE_CMD_MATERIALDIFFUSE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_MATDIFFUSE } ,
{ GE_CMD_MATERIALEMISSIVE , FLAG_FLUSHBEFOREONCHANGE , DIRTY_MATEMISSIVE } ,
{ GE_CMD_MATERIALAMBIENT , FLAG_FLUSHBEFOREONCHANGE , DIRTY_MATAMBIENTALPHA } ,
{ GE_CMD_MATERIALALPHA , FLAG_FLUSHBEFOREONCHANGE , DIRTY_MATAMBIENTALPHA } ,
{ GE_CMD_MATERIALSPECULAR , FLAG_FLUSHBEFOREONCHANGE , DIRTY_MATSPECULAR } ,
{ GE_CMD_MATERIALSPECULARCOEF , FLAG_FLUSHBEFOREONCHANGE , DIRTY_MATSPECULAR } ,
// Light parameters
{ GE_CMD_LX0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT0 } ,
{ GE_CMD_LY0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT0 } ,
{ GE_CMD_LZ0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT0 } ,
{ GE_CMD_LX1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT1 } ,
{ GE_CMD_LY1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT1 } ,
{ GE_CMD_LZ1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT1 } ,
{ GE_CMD_LX2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT2 } ,
{ GE_CMD_LY2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT2 } ,
{ GE_CMD_LZ2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT2 } ,
{ GE_CMD_LX3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT3 } ,
{ GE_CMD_LY3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT3 } ,
{ GE_CMD_LZ3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT3 } ,
{ GE_CMD_LDX0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT0 } ,
{ GE_CMD_LDY0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT0 } ,
{ GE_CMD_LDZ0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT0 } ,
{ GE_CMD_LDX1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT1 } ,
{ GE_CMD_LDY1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT1 } ,
{ GE_CMD_LDZ1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT1 } ,
{ GE_CMD_LDX2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT2 } ,
{ GE_CMD_LDY2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT2 } ,
{ GE_CMD_LDZ2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT2 } ,
{ GE_CMD_LDX3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT3 } ,
{ GE_CMD_LDY3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT3 } ,
{ GE_CMD_LDZ3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT3 } ,
{ GE_CMD_LKA0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT0 } ,
{ GE_CMD_LKB0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT0 } ,
{ GE_CMD_LKC0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT0 } ,
{ GE_CMD_LKA1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT1 } ,
{ GE_CMD_LKB1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT1 } ,
{ GE_CMD_LKC1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT1 } ,
{ GE_CMD_LKA2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT2 } ,
{ GE_CMD_LKB2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT2 } ,
{ GE_CMD_LKC2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT2 } ,
{ GE_CMD_LKA3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT3 } ,
{ GE_CMD_LKB3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT3 } ,
{ GE_CMD_LKC3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT3 } ,
{ GE_CMD_LKS0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT0 } ,
{ GE_CMD_LKS1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT1 } ,
{ GE_CMD_LKS2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT2 } ,
{ GE_CMD_LKS3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT3 } ,
{ GE_CMD_LKO0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT0 } ,
{ GE_CMD_LKO1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT1 } ,
{ GE_CMD_LKO2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT2 } ,
{ GE_CMD_LKO3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT3 } ,
{ GE_CMD_LAC0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT0 } ,
{ GE_CMD_LDC0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT0 } ,
{ GE_CMD_LSC0 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT0 } ,
{ GE_CMD_LAC1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT1 } ,
{ GE_CMD_LDC1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT1 } ,
{ GE_CMD_LSC1 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT1 } ,
{ GE_CMD_LAC2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT2 } ,
{ GE_CMD_LDC2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT2 } ,
{ GE_CMD_LSC2 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT2 } ,
{ GE_CMD_LAC3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT3 } ,
{ GE_CMD_LDC3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT3 } ,
{ GE_CMD_LSC3 , FLAG_FLUSHBEFOREONCHANGE , DIRTY_LIGHT3 } ,
2017-03-14 12:25:52 +00:00
// Ignored commands
{ GE_CMD_TEXFLUSH , 0 } ,
{ GE_CMD_TEXSYNC , 0 } ,
// These are just nop or part of other later commands.
{ GE_CMD_NOP , 0 } ,
{ GE_CMD_BASE , 0 } ,
{ GE_CMD_TRANSFERSRC , 0 } ,
{ GE_CMD_TRANSFERSRCW , 0 } ,
{ GE_CMD_TRANSFERDST , 0 } ,
{ GE_CMD_TRANSFERDSTW , 0 } ,
{ GE_CMD_TRANSFERSRCPOS , 0 } ,
{ GE_CMD_TRANSFERDSTPOS , 0 } ,
{ GE_CMD_TRANSFERSIZE , 0 } ,
2022-01-31 22:57:56 +00:00
{ GE_CMD_TRANSFERSTART , FLAG_EXECUTE | FLAG_READS_PC , 0 , & GPUCommon : : Execute_BlockTransferStart } ,
2017-03-14 12:25:52 +00:00
2017-03-14 12:24:35 +00:00
// We don't use the dither table.
{ GE_CMD_DITH0 } ,
{ GE_CMD_DITH1 } ,
{ GE_CMD_DITH2 } ,
{ GE_CMD_DITH3 } ,
// These handle their own flushing.
{ GE_CMD_WORLDMATRIXNUMBER , FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC , 0 , & GPUCommon : : Execute_WorldMtxNum } ,
{ GE_CMD_WORLDMATRIXDATA , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_WorldMtxData } ,
{ GE_CMD_VIEWMATRIXNUMBER , FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC , 0 , & GPUCommon : : Execute_ViewMtxNum } ,
{ GE_CMD_VIEWMATRIXDATA , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_ViewMtxData } ,
{ GE_CMD_PROJMATRIXNUMBER , FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC , 0 , & GPUCommon : : Execute_ProjMtxNum } ,
{ GE_CMD_PROJMATRIXDATA , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_ProjMtxData } ,
{ GE_CMD_TGENMATRIXNUMBER , FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC , 0 , & GPUCommon : : Execute_TgenMtxNum } ,
{ GE_CMD_TGENMATRIXDATA , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_TgenMtxData } ,
{ GE_CMD_BONEMATRIXNUMBER , FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC , 0 , & GPUCommon : : Execute_BoneMtxNum } ,
{ GE_CMD_BONEMATRIXDATA , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_BoneMtxData } ,
2017-03-14 12:21:24 +00:00
// Vertex Screen/Texture/Color
2017-11-24 16:54:56 +00:00
{ GE_CMD_VSCX } ,
{ GE_CMD_VSCY } ,
{ GE_CMD_VSCZ } ,
{ GE_CMD_VTCS } ,
{ GE_CMD_VTCT } ,
{ GE_CMD_VTCQ } ,
{ GE_CMD_VCV } ,
{ GE_CMD_VAP , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_ImmVertexAlphaPrim } ,
{ GE_CMD_VFC } ,
{ GE_CMD_VSCV } ,
2017-03-14 12:21:24 +00:00
// "Missing" commands (gaps in the sequence)
{ GE_CMD_UNKNOWN_03 , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_0D , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_11 , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_29 , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_34 , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_35 , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_39 , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_4E , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_4F , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_52 , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_59 , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_5A , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_B6 , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_B7 , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_D1 , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_ED , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_EF , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_FA , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_FB , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_FC , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_FD , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
{ GE_CMD_UNKNOWN_FE , FLAG_EXECUTE , 0 , & GPUCommon : : Execute_Unknown } ,
// Appears to be debugging related or something? Hit a lot in GoW.
2017-08-05 16:15:01 +00:00
{ GE_CMD_NOP_FF , 0 } ,
2017-03-14 12:21:24 +00:00
} ;
2018-02-26 10:44:02 +00:00
// TODO: Make class member?
GPUCommon : : CommandInfo GPUCommon : : cmdInfo_ [ 256 ] ;
2017-01-23 19:56:25 +00:00
void GPUCommon : : Flush ( ) {
drawEngineCommon_ - > DispatchFlush ( ) ;
}
2022-01-22 21:12:59 +00:00
void GPUCommon : : DispatchFlush ( ) {
drawEngineCommon_ - > DispatchFlush ( ) ;
}
2017-01-30 15:50:35 +00:00
GPUCommon : : GPUCommon ( GraphicsContext * gfxCtx , Draw : : DrawContext * draw ) :
gfxCtx_ ( gfxCtx ) ,
draw_ ( draw )
2013-04-05 06:19:28 +00:00
{
2015-10-11 16:42:32 +00:00
// This assert failed on GCC x86 32-bit (but not MSVC 32-bit!) before adding the
2015-10-11 12:38:26 +00:00
// "padding" field at the end. This is important for save state compatibility.
2015-10-11 16:42:32 +00:00
// The compiler was not rounding the struct size up to an 8 byte boundary, which
// you'd expect due to the int64 field, but the Linux ABI apparently does not require that.
2015-10-11 12:38:26 +00:00
static_assert ( sizeof ( DisplayList ) = = 456 , " Bad DisplayList size " ) ;
2013-11-28 23:34:41 +00:00
Reinitialize ( ) ;
2016-01-06 22:49:02 +00:00
gstate . Reset ( ) ;
gstate_c . Reset ( ) ;
2016-04-02 08:28:38 +00:00
gpuStats . Reset ( ) ;
2018-02-26 10:44:02 +00:00
memset ( cmdInfo_ , 0 , sizeof ( cmdInfo_ ) ) ;
2018-03-02 06:06:58 +00:00
// Convert the command table to a faster format, and check for dupes.
2018-02-26 10:44:02 +00:00
std : : set < u8 > dupeCheck ;
2022-01-23 06:41:41 +00:00
for ( size_t i = 0 ; i < ARRAY_SIZE ( commonCommandTable ) ; i + + ) {
2018-02-26 10:44:02 +00:00
const u8 cmd = commonCommandTable [ i ] . cmd ;
if ( dupeCheck . find ( cmd ) ! = dupeCheck . end ( ) ) {
ERROR_LOG ( G3D , " Command table Dupe: %02x (%i) " , ( int ) cmd , ( int ) cmd ) ;
} else {
dupeCheck . insert ( cmd ) ;
}
cmdInfo_ [ cmd ] . flags | = ( uint64_t ) commonCommandTable [ i ] . flags | ( commonCommandTable [ i ] . dirty < < 8 ) ;
cmdInfo_ [ cmd ] . func = commonCommandTable [ i ] . func ;
if ( ( cmdInfo_ [ cmd ] . flags & ( FLAG_EXECUTE | FLAG_EXECUTEONCHANGE ) ) & & ! cmdInfo_ [ cmd ] . func ) {
2018-03-02 06:06:58 +00:00
// Can't have FLAG_EXECUTE commands without a function pointer to execute.
2018-02-26 10:44:02 +00:00
Crash ( ) ;
}
}
// Find commands missing from the table.
for ( int i = 0 ; i < 0xEF ; i + + ) {
if ( dupeCheck . find ( ( u8 ) i ) = = dupeCheck . end ( ) ) {
ERROR_LOG ( G3D , " Command missing from table: %02x (%i) " , i , i ) ;
}
}
2018-04-10 10:22:02 +00:00
UpdateCmdInfo ( ) ;
2020-03-01 06:27:00 +00:00
UpdateVsyncInterval ( true ) ;
2020-12-20 11:11:02 +00:00
PPGeSetDrawContext ( draw ) ;
2015-07-26 20:38:40 +00:00
}
GPUCommon : : ~ GPUCommon ( ) {
2020-12-20 11:11:02 +00:00
// Probably not necessary.
PPGeSetDrawContext ( nullptr ) ;
2013-11-28 23:34:41 +00:00
}
2018-04-10 10:22:02 +00:00
void GPUCommon : : UpdateCmdInfo ( ) {
if ( g_Config . bSoftwareSkinning ) {
cmdInfo_ [ GE_CMD_VERTEXTYPE ] . flags & = ~ FLAG_FLUSHBEFOREONCHANGE ;
cmdInfo_ [ GE_CMD_VERTEXTYPE ] . func = & GPUCommon : : Execute_VertexTypeSkinning ;
} else {
cmdInfo_ [ GE_CMD_VERTEXTYPE ] . flags | = FLAG_FLUSHBEFOREONCHANGE ;
cmdInfo_ [ GE_CMD_VERTEXTYPE ] . func = & GPUCommon : : Execute_VertexType ;
}
2018-08-25 17:38:56 +00:00
if ( g_Config . bFastMemory ) {
cmdInfo_ [ GE_CMD_JUMP ] . func = & GPUCommon : : Execute_JumpFast ;
cmdInfo_ [ GE_CMD_CALL ] . func = & GPUCommon : : Execute_CallFast ;
} else {
cmdInfo_ [ GE_CMD_JUMP ] . func = & GPUCommon : : Execute_Jump ;
cmdInfo_ [ GE_CMD_CALL ] . func = & GPUCommon : : Execute_Call ;
}
2018-04-10 10:22:02 +00:00
}
2016-01-06 22:53:21 +00:00
void GPUCommon : : BeginHostFrame ( ) {
2020-03-01 06:27:00 +00:00
UpdateVsyncInterval ( resized_ ) ;
2016-01-06 22:53:21 +00:00
ReapplyGfxState ( ) ;
2017-03-19 17:40:23 +00:00
// TODO: Assume config may have changed - maybe move to resize.
gstate_c . Dirty ( DIRTY_ALL ) ;
2016-01-06 22:53:21 +00:00
}
void GPUCommon : : EndHostFrame ( ) {
}
2013-11-28 23:34:41 +00:00
void GPUCommon : : Reinitialize ( ) {
2013-04-15 20:57:54 +00:00
memset ( dls , 0 , sizeof ( dls ) ) ;
2013-04-06 09:28:49 +00:00
for ( int i = 0 ; i < DisplayListMaxCount ; + + i ) {
2013-04-05 06:19:28 +00:00
dls [ i ] . state = PSP_GE_DL_STATE_NONE ;
2013-04-07 19:45:42 +00:00
dls [ i ] . waitTicks = 0 ;
2013-04-06 09:28:49 +00:00
}
2013-11-28 23:34:41 +00:00
nextListID = 0 ;
2017-11-05 18:44:07 +00:00
currentList = nullptr ;
2013-11-28 23:34:41 +00:00
isbreak = false ;
drawCompleteTicks = 0 ;
busyTicks = 0 ;
2014-06-14 15:42:18 +00:00
timeSpentStepping_ = 0.0 ;
2013-11-28 23:34:41 +00:00
interruptsEnabled_ = true ;
2020-05-13 07:06:13 +00:00
if ( textureCache_ )
textureCache_ - > Clear ( true ) ;
if ( framebufferManager_ )
framebufferManager_ - > DestroyAllFBOs ( ) ;
2017-11-05 19:10:05 +00:00
}
2020-11-06 23:56:48 +00:00
// Call at the END of the GPU implementation's DeviceLost
void GPUCommon : : DeviceLost ( ) {
framebufferManager_ - > DeviceLost ( ) ;
draw_ = nullptr ;
}
// Call at the start of the GPU implementation's DeviceRestore
void GPUCommon : : DeviceRestore ( ) {
draw_ = ( Draw : : DrawContext * ) PSP_CoreParameter ( ) . graphicsContext - > GetDrawContext ( ) ;
framebufferManager_ - > DeviceRestore ( draw_ ) ;
2020-12-20 11:11:02 +00:00
PPGeSetDrawContext ( draw_ ) ;
2020-11-06 23:56:48 +00:00
}
2020-03-01 06:27:00 +00:00
void GPUCommon : : UpdateVsyncInterval ( bool force ) {
2020-07-20 00:02:01 +00:00
# if !(PPSSPP_PLATFORM(ANDROID) || defined(USING_QT_UI) || PPSSPP_PLATFORM(UWP) || PPSSPP_PLATFORM(IOS))
2020-03-01 06:27:00 +00:00
int desiredVSyncInterval = g_Config . bVSync ? 1 : 0 ;
2021-08-17 14:48:47 +00:00
if ( PSP_CoreParameter ( ) . fastForward ) {
2020-03-01 06:27:00 +00:00
desiredVSyncInterval = 0 ;
}
if ( PSP_CoreParameter ( ) . fpsLimit ! = FPSLimit : : NORMAL ) {
2022-07-04 20:10:42 +00:00
int limit ;
if ( PSP_CoreParameter ( ) . fpsLimit = = FPSLimit : : CUSTOM1 )
limit = g_Config . iFpsLimit1 ;
else if ( PSP_CoreParameter ( ) . fpsLimit = = FPSLimit : : CUSTOM2 )
limit = g_Config . iFpsLimit2 ;
else
limit = PSP_CoreParameter ( ) . analogFpsLimit ;
2020-03-01 06:27:00 +00:00
// For an alternative speed that is a clean factor of 60, the user probably still wants vsync.
if ( limit = = 0 | | ( limit > = 0 & & limit ! = 15 & & limit ! = 30 & & limit ! = 60 ) ) {
desiredVSyncInterval = 0 ;
}
}
if ( desiredVSyncInterval ! = lastVsync_ | | force ) {
// Disabled EXT_swap_control_tear for now, it never seems to settle at the correct timing
// so it just keeps tearing. Not what I hoped for... (gl_extensions.EXT_swap_control_tear)
// See http://developer.download.nvidia.com/opengl/specs/WGL_EXT_swap_control_tear.txt
2020-03-08 18:10:16 +00:00
if ( gfxCtx_ )
gfxCtx_ - > SwapInterval ( desiredVSyncInterval ) ;
2020-03-01 06:27:00 +00:00
lastVsync_ = desiredVSyncInterval ;
}
# endif
}
2017-01-23 20:00:44 +00:00
int GPUCommon : : EstimatePerVertexCost ( ) {
// TODO: This is transform cost, also account for rasterization cost somehow... although it probably
// runs in parallel with transform.
// Also, this is all pure guesswork. If we can find a way to do measurements, that would be great.
// GTA wants a low value to run smooth, GoW wants a high value (otherwise it thinks things
// went too fast and starts doing all the work over again).
int cost = 20 ;
if ( gstate . isLightingEnabled ( ) ) {
cost + = 10 ;
for ( int i = 0 ; i < 4 ; i + + ) {
if ( gstate . isLightChanEnabled ( i ) )
2018-07-15 10:13:25 +00:00
cost + = 7 ;
2017-01-23 20:00:44 +00:00
}
}
if ( gstate . getUVGenMode ( ) ! = GE_TEXMAP_TEXTURE_COORDS ) {
cost + = 20 ;
}
int morphCount = gstate . getNumMorphWeights ( ) ;
if ( morphCount > 1 ) {
cost + = 5 * morphCount ;
}
return cost ;
}
2013-04-05 06:19:28 +00:00
void GPUCommon : : PopDLQueue ( ) {
if ( ! dlQueue . empty ( ) ) {
dlQueue . pop_front ( ) ;
if ( ! dlQueue . empty ( ) ) {
bool running = currentList - > state = = PSP_GE_DL_STATE_RUNNING ;
currentList = & dls [ dlQueue . front ( ) ] ;
if ( running )
currentList - > state = PSP_GE_DL_STATE_RUNNING ;
} else {
2017-11-05 19:15:57 +00:00
currentList = nullptr ;
2013-04-05 06:19:28 +00:00
}
}
2012-12-28 20:58:00 +00:00
}
2013-09-22 02:31:54 +00:00
bool GPUCommon : : BusyDrawing ( ) {
u32 state = DrawSync ( 1 ) ;
if ( state = = PSP_GE_LIST_DRAWING | | state = = PSP_GE_LIST_STALLING ) {
if ( currentList & & currentList - > state ! = PSP_GE_DL_STATE_PAUSED ) {
return true ;
}
}
return false ;
}
2016-12-21 17:13:58 +00:00
void GPUCommon : : Resized ( ) {
resized_ = true ;
}
2017-06-03 02:39:11 +00:00
void GPUCommon : : DumpNextFrame ( ) {
dumpNextFrame_ = true ;
}
2013-04-01 06:23:03 +00:00
u32 GPUCommon : : DrawSync ( int mode ) {
2013-04-01 06:42:56 +00:00
if ( mode < 0 | | mode > 1 )
return SCE_KERNEL_ERROR_INVALID_MODE ;
2013-04-04 06:31:01 +00:00
if ( mode = = 0 ) {
2013-08-28 15:13:44 +00:00
if ( ! __KernelIsDispatchEnabled ( ) ) {
return SCE_KERNEL_ERROR_CAN_NOT_WAIT ;
}
2013-08-29 06:15:13 +00:00
if ( __IsInInterrupt ( ) ) {
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT ;
}
2013-08-28 15:13:44 +00:00
2013-04-07 19:45:42 +00:00
if ( drawCompleteTicks > CoreTiming : : GetTicks ( ) ) {
2014-03-30 00:02:41 +00:00
__GeWaitCurrentThread ( GPU_SYNC_DRAW , 1 , " GeDrawSync " ) ;
2013-04-06 05:38:19 +00:00
} else {
for ( int i = 0 ; i < DisplayListMaxCount ; + + i ) {
if ( dls [ i ] . state = = PSP_GE_DL_STATE_COMPLETED ) {
dls [ i ] . state = PSP_GE_DL_STATE_NONE ;
}
}
}
2013-04-04 06:31:01 +00:00
return 0 ;
}
2013-04-06 05:38:19 +00:00
// If there's no current list, it must be complete.
DisplayList * top = NULL ;
for ( auto it = dlQueue . begin ( ) , end = dlQueue . end ( ) ; it ! = end ; + + it ) {
if ( dls [ * it ] . state ! = PSP_GE_DL_STATE_COMPLETED ) {
top = & dls [ * it ] ;
break ;
}
}
if ( ! top | | top - > state = = PSP_GE_DL_STATE_COMPLETED )
2013-04-04 06:31:01 +00:00
return PSP_GE_LIST_COMPLETED ;
if ( currentList - > pc = = currentList - > stall )
return PSP_GE_LIST_STALLING ;
return PSP_GE_LIST_DRAWING ;
2013-04-01 06:23:03 +00:00
}
2013-08-09 07:32:40 +00:00
void GPUCommon : : CheckDrawSync ( ) {
2013-04-05 06:19:28 +00:00
if ( dlQueue . empty ( ) ) {
for ( int i = 0 ; i < DisplayListMaxCount ; + + i )
dls [ i ] . state = PSP_GE_DL_STATE_NONE ;
}
}
2013-08-09 07:32:40 +00:00
int GPUCommon : : ListSync ( int listid , int mode ) {
2013-04-05 06:19:28 +00:00
if ( listid < 0 | | listid > = DisplayListMaxCount )
return SCE_KERNEL_ERROR_INVALID_ID ;
2013-04-01 06:42:56 +00:00
if ( mode < 0 | | mode > 1 )
return SCE_KERNEL_ERROR_INVALID_MODE ;
2013-04-06 09:28:49 +00:00
DisplayList & dl = dls [ listid ] ;
2013-04-04 06:05:54 +00:00
if ( mode = = 1 ) {
2013-04-05 06:19:28 +00:00
switch ( dl . state ) {
2013-04-04 06:05:54 +00:00
case PSP_GE_DL_STATE_QUEUED :
2013-04-05 06:19:28 +00:00
if ( dl . interrupted )
2013-04-04 07:35:38 +00:00
return PSP_GE_LIST_PAUSED ;
2013-04-04 06:05:54 +00:00
return PSP_GE_LIST_QUEUED ;
case PSP_GE_DL_STATE_RUNNING :
2013-04-05 06:19:28 +00:00
if ( dl . pc = = dl . stall )
2013-04-04 06:05:54 +00:00
return PSP_GE_LIST_STALLING ;
return PSP_GE_LIST_DRAWING ;
case PSP_GE_DL_STATE_COMPLETED :
return PSP_GE_LIST_COMPLETED ;
case PSP_GE_DL_STATE_PAUSED :
return PSP_GE_LIST_PAUSED ;
default :
return SCE_KERNEL_ERROR_INVALID_ID ;
2012-12-28 20:58:00 +00:00
}
}
2013-04-04 06:05:54 +00:00
2013-08-28 15:13:44 +00:00
if ( ! __KernelIsDispatchEnabled ( ) ) {
return SCE_KERNEL_ERROR_CAN_NOT_WAIT ;
2013-08-29 06:15:13 +00:00
}
if ( __IsInInterrupt ( ) ) {
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT ;
2013-08-28 15:13:44 +00:00
}
2013-04-07 19:45:42 +00:00
if ( dl . waitTicks > CoreTiming : : GetTicks ( ) ) {
2014-03-30 00:02:41 +00:00
__GeWaitCurrentThread ( GPU_SYNC_LIST , listid , " GeListSync " ) ;
2013-04-06 09:28:49 +00:00
}
2013-04-04 06:05:54 +00:00
return PSP_GE_LIST_COMPLETED ;
2012-12-28 20:58:00 +00:00
}
2013-09-21 17:03:49 +00:00
int GPUCommon : : GetStack ( int index , u32 stackPtr ) {
2017-11-05 18:44:07 +00:00
if ( ! currentList ) {
2013-09-21 17:03:49 +00:00
// Seems like it doesn't return an error code?
return 0 ;
}
if ( currentList - > stackptr < = index ) {
return SCE_KERNEL_ERROR_INVALID_INDEX ;
}
if ( index > = 0 ) {
2021-02-19 06:25:24 +00:00
auto stack = PSPPointer < u32_le > : : Create ( stackPtr ) ;
2013-09-21 17:03:49 +00:00
if ( stack . IsValid ( ) ) {
auto entry = currentList - > stack [ index ] ;
// Not really sure what most of these values are.
stack [ 0 ] = 0 ;
stack [ 1 ] = entry . pc + 4 ;
stack [ 2 ] = entry . offsetAddr ;
stack [ 7 ] = entry . baseAddr ;
}
}
return currentList - > stackptr ;
}
2013-09-20 16:42:09 +00:00
u32 GPUCommon : : EnqueueList ( u32 listpc , u32 stall , int subIntrBase , PSPPointer < PspGeListArgs > args , bool head ) {
2013-04-04 07:04:24 +00:00
// TODO Check the stack values in missing arg and ajust the stack depth
// Check alignment
// TODO Check the context and stack alignement too
2018-08-25 17:32:32 +00:00
if ( ( ( listpc | stall ) & 3 ) ! = 0 | | ! Memory : : IsValidAddress ( listpc ) ) {
ERROR_LOG_REPORT ( G3D , " sceGeListEnqueue: invalid address %08x " , listpc ) ;
2013-09-29 23:51:49 +00:00
return SCE_KERNEL_ERROR_INVALID_POINTER ;
2018-08-25 17:32:32 +00:00
}
2020-05-20 08:25:54 +00:00
2020-05-22 05:16:13 +00:00
// If args->size is below 16, it's the old struct without stack info.
if ( args . IsValid ( ) & & args - > size > = 16 & & args - > numStacks > = 256 ) {
return hleLogError ( G3D , SCE_KERNEL_ERROR_INVALID_SIZE , " invalid stack depth %d " , args - > numStacks ) ;
}
2013-04-05 06:19:28 +00:00
int id = - 1 ;
2013-10-26 17:38:59 +00:00
u64 currentTicks = CoreTiming : : GetTicks ( ) ;
2021-02-19 06:25:24 +00:00
u32 stackAddr = args . IsValid ( ) & & args - > size > = 16 ? ( u32 ) args - > stackAddr : 0 ;
2013-10-26 17:38:59 +00:00
// Check compatibility
2013-04-05 06:19:28 +00:00
if ( sceKernelGetCompiledSdkVersion ( ) > 0x01FFFFFF ) {
//numStacks = 0;
//stack = NULL;
2013-10-26 17:38:59 +00:00
for ( int i = 0 ; i < DisplayListMaxCount ; + + i ) {
if ( dls [ i ] . state ! = PSP_GE_DL_STATE_NONE & & dls [ i ] . state ! = PSP_GE_DL_STATE_COMPLETED ) {
2014-05-26 01:52:31 +00:00
// Logically, if the CPU has not interrupted yet, it hasn't seen the latest pc either.
// Exit enqueues right after an END, which fails without ignoring pendingInterrupt lists.
if ( dls [ i ] . pc = = listpc & & ! dls [ i ] . pendingInterrupt ) {
2013-10-26 17:38:59 +00:00
ERROR_LOG ( G3D , " sceGeListEnqueue: can't enqueue, list address %08X already used " , listpc ) ;
return 0x80000021 ;
2014-08-18 23:04:36 +00:00
} else if ( stackAddr ! = 0 & & dls [ i ] . stackAddr = = stackAddr & & ! dls [ i ] . pendingInterrupt ) {
2014-08-13 13:15:30 +00:00
ERROR_LOG ( G3D , " sceGeListEnqueue: can't enqueue, stack address %08X already used " , stackAddr ) ;
return 0x80000021 ;
}
2013-04-05 06:19:28 +00:00
}
}
2013-09-21 20:18:20 +00:00
}
2013-10-27 13:09:46 +00:00
// TODO Check if list stack dls[i].stack already used then return 0x80000021 as above
2013-10-26 17:38:59 +00:00
2013-09-21 20:18:20 +00:00
for ( int i = 0 ; i < DisplayListMaxCount ; + + i ) {
int possibleID = ( i + nextListID ) % DisplayListMaxCount ;
auto possibleList = dls [ possibleID ] ;
if ( possibleList . pendingInterrupt ) {
continue ;
}
if ( possibleList . state = = PSP_GE_DL_STATE_NONE ) {
id = possibleID ;
2013-04-05 06:19:28 +00:00
break ;
}
2013-09-21 20:18:20 +00:00
if ( possibleList . state = = PSP_GE_DL_STATE_COMPLETED & & possibleList . waitTicks < currentTicks ) {
id = possibleID ;
2013-04-05 06:19:28 +00:00
}
}
2013-09-21 20:18:20 +00:00
if ( id < 0 ) {
2013-04-05 08:13:54 +00:00
ERROR_LOG_REPORT ( G3D , " No DL ID available to enqueue " ) ;
2013-09-21 20:18:20 +00:00
for ( auto it = dlQueue . begin ( ) ; it ! = dlQueue . end ( ) ; + + it ) {
2013-04-05 06:19:28 +00:00
DisplayList & dl = dls [ * it ] ;
DEBUG_LOG ( G3D , " DisplayList %d status %d pc %08x stall %08x " , * it , dl . state , dl . pc , dl . stall ) ;
}
return SCE_KERNEL_ERROR_OUT_OF_MEMORY ;
}
2013-09-21 20:18:20 +00:00
nextListID = id + 1 ;
2013-04-05 06:19:28 +00:00
DisplayList & dl = dls [ id ] ;
dl . id = id ;
2013-08-23 06:23:48 +00:00
dl . startpc = listpc & 0x0FFFFFFF ;
dl . pc = listpc & 0x0FFFFFFF ;
dl . stall = stall & 0x0FFFFFFF ;
2013-04-02 06:54:19 +00:00
dl . subIntrBase = std : : max ( subIntrBase , - 1 ) ;
2013-04-01 06:28:35 +00:00
dl . stackptr = 0 ;
2013-04-02 06:54:19 +00:00
dl . signal = PSP_GE_SIGNAL_NONE ;
2013-04-04 07:35:38 +00:00
dl . interrupted = false ;
2013-04-07 19:45:42 +00:00
dl . waitTicks = ( u64 ) - 1 ;
2013-08-07 06:59:28 +00:00
dl . interruptsEnabled = interruptsEnabled_ ;
2013-09-20 16:42:09 +00:00
dl . started = false ;
2013-09-22 03:47:35 +00:00
dl . offsetAddr = 0 ;
2013-09-24 10:59:34 +00:00
dl . bboxResult = false ;
2014-08-13 13:15:30 +00:00
dl . stackAddr = stackAddr ;
2013-09-24 10:59:34 +00:00
2013-09-20 16:42:09 +00:00
if ( args . IsValid ( ) & & args - > context . IsValid ( ) )
dl . context = args - > context ;
else
2013-10-07 05:07:57 +00:00
dl . context = 0 ;
2013-04-05 06:19:28 +00:00
2013-04-04 07:04:24 +00:00
if ( head ) {
if ( currentList ) {
if ( currentList - > state ! = PSP_GE_DL_STATE_PAUSED )
return SCE_KERNEL_ERROR_INVALID_VALUE ;
currentList - > state = PSP_GE_DL_STATE_QUEUED ;
2019-07-07 20:10:28 +00:00
// Make sure we clear the signal so we don't try to pause it again.
currentList - > signal = PSP_GE_SIGNAL_NONE ;
2013-04-04 07:04:24 +00:00
}
dl . state = PSP_GE_DL_STATE_PAUSED ;
2013-04-05 06:19:28 +00:00
currentList = & dl ;
dlQueue . push_front ( id ) ;
2013-04-04 07:04:24 +00:00
} else if ( currentList ) {
dl . state = PSP_GE_DL_STATE_QUEUED ;
2013-04-05 06:19:28 +00:00
dlQueue . push_back ( id ) ;
2013-04-04 07:04:24 +00:00
} else {
dl . state = PSP_GE_DL_STATE_RUNNING ;
2013-04-05 06:19:28 +00:00
currentList = & dl ;
dlQueue . push_front ( id ) ;
2013-04-04 07:04:24 +00:00
2013-04-07 19:45:42 +00:00
drawCompleteTicks = ( u64 ) - 1 ;
2013-04-06 05:38:19 +00:00
2013-04-04 07:04:24 +00:00
// TODO save context when starting the list if param is set
ProcessDLQueue ( ) ;
}
2013-04-05 06:19:28 +00:00
return id ;
2012-12-28 20:58:00 +00:00
}
2013-08-09 07:32:40 +00:00
u32 GPUCommon : : DequeueList ( int listid ) {
2013-04-05 06:25:13 +00:00
if ( listid < 0 | | listid > = DisplayListMaxCount | | dls [ listid ] . state = = PSP_GE_DL_STATE_NONE )
return SCE_KERNEL_ERROR_INVALID_ID ;
2013-09-21 21:58:39 +00:00
auto & dl = dls [ listid ] ;
if ( dl . started )
return SCE_KERNEL_ERROR_BUSY ;
2013-04-05 06:25:13 +00:00
2013-09-21 21:58:39 +00:00
dl . state = PSP_GE_DL_STATE_NONE ;
2013-04-05 06:25:13 +00:00
if ( listid = = dlQueue . front ( ) )
PopDLQueue ( ) ;
else
dlQueue . remove ( listid ) ;
2013-09-21 21:58:39 +00:00
dl . waitTicks = 0 ;
2014-03-30 00:02:41 +00:00
__GeTriggerWait ( GPU_SYNC_LIST , listid ) ;
2013-04-05 06:25:13 +00:00
CheckDrawSync ( ) ;
2013-04-01 06:23:03 +00:00
return 0 ;
}
2013-08-09 07:32:40 +00:00
u32 GPUCommon : : UpdateStall ( int listid , u32 newstall ) {
2013-04-05 06:19:28 +00:00
if ( listid < 0 | | listid > = DisplayListMaxCount | | dls [ listid ] . state = = PSP_GE_DL_STATE_NONE )
2013-04-05 06:25:13 +00:00
return SCE_KERNEL_ERROR_INVALID_ID ;
2013-09-21 21:54:07 +00:00
auto & dl = dls [ listid ] ;
if ( dl . state = = PSP_GE_DL_STATE_COMPLETED )
return SCE_KERNEL_ERROR_ALREADY ;
2013-04-05 06:19:28 +00:00
2013-09-21 21:54:07 +00:00
dl . stall = newstall & 0x0FFFFFFF ;
2012-12-28 20:58:00 +00:00
ProcessDLQueue ( ) ;
2013-04-01 06:23:03 +00:00
return 0 ;
}
2013-08-09 07:32:40 +00:00
u32 GPUCommon : : Continue ( ) {
2013-04-04 07:35:38 +00:00
if ( ! currentList )
return 0 ;
if ( currentList - > state = = PSP_GE_DL_STATE_PAUSED )
{
2019-07-07 20:10:28 +00:00
if ( ! isbreak ) {
2014-04-12 06:32:34 +00:00
// TODO: Supposedly this returns SCE_KERNEL_ERROR_BUSY in some case, previously it had
// currentList->signal == PSP_GE_SIGNAL_HANDLER_PAUSE, but it doesn't reproduce.
2013-04-04 07:35:38 +00:00
currentList - > state = PSP_GE_DL_STATE_RUNNING ;
currentList - > signal = PSP_GE_SIGNAL_NONE ;
// TODO Restore context of DL is necessary
// TODO Restore BASE
2013-04-06 05:38:19 +00:00
// We have a list now, so it's not complete.
2013-04-07 19:45:42 +00:00
drawCompleteTicks = ( u64 ) - 1 ;
2019-07-07 20:10:28 +00:00
} else {
2013-04-04 07:35:38 +00:00
currentList - > state = PSP_GE_DL_STATE_QUEUED ;
2019-07-07 20:10:28 +00:00
currentList - > signal = PSP_GE_SIGNAL_NONE ;
}
2013-04-04 07:35:38 +00:00
}
else if ( currentList - > state = = PSP_GE_DL_STATE_RUNNING )
{
if ( sceKernelGetCompiledSdkVersion ( ) > = 0x02000000 )
return 0x80000020 ;
return - 1 ;
}
else
{
if ( sceKernelGetCompiledSdkVersion ( ) > = 0x02000000 )
return 0x80000004 ;
return - 1 ;
}
ProcessDLQueue ( ) ;
2013-04-01 06:23:03 +00:00
return 0 ;
}
2013-08-09 07:32:40 +00:00
u32 GPUCommon : : Break ( int mode ) {
2013-04-01 06:42:56 +00:00
if ( mode < 0 | | mode > 1 )
return SCE_KERNEL_ERROR_INVALID_MODE ;
2013-04-04 07:35:38 +00:00
if ( ! currentList )
2013-09-22 02:07:02 +00:00
return SCE_KERNEL_ERROR_ALREADY ;
2013-04-04 07:35:38 +00:00
2013-04-05 06:19:28 +00:00
if ( mode = = 1 )
2013-04-04 07:35:38 +00:00
{
2013-04-05 06:19:28 +00:00
// Clear the queue
2013-04-04 07:35:38 +00:00
dlQueue . clear ( ) ;
2013-04-05 06:19:28 +00:00
for ( int i = 0 ; i < DisplayListMaxCount ; + + i )
{
dls [ i ] . state = PSP_GE_DL_STATE_NONE ;
dls [ i ] . signal = PSP_GE_SIGNAL_NONE ;
}
2013-09-21 20:18:20 +00:00
nextListID = 0 ;
2013-04-05 06:19:28 +00:00
currentList = NULL ;
2013-04-04 07:35:38 +00:00
return 0 ;
}
if ( currentList - > state = = PSP_GE_DL_STATE_NONE | | currentList - > state = = PSP_GE_DL_STATE_COMPLETED )
{
if ( sceKernelGetCompiledSdkVersion ( ) > = 0x02000000 )
return 0x80000004 ;
return - 1 ;
}
if ( currentList - > state = = PSP_GE_DL_STATE_PAUSED )
{
if ( sceKernelGetCompiledSdkVersion ( ) > 0x02000010 )
{
if ( currentList - > signal = = PSP_GE_SIGNAL_HANDLER_PAUSE )
{
2013-04-05 08:13:54 +00:00
ERROR_LOG_REPORT ( G3D , " sceGeBreak: can't break signal-pausing list " ) ;
2013-04-04 07:35:38 +00:00
}
else
2013-09-22 02:07:02 +00:00
return SCE_KERNEL_ERROR_ALREADY ;
2013-04-04 07:35:38 +00:00
}
2013-09-22 02:07:02 +00:00
return SCE_KERNEL_ERROR_BUSY ;
2013-04-04 07:35:38 +00:00
}
if ( currentList - > state = = PSP_GE_DL_STATE_QUEUED )
{
currentList - > state = PSP_GE_DL_STATE_PAUSED ;
return currentList - > id ;
}
// TODO Save BASE
// TODO Adjust pc to be just before SIGNAL/END
// TODO: Is this right?
if ( currentList - > signal = = PSP_GE_SIGNAL_SYNC )
currentList - > pc + = 8 ;
currentList - > interrupted = true ;
currentList - > state = PSP_GE_DL_STATE_PAUSED ;
currentList - > signal = PSP_GE_SIGNAL_HANDLER_SUSPEND ;
isbreak = true ;
return currentList - > id ;
2012-12-29 01:10:29 +00:00
}
2014-06-14 15:42:18 +00:00
void GPUCommon : : NotifySteppingEnter ( ) {
2017-03-24 01:57:18 +00:00
if ( coreCollectDebugStats ) {
2014-06-14 15:42:18 +00:00
timeSteppingStarted_ = time_now_d ( ) ;
}
}
void GPUCommon : : NotifySteppingExit ( ) {
2017-03-24 01:57:18 +00:00
if ( coreCollectDebugStats ) {
2014-06-14 15:42:18 +00:00
if ( timeSteppingStarted_ < = 0.0 ) {
ERROR_LOG ( G3D , " Mismatched stepping enter/exit. " ) ;
}
2021-01-31 23:06:11 +00:00
double total = time_now_d ( ) - timeSteppingStarted_ ;
_dbg_assert_msg_ ( total > = 0.0 , " Time spent stepping became negative " ) ;
timeSpentStepping_ + = total ;
2014-06-14 15:42:18 +00:00
timeSteppingStarted_ = 0.0 ;
}
}
2013-08-09 06:46:10 +00:00
bool GPUCommon : : InterpretList ( DisplayList & list ) {
2013-04-21 22:16:45 +00:00
// Initialized to avoid a race condition with bShowDebugStats changing.
double start = 0.0 ;
2017-03-24 01:57:18 +00:00
if ( coreCollectDebugStats ) {
2013-04-21 22:16:45 +00:00
start = time_now_d ( ) ;
}
2014-04-12 06:32:34 +00:00
if ( list . state = = PSP_GE_DL_STATE_PAUSED )
return false ;
2013-08-09 07:32:40 +00:00
currentList = & list ;
2013-04-06 16:59:24 +00:00
2013-10-07 05:07:57 +00:00
if ( ! list . started & & list . context . IsValid ( ) ) {
2013-09-20 16:42:09 +00:00
gstate . Save ( list . context ) ;
}
list . started = true ;
2013-09-22 03:47:35 +00:00
gstate_c . offsetAddr = list . offsetAddr ;
2013-01-29 16:07:36 +00:00
2013-08-09 07:32:40 +00:00
if ( ! Memory : : IsValidAddress ( list . pc ) ) {
ERROR_LOG_REPORT ( G3D , " DL PC = %08x WTF!!!! " , list . pc ) ;
return true ;
}
2013-02-02 22:47:35 +00:00
2013-08-09 07:32:40 +00:00
cycleLastPC = list . pc ;
2013-10-12 08:20:27 +00:00
cyclesExecuted + = 60 ;
2013-08-23 06:23:48 +00:00
downcount = list . stall = = 0 ? 0x0FFFFFFF : ( list . stall - list . pc ) / 4 ;
2013-08-09 07:32:40 +00:00
list . state = PSP_GE_DL_STATE_RUNNING ;
list . interrupted = false ;
2013-04-03 15:10:35 +00:00
2013-08-09 07:32:40 +00:00
gpuState = list . pc = = list . stall ? GPUSTATE_STALL : GPUSTATE_RUNNING ;
2013-04-28 21:56:38 +00:00
2022-02-06 08:16:35 +00:00
// To enable breakpoints, we don't do fast matrix loads while debugger active.
debugRecording_ = GPUDebug : : IsActive ( ) | | GPURecord : : IsActive ( ) ;
const bool useFastRunLoop = ! dumpThisFrame_ & & ! debugRecording_ ;
2013-08-09 06:46:10 +00:00
while ( gpuState = = GPUSTATE_RUNNING ) {
2013-08-09 08:03:54 +00:00
{
if ( list . pc = = list . stall ) {
gpuState = GPUSTATE_STALL ;
downcount = 0 ;
}
2013-04-04 08:07:30 +00:00
}
2013-02-17 00:06:06 +00:00
2013-08-09 06:46:10 +00:00
if ( useFastRunLoop ) {
2013-04-28 21:23:30 +00:00
FastRunLoop ( list ) ;
2013-08-09 06:46:10 +00:00
} else {
2013-04-28 21:23:30 +00:00
SlowRunLoop ( list ) ;
2013-08-09 06:46:10 +00:00
}
2013-04-28 21:23:30 +00:00
2013-08-09 08:03:54 +00:00
{
2013-08-23 06:23:48 +00:00
downcount = list . stall = = 0 ? 0x0FFFFFFF : ( list . stall - list . pc ) / 4 ;
2013-08-09 06:46:10 +00:00
2013-08-09 08:03:54 +00:00
if ( gpuState = = GPUSTATE_STALL & & list . stall ! = list . pc ) {
// Unstalled.
gpuState = GPUSTATE_RUNNING ;
}
2013-08-09 06:46:10 +00:00
}
2013-04-28 21:23:30 +00:00
}
2015-03-15 01:11:00 +00:00
FinishDeferred ( ) ;
2022-02-20 21:51:40 +00:00
if ( debugRecording_ )
GPURecord : : NotifyCPU ( ) ;
2015-03-15 01:11:00 +00:00
2013-04-28 21:23:30 +00:00
// We haven't run the op at list.pc, so it shouldn't count.
2013-08-09 06:46:10 +00:00
if ( cycleLastPC ! = list . pc ) {
2013-04-28 21:23:30 +00:00
UpdatePC ( list . pc - 4 , list . pc ) ;
2013-08-09 06:46:10 +00:00
}
2013-04-28 21:23:30 +00:00
2013-09-22 03:47:35 +00:00
list . offsetAddr = gstate_c . offsetAddr ;
2017-03-24 01:57:18 +00:00
if ( coreCollectDebugStats ) {
2014-06-14 15:42:18 +00:00
double total = time_now_d ( ) - start - timeSpentStepping_ ;
2021-01-31 23:06:11 +00:00
_dbg_assert_msg_ ( total > = 0.0 , " Time spent DL processing became negative " ) ;
2014-06-14 15:42:18 +00:00
hleSetSteppingTime ( timeSpentStepping_ ) ;
2022-01-30 18:46:50 +00:00
DisplayNotifySleep ( timeSpentStepping_ ) ;
2014-06-14 15:42:18 +00:00
timeSpentStepping_ = 0.0 ;
gpuStats . msProcessingDisplayLists + = total ;
2013-04-28 21:23:30 +00:00
}
return gpuState = = GPUSTATE_DONE | | gpuState = = GPUSTATE_ERROR ;
}
2018-02-26 10:52:16 +00:00
// Maybe should write this in ASM...
void GPUCommon : : FastRunLoop ( DisplayList & list ) {
PROFILE_THIS_SCOPE ( " gpuloop " ) ;
const CommandInfo * cmdInfo = cmdInfo_ ;
int dc = downcount ;
for ( ; dc > 0 ; - - dc ) {
// We know that display list PCs have the upper nibble == 0 - no need to mask the pointer
2021-02-19 06:25:24 +00:00
const u32 op = * ( const u32_le * ) ( Memory : : base + list . pc ) ;
2018-02-26 10:52:16 +00:00
const u32 cmd = op > > 24 ;
const CommandInfo & info = cmdInfo [ cmd ] ;
const u32 diff = op ^ gstate . cmdmem [ cmd ] ;
if ( diff = = 0 ) {
if ( info . flags & FLAG_EXECUTE ) {
downcount = dc ;
( this - > * info . func ) ( op , diff ) ;
dc = downcount ;
}
} else {
uint64_t flags = info . flags ;
if ( flags & FLAG_FLUSHBEFOREONCHANGE ) {
2018-02-26 12:51:14 +00:00
if ( drawEngineCommon_ - > GetNumDrawCalls ( ) ) {
drawEngineCommon_ - > DispatchFlush ( ) ;
}
2018-02-26 10:52:16 +00:00
}
gstate . cmdmem [ cmd ] = op ;
if ( flags & ( FLAG_EXECUTE | FLAG_EXECUTEONCHANGE ) ) {
downcount = dc ;
( this - > * info . func ) ( op , diff ) ;
dc = downcount ;
} else {
uint64_t dirty = flags > > 8 ;
if ( dirty )
gstate_c . Dirty ( dirty ) ;
}
}
list . pc + = 4 ;
}
downcount = 0 ;
}
2017-06-04 05:28:29 +00:00
void GPUCommon : : BeginFrame ( ) {
2017-12-01 19:01:13 +00:00
immCount_ = 0 ;
2017-06-03 02:39:11 +00:00
if ( dumpNextFrame_ ) {
NOTICE_LOG ( G3D , " DUMPING THIS FRAME " ) ;
dumpThisFrame_ = true ;
dumpNextFrame_ = false ;
} else if ( dumpThisFrame_ ) {
dumpThisFrame_ = false ;
}
2022-08-24 02:48:34 +00:00
GPUDebug : : NotifyBeginFrame ( ) ;
2022-08-24 02:20:14 +00:00
GPURecord : : NotifyBeginFrame ( ) ;
2017-06-03 02:39:11 +00:00
}
2013-04-28 21:23:30 +00:00
void GPUCommon : : SlowRunLoop ( DisplayList & list )
{
const bool dumpThisFrame = dumpThisFrame_ ;
while ( downcount > 0 )
{
2022-02-07 05:25:33 +00:00
bool process = GPUDebug : : NotifyCommand ( list . pc ) ;
if ( process ) {
GPURecord : : NotifyCommand ( list . pc ) ;
u32 op = Memory : : ReadUnchecked_U32 ( list . pc ) ;
u32 cmd = op > > 24 ;
u32 diff = op ^ gstate . cmdmem [ cmd ] ;
PreExecuteOp ( op , diff ) ;
if ( dumpThisFrame ) {
char temp [ 256 ] ;
u32 prev ;
if ( Memory : : IsValidAddress ( list . pc - 4 ) ) {
prev = Memory : : ReadUnchecked_U32 ( list . pc - 4 ) ;
} else {
prev = 0 ;
}
GeDisassembleOp ( list . pc , op , prev , temp , 256 ) ;
NOTICE_LOG ( G3D , " %08x: %s " , op , temp ) ;
2013-10-07 20:57:44 +00:00
}
2022-02-07 05:25:33 +00:00
gstate . cmdmem [ cmd ] = op ;
2013-04-03 15:10:35 +00:00
2022-02-07 05:25:33 +00:00
ExecuteOp ( op , diff ) ;
}
2013-04-03 15:10:35 +00:00
2013-04-28 21:23:30 +00:00
list . pc + = 4 ;
- - downcount ;
2013-04-21 22:16:45 +00:00
}
2012-12-29 01:10:29 +00:00
}
2013-04-28 20:34:29 +00:00
// The newPC parameter is used for jumps, we don't count cycles between.
2014-03-04 10:38:25 +00:00
void GPUCommon : : UpdatePC ( u32 currentPC , u32 newPC ) {
2013-04-07 23:55:48 +00:00
// Rough estimate, 2 CPU ticks (it's double the clock rate) per GPU instruction.
2014-04-05 08:41:54 +00:00
u32 executed = ( currentPC - cycleLastPC ) / 4 ;
2013-08-23 09:42:15 +00:00
cyclesExecuted + = 2 * executed ;
2014-04-05 21:08:44 +00:00
cycleLastPC = newPC ;
2013-04-28 21:23:30 +00:00
2017-03-24 01:57:18 +00:00
if ( coreCollectDebugStats ) {
2014-04-05 08:41:54 +00:00
gpuStats . otherGPUCycles + = 2 * executed ;
gpuStats . gpuCommandsAtCallLevel [ std : : min ( currentList - > stackptr , 3 ) ] + = executed ;
}
2013-08-23 09:26:13 +00:00
2014-03-06 07:24:18 +00:00
// Exit the runloop and recalculate things. This happens a lot in some games.
if ( currentList )
2014-04-05 21:08:44 +00:00
downcount = currentList - > stall = = 0 ? 0x0FFFFFFF : ( currentList - > stall - newPC ) / 4 ;
2014-03-06 07:24:18 +00:00
else
downcount = 0 ;
2013-04-28 20:34:29 +00:00
}
2013-08-08 06:59:32 +00:00
void GPUCommon : : ReapplyGfxState ( ) {
2013-08-04 22:15:50 +00:00
// The commands are embedded in the command memory so we can just reexecute the words. Convenient.
2013-08-23 06:23:48 +00:00
// To be safe we pass 0xFFFFFFFF as the diff.
2013-08-04 22:15:50 +00:00
2020-05-24 14:30:37 +00:00
// TODO: Consider whether any of this should really be done. We might be able to get all the way
// by simplying dirtying the appropriate gstate_c dirty flags.
2013-08-08 06:59:32 +00:00
for ( int i = GE_CMD_VERTEXTYPE ; i < GE_CMD_BONEMATRIXNUMBER ; i + + ) {
2014-04-16 14:53:07 +00:00
if ( i ! = GE_CMD_ORIGIN & & i ! = GE_CMD_OFFSETADDR ) {
2013-08-04 22:15:50 +00:00
ExecuteOp ( gstate . cmdmem [ i ] , 0xFFFFFFFF ) ;
2013-08-08 06:59:32 +00:00
}
2013-08-04 22:15:50 +00:00
}
// Can't write to bonematrixnumber here
2013-09-20 07:43:45 +00:00
for ( int i = GE_CMD_MORPHWEIGHT0 ; i < = GE_CMD_PATCHFACING ; i + + ) {
2013-08-04 22:15:50 +00:00
ExecuteOp ( gstate . cmdmem [ i ] , 0xFFFFFFFF ) ;
}
// There are a few here in the middle that we shouldn't execute...
2020-05-24 14:30:37 +00:00
// 0x42 to 0xEA
2015-11-04 21:03:29 +00:00
for ( int i = GE_CMD_VIEWPORTXSCALE ; i < GE_CMD_TRANSFERSTART ; i + + ) {
2020-05-24 14:30:37 +00:00
switch ( i ) {
case GE_CMD_LOADCLUT :
case GE_CMD_TEXSYNC :
case GE_CMD_TEXFLUSH :
break ;
default :
2020-05-24 07:12:41 +00:00
ExecuteOp ( gstate . cmdmem [ i ] , 0xFFFFFFFF ) ;
2020-05-24 14:30:37 +00:00
break ;
2020-05-24 07:12:41 +00:00
}
2013-08-04 22:15:50 +00:00
}
2013-09-20 07:43:45 +00:00
// Let's just skip the transfer size stuff, it's just values.
2013-08-04 22:15:50 +00:00
}
2015-07-26 20:38:40 +00:00
inline void GPUCommon : : UpdateState ( GPURunState state ) {
2013-04-28 20:34:29 +00:00
gpuState = state ;
2013-04-28 21:23:30 +00:00
if ( state ! = GPUSTATE_RUNNING )
downcount = 0 ;
2013-04-03 15:10:35 +00:00
}
2013-08-04 23:31:11 +00:00
int GPUCommon : : GetNextListIndex ( ) {
auto iter = dlQueue . begin ( ) ;
if ( iter ! = dlQueue . end ( ) ) {
return * iter ;
} else {
return - 1 ;
}
}
2017-11-05 19:33:28 +00:00
void GPUCommon : : ProcessDLQueue ( ) {
2013-04-03 15:10:35 +00:00
startingTicks = CoreTiming : : GetTicks ( ) ;
cyclesExecuted = 0 ;
2013-08-25 01:13:34 +00:00
// Seems to be correct behaviour to process the list anyway?
2013-08-04 23:31:11 +00:00
if ( startingTicks < busyTicks ) {
2013-09-07 20:31:14 +00:00
DEBUG_LOG ( G3D , " Can't execute a list yet, still busy for %lld ticks " , busyTicks - startingTicks ) ;
2013-08-25 01:13:34 +00:00
//return;
2013-04-09 07:56:04 +00:00
}
2013-08-04 23:31:11 +00:00
for ( int listIndex = GetNextListIndex ( ) ; listIndex ! = - 1 ; listIndex = GetNextListIndex ( ) ) {
DisplayList & l = dls [ listIndex ] ;
2015-05-25 22:39:27 +00:00
DEBUG_LOG ( G3D , " Starting DL execution at %08x - stall = %08x " , l . pc , l . stall ) ;
2013-08-04 23:31:11 +00:00
if ( ! InterpretList ( l ) ) {
2013-08-08 06:59:32 +00:00
return ;
2013-08-04 23:31:11 +00:00
} else {
2014-05-04 01:40:27 +00:00
// Some other list could've taken the spot while we dilly-dallied around.
if ( l . state ! = PSP_GE_DL_STATE_QUEUED ) {
// At the end, we can remove it from the queue and continue.
dlQueue . erase ( std : : remove ( dlQueue . begin ( ) , dlQueue . end ( ) , listIndex ) , dlQueue . end ( ) ) ;
}
2012-12-29 01:10:29 +00:00
}
}
2013-08-04 23:31:11 +00:00
2017-11-05 18:44:07 +00:00
currentList = nullptr ;
2013-04-06 05:38:19 +00:00
2013-04-07 19:45:42 +00:00
drawCompleteTicks = startingTicks + cyclesExecuted ;
2013-04-09 07:56:04 +00:00
busyTicks = std : : max ( busyTicks , drawCompleteTicks ) ;
2014-03-30 00:02:41 +00:00
__GeTriggerSync ( GPU_SYNC_DRAW , 1 , drawCompleteTicks ) ;
2013-08-11 20:41:42 +00:00
// Since the event is in CoreTiming, we're in sync. Just set 0 now.
2012-12-29 01:10:29 +00:00
}
void GPUCommon : : PreExecuteOp ( u32 op , u32 diff ) {
// Nothing to do
2012-12-29 19:41:33 +00:00
}
2014-04-16 15:12:21 +00:00
void GPUCommon : : Execute_OffsetAddr ( u32 op , u32 diff ) {
gstate_c . offsetAddr = op < < 8 ;
}
2013-04-01 06:02:46 +00:00
2017-01-28 10:53:28 +00:00
void GPUCommon : : Execute_Vaddr ( u32 op , u32 diff ) {
gstate_c . vertexAddr = gstate_c . getRelativeAddress ( op & 0x00FFFFFF ) ;
}
void GPUCommon : : Execute_Iaddr ( u32 op , u32 diff ) {
gstate_c . indexAddr = gstate_c . getRelativeAddress ( op & 0x00FFFFFF ) ;
}
2014-04-16 15:12:21 +00:00
void GPUCommon : : Execute_Origin ( u32 op , u32 diff ) {
gstate_c . offsetAddr = currentList - > pc ;
}
2013-04-01 06:02:46 +00:00
2014-04-16 15:12:21 +00:00
void GPUCommon : : Execute_Jump ( u32 op , u32 diff ) {
2014-05-05 00:58:44 +00:00
const u32 target = gstate_c . getRelativeAddress ( op & 0x00FFFFFC ) ;
2017-01-28 10:39:34 +00:00
if ( ! Memory : : IsValidAddress ( target ) ) {
2020-09-22 19:57:51 +00:00
ERROR_LOG ( G3D , " JUMP to illegal address %08x - ignoring! data=%06x " , target , op & 0x00FFFFFF ) ;
2018-08-25 17:32:32 +00:00
UpdateState ( GPUSTATE_ERROR ) ;
2017-01-28 10:39:34 +00:00
return ;
2014-04-16 15:12:21 +00:00
}
2017-01-28 10:39:34 +00:00
UpdatePC ( currentList - > pc , target - 4 ) ;
currentList - > pc = target - 4 ; // pc will be increased after we return, counteract that
2014-04-16 15:12:21 +00:00
}
2013-04-01 06:02:46 +00:00
2018-08-25 17:38:56 +00:00
void GPUCommon : : Execute_JumpFast ( u32 op , u32 diff ) {
const u32 target = gstate_c . getRelativeAddress ( op & 0x00FFFFFC ) ;
UpdatePC ( currentList - > pc , target - 4 ) ;
currentList - > pc = target - 4 ; // pc will be increased after we return, counteract that
}
2014-04-16 15:12:21 +00:00
void GPUCommon : : Execute_BJump ( u32 op , u32 diff ) {
if ( ! currentList - > bboxResult ) {
// bounding box jump.
2014-05-05 00:58:44 +00:00
const u32 target = gstate_c . getRelativeAddress ( op & 0x00FFFFFC ) ;
2014-04-16 15:12:21 +00:00
if ( Memory : : IsValidAddress ( target ) ) {
UpdatePC ( currentList - > pc , target - 4 ) ;
currentList - > pc = target - 4 ; // pc will be increased after we return, counteract that
} else {
2020-09-22 19:57:51 +00:00
ERROR_LOG ( G3D , " BJUMP to illegal address %08x - ignoring! data=%06x " , target , op & 0x00FFFFFF ) ;
2018-08-25 17:32:32 +00:00
UpdateState ( GPUSTATE_ERROR ) ;
2013-08-08 06:27:29 +00:00
}
2014-04-16 15:12:21 +00:00
}
}
2013-04-01 06:02:46 +00:00
2014-04-16 15:12:21 +00:00
void GPUCommon : : Execute_Call ( u32 op , u32 diff ) {
2017-08-18 11:39:42 +00:00
PROFILE_THIS_SCOPE ( " gpu_call " ) ;
2013-04-01 06:02:46 +00:00
2014-05-05 00:58:44 +00:00
const u32 target = gstate_c . getRelativeAddress ( op & 0x00FFFFFC ) ;
2014-09-23 15:31:29 +00:00
if ( ! Memory : : IsValidAddress ( target ) ) {
2020-09-22 19:57:51 +00:00
ERROR_LOG ( G3D , " CALL to illegal address %08x - ignoring! data=%06x " , target , op & 0x00FFFFFF ) ;
2018-08-25 17:32:32 +00:00
UpdateState ( GPUSTATE_ERROR ) ;
2014-09-23 15:31:29 +00:00
return ;
}
2018-08-25 17:38:56 +00:00
DoExecuteCall ( target ) ;
}
void GPUCommon : : Execute_CallFast ( u32 op , u32 diff ) {
PROFILE_THIS_SCOPE ( " gpu_call " ) ;
const u32 target = gstate_c . getRelativeAddress ( op & 0x00FFFFFC ) ;
DoExecuteCall ( target ) ;
}
void GPUCommon : : DoExecuteCall ( u32 target ) {
// Saint Seiya needs correct support for relative calls.
const u32 retval = currentList - > pc + 4 ;
2014-04-16 15:12:21 +00:00
// Bone matrix optimization - many games will CALL a bone matrix (!).
2017-06-04 19:50:12 +00:00
// We don't optimize during recording - so the matrix data gets recorded.
if ( ! debugRecording_ & & ( Memory : : ReadUnchecked_U32 ( target ) > > 24 ) = = GE_CMD_BONEMATRIXDATA ) {
2014-04-16 15:12:21 +00:00
// Check for the end
if ( ( Memory : : ReadUnchecked_U32 ( target + 11 * 4 ) > > 24 ) = = GE_CMD_BONEMATRIXDATA & &
2022-01-01 23:26:00 +00:00
( Memory : : ReadUnchecked_U32 ( target + 12 * 4 ) > > 24 ) = = GE_CMD_RET & &
( gstate . boneMatrixNumber & 0x7F ) < = 96 - 12 ) {
2017-06-04 19:50:12 +00:00
// Yep, pretty sure this is a bone matrix call. Double check stall first.
if ( target > currentList - > stall | | target + 12 * 4 < currentList - > stall ) {
FastLoadBoneMatrix ( target ) ;
return ;
}
2013-09-24 10:59:34 +00:00
}
2014-04-16 15:12:21 +00:00
}
2013-11-14 13:02:31 +00:00
2014-04-16 15:12:21 +00:00
if ( currentList - > stackptr = = ARRAY_SIZE ( currentList - > stack ) ) {
2020-09-22 19:57:51 +00:00
ERROR_LOG ( G3D , " CALL: Stack full! " ) ;
2014-04-16 15:12:21 +00:00
} else {
auto & stackEntry = currentList - > stack [ currentList - > stackptr + + ] ;
stackEntry . pc = retval ;
stackEntry . offsetAddr = gstate_c . offsetAddr ;
// The base address is NOT saved/restored for a regular call.
UpdatePC ( currentList - > pc , target - 4 ) ;
currentList - > pc = target - 4 ; // pc will be increased after we return, counteract that
}
}
2013-04-01 06:02:46 +00:00
2014-04-16 15:12:21 +00:00
void GPUCommon : : Execute_Ret ( u32 op , u32 diff ) {
if ( currentList - > stackptr = = 0 ) {
2020-09-22 19:57:51 +00:00
DEBUG_LOG ( G3D , " RET: Stack empty! " ) ;
2014-04-16 15:12:21 +00:00
} else {
auto & stackEntry = currentList - > stack [ - - currentList - > stackptr ] ;
gstate_c . offsetAddr = stackEntry . offsetAddr ;
2014-05-05 00:58:44 +00:00
// We always clear the top (uncached/etc.) bits
const u32 target = stackEntry . pc & 0x0FFFFFFF ;
2014-04-16 15:12:21 +00:00
UpdatePC ( currentList - > pc , target - 4 ) ;
currentList - > pc = target - 4 ;
2017-01-28 10:39:34 +00:00
# ifdef _DEBUG
2014-04-16 15:12:21 +00:00
if ( ! Memory : : IsValidAddress ( currentList - > pc ) ) {
ERROR_LOG_REPORT ( G3D , " Invalid DL PC %08x on return " , currentList - > pc ) ;
UpdateState ( GPUSTATE_ERROR ) ;
2013-04-01 06:02:46 +00:00
}
2017-01-28 10:39:34 +00:00
# endif
2014-04-16 15:12:21 +00:00
}
}
2013-04-01 06:02:46 +00:00
2014-04-16 15:12:21 +00:00
void GPUCommon : : Execute_End ( u32 op , u32 diff ) {
2022-02-01 03:32:46 +00:00
if ( flushOnParams_ )
Flush ( ) ;
2022-01-31 22:57:56 +00:00
2014-04-16 15:12:21 +00:00
const u32 prev = Memory : : ReadUnchecked_U32 ( currentList - > pc - 4 ) ;
2017-02-27 20:57:46 +00:00
UpdatePC ( currentList - > pc , currentList - > pc ) ;
2014-04-29 07:08:43 +00:00
// Count in a few extra cycles on END.
cyclesExecuted + = 60 ;
2014-04-16 15:12:21 +00:00
switch ( prev > > 24 ) {
2013-04-01 06:02:46 +00:00
case GE_CMD_SIGNAL :
2014-04-16 15:12:21 +00:00
{
// TODO: see http://code.google.com/p/jpcsp/source/detail?r=2935#
SignalBehavior behaviour = static_cast < SignalBehavior > ( ( prev > > 16 ) & 0xFF ) ;
2014-04-22 15:07:10 +00:00
const int signal = prev & 0xFFFF ;
const int enddata = op & 0xFFFF ;
2014-04-16 15:12:21 +00:00
bool trigger = true ;
currentList - > subIntrToken = signal ;
switch ( behaviour ) {
case PSP_GE_SIGNAL_HANDLER_SUSPEND :
// Suspend the list, and call the signal handler. When it's done, resume.
// Before sdkver 0x02000010, listsync should return paused.
if ( sceKernelGetCompiledSdkVersion ( ) < = 0x02000010 )
currentList - > state = PSP_GE_DL_STATE_PAUSED ;
currentList - > signal = behaviour ;
DEBUG_LOG ( G3D , " Signal with wait. signal/end: %04x %04x " , signal , enddata ) ;
break ;
case PSP_GE_SIGNAL_HANDLER_CONTINUE :
// Resume the list right away, then call the handler.
currentList - > signal = behaviour ;
DEBUG_LOG ( G3D , " Signal without wait. signal/end: %04x %04x " , signal , enddata ) ;
break ;
case PSP_GE_SIGNAL_HANDLER_PAUSE :
// Pause the list instead of ending at the next FINISH.
// Call the handler with the PAUSE signal value at that FINISH.
// Technically, this ought to trigger an interrupt, but it won't do anything.
// But right now, signal is always reset by interrupts, so that causes pause to not work.
trigger = false ;
currentList - > signal = behaviour ;
DEBUG_LOG ( G3D , " Signal with Pause. signal/end: %04x %04x " , signal , enddata ) ;
break ;
case PSP_GE_SIGNAL_SYNC :
// Acts as a memory barrier, never calls any user code.
// Technically, this ought to trigger an interrupt, but it won't do anything.
// Triggering here can cause incorrect rescheduling, which breaks 3rd Birthday.
// However, this is likely a bug in how GE signal interrupts are handled.
trigger = false ;
currentList - > signal = behaviour ;
DEBUG_LOG ( G3D , " Signal with Sync. signal/end: %04x %04x " , signal , enddata ) ;
break ;
case PSP_GE_SIGNAL_JUMP :
2022-01-24 07:03:30 +00:00
case PSP_GE_SIGNAL_RJUMP :
case PSP_GE_SIGNAL_OJUMP :
2014-04-16 15:12:21 +00:00
{
2014-04-09 05:20:33 +00:00
trigger = false ;
2013-04-06 17:05:09 +00:00
currentList - > signal = behaviour ;
2014-04-16 15:12:21 +00:00
// pc will be increased after we return, counteract that.
2017-08-20 20:30:39 +00:00
u32 target = ( ( ( signal < < 16 ) | enddata ) & 0xFFFFFFFC ) - 4 ;
2022-01-24 07:03:30 +00:00
const char * targetType = " absolute " ;
if ( behaviour = = PSP_GE_SIGNAL_RJUMP ) {
target + = currentList - > pc - 4 ;
targetType = " relative " ;
} else if ( behaviour = = PSP_GE_SIGNAL_OJUMP ) {
target = gstate_c . getRelativeAddress ( target ) ;
targetType = " origin " ;
}
2014-04-16 15:12:21 +00:00
if ( ! Memory : : IsValidAddress ( target ) ) {
2022-01-24 07:03:30 +00:00
ERROR_LOG_REPORT ( G3D , " Signal with Jump (%s): bad address. signal/end: %04x %04x " , targetType , signal , enddata ) ;
2018-08-25 17:32:32 +00:00
UpdateState ( GPUSTATE_ERROR ) ;
2014-04-16 15:12:21 +00:00
} else {
UpdatePC ( currentList - > pc , target ) ;
currentList - > pc = target ;
2022-01-24 07:03:30 +00:00
DEBUG_LOG ( G3D , " Signal with Jump (%s). signal/end: %04x %04x " , targetType , signal , enddata ) ;
2013-04-06 17:30:12 +00:00
}
2013-04-01 06:02:46 +00:00
}
2014-04-16 15:12:21 +00:00
break ;
case PSP_GE_SIGNAL_CALL :
2022-01-24 07:03:30 +00:00
case PSP_GE_SIGNAL_RCALL :
case PSP_GE_SIGNAL_OCALL :
2014-04-16 15:12:21 +00:00
{
trigger = false ;
currentList - > signal = behaviour ;
// pc will be increased after we return, counteract that.
2017-08-20 20:30:39 +00:00
u32 target = ( ( ( signal < < 16 ) | enddata ) & 0xFFFFFFFC ) - 4 ;
2022-01-24 07:03:30 +00:00
const char * targetType = " absolute " ;
if ( behaviour = = PSP_GE_SIGNAL_RCALL ) {
target + = currentList - > pc - 4 ;
targetType = " relative " ;
} else if ( behaviour = = PSP_GE_SIGNAL_OCALL ) {
target = gstate_c . getRelativeAddress ( target ) ;
targetType = " origin " ;
}
2014-04-16 15:12:21 +00:00
if ( currentList - > stackptr = = ARRAY_SIZE ( currentList - > stack ) ) {
2022-01-24 07:03:30 +00:00
ERROR_LOG_REPORT ( G3D , " Signal with Call (%s): stack full. signal/end: %04x %04x " , targetType , signal , enddata ) ;
2014-04-16 15:12:21 +00:00
} else if ( ! Memory : : IsValidAddress ( target ) ) {
2022-01-24 07:03:30 +00:00
ERROR_LOG_REPORT ( G3D , " Signal with Call (%s): bad address. signal/end: %04x %04x " , targetType , signal , enddata ) ;
2018-08-25 17:32:32 +00:00
UpdateState ( GPUSTATE_ERROR ) ;
2014-04-16 15:12:21 +00:00
} else {
// TODO: This might save/restore other state...
auto & stackEntry = currentList - > stack [ currentList - > stackptr + + ] ;
stackEntry . pc = currentList - > pc ;
stackEntry . offsetAddr = gstate_c . offsetAddr ;
stackEntry . baseAddr = gstate . base ;
UpdatePC ( currentList - > pc , target ) ;
currentList - > pc = target ;
2022-01-24 07:03:30 +00:00
DEBUG_LOG ( G3D , " Signal with Call (%s). signal/end: %04x %04x " , targetType , signal , enddata ) ;
2013-09-01 17:16:29 +00:00
}
2013-04-05 07:21:47 +00:00
}
2014-04-16 15:12:21 +00:00
break ;
case PSP_GE_SIGNAL_RET :
{
trigger = false ;
currentList - > signal = behaviour ;
if ( currentList - > stackptr = = 0 ) {
ERROR_LOG_REPORT ( G3D , " Signal with Return: stack empty. signal/end: %04x %04x " , signal , enddata ) ;
} else {
// TODO: This might save/restore other state...
auto & stackEntry = currentList - > stack [ - - currentList - > stackptr ] ;
gstate_c . offsetAddr = stackEntry . offsetAddr ;
gstate . base = stackEntry . baseAddr ;
UpdatePC ( currentList - > pc , stackEntry . pc ) ;
currentList - > pc = stackEntry . pc ;
DEBUG_LOG ( G3D , " Signal with Return. signal/end: %04x %04x " , signal , enddata ) ;
2013-09-01 17:16:29 +00:00
}
2013-04-06 16:59:24 +00:00
}
break ;
default :
2014-04-16 15:12:21 +00:00
ERROR_LOG_REPORT ( G3D , " UNKNOWN Signal UNIMPLEMENTED %i ! signal/end: %04x %04x " , behaviour , signal , enddata ) ;
break ;
}
// TODO: Technically, jump/call/ret should generate an interrupt, but before the pc change maybe?
if ( currentList - > interruptsEnabled & & trigger ) {
if ( __GeTriggerInterrupt ( currentList - > id , currentList - > pc , startingTicks + cyclesExecuted ) ) {
2013-09-01 17:16:29 +00:00
currentList - > pendingInterrupt = true ;
2014-04-16 15:12:21 +00:00
UpdateState ( GPUSTATE_INTERRUPT ) ;
}
}
}
break ;
case GE_CMD_FINISH :
switch ( currentList - > signal ) {
case PSP_GE_SIGNAL_HANDLER_PAUSE :
currentList - > state = PSP_GE_DL_STATE_PAUSED ;
if ( currentList - > interruptsEnabled ) {
if ( __GeTriggerInterrupt ( currentList - > id , currentList - > pc , startingTicks + cyclesExecuted ) ) {
currentList - > pendingInterrupt = true ;
UpdateState ( GPUSTATE_INTERRUPT ) ;
2013-04-06 16:59:24 +00:00
}
2013-04-06 15:19:54 +00:00
}
2013-04-01 06:02:46 +00:00
break ;
2014-04-16 15:12:21 +00:00
case PSP_GE_SIGNAL_SYNC :
currentList - > signal = PSP_GE_SIGNAL_NONE ;
// TODO: Technically this should still cause an interrupt. Probably for memory sync.
break ;
2013-04-01 06:02:46 +00:00
default :
2022-09-06 05:45:34 +00:00
FlushImm ( ) ;
2014-04-16 15:12:21 +00:00
currentList - > subIntrToken = prev & 0xFFFF ;
UpdateState ( GPUSTATE_DONE ) ;
2021-01-31 08:28:42 +00:00
// Since we marked done, we have to restore the context now before the next list runs.
if ( currentList - > started & & currentList - > context . IsValid ( ) ) {
gstate . Restore ( currentList - > context ) ;
ReapplyGfxState ( ) ;
// Don't restore the context again.
currentList - > started = false ;
}
2014-04-16 15:12:21 +00:00
if ( currentList - > interruptsEnabled & & __GeTriggerInterrupt ( currentList - > id , currentList - > pc , startingTicks + cyclesExecuted ) ) {
currentList - > pendingInterrupt = true ;
} else {
currentList - > state = PSP_GE_DL_STATE_COMPLETED ;
currentList - > waitTicks = startingTicks + cyclesExecuted ;
busyTicks = std : : max ( busyTicks , currentList - > waitTicks ) ;
__GeTriggerSync ( GPU_SYNC_LIST , currentList - > id , currentList - > waitTicks ) ;
}
2013-04-01 06:02:46 +00:00
break ;
}
break ;
2014-04-16 15:12:21 +00:00
default :
DEBUG_LOG ( G3D , " Ah, not finished: %06x " , prev & 0xFFFFFF ) ;
break ;
2013-04-20 22:23:35 +00:00
}
2014-04-16 15:12:21 +00:00
}
2017-02-13 21:02:26 +00:00
void GPUCommon : : Execute_TexLevel ( u32 op , u32 diff ) {
2018-03-17 22:57:50 +00:00
// TODO: If you change the rules here, don't forget to update the inner interpreter in Execute_Prim.
if ( diff = = 0xFFFFFFFF )
return ;
2022-07-30 22:37:21 +00:00
2017-02-14 15:51:56 +00:00
gstate . texlevel ^ = diff ;
2022-07-25 16:51:08 +00:00
if ( diff & 0xFF0000 ) {
// Piggyback on this flag for 3D textures.
2022-07-31 08:43:12 +00:00
gstate_c . Dirty ( DIRTY_MIPBIAS ) ;
2022-07-25 16:51:08 +00:00
}
2022-07-30 22:37:21 +00:00
if ( gstate . getTexLevelMode ( ) ! = GE_TEXLEVEL_MODE_AUTO & & ( 0x00FF0000 & gstate . texlevel ) ! = 0 ) {
Flush ( ) ;
}
gstate . texlevel ^ = diff ;
2022-07-25 16:51:08 +00:00
2017-03-19 10:32:29 +00:00
gstate_c . Dirty ( DIRTY_TEXTURE_PARAMS | DIRTY_FRAGMENTSHADER_STATE ) ;
2017-02-13 21:02:26 +00:00
}
2017-11-13 17:57:59 +00:00
void GPUCommon : : Execute_TexSize0 ( u32 op , u32 diff ) {
// Render to texture may have overridden the width/height.
// Don't reset it unless the size is different / the texture has changed.
if ( diff | | gstate_c . IsDirty ( DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS ) ) {
gstate_c . curTextureWidth = gstate . getTextureWidth ( 0 ) ;
gstate_c . curTextureHeight = gstate . getTextureHeight ( 0 ) ;
gstate_c . Dirty ( DIRTY_UVSCALEOFFSET ) ;
// We will need to reset the texture now.
gstate_c . Dirty ( DIRTY_TEXTURE_PARAMS ) ;
}
}
2018-04-10 10:22:02 +00:00
void GPUCommon : : Execute_VertexType ( u32 op , u32 diff ) {
if ( diff )
gstate_c . Dirty ( DIRTY_VERTEXSHADER_STATE ) ;
if ( diff & ( GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK ) ) {
gstate_c . Dirty ( DIRTY_UVSCALEOFFSET ) ;
if ( diff & GE_VTYPE_THROUGH_MASK )
2018-09-22 04:55:11 +00:00
gstate_c . Dirty ( DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_CULLRANGE ) ;
2018-04-10 10:22:02 +00:00
}
}
2018-02-26 10:26:52 +00:00
void GPUCommon : : Execute_LoadClut ( u32 op , u32 diff ) {
gstate_c . Dirty ( DIRTY_TEXTURE_PARAMS ) ;
textureCache_ - > LoadClut ( gstate . getClutAddress ( ) , gstate . getClutLoadBytes ( ) ) ;
}
2018-04-10 10:22:02 +00:00
void GPUCommon : : Execute_VertexTypeSkinning ( u32 op , u32 diff ) {
2018-03-02 12:59:11 +00:00
// Don't flush when weight count changes.
if ( diff & ~ GE_VTYPE_WEIGHTCOUNT_MASK ) {
2017-11-13 17:57:59 +00:00
// Restore and flush
gstate . vertType ^ = diff ;
Flush ( ) ;
gstate . vertType ^ = diff ;
if ( diff & ( GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK ) )
gstate_c . Dirty ( DIRTY_UVSCALEOFFSET ) ;
2018-04-10 10:22:26 +00:00
// In this case, we may be doing weights and morphs.
// Update any bone matrix uniforms so it uses them correctly.
if ( ( op & GE_VTYPE_MORPHCOUNT_MASK ) ! = 0 ) {
gstate_c . Dirty ( gstate_c . deferredVertTypeDirty ) ;
gstate_c . deferredVertTypeDirty = 0 ;
}
2017-11-13 17:57:59 +00:00
gstate_c . Dirty ( DIRTY_VERTEXSHADER_STATE ) ;
}
if ( diff & GE_VTYPE_THROUGH_MASK )
2018-09-22 04:55:11 +00:00
gstate_c . Dirty ( DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_CULLRANGE ) ;
2017-11-13 17:57:59 +00:00
}
2022-08-18 07:38:17 +00:00
void GPUCommon : : CheckDepthUsage ( VirtualFramebuffer * vfb ) {
if ( ! gstate_c . usingDepth ) {
bool isClearingDepth = gstate . isModeClear ( ) & & gstate . isClearModeDepthMask ( ) ;
if ( ( gstate . isDepthTestEnabled ( ) | | isClearingDepth ) ) {
gstate_c . usingDepth = true ;
gstate_c . clearingDepth = isClearingDepth ;
vfb - > last_frame_depth_render = gpuStats . numFlips ;
if ( isClearingDepth | | gstate . isDepthWriteEnabled ( ) ) {
vfb - > last_frame_depth_updated = gpuStats . numFlips ;
}
2022-08-20 07:46:15 +00:00
framebufferManager_ - > SetDepthFrameBuffer ( isClearingDepth ) ;
2022-08-18 07:38:17 +00:00
}
}
}
2018-02-26 10:18:52 +00:00
void GPUCommon : : Execute_Prim ( u32 op , u32 diff ) {
// This drives all drawing. All other state we just buffer up, then we apply it only
// when it's time to draw. As most PSP games set state redundantly ALL THE TIME, this is a huge optimization.
PROFILE_THIS_SCOPE ( " execprim " ) ;
u32 data = op & 0xFFFFFF ;
u32 count = data & 0xFFFF ;
if ( count = = 0 )
return ;
2022-09-06 05:45:34 +00:00
FlushImm ( ) ;
2018-02-26 10:18:52 +00:00
// Upper bits are ignored.
GEPrimitiveType prim = static_cast < GEPrimitiveType > ( ( data > > 16 ) & 7 ) ;
SetDrawType ( DRAW_PRIM , prim ) ;
// Discard AA lines as we can't do anything that makes sense with these anyway. The SW plugin might, though.
if ( gstate . isAntiAliasEnabled ( ) ) {
2020-03-09 23:49:16 +00:00
// Heuristic derived from discussions in #6483 and #12588.
// Discard AA lines in Persona 3 Portable, DOA Paradise and Summon Night 5, while still keeping AA lines in Echochrome.
if ( ( prim = = GE_PRIM_LINE_STRIP | | prim = = GE_PRIM_LINES ) & & gstate . getTextureFunction ( ) = = GE_TEXFUNC_REPLACE )
2018-02-26 10:18:52 +00:00
return ;
}
2022-04-24 15:30:33 +00:00
// Update cached framebuffer format.
// We store it in the cache so it can be modified for blue-to-alpha, next.
gstate_c . framebufFormat = gstate . FrameBufFormat ( ) ;
2018-02-26 10:18:52 +00:00
if ( ! Memory : : IsValidAddress ( gstate_c . vertexAddr ) ) {
2020-09-24 21:00:48 +00:00
ERROR_LOG ( G3D , " Bad vertex address %08x! " , gstate_c . vertexAddr ) ;
2018-02-26 10:18:52 +00:00
return ;
}
2022-04-30 16:13:24 +00:00
// See the documentation for gstate_c.blueToAlpha.
bool blueToAlpha = false ;
if ( PSP_CoreParameter ( ) . compat . flags ( ) . BlueToAlpha ) {
2022-09-05 07:42:38 +00:00
if ( gstate_c . framebufFormat = = GEBufferFormat : : GE_FORMAT_565 & & gstate . getColorMask ( ) = = 0x0FFFFF & & ! gstate . isLogicOpEnabled ( ) ) {
2022-04-30 16:13:24 +00:00
blueToAlpha = true ;
2022-08-23 11:09:29 +00:00
gstate_c . framebufFormat = GEBufferFormat : : GE_FORMAT_4444 ;
2022-04-30 16:13:24 +00:00
}
if ( blueToAlpha ! = gstate_c . blueToAlpha ) {
gstate_c . blueToAlpha = blueToAlpha ;
2022-08-23 11:09:29 +00:00
gstate_c . Dirty ( DIRTY_FRAMEBUF | DIRTY_FRAGMENTSHADER_STATE | DIRTY_BLEND_STATE ) ;
2022-04-30 16:13:24 +00:00
}
}
2022-08-31 09:40:10 +00:00
if ( PSP_CoreParameter ( ) . compat . flags ( ) . SplitFramebufferMargin ) {
switch ( gstate . vertType & 0xFFFFFF ) {
case 0x00800102 : // through, u16 uv, u16 pos (used for the framebuffer effect in-game)
case 0x0080011c : // through, 8888 color, s16 pos (used for clearing in the margin of the title screen)
case 0x00000183 : // float uv, float pos (used for drawing in the margin of the title screen)
// Need to re-check the framebuffer every one of these draws, to update the split if needed.
gstate_c . Dirty ( DIRTY_FRAMEBUF ) ;
}
2022-08-30 23:09:23 +00:00
}
2022-04-30 16:13:24 +00:00
// This also makes skipping drawing very effective.
VirtualFramebuffer * vfb = framebufferManager_ - > SetRenderFrameBuffer ( gstate_c . IsDirty ( DIRTY_FRAMEBUF ) , gstate_c . skipDrawReason ) ;
if ( blueToAlpha ) {
vfb - > usageFlags | = FB_USAGE_BLUE_TO_ALPHA ;
}
2022-05-22 19:24:03 +00:00
// Must check this after SetRenderFrameBuffer so we know SKIPDRAW_NON_DISPLAYED_FB.
if ( gstate_c . skipDrawReason & ( SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB ) ) {
// Rough estimate, not sure what's correct.
cyclesExecuted + = EstimatePerVertexCost ( ) * count ;
if ( gstate . isModeClear ( ) ) {
gpuStats . numClears + + ;
}
return ;
}
2022-08-18 07:38:17 +00:00
CheckDepthUsage ( vfb ) ;
2022-08-01 21:55:58 +00:00
2022-07-20 10:40:22 +00:00
const void * verts = Memory : : GetPointerUnchecked ( gstate_c . vertexAddr ) ;
const void * inds = nullptr ;
2018-02-26 10:18:52 +00:00
u32 vertexType = gstate . vertType ;
2018-02-27 23:01:02 +00:00
if ( ( vertexType & GE_VTYPE_IDX_MASK ) ! = GE_VTYPE_IDX_NONE ) {
2018-02-26 10:18:52 +00:00
u32 indexAddr = gstate_c . indexAddr ;
if ( ! Memory : : IsValidAddress ( indexAddr ) ) {
2020-09-24 21:00:48 +00:00
ERROR_LOG ( G3D , " Bad index address %08x! " , indexAddr ) ;
2018-02-26 10:18:52 +00:00
return ;
}
inds = Memory : : GetPointerUnchecked ( indexAddr ) ;
}
if ( gstate_c . dirty & DIRTY_VERTEXSHADER_STATE ) {
vertexCost_ = EstimatePerVertexCost ( ) ;
}
int bytesRead = 0 ;
UpdateUVScaleOffset ( ) ;
2018-04-27 08:33:35 +00:00
// cull mode
2018-08-23 04:46:09 +00:00
int cullMode = gstate . getCullMode ( ) ;
2018-04-27 08:33:35 +00:00
2018-03-05 11:24:02 +00:00
uint32_t vertTypeID = GetVertTypeID ( vertexType , gstate . getUVGenMode ( ) ) ;
2018-04-27 08:33:35 +00:00
drawEngineCommon_ - > SubmitPrim ( verts , inds , prim , count , vertTypeID , cullMode , & bytesRead ) ;
2018-02-26 10:18:52 +00:00
// After drawing, we advance the vertexAddr (when non indexed) or indexAddr (when indexed).
// Some games rely on this, they don't bother reloading VADDR and IADDR.
// The VADDR/IADDR registers are NOT updated.
AdvanceVerts ( vertexType , count , bytesRead ) ;
2018-02-27 23:01:02 +00:00
int totalVertCount = count ;
// PRIMs are often followed by more PRIMs. Save some work and submit them immediately.
2021-02-19 06:25:24 +00:00
const u32_le * src = ( const u32_le * ) Memory : : GetPointerUnchecked ( currentList - > pc + 4 ) ;
const u32_le * stall = currentList - > stall ? ( const u32_le * ) Memory : : GetPointerUnchecked ( currentList - > stall ) : 0 ;
2018-02-27 23:01:02 +00:00
int cmdCount = 0 ;
// Optimized submission of sequences of PRIM. Allows us to avoid going through all the mess
// above for each one. This can be expanded to support additional games that intersperse
2020-09-24 21:00:48 +00:00
// PRIM commands with other commands. A special case is Earth Defence Force 2 that changes culling mode
// between each prim, we just change the triangle winding right here to still be able to join draw calls.
2018-02-28 00:26:43 +00:00
2018-04-10 12:37:32 +00:00
uint32_t vtypeCheckMask = ~ GE_VTYPE_WEIGHTCOUNT_MASK ;
if ( ! g_Config . bSoftwareSkinning )
vtypeCheckMask = 0xFFFFFFFF ;
2022-02-06 08:16:35 +00:00
if ( debugRecording_ )
2018-04-10 14:28:06 +00:00
goto bail ;
2018-02-27 23:01:02 +00:00
while ( src ! = stall ) {
uint32_t data = * src ;
switch ( data > > 24 ) {
case GE_CMD_PRIM :
{
u32 count = data & 0xFFFF ;
if ( count = = 0 ) {
2018-03-05 11:49:55 +00:00
// Ignore.
2018-02-27 23:01:02 +00:00
break ;
}
GEPrimitiveType newPrim = static_cast < GEPrimitiveType > ( ( data > > 16 ) & 7 ) ;
2018-11-30 15:22:28 +00:00
SetDrawType ( DRAW_PRIM , newPrim ) ;
2018-02-27 23:01:02 +00:00
// TODO: more efficient updating of verts/inds
verts = Memory : : GetPointerUnchecked ( gstate_c . vertexAddr ) ;
2018-11-30 15:22:28 +00:00
inds = nullptr ;
2018-02-27 23:01:02 +00:00
if ( ( vertexType & GE_VTYPE_IDX_MASK ) ! = GE_VTYPE_IDX_NONE ) {
2018-03-05 11:49:55 +00:00
inds = Memory : : GetPointerUnchecked ( gstate_c . indexAddr ) ;
2018-02-27 23:01:02 +00:00
}
2018-04-27 08:33:35 +00:00
drawEngineCommon_ - > SubmitPrim ( verts , inds , newPrim , count , vertTypeID , cullMode , & bytesRead ) ;
2018-02-27 23:01:02 +00:00
AdvanceVerts ( vertexType , count , bytesRead ) ;
totalVertCount + = count ;
break ;
}
case GE_CMD_VERTEXTYPE :
2018-03-05 11:49:55 +00:00
{
uint32_t diff = data ^ vertexType ;
// don't mask upper bits, vertexType is unmasked
2018-04-10 12:37:32 +00:00
if ( diff & vtypeCheckMask ) {
2018-02-27 23:01:02 +00:00
goto bail ;
2018-03-05 11:49:55 +00:00
} else {
vertexType = data ;
vertTypeID = GetVertTypeID ( vertexType , gstate . getUVGenMode ( ) ) ;
2018-02-27 23:01:02 +00:00
}
break ;
2018-03-05 11:49:55 +00:00
}
2018-02-27 23:01:02 +00:00
case GE_CMD_VADDR :
2020-09-24 21:00:48 +00:00
gstate . cmdmem [ GE_CMD_VADDR ] = data ;
2018-02-27 23:01:02 +00:00
gstate_c . vertexAddr = gstate_c . getRelativeAddress ( data & 0x00FFFFFF ) ;
break ;
2018-11-30 03:28:10 +00:00
case GE_CMD_IADDR :
2020-09-24 21:00:48 +00:00
gstate . cmdmem [ GE_CMD_IADDR ] = data ;
2018-11-30 03:28:10 +00:00
gstate_c . indexAddr = gstate_c . getRelativeAddress ( data & 0x00FFFFFF ) ;
break ;
2018-03-05 11:49:55 +00:00
case GE_CMD_OFFSETADDR :
gstate . cmdmem [ GE_CMD_OFFSETADDR ] = data ;
gstate_c . offsetAddr = data < < 8 ;
break ;
2018-02-27 23:01:02 +00:00
case GE_CMD_BASE :
gstate . cmdmem [ GE_CMD_BASE ] = data ;
break ;
2018-08-23 04:46:09 +00:00
case GE_CMD_CULLFACEENABLE :
// Earth Defence Force 2
if ( gstate . cmdmem [ GE_CMD_CULLFACEENABLE ] ! = data ) {
goto bail ;
}
break ;
2018-04-27 08:33:35 +00:00
case GE_CMD_CULL :
2018-08-23 04:46:09 +00:00
// flip face by indices for triangles
2018-04-27 08:33:35 +00:00
cullMode = data & 1 ;
break ;
2018-11-30 03:28:10 +00:00
case GE_CMD_TEXFLUSH :
2018-02-28 22:22:33 +00:00
case GE_CMD_NOP :
2018-02-27 23:01:02 +00:00
case GE_CMD_NOP_FF :
2018-11-30 03:28:10 +00:00
gstate . cmdmem [ data > > 24 ] = data ;
2018-02-27 23:01:02 +00:00
break ;
2018-03-05 11:49:55 +00:00
case GE_CMD_BONEMATRIXNUMBER :
gstate . cmdmem [ GE_CMD_BONEMATRIXNUMBER ] = data ;
break ;
case GE_CMD_TEXSCALEU :
gstate . cmdmem [ GE_CMD_TEXSCALEU ] = data ;
gstate_c . uv . uScale = getFloat24 ( data ) ;
break ;
case GE_CMD_TEXSCALEV :
gstate . cmdmem [ GE_CMD_TEXSCALEV ] = data ;
gstate_c . uv . vScale = getFloat24 ( data ) ;
break ;
2018-04-27 08:33:35 +00:00
case GE_CMD_TEXOFFSETU :
gstate . cmdmem [ GE_CMD_TEXOFFSETU ] = data ;
gstate_c . uv . uOff = getFloat24 ( data ) ;
break ;
case GE_CMD_TEXOFFSETV :
gstate . cmdmem [ GE_CMD_TEXOFFSETV ] = data ;
gstate_c . uv . vOff = getFloat24 ( data ) ;
break ;
2018-03-17 22:57:50 +00:00
case GE_CMD_TEXLEVEL :
// Same Gran Turismo hack from Execute_TexLevel
if ( ( data & 3 ) ! = GE_TEXLEVEL_MODE_AUTO & & ( 0x00FF0000 & data ) ! = 0 ) {
goto bail ;
}
gstate . cmdmem [ GE_CMD_TEXLEVEL ] = data ;
break ;
2018-03-05 11:49:55 +00:00
case GE_CMD_CALL :
{
// A bone matrix probably. If not we bail.
const u32 target = gstate_c . getRelativeAddress ( data & 0x00FFFFFC ) ;
if ( ( Memory : : ReadUnchecked_U32 ( target ) > > 24 ) = = GE_CMD_BONEMATRIXDATA & &
( Memory : : ReadUnchecked_U32 ( target + 11 * 4 ) > > 24 ) = = GE_CMD_BONEMATRIXDATA & &
( Memory : : ReadUnchecked_U32 ( target + 12 * 4 ) > > 24 ) = = GE_CMD_RET & &
2022-01-01 23:26:00 +00:00
( target > currentList - > stall | | target + 12 * 4 < currentList - > stall ) & &
( gstate . boneMatrixNumber & 0x7F ) < = 96 - 12 ) {
2018-03-05 11:49:55 +00:00
FastLoadBoneMatrix ( target ) ;
} else {
goto bail ;
}
break ;
}
2018-11-30 03:28:10 +00:00
case GE_CMD_TEXBUFWIDTH0 :
case GE_CMD_TEXADDR0 :
if ( data ! = gstate . cmdmem [ data > > 24 ] )
goto bail ;
break ;
2018-02-27 23:01:02 +00:00
default :
// All other commands might need a flush or something, stop this inner loop.
goto bail ;
}
cmdCount + + ;
src + + ;
}
bail :
2018-03-05 11:49:55 +00:00
gstate . cmdmem [ GE_CMD_VERTEXTYPE ] = vertexType ;
2018-02-27 23:01:02 +00:00
// Skip over the commands we just read out manually.
if ( cmdCount > 0 ) {
UpdatePC ( currentList - > pc , currentList - > pc + cmdCount * 4 ) ;
currentList - > pc + = cmdCount * 4 ;
2018-04-27 13:49:43 +00:00
// flush back cull mode
2018-08-23 04:46:09 +00:00
if ( cullMode ! = gstate . getCullMode ( ) ) {
2018-11-26 00:56:39 +00:00
// We rewrote everything to the old cull mode, so flush first.
drawEngineCommon_ - > DispatchFlush ( ) ;
// Now update things for next time.
2018-04-27 13:49:43 +00:00
gstate . cmdmem [ GE_CMD_CULL ] ^ = 1 ;
gstate_c . Dirty ( DIRTY_RASTER_STATE ) ;
}
2018-02-27 23:01:02 +00:00
}
gpuStats . vertexGPUCycles + = vertexCost_ * totalVertCount ;
cyclesExecuted + = vertexCost_ * totalVertCount ;
2018-02-26 10:18:52 +00:00
}
2017-01-21 19:42:40 +00:00
void GPUCommon : : Execute_Bezier ( u32 op , u32 diff ) {
2017-11-13 09:42:05 +00:00
// We don't dirty on normal changes anymore as we prescale, but it's needed for splines/bezier.
gstate_c . Dirty ( DIRTY_UVSCALEOFFSET ) ;
2022-04-24 21:23:54 +00:00
gstate_c . framebufFormat = gstate . FrameBufFormat ( ) ;
2017-01-21 19:42:40 +00:00
// This also make skipping drawing very effective.
2022-08-18 07:38:17 +00:00
VirtualFramebuffer * vfb = framebufferManager_ - > SetRenderFrameBuffer ( gstate_c . IsDirty ( DIRTY_FRAMEBUF ) , gstate_c . skipDrawReason ) ;
2017-01-21 19:42:40 +00:00
if ( gstate_c . skipDrawReason & ( SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB ) ) {
// TODO: Should this eat some cycles? Probably yes. Not sure if important.
return ;
}
2022-08-18 07:38:17 +00:00
CheckDepthUsage ( vfb ) ;
2017-01-21 19:42:40 +00:00
if ( ! Memory : : IsValidAddress ( gstate_c . vertexAddr ) ) {
ERROR_LOG_REPORT ( G3D , " Bad vertex address %08x! " , gstate_c . vertexAddr ) ;
return ;
}
2022-07-20 10:40:22 +00:00
const void * control_points = Memory : : GetPointerUnchecked ( gstate_c . vertexAddr ) ;
const void * indices = NULL ;
2017-01-21 19:42:40 +00:00
if ( ( gstate . vertType & GE_VTYPE_IDX_MASK ) ! = GE_VTYPE_IDX_NONE ) {
if ( ! Memory : : IsValidAddress ( gstate_c . indexAddr ) ) {
ERROR_LOG_REPORT ( G3D , " Bad index address %08x! " , gstate_c . indexAddr ) ;
return ;
}
indices = Memory : : GetPointerUnchecked ( gstate_c . indexAddr ) ;
}
2018-04-10 10:22:02 +00:00
if ( vertTypeIsSkinningEnabled ( gstate . vertType ) ) {
DEBUG_LOG_REPORT ( G3D , " Unusual bezier/spline vtype: %08x, morph: %d, bones: %d " , gstate . vertType , ( gstate . vertType & GE_VTYPE_MORPHCOUNT_MASK ) > > GE_VTYPE_MORPHCOUNT_SHIFT , vertTypeGetNumBoneWeights ( gstate . vertType ) ) ;
}
2021-09-07 21:52:06 +00:00
// Can't flush after setting gstate_c.submitType below since it'll be a mess - it must be done already.
drawEngineCommon_ - > DispatchFlush ( ) ;
2018-11-13 08:12:41 +00:00
Spline : : BezierSurface surface ;
surface . tess_u = gstate . getPatchDivisionU ( ) ;
surface . tess_v = gstate . getPatchDivisionV ( ) ;
surface . num_points_u = op & 0xFF ;
surface . num_points_v = ( op > > 8 ) & 0xFF ;
surface . num_patches_u = ( surface . num_points_u - 1 ) / 3 ;
surface . num_patches_v = ( surface . num_points_v - 1 ) / 3 ;
surface . primType = gstate . getPatchPrimitiveType ( ) ;
surface . patchFacing = gstate . patchfacing & 1 ;
SetDrawType ( DRAW_BEZIER , PatchPrimToPrim ( surface . primType ) ) ;
2020-04-04 18:14:32 +00:00
if ( drawEngineCommon_ - > CanUseHardwareTessellation ( surface . primType ) ) {
2017-11-13 09:42:05 +00:00
gstate_c . Dirty ( DIRTY_VERTEXSHADER_STATE ) ;
2020-12-13 15:04:16 +00:00
gstate_c . submitType = SubmitType : : HW_BEZIER ;
2018-11-13 08:12:41 +00:00
if ( gstate_c . spline_num_points_u ! = surface . num_points_u ) {
2017-11-13 09:42:05 +00:00
gstate_c . Dirty ( DIRTY_BEZIERSPLINE ) ;
2018-11-13 08:12:41 +00:00
gstate_c . spline_num_points_u = surface . num_points_u ;
2017-11-13 09:42:05 +00:00
}
2020-12-13 15:04:16 +00:00
} else {
gstate_c . submitType = SubmitType : : BEZIER ;
2017-11-13 09:42:05 +00:00
}
2017-01-21 19:42:40 +00:00
int bytesRead = 0 ;
2017-11-13 09:42:05 +00:00
UpdateUVScaleOffset ( ) ;
2018-11-13 08:12:41 +00:00
drawEngineCommon_ - > SubmitCurve ( control_points , indices , surface , gstate . vertType , & bytesRead , " bezier " ) ;
2017-01-21 19:42:40 +00:00
2020-12-13 15:04:16 +00:00
gstate_c . Dirty ( DIRTY_VERTEXSHADER_STATE ) ;
gstate_c . submitType = SubmitType : : DRAW ;
2017-11-13 09:42:05 +00:00
2017-01-21 19:42:40 +00:00
// After drawing, we advance pointers - see SubmitPrim which does the same.
2018-11-13 08:12:41 +00:00
int count = surface . num_points_u * surface . num_points_v ;
2017-01-21 19:42:40 +00:00
AdvanceVerts ( gstate . vertType , count , bytesRead ) ;
}
void GPUCommon : : Execute_Spline ( u32 op , u32 diff ) {
2017-11-13 09:42:05 +00:00
// We don't dirty on normal changes anymore as we prescale, but it's needed for splines/bezier.
gstate_c . Dirty ( DIRTY_UVSCALEOFFSET ) ;
2022-04-24 21:23:54 +00:00
gstate_c . framebufFormat = gstate . FrameBufFormat ( ) ;
2017-01-21 19:42:40 +00:00
// This also make skipping drawing very effective.
2022-08-18 07:38:17 +00:00
VirtualFramebuffer * vfb = framebufferManager_ - > SetRenderFrameBuffer ( gstate_c . IsDirty ( DIRTY_FRAMEBUF ) , gstate_c . skipDrawReason ) ;
2017-01-21 19:42:40 +00:00
if ( gstate_c . skipDrawReason & ( SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB ) ) {
// TODO: Should this eat some cycles? Probably yes. Not sure if important.
return ;
}
2022-08-18 07:38:17 +00:00
CheckDepthUsage ( vfb ) ;
2017-01-21 19:42:40 +00:00
if ( ! Memory : : IsValidAddress ( gstate_c . vertexAddr ) ) {
ERROR_LOG_REPORT ( G3D , " Bad vertex address %08x! " , gstate_c . vertexAddr ) ;
return ;
}
2022-07-20 10:40:22 +00:00
const void * control_points = Memory : : GetPointerUnchecked ( gstate_c . vertexAddr ) ;
const void * indices = NULL ;
2017-01-21 19:42:40 +00:00
if ( ( gstate . vertType & GE_VTYPE_IDX_MASK ) ! = GE_VTYPE_IDX_NONE ) {
if ( ! Memory : : IsValidAddress ( gstate_c . indexAddr ) ) {
ERROR_LOG_REPORT ( G3D , " Bad index address %08x! " , gstate_c . indexAddr ) ;
return ;
}
indices = Memory : : GetPointerUnchecked ( gstate_c . indexAddr ) ;
}
2018-04-10 10:22:02 +00:00
if ( vertTypeIsSkinningEnabled ( gstate . vertType ) ) {
DEBUG_LOG_REPORT ( G3D , " Unusual bezier/spline vtype: %08x, morph: %d, bones: %d " , gstate . vertType , ( gstate . vertType & GE_VTYPE_MORPHCOUNT_MASK ) > > GE_VTYPE_MORPHCOUNT_SHIFT , vertTypeGetNumBoneWeights ( gstate . vertType ) ) ;
}
2021-09-07 21:52:06 +00:00
// Can't flush after setting gstate_c.submitType below since it'll be a mess - it must be done already.
drawEngineCommon_ - > DispatchFlush ( ) ;
2018-11-13 08:12:41 +00:00
Spline : : SplineSurface surface ;
surface . tess_u = gstate . getPatchDivisionU ( ) ;
surface . tess_v = gstate . getPatchDivisionV ( ) ;
surface . type_u = ( op > > 16 ) & 0x3 ;
surface . type_v = ( op > > 18 ) & 0x3 ;
surface . num_points_u = op & 0xFF ;
surface . num_points_v = ( op > > 8 ) & 0xFF ;
surface . num_patches_u = surface . num_points_u - 3 ;
surface . num_patches_v = surface . num_points_v - 3 ;
surface . primType = gstate . getPatchPrimitiveType ( ) ;
surface . patchFacing = gstate . patchfacing & 1 ;
SetDrawType ( DRAW_SPLINE , PatchPrimToPrim ( surface . primType ) ) ;
2017-11-13 09:42:05 +00:00
2020-04-04 18:14:32 +00:00
if ( drawEngineCommon_ - > CanUseHardwareTessellation ( surface . primType ) ) {
2017-11-13 09:42:05 +00:00
gstate_c . Dirty ( DIRTY_VERTEXSHADER_STATE ) ;
2020-12-13 15:04:16 +00:00
gstate_c . submitType = SubmitType : : HW_SPLINE ;
2018-11-13 08:12:41 +00:00
if ( gstate_c . spline_num_points_u ! = surface . num_points_u ) {
2017-11-13 09:42:05 +00:00
gstate_c . Dirty ( DIRTY_BEZIERSPLINE ) ;
2018-11-13 08:12:41 +00:00
gstate_c . spline_num_points_u = surface . num_points_u ;
2017-11-13 09:42:05 +00:00
}
2020-12-13 15:04:16 +00:00
} else {
gstate_c . submitType = SubmitType : : SPLINE ;
2017-11-13 09:42:05 +00:00
}
2017-01-21 19:42:40 +00:00
int bytesRead = 0 ;
2017-11-13 09:42:05 +00:00
UpdateUVScaleOffset ( ) ;
2018-11-13 08:12:41 +00:00
drawEngineCommon_ - > SubmitCurve ( control_points , indices , surface , gstate . vertType , & bytesRead , " spline " ) ;
2017-01-21 19:42:40 +00:00
2020-12-13 15:04:16 +00:00
gstate_c . Dirty ( DIRTY_VERTEXSHADER_STATE ) ;
gstate_c . submitType = SubmitType : : DRAW ;
2017-11-13 09:42:05 +00:00
2017-01-21 19:42:40 +00:00
// After drawing, we advance pointers - see SubmitPrim which does the same.
2018-11-13 08:12:41 +00:00
int count = surface . num_points_u * surface . num_points_v ;
2017-01-21 19:42:40 +00:00
AdvanceVerts ( gstate . vertType , count , bytesRead ) ;
}
void GPUCommon : : Execute_BoundingBox ( u32 op , u32 diff ) {
// Just resetting, nothing to check bounds for.
2017-11-17 12:36:48 +00:00
const u32 count = op & 0xFFFFFF ;
if ( count = = 0 ) {
2017-08-20 20:30:10 +00:00
currentList - > bboxResult = false ;
2017-01-21 19:42:40 +00:00
return ;
}
2017-11-17 12:36:48 +00:00
if ( ( ( count & 7 ) = = 0 ) & & count < = 64 ) { // Sanity check
2022-07-20 10:40:22 +00:00
const void * control_points = Memory : : GetPointer ( gstate_c . vertexAddr ) ;
2017-11-17 12:36:48 +00:00
if ( ! control_points ) {
2022-01-23 06:41:41 +00:00
ERROR_LOG_REPORT_ONCE ( boundingbox , G3D , " Invalid verts in bounding box check " ) ;
currentList - > bboxResult = true ;
2017-11-17 12:36:48 +00:00
return ;
}
2017-01-21 19:42:40 +00:00
if ( gstate . vertType & GE_VTYPE_IDX_MASK ) {
ERROR_LOG_REPORT_ONCE ( boundingbox , G3D , " Indexed bounding box data not supported. " ) ;
// Data seems invalid. Let's assume the box test passed.
currentList - > bboxResult = true ;
return ;
}
// Test if the bounding box is within the drawing region.
2017-11-17 12:36:48 +00:00
int bytesRead ;
currentList - > bboxResult = drawEngineCommon_ - > TestBoundingBox ( control_points , count , gstate . vertType , & bytesRead ) ;
AdvanceVerts ( gstate . vertType , count , bytesRead ) ;
2017-01-21 19:42:40 +00:00
} else {
2017-11-17 12:36:48 +00:00
ERROR_LOG_REPORT_ONCE ( boundingbox , G3D , " Bad bounding box data: %06x " , count ) ;
2017-01-21 19:42:40 +00:00
// Data seems invalid. Let's assume the box test passed.
currentList - > bboxResult = true ;
}
}
2017-01-23 15:57:16 +00:00
void GPUCommon : : Execute_BlockTransferStart ( u32 op , u32 diff ) {
2017-08-17 11:05:13 +00:00
Flush ( ) ;
2022-01-31 22:57:56 +00:00
PROFILE_THIS_SCOPE ( " block " ) ; // don't include the flush in the profile, would be misleading.
2022-04-24 21:23:54 +00:00
gstate_c . framebufFormat = gstate . FrameBufFormat ( ) ;
2017-01-23 15:57:16 +00:00
// and take appropriate action. This is a block transfer between RAM and VRAM, or vice versa.
// Can we skip this on SkipDraw?
DoBlockTransfer ( gstate_c . skipDrawReason ) ;
}
2017-01-23 19:56:25 +00:00
void GPUCommon : : Execute_WorldMtxNum ( u32 op , u32 diff ) {
// This is almost always followed by GE_CMD_WORLDMATRIXDATA.
const u32_le * src = ( const u32_le * ) Memory : : GetPointerUnchecked ( currentList - > pc + 4 ) ;
u32 * dst = ( u32 * ) ( gstate . worldMatrix + ( op & 0xF ) ) ;
const int end = 12 - ( op & 0xF ) ;
int i = 0 ;
2017-06-04 19:50:12 +00:00
// We must record the individual data commands while debugRecording_.
bool fastLoad = ! debugRecording_ ;
2017-08-18 11:39:42 +00:00
// Stalling in the middle of a matrix would be stupid, I doubt this check is necessary.
2017-06-04 19:50:12 +00:00
if ( currentList - > pc < currentList - > stall & & currentList - > pc + end * 4 > = currentList - > stall ) {
fastLoad = false ;
}
if ( fastLoad ) {
while ( ( src [ i ] > > 24 ) = = GE_CMD_WORLDMATRIXDATA ) {
const u32 newVal = src [ i ] < < 8 ;
if ( dst [ i ] ! = newVal ) {
Flush ( ) ;
dst [ i ] = newVal ;
gstate_c . Dirty ( DIRTY_WORLDMATRIX ) ;
}
if ( + + i > = end ) {
break ;
}
2017-01-23 19:56:25 +00:00
}
}
2017-06-04 19:50:12 +00:00
const int count = i ;
2017-01-23 19:56:25 +00:00
gstate . worldmtxnum = ( GE_CMD_WORLDMATRIXNUMBER < < 24 ) | ( ( op + count ) & 0xF ) ;
// Skip over the loaded data, it's done now.
UpdatePC ( currentList - > pc , currentList - > pc + count * 4 ) ;
currentList - > pc + = count * 4 ;
}
void GPUCommon : : Execute_WorldMtxData ( u32 op , u32 diff ) {
// Note: it's uncommon to get here now, see above.
int num = gstate . worldmtxnum & 0xF ;
u32 newVal = op < < 8 ;
if ( num < 12 & & newVal ! = ( ( const u32 * ) gstate . worldMatrix ) [ num ] ) {
Flush ( ) ;
( ( u32 * ) gstate . worldMatrix ) [ num ] = newVal ;
2017-01-24 08:41:38 +00:00
gstate_c . Dirty ( DIRTY_WORLDMATRIX ) ;
2017-01-23 19:56:25 +00:00
}
num + + ;
gstate . worldmtxnum = ( GE_CMD_WORLDMATRIXNUMBER < < 24 ) | ( num & 0xF ) ;
2022-09-06 02:38:54 +00:00
gstate . worldmtxdata = GE_CMD_WORLDMATRIXDATA < < 24 ;
2017-01-23 19:56:25 +00:00
}
void GPUCommon : : Execute_ViewMtxNum ( u32 op , u32 diff ) {
// This is almost always followed by GE_CMD_VIEWMATRIXDATA.
const u32_le * src = ( const u32_le * ) Memory : : GetPointerUnchecked ( currentList - > pc + 4 ) ;
u32 * dst = ( u32 * ) ( gstate . viewMatrix + ( op & 0xF ) ) ;
const int end = 12 - ( op & 0xF ) ;
int i = 0 ;
2017-06-04 19:50:12 +00:00
bool fastLoad = ! debugRecording_ ;
if ( currentList - > pc < currentList - > stall & & currentList - > pc + end * 4 > = currentList - > stall ) {
fastLoad = false ;
}
if ( fastLoad ) {
while ( ( src [ i ] > > 24 ) = = GE_CMD_VIEWMATRIXDATA ) {
const u32 newVal = src [ i ] < < 8 ;
if ( dst [ i ] ! = newVal ) {
Flush ( ) ;
dst [ i ] = newVal ;
gstate_c . Dirty ( DIRTY_VIEWMATRIX ) ;
}
if ( + + i > = end ) {
break ;
}
2017-01-23 19:56:25 +00:00
}
}
2017-06-04 19:50:12 +00:00
const int count = i ;
2017-01-23 19:56:25 +00:00
gstate . viewmtxnum = ( GE_CMD_VIEWMATRIXNUMBER < < 24 ) | ( ( op + count ) & 0xF ) ;
// Skip over the loaded data, it's done now.
UpdatePC ( currentList - > pc , currentList - > pc + count * 4 ) ;
currentList - > pc + = count * 4 ;
}
void GPUCommon : : Execute_ViewMtxData ( u32 op , u32 diff ) {
// Note: it's uncommon to get here now, see above.
int num = gstate . viewmtxnum & 0xF ;
u32 newVal = op < < 8 ;
if ( num < 12 & & newVal ! = ( ( const u32 * ) gstate . viewMatrix ) [ num ] ) {
Flush ( ) ;
( ( u32 * ) gstate . viewMatrix ) [ num ] = newVal ;
2017-01-24 08:41:38 +00:00
gstate_c . Dirty ( DIRTY_VIEWMATRIX ) ;
2017-01-23 19:56:25 +00:00
}
num + + ;
gstate . viewmtxnum = ( GE_CMD_VIEWMATRIXNUMBER < < 24 ) | ( num & 0xF ) ;
2022-09-06 02:38:54 +00:00
gstate . viewmtxdata = GE_CMD_VIEWMATRIXDATA < < 24 ;
2017-01-23 19:56:25 +00:00
}
void GPUCommon : : Execute_ProjMtxNum ( u32 op , u32 diff ) {
// This is almost always followed by GE_CMD_PROJMATRIXDATA.
const u32_le * src = ( const u32_le * ) Memory : : GetPointerUnchecked ( currentList - > pc + 4 ) ;
u32 * dst = ( u32 * ) ( gstate . projMatrix + ( op & 0xF ) ) ;
const int end = 16 - ( op & 0xF ) ;
int i = 0 ;
2017-06-04 19:50:12 +00:00
bool fastLoad = ! debugRecording_ ;
if ( currentList - > pc < currentList - > stall & & currentList - > pc + end * 4 > = currentList - > stall ) {
fastLoad = false ;
}
if ( fastLoad ) {
while ( ( src [ i ] > > 24 ) = = GE_CMD_PROJMATRIXDATA ) {
const u32 newVal = src [ i ] < < 8 ;
if ( dst [ i ] ! = newVal ) {
Flush ( ) ;
dst [ i ] = newVal ;
gstate_c . Dirty ( DIRTY_PROJMATRIX ) ;
}
if ( + + i > = end ) {
break ;
}
2017-01-23 19:56:25 +00:00
}
}
2017-06-04 19:50:12 +00:00
const int count = i ;
2017-01-24 01:33:16 +00:00
gstate . projmtxnum = ( GE_CMD_PROJMATRIXNUMBER < < 24 ) | ( ( op + count ) & 0x1F ) ;
2017-01-23 19:56:25 +00:00
// Skip over the loaded data, it's done now.
UpdatePC ( currentList - > pc , currentList - > pc + count * 4 ) ;
currentList - > pc + = count * 4 ;
}
void GPUCommon : : Execute_ProjMtxData ( u32 op , u32 diff ) {
// Note: it's uncommon to get here now, see above.
2017-01-24 01:33:16 +00:00
int num = gstate . projmtxnum & 0x1F ; // NOTE: Changed from 0xF to catch overflows
2017-01-23 19:56:25 +00:00
u32 newVal = op < < 8 ;
2017-01-24 01:33:16 +00:00
if ( num < 0x10 & & newVal ! = ( ( const u32 * ) gstate . projMatrix ) [ num ] ) {
2017-01-23 19:56:25 +00:00
Flush ( ) ;
( ( u32 * ) gstate . projMatrix ) [ num ] = newVal ;
2017-01-24 08:41:38 +00:00
gstate_c . Dirty ( DIRTY_PROJMATRIX ) ;
2017-01-23 19:56:25 +00:00
}
num + + ;
2017-01-24 01:33:16 +00:00
if ( num < = 16 )
gstate . projmtxnum = ( GE_CMD_PROJMATRIXNUMBER < < 24 ) | ( num & 0xF ) ;
2022-09-06 02:38:54 +00:00
gstate . projmtxdata = GE_CMD_PROJMATRIXDATA < < 24 ;
2017-01-23 19:56:25 +00:00
}
void GPUCommon : : Execute_TgenMtxNum ( u32 op , u32 diff ) {
// This is almost always followed by GE_CMD_TGENMATRIXDATA.
const u32_le * src = ( const u32_le * ) Memory : : GetPointerUnchecked ( currentList - > pc + 4 ) ;
u32 * dst = ( u32 * ) ( gstate . tgenMatrix + ( op & 0xF ) ) ;
const int end = 12 - ( op & 0xF ) ;
int i = 0 ;
2017-06-04 19:50:12 +00:00
bool fastLoad = ! debugRecording_ ;
if ( currentList - > pc < currentList - > stall & & currentList - > pc + end * 4 > = currentList - > stall ) {
fastLoad = false ;
}
if ( fastLoad ) {
while ( ( src [ i ] > > 24 ) = = GE_CMD_TGENMATRIXDATA ) {
const u32 newVal = src [ i ] < < 8 ;
if ( dst [ i ] ! = newVal ) {
Flush ( ) ;
dst [ i ] = newVal ;
gstate_c . Dirty ( DIRTY_TEXMATRIX ) ;
}
if ( + + i > = end ) {
break ;
}
2017-01-23 19:56:25 +00:00
}
}
2017-06-04 19:50:12 +00:00
const int count = i ;
2017-01-23 19:56:25 +00:00
gstate . texmtxnum = ( GE_CMD_TGENMATRIXNUMBER < < 24 ) | ( ( op + count ) & 0xF ) ;
// Skip over the loaded data, it's done now.
UpdatePC ( currentList - > pc , currentList - > pc + count * 4 ) ;
currentList - > pc + = count * 4 ;
}
void GPUCommon : : Execute_TgenMtxData ( u32 op , u32 diff ) {
// Note: it's uncommon to get here now, see above.
int num = gstate . texmtxnum & 0xF ;
u32 newVal = op < < 8 ;
if ( num < 12 & & newVal ! = ( ( const u32 * ) gstate . tgenMatrix ) [ num ] ) {
Flush ( ) ;
( ( u32 * ) gstate . tgenMatrix ) [ num ] = newVal ;
2017-03-19 10:32:29 +00:00
gstate_c . Dirty ( DIRTY_TEXMATRIX | DIRTY_FRAGMENTSHADER_STATE ) ; // We check the matrix to see if we need projection
2017-01-23 19:56:25 +00:00
}
num + + ;
gstate . texmtxnum = ( GE_CMD_TGENMATRIXNUMBER < < 24 ) | ( num & 0xF ) ;
2022-09-06 02:38:54 +00:00
gstate . texmtxdata = GE_CMD_TGENMATRIXDATA < < 24 ;
2017-01-23 19:56:25 +00:00
}
void GPUCommon : : Execute_BoneMtxNum ( u32 op , u32 diff ) {
// This is almost always followed by GE_CMD_BONEMATRIXDATA.
const u32_le * src = ( const u32_le * ) Memory : : GetPointerUnchecked ( currentList - > pc + 4 ) ;
u32 * dst = ( u32 * ) ( gstate . boneMatrix + ( op & 0x7F ) ) ;
const int end = 12 * 8 - ( op & 0x7F ) ;
int i = 0 ;
2017-12-26 09:08:37 +00:00
bool fastLoad = ! debugRecording_ & & end > 0 ;
2017-06-04 19:50:12 +00:00
if ( currentList - > pc < currentList - > stall & & currentList - > pc + end * 4 > = currentList - > stall ) {
fastLoad = false ;
}
if ( fastLoad ) {
2018-04-10 10:22:02 +00:00
// If we can't use software skinning, we have to flush and dirty.
if ( ! g_Config . bSoftwareSkinning ) {
while ( ( src [ i ] > > 24 ) = = GE_CMD_BONEMATRIXDATA ) {
const u32 newVal = src [ i ] < < 8 ;
if ( dst [ i ] ! = newVal ) {
Flush ( ) ;
dst [ i ] = newVal ;
}
if ( + + i > = end ) {
break ;
}
}
const unsigned int numPlusCount = ( op & 0x7F ) + i ;
for ( unsigned int num = op & 0x7F ; num < numPlusCount ; num + = 12 ) {
gstate_c . Dirty ( DIRTY_BONEMATRIX0 < < ( num / 12 ) ) ;
}
} else {
while ( ( src [ i ] > > 24 ) = = GE_CMD_BONEMATRIXDATA ) {
dst [ i ] = src [ i ] < < 8 ;
if ( + + i > = end ) {
break ;
}
}
const unsigned int numPlusCount = ( op & 0x7F ) + i ;
for ( unsigned int num = op & 0x7F ; num < numPlusCount ; num + = 12 ) {
gstate_c . deferredVertTypeDirty | = DIRTY_BONEMATRIX0 < < ( num / 12 ) ;
2017-06-04 19:50:12 +00:00
}
2017-01-23 19:56:25 +00:00
}
}
2017-06-04 19:50:12 +00:00
const int count = i ;
2017-01-23 19:56:25 +00:00
gstate . boneMatrixNumber = ( GE_CMD_BONEMATRIXNUMBER < < 24 ) | ( ( op + count ) & 0x7F ) ;
// Skip over the loaded data, it's done now.
UpdatePC ( currentList - > pc , currentList - > pc + count * 4 ) ;
currentList - > pc + = count * 4 ;
}
void GPUCommon : : Execute_BoneMtxData ( u32 op , u32 diff ) {
// Note: it's uncommon to get here now, see above.
int num = gstate . boneMatrixNumber & 0x7F ;
u32 newVal = op < < 8 ;
if ( num < 96 & & newVal ! = ( ( const u32 * ) gstate . boneMatrix ) [ num ] ) {
2018-04-10 10:22:02 +00:00
// Bone matrices should NOT flush when software skinning is enabled!
if ( ! g_Config . bSoftwareSkinning ) {
Flush ( ) ;
gstate_c . Dirty ( DIRTY_BONEMATRIX0 < < ( num / 12 ) ) ;
} else {
gstate_c . deferredVertTypeDirty | = DIRTY_BONEMATRIX0 < < ( num / 12 ) ;
}
2017-01-23 19:56:25 +00:00
( ( u32 * ) gstate . boneMatrix ) [ num ] = newVal ;
}
num + + ;
gstate . boneMatrixNumber = ( GE_CMD_BONEMATRIXNUMBER < < 24 ) | ( num & 0x7F ) ;
2022-09-06 02:38:54 +00:00
gstate . boneMatrixData = GE_CMD_BONEMATRIXDATA < < 24 ;
2017-01-23 19:56:25 +00:00
}
2017-01-24 09:44:02 +00:00
void GPUCommon : : Execute_MorphWeight ( u32 op , u32 diff ) {
gstate_c . morphWeights [ ( op > > 24 ) - GE_CMD_MORPHWEIGHT0 ] = getFloat24 ( op ) ;
}
2017-11-24 16:54:56 +00:00
void GPUCommon : : Execute_ImmVertexAlphaPrim ( u32 op , u32 diff ) {
2017-11-24 20:55:25 +00:00
// Safety check.
2017-11-25 11:13:02 +00:00
if ( immCount_ > = MAX_IMMBUFFER_SIZE ) {
// Only print once for each overrun.
if ( immCount_ = = MAX_IMMBUFFER_SIZE ) {
2017-12-01 19:01:13 +00:00
ERROR_LOG_REPORT_ONCE ( exceed_imm_buffer , G3D , " Exceeded immediate draw buffer size. gstate.imm_ap=%06x , prim=%d " , gstate . imm_ap & 0xFFFFFF , ( int ) immPrim_ ) ;
2017-11-25 11:13:02 +00:00
}
if ( immCount_ < 0x7fffffff ) // Paranoia :)
immCount_ + + ;
2017-11-24 20:55:25 +00:00
return ;
2017-11-25 11:13:02 +00:00
}
2017-11-24 20:55:25 +00:00
2022-09-06 05:45:34 +00:00
int prim = ( op > > 8 ) & 0x7 ;
if ( prim ! = GE_PRIM_KEEP_PREVIOUS ) {
// Flush before changing the prim type. Only continue can be used to continue a prim.
FlushImm ( ) ;
}
2017-11-24 16:54:56 +00:00
TransformedVertex & v = immBuffer_ [ immCount_ + + ] ;
2022-09-06 05:45:34 +00:00
// ThrillVille does a clear with this, additional parameters found via tests.
// The current vtype affects how the coordinate is processed.
if ( gstate . isModeThrough ( ) ) {
v . x = ( ( int ) ( gstate . imm_vscx & 0xFFFF ) - 0x8000 ) / 16.0f ;
v . y = ( ( int ) ( gstate . imm_vscy & 0xFFFF ) - 0x8000 ) / 16.0f ;
} else {
int offsetX = gstate . getOffsetX16 ( ) ;
int offsetY = gstate . getOffsetY16 ( ) ;
v . x = ( ( int ) ( gstate . imm_vscx & 0xFFFF ) - offsetX ) / 16.0f ;
v . y = ( ( int ) ( gstate . imm_vscy & 0xFFFF ) - offsetY ) / 16.0f ;
}
2017-11-24 16:54:56 +00:00
v . z = gstate . imm_vscz & 0xFFFF ;
2021-10-31 04:03:01 +00:00
v . pos_w = 1.0f ;
2017-11-25 11:13:02 +00:00
v . u = getFloat24 ( gstate . imm_vtcs ) ;
v . v = getFloat24 ( gstate . imm_vtct ) ;
2021-10-31 04:03:01 +00:00
v . uv_w = getFloat24 ( gstate . imm_vtcq ) ;
2017-11-24 16:54:56 +00:00
v . color0_32 = ( gstate . imm_cv & 0xFFFFFF ) | ( gstate . imm_ap < < 24 ) ;
2022-09-07 05:03:46 +00:00
// TODO: When !gstate.isModeThrough(), direct fog coefficient (0 = entirely fog), ignore fog flag (also GE_IMM_FOG.)
v . fog = ( gstate . imm_fc & 0xFF ) / 255.0f ;
// TODO: Apply if gstate.isUsingSecondaryColor() && !gstate.isModeThrough(), ignore lighting flag.
2017-11-24 16:54:56 +00:00
v . color1_32 = gstate . imm_scv & 0xFFFFFF ;
2017-11-25 11:13:02 +00:00
if ( prim ! = GE_PRIM_KEEP_PREVIOUS ) {
2017-11-24 20:45:25 +00:00
immPrim_ = ( GEPrimitiveType ) prim ;
2022-09-06 06:13:16 +00:00
// Flags seem to only be respected from the first prim.
immFlags_ = op & 0x00FFF800 ;
2022-09-18 03:15:40 +00:00
immFirstSent_ = false ;
2022-09-06 05:45:34 +00:00
} else if ( prim = = GE_PRIM_KEEP_PREVIOUS & & immPrim_ ! = GE_PRIM_INVALID ) {
static constexpr int flushPrimCount [ ] = { 1 , 2 , 0 , 3 , 0 , 0 , 2 , 0 } ;
2022-09-18 01:42:59 +00:00
// Instead of finding a proper point to flush, we just emit prims when we can.
2022-09-06 05:45:34 +00:00
if ( immCount_ = = flushPrimCount [ immPrim_ & 7 ] )
FlushImm ( ) ;
2017-11-25 11:13:02 +00:00
} else {
ERROR_LOG_REPORT_ONCE ( imm_draw_prim , G3D , " Immediate draw: Unexpected primitive %d at count %d " , prim , immCount_ ) ;
2017-11-24 16:54:56 +00:00
}
}
2017-11-24 20:45:25 +00:00
void GPUCommon : : FlushImm ( ) {
2022-09-06 05:45:34 +00:00
if ( immCount_ = = 0 | | immPrim_ = = GE_PRIM_INVALID )
return ;
2017-11-24 20:45:25 +00:00
SetDrawType ( DRAW_PRIM , immPrim_ ) ;
2022-09-06 05:38:21 +00:00
if ( framebufferManager_ )
framebufferManager_ - > SetRenderFrameBuffer ( gstate_c . IsDirty ( DIRTY_FRAMEBUF ) , gstate_c . skipDrawReason ) ;
2017-11-24 20:45:25 +00:00
if ( gstate_c . skipDrawReason & ( SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB ) ) {
// No idea how many cycles to skip, heh.
2022-09-06 05:45:34 +00:00
immCount_ = 0 ;
2017-11-24 20:45:25 +00:00
return ;
}
UpdateUVScaleOffset ( ) ;
2022-09-07 05:03:46 +00:00
bool antialias = ( immFlags_ & GE_IMM_ANTIALIAS ) ! = 0 ;
bool prevAntialias = gstate . isAntiAliasEnabled ( ) ;
bool shading = ( immFlags_ & GE_IMM_SHADING ) ! = 0 ;
bool prevShading = gstate . getShadeMode ( ) = = GE_SHADE_GOURAUD ;
2022-09-06 06:13:16 +00:00
bool cullEnable = ( immFlags_ & GE_IMM_CULLENABLE ) ! = 0 ;
bool prevCullEnable = gstate . isCullEnabled ( ) ;
int cullMode = ( immFlags_ & GE_IMM_CULLFACE ) ! = 0 ? 1 : 0 ;
2022-09-07 05:03:46 +00:00
bool texturing = ( immFlags_ & GE_IMM_TEXTURE ) ! = 0 ;
bool prevTexturing = gstate . isTextureMapEnabled ( ) ;
2022-09-18 02:42:41 +00:00
bool fog = ( immFlags_ & GE_IMM_FOG ) ! = 0 ;
bool prevFog = gstate . isFogEnabled ( ) ;
2022-09-06 06:13:16 +00:00
bool dither = ( immFlags_ & GE_IMM_DITHER ) ! = 0 ;
2022-09-07 05:03:46 +00:00
bool prevDither = gstate . isDitherEnabled ( ) ;
2022-09-06 06:13:16 +00:00
2022-09-07 05:18:55 +00:00
if ( ( immFlags_ & GE_IMM_CLIPMASK ) ! = 0 ) {
WARN_LOG_REPORT_ONCE ( geimmclipvalue , G3D , " Imm vertex used clip value, flags=%06x " , immFlags_ ) ;
}
2022-09-18 04:08:26 +00:00
bool changed = texturing ! = prevTexturing | | cullEnable ! = prevCullEnable | | dither ! = prevDither ;
changed = changed | | prevShading ! = shading | | prevFog ! = fog ;
if ( changed ) {
2022-09-06 06:13:16 +00:00
DispatchFlush ( ) ;
2022-09-07 05:03:46 +00:00
gstate . antiAliasEnable = ( GE_CMD_ANTIALIASENABLE < < 24 ) | ( int ) antialias ;
gstate . shademodel = ( GE_CMD_SHADEMODE < < 24 ) | ( int ) shading ;
2022-09-06 06:13:16 +00:00
gstate . cullfaceEnable = ( GE_CMD_CULLFACEENABLE < < 24 ) | ( int ) cullEnable ;
2022-09-07 05:03:46 +00:00
gstate . textureMapEnable = ( GE_CMD_TEXTUREMAPENABLE < < 24 ) | ( int ) texturing ;
2022-09-18 02:42:41 +00:00
gstate . fogEnable = ( GE_CMD_FOGENABLE < < 24 ) | ( int ) fog ;
2022-09-06 06:13:16 +00:00
gstate . ditherEnable = ( GE_CMD_DITHERENABLE < < 24 ) | ( int ) dither ;
2022-09-18 04:08:26 +00:00
gstate_c . Dirty ( DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_UVSCALEOFFSET | DIRTY_CULLRANGE ) ;
2022-09-06 06:13:16 +00:00
}
2022-09-18 03:15:40 +00:00
drawEngineCommon_ - > DispatchSubmitImm ( immPrim_ , immBuffer_ , immCount_ , cullMode , immFirstSent_ ) ;
2022-09-06 05:45:34 +00:00
immCount_ = 0 ;
2022-09-18 03:15:40 +00:00
immFirstSent_ = true ;
2022-09-06 06:13:16 +00:00
2022-09-18 04:08:26 +00:00
if ( changed ) {
DispatchFlush ( ) ;
gstate . antiAliasEnable = ( GE_CMD_ANTIALIASENABLE < < 24 ) | ( int ) prevAntialias ;
gstate . shademodel = ( GE_CMD_SHADEMODE < < 24 ) | ( int ) prevShading ;
gstate . cullfaceEnable = ( GE_CMD_CULLFACEENABLE < < 24 ) | ( int ) prevCullEnable ;
gstate . textureMapEnable = ( GE_CMD_TEXTUREMAPENABLE < < 24 ) | ( int ) prevTexturing ;
gstate . fogEnable = ( GE_CMD_FOGENABLE < < 24 ) | ( int ) prevFog ;
gstate . ditherEnable = ( GE_CMD_DITHERENABLE < < 24 ) | ( int ) prevDither ;
gstate_c . Dirty ( DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_UVSCALEOFFSET | DIRTY_CULLRANGE ) ;
}
2017-11-24 20:45:25 +00:00
}
2014-04-16 15:12:21 +00:00
void GPUCommon : : ExecuteOp ( u32 op , u32 diff ) {
const u32 cmd = op > > 24 ;
// Handle control and drawing commands here directly. The others we delegate.
switch ( cmd ) {
case GE_CMD_NOP :
break ;
case GE_CMD_OFFSETADDR :
Execute_OffsetAddr ( op , diff ) ;
break ;
case GE_CMD_ORIGIN :
Execute_Origin ( op , diff ) ;
break ;
case GE_CMD_JUMP :
Execute_Jump ( op , diff ) ;
break ;
case GE_CMD_BJUMP :
Execute_BJump ( op , diff ) ;
break ;
case GE_CMD_CALL :
Execute_Call ( op , diff ) ;
break ;
case GE_CMD_RET :
Execute_Ret ( op , diff ) ;
break ;
case GE_CMD_SIGNAL :
case GE_CMD_FINISH :
// Processed in GE_END.
break ;
case GE_CMD_END :
Execute_End ( op , diff ) ;
break ;
2013-04-01 06:02:46 +00:00
default :
2017-01-24 09:44:02 +00:00
DEBUG_LOG ( G3D , " DL Unknown: %08x @ %08x " , op , currentList = = NULL ? 0 : currentList - > pc ) ;
break ;
}
}
void GPUCommon : : Execute_Unknown ( u32 op , u32 diff ) {
2017-12-01 17:32:23 +00:00
if ( ( op & 0xFFFFFF ) ! = 0 )
WARN_LOG_REPORT_ONCE ( unknowncmd , G3D , " Unknown GE command : %08x " , op ) ;
2013-04-01 06:02:46 +00:00
}
2014-03-03 02:12:40 +00:00
void GPUCommon : : FastLoadBoneMatrix ( u32 target ) {
2020-09-28 22:35:38 +00:00
const u32 num = gstate . boneMatrixNumber & 0x7F ;
2022-01-01 23:26:00 +00:00
_dbg_assert_msg_ ( num + 12 < = 96 , " FastLoadBoneMatrix would corrupt memory " ) ;
2020-09-28 22:35:38 +00:00
const u32 mtxNum = num / 12 ;
u32 uniformsToDirty = DIRTY_BONEMATRIX0 < < mtxNum ;
2020-09-24 21:00:48 +00:00
if ( num ! = 12 * mtxNum ) {
2018-04-10 10:22:02 +00:00
uniformsToDirty | = DIRTY_BONEMATRIX0 < < ( ( mtxNum + 1 ) & 7 ) ;
}
if ( ! g_Config . bSoftwareSkinning ) {
2022-02-01 03:32:46 +00:00
if ( flushOnParams_ )
Flush ( ) ;
2018-04-10 10:22:02 +00:00
gstate_c . Dirty ( uniformsToDirty ) ;
} else {
gstate_c . deferredVertTypeDirty | = uniformsToDirty ;
}
2014-03-03 02:12:40 +00:00
gstate . FastLoadBoneMatrix ( target ) ;
}
2014-08-17 08:52:06 +00:00
struct DisplayList_v1 {
2013-10-07 05:07:57 +00:00
int id ;
u32 startpc ;
u32 pc ;
u32 stall ;
DisplayListState state ;
SignalBehavior signal ;
int subIntrBase ;
u16 subIntrToken ;
DisplayListStackEntry stack [ 32 ] ;
int stackptr ;
bool interrupted ;
u64 waitTicks ;
bool interruptsEnabled ;
bool pendingInterrupt ;
bool started ;
size_t contextPtr ;
u32 offsetAddr ;
bool bboxResult ;
} ;
2014-08-17 08:52:06 +00:00
struct DisplayList_v2 {
int id ;
u32 startpc ;
u32 pc ;
u32 stall ;
DisplayListState state ;
SignalBehavior signal ;
int subIntrBase ;
u16 subIntrToken ;
DisplayListStackEntry stack [ 32 ] ;
int stackptr ;
bool interrupted ;
u64 waitTicks ;
bool interruptsEnabled ;
bool pendingInterrupt ;
bool started ;
PSPPointer < u32_le > context ;
u32 offsetAddr ;
bool bboxResult ;
} ;
2012-12-29 19:41:33 +00:00
void GPUCommon : : DoState ( PointerWrap & p ) {
2015-10-11 12:38:26 +00:00
auto s = p . Section ( " GPUCommon " , 1 , 4 ) ;
2013-09-15 03:23:03 +00:00
if ( ! s )
return ;
2020-08-10 04:20:42 +00:00
Do < int > ( p , dlQueue ) ;
2015-10-11 12:38:26 +00:00
if ( s > = 4 ) {
2020-08-10 04:20:42 +00:00
DoArray ( p , dls , ARRAY_SIZE ( dls ) ) ;
2015-10-11 12:38:26 +00:00
} else if ( s > = 3 ) {
2015-12-24 22:31:43 +00:00
// This may have been saved with or without padding, depending on platform.
// We need to upconvert it to our consistently-padded struct.
static const size_t DisplayList_v3_size = 452 ;
static const size_t DisplayList_v4_size = 456 ;
static_assert ( DisplayList_v4_size = = sizeof ( DisplayList ) , " Make sure to change here when updating DisplayList " ) ;
p . DoVoid ( & dls [ 0 ] , DisplayList_v3_size ) ;
dls [ 0 ] . padding = 0 ;
const u8 * savedPtr = * p . GetPPtr ( ) ;
const u32 * savedPtr32 = ( const u32 * ) savedPtr ;
// Here's the trick: the first member (id) is always the same as the index.
// The second member (startpc) is always an address, or 0, never 1. So we can see the padding.
const bool hasPadding = savedPtr32 [ 1 ] = = 1 ;
if ( hasPadding ) {
u32 padding ;
2020-08-10 04:20:42 +00:00
Do ( p , padding ) ;
2015-12-24 22:31:43 +00:00
}
for ( size_t i = 1 ; i < ARRAY_SIZE ( dls ) ; + + i ) {
p . DoVoid ( & dls [ i ] , DisplayList_v3_size ) ;
2015-10-11 12:38:26 +00:00
dls [ i ] . padding = 0 ;
2015-12-24 22:31:43 +00:00
if ( hasPadding ) {
u32 padding ;
2020-08-10 04:20:42 +00:00
Do ( p , padding ) ;
2015-12-24 22:31:43 +00:00
}
2015-10-11 12:38:26 +00:00
}
2014-08-17 08:52:06 +00:00
} else if ( s > = 2 ) {
for ( size_t i = 0 ; i < ARRAY_SIZE ( dls ) ; + + i ) {
DisplayList_v2 oldDL ;
2020-08-10 04:20:42 +00:00
Do ( p , oldDL ) ;
2014-08-17 08:52:06 +00:00
// Copy over everything except the last, new member (stackAddr.)
memcpy ( & dls [ i ] , & oldDL , sizeof ( DisplayList_v2 ) ) ;
dls [ i ] . stackAddr = 0 ;
}
2013-10-07 05:07:57 +00:00
} else {
// Can only be in read mode here.
for ( size_t i = 0 ; i < ARRAY_SIZE ( dls ) ; + + i ) {
2014-08-17 08:52:06 +00:00
DisplayList_v1 oldDL ;
2020-08-10 04:20:42 +00:00
Do ( p , oldDL ) ;
2013-10-07 05:07:57 +00:00
// On 32-bit, they're the same, on 64-bit oldDL is bigger.
2020-05-17 06:49:45 +00:00
memcpy ( & dls [ i ] , & oldDL , sizeof ( DisplayList_v1 ) ) ;
2013-10-07 05:07:57 +00:00
// Fix the other fields. Let's hope context wasn't important, it was a pointer.
dls [ i ] . context = 0 ;
dls [ i ] . offsetAddr = oldDL . offsetAddr ;
dls [ i ] . bboxResult = oldDL . bboxResult ;
2014-08-17 08:52:06 +00:00
dls [ i ] . stackAddr = 0 ;
2013-10-07 05:07:57 +00:00
}
}
2013-04-05 06:19:28 +00:00
int currentID = 0 ;
2016-01-22 06:14:47 +00:00
if ( currentList ! = nullptr ) {
currentID = ( int ) ( currentList - & dls [ 0 ] ) ;
2013-04-05 06:19:28 +00:00
}
2020-08-10 04:20:42 +00:00
Do ( p , currentID ) ;
2013-02-12 09:06:11 +00:00
if ( currentID = = 0 ) {
2016-01-22 06:14:47 +00:00
currentList = nullptr ;
2013-02-12 09:06:11 +00:00
} else {
2013-04-05 06:19:28 +00:00
currentList = & dls [ currentID ] ;
2013-02-12 09:06:11 +00:00
}
2020-08-10 04:20:42 +00:00
Do ( p , interruptRunning ) ;
Do ( p , gpuState ) ;
Do ( p , isbreak ) ;
Do ( p , drawCompleteTicks ) ;
Do ( p , busyTicks ) ;
2012-12-29 19:41:33 +00:00
}
2013-02-03 23:41:16 +00:00
2013-08-09 07:32:40 +00:00
void GPUCommon : : InterruptStart ( int listid ) {
2013-02-03 23:41:16 +00:00
interruptRunning = true ;
}
2013-08-09 07:32:40 +00:00
void GPUCommon : : InterruptEnd ( int listid ) {
2013-02-03 23:41:16 +00:00
interruptRunning = false ;
2013-04-04 07:35:38 +00:00
isbreak = false ;
2013-04-06 15:19:54 +00:00
DisplayList & dl = dls [ listid ] ;
2013-09-01 17:16:29 +00:00
dl . pendingInterrupt = false ;
2013-04-06 15:19:54 +00:00
// TODO: Unless the signal handler could change it?
2013-04-07 23:47:29 +00:00
if ( dl . state = = PSP_GE_DL_STATE_COMPLETED | | dl . state = = PSP_GE_DL_STATE_NONE ) {
2013-10-07 05:07:57 +00:00
if ( dl . started & & dl . context . IsValid ( ) ) {
2013-09-20 16:42:09 +00:00
gstate . Restore ( dl . context ) ;
2013-09-24 06:17:36 +00:00
ReapplyGfxState ( ) ;
2013-09-20 16:42:09 +00:00
}
2013-04-07 19:45:42 +00:00
dl . waitTicks = 0 ;
2014-03-30 00:02:41 +00:00
__GeTriggerWait ( GPU_SYNC_LIST , listid ) ;
2019-07-07 20:10:28 +00:00
// Make sure the list isn't still queued since it's now completed.
if ( ! dlQueue . empty ( ) ) {
if ( listid = = dlQueue . front ( ) )
PopDLQueue ( ) ;
else
dlQueue . remove ( listid ) ;
}
2013-04-06 15:19:54 +00:00
}
2013-02-03 23:41:16 +00:00
ProcessDLQueue ( ) ;
}
2013-04-07 19:45:42 +00:00
// TODO: Maybe cleaner to keep this in GE and trigger the clear directly?
2014-03-29 23:51:38 +00:00
void GPUCommon : : SyncEnd ( GPUSyncType waitType , int listid , bool wokeThreads ) {
if ( waitType = = GPU_SYNC_DRAW & & wokeThreads )
2013-04-07 19:45:42 +00:00
{
for ( int i = 0 ; i < DisplayListMaxCount ; + + i ) {
if ( dls [ i ] . state = = PSP_GE_DL_STATE_COMPLETED ) {
dls [ i ] . state = PSP_GE_DL_STATE_NONE ;
}
}
}
}
2013-09-22 17:22:33 +00:00
bool GPUCommon : : GetCurrentDisplayList ( DisplayList & list ) {
if ( ! currentList ) {
return false ;
}
list = * currentList ;
return true ;
}
std : : vector < DisplayList > GPUCommon : : ActiveDisplayLists ( ) {
std : : vector < DisplayList > result ;
for ( auto it = dlQueue . begin ( ) , end = dlQueue . end ( ) ; it ! = end ; + + it ) {
result . push_back ( dls [ * it ] ) ;
}
return result ;
}
void GPUCommon : : ResetListPC ( int listID , u32 pc ) {
if ( listID < 0 | | listID > = DisplayListMaxCount ) {
2020-07-19 15:47:02 +00:00
_dbg_assert_msg_ ( false , " listID out of range: %d " , listID ) ;
2013-09-22 17:22:33 +00:00
return ;
}
2022-07-31 01:21:32 +00:00
Reporting : : NotifyDebugger ( ) ;
2013-09-22 17:22:33 +00:00
dls [ listID ] . pc = pc ;
2022-02-06 00:57:36 +00:00
downcount = 0 ;
2013-09-22 17:22:33 +00:00
}
void GPUCommon : : ResetListStall ( int listID , u32 stall ) {
if ( listID < 0 | | listID > = DisplayListMaxCount ) {
2020-07-19 15:47:02 +00:00
_dbg_assert_msg_ ( false , " listID out of range: %d " , listID ) ;
2013-09-22 17:22:33 +00:00
return ;
}
2022-07-31 01:21:32 +00:00
Reporting : : NotifyDebugger ( ) ;
2013-09-22 17:22:33 +00:00
dls [ listID ] . stall = stall ;
2022-02-06 00:57:36 +00:00
downcount = 0 ;
2013-09-22 17:22:33 +00:00
}
void GPUCommon : : ResetListState ( int listID , DisplayListState state ) {
if ( listID < 0 | | listID > = DisplayListMaxCount ) {
2020-07-19 15:47:02 +00:00
_dbg_assert_msg_ ( false , " listID out of range: %d " , listID ) ;
2013-09-22 17:22:33 +00:00
return ;
}
2022-07-31 01:21:32 +00:00
Reporting : : NotifyDebugger ( ) ;
2013-09-22 17:22:33 +00:00
dls [ listID ] . state = state ;
2022-02-06 00:57:36 +00:00
downcount = 0 ;
2013-09-22 17:22:33 +00:00
}
GPUDebugOp GPUCommon : : DissassembleOp ( u32 pc , u32 op ) {
char buffer [ 1024 ] ;
2022-08-24 02:50:01 +00:00
u32 prev = Memory : : IsValidAddress ( pc - 4 ) ? Memory : : ReadUnchecked_U32 ( pc - 4 ) : 0 ;
GeDisassembleOp ( pc , op , prev , buffer , sizeof ( buffer ) ) ;
2013-09-22 17:22:33 +00:00
GPUDebugOp info ;
info . pc = pc ;
info . cmd = op > > 24 ;
info . op = op ;
info . desc = buffer ;
return info ;
}
std : : vector < GPUDebugOp > GPUCommon : : DissassembleOpRange ( u32 startpc , u32 endpc ) {
char buffer [ 1024 ] ;
std : : vector < GPUDebugOp > result ;
GPUDebugOp info ;
2013-10-06 22:07:54 +00:00
// Don't trigger a pause.
u32 prev = Memory : : IsValidAddress ( startpc - 4 ) ? Memory : : Read_U32 ( startpc - 4 ) : 0 ;
2013-09-22 17:22:33 +00:00
for ( u32 pc = startpc ; pc < endpc ; pc + = 4 ) {
2013-10-06 22:07:54 +00:00
u32 op = Memory : : IsValidAddress ( pc ) ? Memory : : Read_U32 ( pc ) : 0 ;
2014-09-13 21:50:42 +00:00
GeDisassembleOp ( pc , op , prev , buffer , sizeof ( buffer ) ) ;
2013-09-22 17:22:33 +00:00
prev = op ;
info . pc = pc ;
info . cmd = op > > 24 ;
info . op = op ;
info . desc = buffer ;
result . push_back ( info ) ;
}
return result ;
}
u32 GPUCommon : : GetRelativeAddress ( u32 data ) {
return gstate_c . getRelativeAddress ( data ) ;
}
u32 GPUCommon : : GetVertexAddress ( ) {
return gstate_c . vertexAddr ;
}
u32 GPUCommon : : GetIndexAddress ( ) {
return gstate_c . indexAddr ;
}
GPUgstate GPUCommon : : GetGState ( ) {
return gstate ;
}
2013-10-07 02:17:06 +00:00
void GPUCommon : : SetCmdValue ( u32 op ) {
u32 cmd = op > > 24 ;
u32 diff = op ^ gstate . cmdmem [ cmd ] ;
2022-07-31 01:21:32 +00:00
Reporting : : NotifyDebugger ( ) ;
2013-10-07 02:17:06 +00:00
PreExecuteOp ( op , diff ) ;
gstate . cmdmem [ cmd ] = op ;
ExecuteOp ( op , diff ) ;
2022-02-06 00:57:36 +00:00
downcount = 0 ;
2013-10-07 02:17:06 +00:00
}
2016-04-10 20:07:08 +00:00
2022-08-24 02:29:06 +00:00
void GPUCommon : : SetDisplayFramebuffer ( u32 framebuf , u32 stride , GEBufferFormat format ) {
framebufferManager_ - > SetDisplayFramebuffer ( framebuf , stride , format ) ;
}
2016-12-21 17:07:17 +00:00
void GPUCommon : : DoBlockTransfer ( u32 skipDrawReason ) {
u32 srcBasePtr = gstate . getTransferSrcAddress ( ) ;
u32 srcStride = gstate . getTransferSrcStride ( ) ;
u32 dstBasePtr = gstate . getTransferDstAddress ( ) ;
u32 dstStride = gstate . getTransferDstStride ( ) ;
int srcX = gstate . getTransferSrcX ( ) ;
int srcY = gstate . getTransferSrcY ( ) ;
int dstX = gstate . getTransferDstX ( ) ;
int dstY = gstate . getTransferDstY ( ) ;
int width = gstate . getTransferWidth ( ) ;
int height = gstate . getTransferHeight ( ) ;
int bpp = gstate . getTransferBpp ( ) ;
DEBUG_LOG ( G3D , " Block transfer: %08x/%x -> %08x/%x, %ix%ix%i (%i,%i)->(%i,%i) " , srcBasePtr , srcStride , dstBasePtr , dstStride , width , height , bpp , srcX , srcY , dstX , dstY ) ;
if ( ! Memory : : IsValidAddress ( srcBasePtr ) ) {
ERROR_LOG_REPORT ( G3D , " BlockTransfer: Bad source transfer address %08x! " , srcBasePtr ) ;
return ;
}
if ( ! Memory : : IsValidAddress ( dstBasePtr ) ) {
ERROR_LOG_REPORT ( G3D , " BlockTransfer: Bad destination transfer address %08x! " , dstBasePtr ) ;
return ;
}
// Check that the last address of both source and dest are valid addresses
u32 srcLastAddr = srcBasePtr + ( ( srcY + height - 1 ) * srcStride + ( srcX + width - 1 ) ) * bpp ;
u32 dstLastAddr = dstBasePtr + ( ( dstY + height - 1 ) * dstStride + ( dstX + width - 1 ) ) * bpp ;
if ( ! Memory : : IsValidAddress ( srcLastAddr ) ) {
ERROR_LOG_REPORT ( G3D , " Bottom-right corner of source of block transfer is at an invalid address: %08x " , srcLastAddr ) ;
return ;
}
if ( ! Memory : : IsValidAddress ( dstLastAddr ) ) {
ERROR_LOG_REPORT ( G3D , " Bottom-right corner of destination of block transfer is at an invalid address: %08x " , srcLastAddr ) ;
return ;
}
// Tell the framebuffer manager to take action if possible. If it does the entire thing, let's just return.
if ( ! framebufferManager_ - > NotifyBlockTransferBefore ( dstBasePtr , dstStride , dstX , dstY , srcBasePtr , srcStride , srcX , srcY , width , height , bpp , skipDrawReason ) ) {
// Do the copy! (Hm, if we detect a drawn video frame (see below) then we could maybe skip this?)
// Can use GetPointerUnchecked because we checked the addresses above. We could also avoid them
// entirely by walking a couple of pointers...
if ( srcStride = = dstStride & & ( u32 ) width = = srcStride ) {
// Common case in God of War, let's do it all in one chunk.
u32 srcLineStartAddr = srcBasePtr + ( srcY * srcStride + srcX ) * bpp ;
u32 dstLineStartAddr = dstBasePtr + ( dstY * dstStride + dstX ) * bpp ;
const u8 * src = Memory : : GetPointerUnchecked ( srcLineStartAddr ) ;
2022-07-20 10:40:22 +00:00
u8 * dst = Memory : : GetPointerWriteUnchecked ( dstLineStartAddr ) ;
2016-12-21 17:07:17 +00:00
memcpy ( dst , src , width * height * bpp ) ;
2018-11-17 16:55:54 +00:00
GPURecord : : NotifyMemcpy ( dstLineStartAddr , srcLineStartAddr , width * height * bpp ) ;
2016-12-21 17:07:17 +00:00
} else {
for ( int y = 0 ; y < height ; y + + ) {
u32 srcLineStartAddr = srcBasePtr + ( ( y + srcY ) * srcStride + srcX ) * bpp ;
u32 dstLineStartAddr = dstBasePtr + ( ( y + dstY ) * dstStride + dstX ) * bpp ;
const u8 * src = Memory : : GetPointerUnchecked ( srcLineStartAddr ) ;
2022-07-20 10:40:22 +00:00
u8 * dst = Memory : : GetPointerWriteUnchecked ( dstLineStartAddr ) ;
2016-12-21 17:07:17 +00:00
memcpy ( dst , src , width * bpp ) ;
2018-11-17 16:55:54 +00:00
GPURecord : : NotifyMemcpy ( dstLineStartAddr , srcLineStartAddr , width * bpp ) ;
2016-12-21 17:07:17 +00:00
}
}
2017-03-25 18:43:19 +00:00
// Fixes Gran Turismo's funky text issue, since it overwrites the current texture.
2016-12-21 17:07:17 +00:00
textureCache_ - > Invalidate ( dstBasePtr + ( dstY * dstStride + dstX ) * bpp , height * dstStride * bpp , GPU_INVALIDATE_HINT ) ;
framebufferManager_ - > NotifyBlockTransferAfter ( dstBasePtr , dstStride , dstX , dstY , srcBasePtr , srcStride , srcX , srcY , width , height , bpp , skipDrawReason ) ;
}
2022-03-08 18:46:00 +00:00
const uint32_t numBytes = width * height * bpp ;
2021-04-04 00:04:35 +00:00
const uint32_t srcSize = height * srcStride * bpp ;
2022-02-06 17:28:48 +00:00
const uint32_t dstSize = height * dstStride * bpp ;
2022-03-08 18:46:00 +00:00
// We do the check here on the number of bytes to avoid marking really tiny images.
// Helps perf in GT menu which does insane amounts of these, one for each text character per frame.
if ( MemBlockInfoDetailed ( numBytes , numBytes ) ) {
2022-02-06 17:28:48 +00:00
const uint32_t src = srcBasePtr + ( srcY * srcStride + srcX ) * bpp ;
const uint32_t dst = dstBasePtr + ( dstY * dstStride + dstX ) * bpp ;
2022-09-01 09:55:14 +00:00
char tag [ 128 ] ;
size_t tagSize = FormatMemWriteTagAt ( tag , sizeof ( tag ) , " GPUBlockTransfer/ " , src , srcSize ) ;
NotifyMemInfo ( MemBlockFlags : : READ , src , srcSize , tag , tagSize ) ;
NotifyMemInfo ( MemBlockFlags : : WRITE , dst , dstSize , tag , tagSize ) ;
2022-02-06 17:28:48 +00:00
}
2016-12-21 17:07:17 +00:00
// TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate.
cyclesExecuted + = ( ( height * width * bpp ) * 16 ) / 10 ;
2016-12-21 17:26:06 +00:00
}
bool GPUCommon : : PerformMemoryCopy ( u32 dest , u32 src , int size ) {
// Track stray copies of a framebuffer in RAM. MotoGP does this.
if ( framebufferManager_ - > MayIntersectFramebuffer ( src ) | | framebufferManager_ - > MayIntersectFramebuffer ( dest ) ) {
2017-11-05 19:33:28 +00:00
if ( ! framebufferManager_ - > NotifyFramebufferCopy ( src , dest , size , false , gstate_c . skipDrawReason ) ) {
2021-02-06 05:43:27 +00:00
// We use a little hack for PerformMemoryDownload/PerformMemoryUpload using a VRAM mirror.
2017-11-05 19:33:28 +00:00
// Since they're identical we don't need to copy.
if ( ! Memory : : IsVRAMAddress ( dest ) | | ( dest ^ 0x00400000 ) ! = src ) {
2022-02-06 17:28:48 +00:00
if ( MemBlockInfoDetailed ( size ) ) {
2022-09-01 09:55:14 +00:00
const std : : string tag = GetMemWriteTagAt ( " GPUMemcpy/ " , src , size ) ;
2022-02-06 17:28:48 +00:00
Memory : : Memcpy ( dest , src , size , tag . c_str ( ) , tag . size ( ) ) ;
} else {
Memory : : Memcpy ( dest , src , size , " GPUMemcpy " ) ;
}
2017-11-05 19:33:28 +00:00
}
}
InvalidateCache ( dest , size , GPU_INVALIDATE_HINT ) ;
2016-12-21 17:26:06 +00:00
return true ;
}
2022-02-06 17:28:48 +00:00
if ( MemBlockInfoDetailed ( size ) ) {
2022-09-01 09:55:14 +00:00
const std : : string tag = GetMemWriteTagAt ( " GPUMemcpy/ " , src , size ) ;
2022-02-06 17:28:48 +00:00
NotifyMemInfo ( MemBlockFlags : : READ , src , size , tag . c_str ( ) , tag . size ( ) ) ;
NotifyMemInfo ( MemBlockFlags : : WRITE , dest , size , tag . c_str ( ) , tag . size ( ) ) ;
}
2016-12-21 17:26:06 +00:00
InvalidateCache ( dest , size , GPU_INVALIDATE_HINT ) ;
2017-06-03 07:09:58 +00:00
GPURecord : : NotifyMemcpy ( dest , src , size ) ;
2016-12-21 17:26:06 +00:00
return false ;
}
bool GPUCommon : : PerformMemorySet ( u32 dest , u8 v , int size ) {
// This may indicate a memset, usually to 0, of a framebuffer.
if ( framebufferManager_ - > MayIntersectFramebuffer ( dest ) ) {
2021-02-03 07:53:16 +00:00
Memory : : Memset ( dest , v , size , " GPUMemset " ) ;
2017-11-05 19:33:28 +00:00
if ( ! framebufferManager_ - > NotifyFramebufferCopy ( dest , dest , size , true , gstate_c . skipDrawReason ) ) {
InvalidateCache ( dest , size , GPU_INVALIDATE_HINT ) ;
}
2016-12-21 17:26:06 +00:00
return true ;
}
2021-02-03 07:53:16 +00:00
NotifyMemInfo ( MemBlockFlags : : WRITE , dest , size , " GPUMemset " ) ;
2016-12-21 17:26:06 +00:00
// Or perhaps a texture, let's invalidate.
InvalidateCache ( dest , size , GPU_INVALIDATE_HINT ) ;
2017-06-03 07:09:58 +00:00
GPURecord : : NotifyMemset ( dest , v , size ) ;
2016-12-21 17:26:06 +00:00
return false ;
}
bool GPUCommon : : PerformMemoryDownload ( u32 dest , int size ) {
// Cheat a bit to force a download of the framebuffer.
// VRAM + 0x00400000 is simply a VRAM mirror.
if ( Memory : : IsVRAMAddress ( dest ) ) {
return PerformMemoryCopy ( dest ^ 0x00400000 , dest , size ) ;
}
return false ;
}
bool GPUCommon : : PerformMemoryUpload ( u32 dest , int size ) {
// Cheat a bit to force an upload of the framebuffer.
// VRAM + 0x00400000 is simply a VRAM mirror.
if ( Memory : : IsVRAMAddress ( dest ) ) {
2017-06-03 07:09:58 +00:00
GPURecord : : NotifyUpload ( dest , size ) ;
2016-12-21 17:26:06 +00:00
return PerformMemoryCopy ( dest , dest ^ 0x00400000 , size ) ;
}
return false ;
}
2016-12-21 17:33:08 +00:00
void GPUCommon : : InvalidateCache ( u32 addr , int size , GPUInvalidationType type ) {
if ( size > 0 )
textureCache_ - > Invalidate ( addr , size , type ) ;
else
textureCache_ - > InvalidateAll ( type ) ;
if ( type ! = GPU_INVALIDATE_ALL & & framebufferManager_ - > MayIntersectFramebuffer ( addr ) ) {
// Vempire invalidates (with writeback) after drawing, but before blitting.
2022-08-29 08:14:29 +00:00
// TODO: Investigate whether we can get this to work some other way.
2019-02-08 13:50:47 +00:00
if ( type = = GPU_INVALIDATE_SAFE ) {
2022-08-30 04:41:37 +00:00
framebufferManager_ - > UpdateFromMemory ( addr , size ) ;
2016-12-21 17:33:08 +00:00
}
}
}
2022-08-29 08:14:29 +00:00
void GPUCommon : : NotifyVideoUpload ( u32 addr , int size , int frameWidth , int format ) {
2016-12-21 17:33:08 +00:00
if ( Memory : : IsVRAMAddress ( addr ) ) {
2022-08-29 08:14:29 +00:00
framebufferManager_ - > NotifyVideoUpload ( addr , size , frameWidth , ( GEBufferFormat ) format ) ;
2016-12-21 17:33:08 +00:00
}
2022-08-29 08:14:29 +00:00
textureCache_ - > NotifyVideoUpload ( addr , size , frameWidth , ( GEBufferFormat ) format ) ;
2016-12-21 17:33:08 +00:00
InvalidateCache ( addr , size , GPU_INVALIDATE_SAFE ) ;
}
2022-08-07 04:12:42 +00:00
bool GPUCommon : : PerformStencilUpload ( u32 dest , int size , StencilUpload flags ) {
2016-12-21 17:33:08 +00:00
if ( framebufferManager_ - > MayIntersectFramebuffer ( dest ) ) {
2022-08-07 04:12:42 +00:00
framebufferManager_ - > PerformStencilUpload ( dest , size , flags ) ;
2016-12-21 17:33:08 +00:00
return true ;
}
return false ;
}
2017-02-14 11:42:35 +00:00
bool GPUCommon : : GetCurrentFramebuffer ( GPUDebugBuffer & buffer , GPUDebugFramebufferType type , int maxRes ) {
2018-11-17 16:56:32 +00:00
u32 fb_address = type = = GPU_DBG_FRAMEBUF_RENDER ? ( gstate . getFrameBufRawAddress ( ) | 0x04000000 ) : framebufferManager_ - > DisplayFramebufAddr ( ) ;
2017-02-14 11:42:35 +00:00
int fb_stride = type = = GPU_DBG_FRAMEBUF_RENDER ? gstate . FrameBufStride ( ) : framebufferManager_ - > DisplayFramebufStride ( ) ;
2022-04-24 15:30:33 +00:00
GEBufferFormat format = type = = GPU_DBG_FRAMEBUF_RENDER ? gstate_c . framebufFormat : framebufferManager_ - > DisplayFramebufFormat ( ) ;
2017-02-14 11:42:35 +00:00
return framebufferManager_ - > GetFramebuffer ( fb_address , fb_stride , format , buffer , maxRes ) ;
}
bool GPUCommon : : GetCurrentDepthbuffer ( GPUDebugBuffer & buffer ) {
2018-11-17 16:56:32 +00:00
u32 fb_address = gstate . getFrameBufRawAddress ( ) | 0x04000000 ;
2017-02-14 11:42:35 +00:00
int fb_stride = gstate . FrameBufStride ( ) ;
2018-11-17 16:56:32 +00:00
u32 z_address = gstate . getDepthBufRawAddress ( ) | 0x04000000 ;
2017-02-14 11:42:35 +00:00
int z_stride = gstate . DepthBufStride ( ) ;
return framebufferManager_ - > GetDepthbuffer ( fb_address , fb_stride , z_address , z_stride , buffer ) ;
}
bool GPUCommon : : GetCurrentStencilbuffer ( GPUDebugBuffer & buffer ) {
2018-11-17 16:56:32 +00:00
u32 fb_address = gstate . getFrameBufRawAddress ( ) | 0x04000000 ;
2017-02-14 11:42:35 +00:00
int fb_stride = gstate . FrameBufStride ( ) ;
return framebufferManager_ - > GetStencilbuffer ( fb_address , fb_stride , buffer ) ;
}
2017-02-17 23:43:02 +00:00
bool GPUCommon : : GetOutputFramebuffer ( GPUDebugBuffer & buffer ) {
2017-05-24 09:09:08 +00:00
// framebufferManager_ can be null here when taking screens in software rendering mode.
// TODO: Actually grab the framebuffer anyway.
return framebufferManager_ ? framebufferManager_ - > GetOutputFramebuffer ( buffer ) : false ;
2017-02-17 23:43:02 +00:00
}
2017-03-06 15:46:15 +00:00
2022-08-16 08:55:44 +00:00
std : : vector < FramebufferInfo > GPUCommon : : GetFramebufferList ( ) const {
2017-10-18 10:49:15 +00:00
return framebufferManager_ - > GetFramebufferList ( ) ;
}
2017-10-18 11:03:49 +00:00
bool GPUCommon : : GetCurrentSimpleVertices ( int count , std : : vector < GPUDebugVertex > & vertices , std : : vector < u16 > & indices ) {
return drawEngineCommon_ - > GetCurrentSimpleVertices ( count , vertices , indices ) ;
}
bool GPUCommon : : GetCurrentClut ( GPUDebugBuffer & buffer ) {
return textureCache_ - > GetCurrentClutBuffer ( buffer ) ;
}
bool GPUCommon : : GetCurrentTexture ( GPUDebugBuffer & buffer , int level ) {
if ( ! gstate . isTextureMapEnabled ( ) ) {
return false ;
}
return textureCache_ - > GetCurrentTextureDebug ( buffer , level ) ;
}
2017-10-20 09:06:06 +00:00
bool GPUCommon : : DescribeCodePtr ( const u8 * ptr , std : : string & name ) {
if ( drawEngineCommon_ - > IsCodePtrVertexDecoder ( ptr ) ) {
name = " VertexDecoderJit " ;
return true ;
}
return false ;
}
2018-02-26 10:58:17 +00:00
bool GPUCommon : : FramebufferDirty ( ) {
VirtualFramebuffer * vfb = framebufferManager_ - > GetDisplayVFB ( ) ;
if ( vfb ) {
bool dirty = vfb - > dirtyAfterDisplay ;
vfb - > dirtyAfterDisplay = false ;
return dirty ;
}
return true ;
}
bool GPUCommon : : FramebufferReallyDirty ( ) {
VirtualFramebuffer * vfb = framebufferManager_ - > GetDisplayVFB ( ) ;
if ( vfb ) {
bool dirty = vfb - > reallyDirtyAfterDisplay ;
vfb - > reallyDirtyAfterDisplay = false ;
return dirty ;
}
return true ;
}
2020-09-20 19:33:06 +00:00
2022-04-14 22:35:22 +00:00
void GPUCommon : : UpdateUVScaleOffset ( ) {
# ifdef _M_SSE
__m128i values = _mm_slli_epi32 ( _mm_load_si128 ( ( const __m128i * ) & gstate . texscaleu ) , 8 ) ;
2022-04-30 16:13:24 +00:00
_mm_storeu_si128 ( ( __m128i * ) & gstate_c . uv , values ) ;
2022-04-14 22:35:22 +00:00
# elif PPSSPP_ARCH(ARM_NEON)
const uint32x4_t values = vshlq_n_u32 ( vld1q_u32 ( ( const u32 * ) & gstate . texscaleu ) , 8 ) ;
vst1q_u32 ( ( u32 * ) & gstate_c . uv , values ) ;
# else
gstate_c . uv . uScale = getFloat24 ( gstate . texscaleu ) ;
gstate_c . uv . vScale = getFloat24 ( gstate . texscalev ) ;
gstate_c . uv . uOff = getFloat24 ( gstate . texoffsetu ) ;
gstate_c . uv . vOff = getFloat24 ( gstate . texoffsetv ) ;
# endif
}
2020-09-20 19:33:06 +00:00
size_t GPUCommon : : FormatGPUStatsCommon ( char * buffer , size_t size ) {
float vertexAverageCycles = gpuStats . numVertsSubmitted > 0 ? ( float ) gpuStats . vertexGPUCycles / ( float ) gpuStats . numVertsSubmitted : 0.0f ;
return snprintf ( buffer , size ,
" DL processing time: %0.2f ms \n "
2020-09-20 20:03:08 +00:00
" Draw calls: %d, flushes %d, clears %d (cached: %d) \n "
" Num Tracked Vertex Arrays: %d \n "
2020-09-20 19:33:06 +00:00
" Commands per call level: %i %i %i %i \n "
2020-09-20 20:03:08 +00:00
" Vertices: %d cached: %d uncached: %d \n "
" FBOs active: %d (evaluations: %d) \n "
" Textures: %d, dec: %d, invalidated: %d, hashed: %d kB \n "
2022-08-29 13:57:39 +00:00
" readbacks %d, uploads %d, depal %d \n "
" Copies: depth %d, color %d, reint %d, blend %d, selftex %d \n "
2020-09-20 20:03:08 +00:00
" GPU cycles executed: %d (%f per vertex) \n " ,
2020-09-20 19:33:06 +00:00
gpuStats . msProcessingDisplayLists * 1000.0f ,
gpuStats . numDrawCalls ,
gpuStats . numFlushes ,
gpuStats . numClears ,
gpuStats . numCachedDrawCalls ,
gpuStats . numTrackedVertexArrays ,
gpuStats . gpuCommandsAtCallLevel [ 0 ] , gpuStats . gpuCommandsAtCallLevel [ 1 ] , gpuStats . gpuCommandsAtCallLevel [ 2 ] , gpuStats . gpuCommandsAtCallLevel [ 3 ] ,
gpuStats . numVertsSubmitted ,
gpuStats . numCachedVertsDrawn ,
gpuStats . numUncachedVertsDrawn ,
( int ) framebufferManager_ - > NumVFBs ( ) ,
gpuStats . numFramebufferEvaluations ,
( int ) textureCache_ - > NumLoadedTextures ( ) ,
gpuStats . numTexturesDecoded ,
gpuStats . numTextureInvalidations ,
2020-09-20 20:03:08 +00:00
gpuStats . numTextureDataBytesHashed / 1024 ,
2020-09-20 19:33:06 +00:00
gpuStats . numReadbacks ,
2020-09-20 20:03:08 +00:00
gpuStats . numUploads ,
2022-08-29 13:39:29 +00:00
gpuStats . numDepal ,
2022-07-24 15:12:43 +00:00
gpuStats . numDepthCopies ,
2022-08-18 08:51:50 +00:00
gpuStats . numColorCopies ,
gpuStats . numReinterpretCopies ,
2022-08-29 13:39:29 +00:00
gpuStats . numCopiesForShaderBlend ,
2022-08-29 13:57:39 +00:00
gpuStats . numCopiesForSelfTex ,
2020-09-20 20:03:08 +00:00
gpuStats . vertexGPUCycles + gpuStats . otherGPUCycles ,
vertexAverageCycles
2020-09-20 19:33:06 +00:00
) ;
}
2022-09-20 08:02:15 +00:00
u32 GPUCommon : : CheckGPUFeatures ( ) const {
u32 features = 0 ;
if ( draw_ - > GetDeviceCaps ( ) . logicOpSupported ) {
features | = GPU_SUPPORTS_LOGIC_OP ;
}
if ( draw_ - > GetDeviceCaps ( ) . anisoSupported ) {
features | = GPU_SUPPORTS_ANISOTROPY ;
}
if ( draw_ - > GetDeviceCaps ( ) . textureNPOTFullySupported ) {
features | = GPU_SUPPORTS_TEXTURE_NPOT ;
}
if ( draw_ - > GetDeviceCaps ( ) . dualSourceBlend ) {
if ( ! g_Config . bVendorBugChecksEnabled | | ! draw_ - > GetBugs ( ) . Has ( Draw : : Bugs : : DUAL_SOURCE_BLENDING_BROKEN ) ) {
features | = GPU_SUPPORTS_DUALSOURCE_BLEND ;
}
}
if ( draw_ - > GetDeviceCaps ( ) . blendMinMaxSupported ) {
features | = GPU_SUPPORTS_BLEND_MINMAX ;
}
if ( PSP_CoreParameter ( ) . compat . flags ( ) . ClearToRAM ) {
features | = GPU_USE_CLEAR_RAM_HACK ;
}
return features ;
}