CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/GPU/GPUCommonHW.cpp
Views: 1401
#include "Common/Profiler/Profiler.h"12#include "Common/GPU/thin3d.h"3#include "Common/Serialize/Serializer.h"4#include "Common/System/System.h"56#include "Core/System.h"7#include "Core/Config.h"8#include "Core/Util/PPGeDraw.h"910#include "GPU/GPUCommonHW.h"11#include "GPU/Common/SplineCommon.h"12#include "GPU/Common/DrawEngineCommon.h"13#include "GPU/Common/TextureCacheCommon.h"14#include "GPU/Common/FramebufferManagerCommon.h"1516struct CommonCommandTableEntry {17uint8_t cmd;18uint8_t flags;19uint64_t dirty;20GPUCommonHW::CmdFunc func;21};2223struct CommandInfo {24uint64_t flags;25GPUCommonHW::CmdFunc func;2627// Dirty flags are mashed into the regular flags by a left shift of 8.28void AddDirty(u64 dirty) {29flags |= dirty << 8;30}31void RemoveDirty(u64 dirty) {32flags &= ~(dirty << 8);33}34};3536static CommandInfo cmdInfo_[256];3738const CommonCommandTableEntry commonCommandTable[] = {39// From Common. No flushing but definitely need execute.40{ GE_CMD_OFFSETADDR, FLAG_EXECUTE, 0, &GPUCommon::Execute_OffsetAddr },41{ GE_CMD_ORIGIN, FLAG_EXECUTE | FLAG_READS_PC, 0, &GPUCommon::Execute_Origin },42{ GE_CMD_JUMP, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommon::Execute_Jump },43{ GE_CMD_CALL, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommon::Execute_Call },44{ GE_CMD_RET, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommon::Execute_Ret },45{ GE_CMD_END, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommon::Execute_End },46{ GE_CMD_VADDR, FLAG_EXECUTE, 0, &GPUCommon::Execute_Vaddr },47{ GE_CMD_IADDR, FLAG_EXECUTE, 0, &GPUCommon::Execute_Iaddr },48{ GE_CMD_BJUMP, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommon::Execute_BJump }, // EXECUTE49{ GE_CMD_BOUNDINGBOX, FLAG_EXECUTE, 0, &GPUCommonHW::Execute_BoundingBox }, // Shouldn't need to FLUSHBEFORE.5051{ GE_CMD_PRIM, FLAG_EXECUTE, 0, &GPUCommonHW::Execute_Prim },52{ GE_CMD_BEZIER, FLAG_EXECUTE, 0, &GPUCommonHW::Execute_Bezier },53{ GE_CMD_SPLINE, FLAG_EXECUTE, 0, &GPUCommonHW::Execute_Spline },5455// Changing the vertex type requires us to flush.56{ GE_CMD_VERTEXTYPE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommonHW::Execute_VertexType },5758{ GE_CMD_LOADCLUT, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPUCommonHW::Execute_LoadClut},5960// These two are actually processed in CMD_END.61{ GE_CMD_SIGNAL },62{ GE_CMD_FINISH },6364// Changes that dirty the framebuffer65{ GE_CMD_FRAMEBUFPTR, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS },66{ GE_CMD_FRAMEBUFWIDTH, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE },67{ GE_CMD_FRAMEBUFPIXFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_FRAGMENTSHADER_STATE },68{ GE_CMD_ZBUFPTR, FLAG_FLUSHBEFOREONCHANGE },69{ GE_CMD_ZBUFWIDTH, FLAG_FLUSHBEFOREONCHANGE },7071{ GE_CMD_FOGCOLOR, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FOGCOLOR },72{ GE_CMD_FOG1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FOGCOEF },73{ GE_CMD_FOG2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FOGCOEF },7475// These affect the fragment shader so need flushing.76{ 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 | DIRTY_GEOMETRYSHADER_STATE },77{ GE_CMD_TEXTUREMAPENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE },78{ GE_CMD_FOGENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAGMENTSHADER_STATE },79{ GE_CMD_TEXMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS | DIRTY_FRAGMENTSHADER_STATE },80{ GE_CMD_TEXSHADELS, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE },81// Raster state for Direct3D 9, uncommon.82{ GE_CMD_SHADEMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE },83{ GE_CMD_TEXFUNC, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAGMENTSHADER_STATE | DIRTY_TEX_ALPHA_MUL },84{ GE_CMD_COLORTEST, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAGMENTSHADER_STATE },85{ GE_CMD_ALPHATESTENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAGMENTSHADER_STATE },86{ GE_CMD_COLORTESTENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAGMENTSHADER_STATE },87{ GE_CMD_COLORTESTMASK, FLAG_FLUSHBEFOREONCHANGE, DIRTY_ALPHACOLORMASK | DIRTY_FRAGMENTSHADER_STATE },8889// These change the vertex shader so need flushing.90{ GE_CMD_REVERSENORMAL, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE },91{ GE_CMD_LIGHTINGENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE },92{ GE_CMD_LIGHTENABLE0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE },93{ GE_CMD_LIGHTENABLE1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE },94{ GE_CMD_LIGHTENABLE2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE },95{ GE_CMD_LIGHTENABLE3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE },96{ GE_CMD_LIGHTTYPE0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_LIGHT0 },97{ GE_CMD_LIGHTTYPE1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_LIGHT1 },98{ GE_CMD_LIGHTTYPE2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_LIGHT2 },99{ GE_CMD_LIGHTTYPE3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_LIGHT3 },100{ GE_CMD_MATERIALUPDATE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE },101102// These change all shaders so need flushing.103{ GE_CMD_LIGHTMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE },104105{ GE_CMD_TEXFILTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },106{ GE_CMD_TEXWRAP, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS | DIRTY_FRAGMENTSHADER_STATE },107108// Uniform changes. though the fragmentshader optimizes based on these sometimes.109{ GE_CMD_ALPHATEST, FLAG_FLUSHBEFOREONCHANGE, DIRTY_ALPHACOLORREF | DIRTY_ALPHACOLORMASK | DIRTY_FRAGMENTSHADER_STATE },110{ GE_CMD_COLORREF, FLAG_FLUSHBEFOREONCHANGE, DIRTY_ALPHACOLORREF | DIRTY_FRAGMENTSHADER_STATE },111{ GE_CMD_TEXENVCOLOR, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXENV },112113// Simple render state changes. Handled in StateMapping.cpp.114{ GE_CMD_CULL, FLAG_FLUSHBEFOREONCHANGE, DIRTY_RASTER_STATE },115{ GE_CMD_CULLFACEENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_RASTER_STATE },116{ GE_CMD_DITHERENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_RASTER_STATE },117{ GE_CMD_STENCILOP, FLAG_FLUSHBEFOREONCHANGE, DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_FRAGMENTSHADER_STATE },118{ GE_CMD_STENCILTEST, FLAG_FLUSHBEFOREONCHANGE, DIRTY_STENCILREPLACEVALUE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE },119{ GE_CMD_STENCILTESTENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_FRAGMENTSHADER_STATE },120{ GE_CMD_ALPHABLENDENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE },121{ GE_CMD_BLENDMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE },122{ GE_CMD_BLENDFIXEDA, FLAG_FLUSHBEFOREONCHANGE, DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE },123{ GE_CMD_BLENDFIXEDB, FLAG_FLUSHBEFOREONCHANGE, DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE },124{ GE_CMD_MASKRGB, FLAG_FLUSHBEFOREONCHANGE, DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_COLORWRITEMASK },125{ GE_CMD_MASKALPHA, FLAG_FLUSHBEFOREONCHANGE, DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_COLORWRITEMASK },126{ GE_CMD_ZTEST, FLAG_FLUSHBEFOREONCHANGE, DIRTY_DEPTHSTENCIL_STATE | DIRTY_FRAGMENTSHADER_STATE },127{ GE_CMD_ZTESTENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_DEPTHSTENCIL_STATE | DIRTY_FRAGMENTSHADER_STATE },128{ GE_CMD_ZWRITEDISABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_DEPTHSTENCIL_STATE | DIRTY_FRAGMENTSHADER_STATE },129{ GE_CMD_LOGICOP, FLAG_FLUSHBEFOREONCHANGE, DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE },130{ GE_CMD_LOGICOPENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE },131132{ GE_CMD_TEXMAPMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE },133134// These are read on every SubmitPrim, no need for dirtying or flushing.135{ GE_CMD_TEXSCALEU },136{ GE_CMD_TEXSCALEV },137{ GE_CMD_TEXOFFSETU },138{ GE_CMD_TEXOFFSETV },139140{ GE_CMD_TEXSIZE0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPUCommonHW::Execute_TexSize0 },141{ GE_CMD_TEXSIZE1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },142{ GE_CMD_TEXSIZE2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },143{ GE_CMD_TEXSIZE3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },144{ GE_CMD_TEXSIZE4, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },145{ GE_CMD_TEXSIZE5, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },146{ GE_CMD_TEXSIZE6, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },147{ GE_CMD_TEXSIZE7, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },148{ GE_CMD_TEXFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE },149{ GE_CMD_TEXLEVEL, FLAG_EXECUTEONCHANGE, DIRTY_TEXTURE_PARAMS, &GPUCommonHW::Execute_TexLevel },150{ GE_CMD_TEXLODSLOPE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },151{ GE_CMD_TEXADDR0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE },152{ GE_CMD_TEXADDR1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },153{ GE_CMD_TEXADDR2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },154{ GE_CMD_TEXADDR3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },155{ GE_CMD_TEXADDR4, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },156{ GE_CMD_TEXADDR5, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },157{ GE_CMD_TEXADDR6, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },158{ GE_CMD_TEXADDR7, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },159{ GE_CMD_TEXBUFWIDTH0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE },160{ GE_CMD_TEXBUFWIDTH1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },161{ GE_CMD_TEXBUFWIDTH2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },162{ GE_CMD_TEXBUFWIDTH3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },163{ GE_CMD_TEXBUFWIDTH4, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },164{ GE_CMD_TEXBUFWIDTH5, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },165{ GE_CMD_TEXBUFWIDTH6, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },166{ GE_CMD_TEXBUFWIDTH7, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },167168// These must flush on change, so that LoadClut doesn't have to always flush.169{ GE_CMD_CLUTADDR, FLAG_FLUSHBEFOREONCHANGE },170{ GE_CMD_CLUTADDRUPPER, FLAG_FLUSHBEFOREONCHANGE },171{ GE_CMD_CLUTFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS | DIRTY_DEPAL },172173// Morph weights. TODO: Remove precomputation?174{ GE_CMD_MORPHWEIGHT0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight },175{ GE_CMD_MORPHWEIGHT1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight },176{ GE_CMD_MORPHWEIGHT2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight },177{ GE_CMD_MORPHWEIGHT3, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight },178{ GE_CMD_MORPHWEIGHT4, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight },179{ GE_CMD_MORPHWEIGHT5, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight },180{ GE_CMD_MORPHWEIGHT6, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight },181{ GE_CMD_MORPHWEIGHT7, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight },182183// Control spline/bezier patches. Don't really require flushing as such, but meh.184{ GE_CMD_PATCHDIVISION, FLAG_FLUSHBEFOREONCHANGE },185{ GE_CMD_PATCHPRIMITIVE, FLAG_FLUSHBEFOREONCHANGE },186{ GE_CMD_PATCHFACING, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE },187{ GE_CMD_PATCHCULLENABLE, FLAG_FLUSHBEFOREONCHANGE },188189// Can probably ignore this one as we don't support AA lines.190{ GE_CMD_ANTIALIASENABLE, FLAG_FLUSHBEFOREONCHANGE },191192// Viewport.193{ GE_CMD_OFFSETX, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES },194{ GE_CMD_OFFSETY, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES },195{ GE_CMD_VIEWPORTXSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES },196{ GE_CMD_VIEWPORTYSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES },197{ GE_CMD_VIEWPORTXCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES },198{ GE_CMD_VIEWPORTYCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES },199{ GE_CMD_VIEWPORTZSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_DEPTHRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE },200{ GE_CMD_VIEWPORTZCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_DEPTHRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE },201{ GE_CMD_DEPTHCLAMPENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_RASTER_STATE },202203// Z clip204{ GE_CMD_MINZ, FLAG_FLUSHBEFOREONCHANGE, DIRTY_DEPTHRANGE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE },205{ GE_CMD_MAXZ, FLAG_FLUSHBEFOREONCHANGE, DIRTY_DEPTHRANGE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE },206207// Region208{ GE_CMD_REGION1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES },209{ GE_CMD_REGION2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES },210211// Scissor212{ GE_CMD_SCISSOR1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES },213{ GE_CMD_SCISSOR2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES },214215// Lighting base colors216{ GE_CMD_AMBIENTCOLOR, FLAG_FLUSHBEFOREONCHANGE, DIRTY_AMBIENT },217{ GE_CMD_AMBIENTALPHA, FLAG_FLUSHBEFOREONCHANGE, DIRTY_AMBIENT },218{ GE_CMD_MATERIALDIFFUSE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_MATDIFFUSE },219{ GE_CMD_MATERIALEMISSIVE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_MATEMISSIVE },220{ GE_CMD_MATERIALAMBIENT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_MATAMBIENTALPHA },221{ GE_CMD_MATERIALALPHA, FLAG_FLUSHBEFOREONCHANGE, DIRTY_MATAMBIENTALPHA },222{ GE_CMD_MATERIALSPECULAR, FLAG_FLUSHBEFOREONCHANGE, DIRTY_MATSPECULAR },223{ GE_CMD_MATERIALSPECULARCOEF, FLAG_FLUSHBEFOREONCHANGE, DIRTY_MATSPECULAR },224225// Light parameters226{ GE_CMD_LX0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 },227{ GE_CMD_LY0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 },228{ GE_CMD_LZ0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 },229{ GE_CMD_LX1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 },230{ GE_CMD_LY1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 },231{ GE_CMD_LZ1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 },232{ GE_CMD_LX2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 },233{ GE_CMD_LY2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 },234{ GE_CMD_LZ2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 },235{ GE_CMD_LX3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 },236{ GE_CMD_LY3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 },237{ GE_CMD_LZ3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 },238239{ GE_CMD_LDX0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 },240{ GE_CMD_LDY0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 },241{ GE_CMD_LDZ0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 },242{ GE_CMD_LDX1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 },243{ GE_CMD_LDY1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 },244{ GE_CMD_LDZ1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 },245{ GE_CMD_LDX2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 },246{ GE_CMD_LDY2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 },247{ GE_CMD_LDZ2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 },248{ GE_CMD_LDX3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 },249{ GE_CMD_LDY3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 },250{ GE_CMD_LDZ3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 },251252{ GE_CMD_LKA0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 },253{ GE_CMD_LKB0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 },254{ GE_CMD_LKC0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 },255{ GE_CMD_LKA1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 },256{ GE_CMD_LKB1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 },257{ GE_CMD_LKC1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 },258{ GE_CMD_LKA2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 },259{ GE_CMD_LKB2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 },260{ GE_CMD_LKC2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 },261{ GE_CMD_LKA3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 },262{ GE_CMD_LKB3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 },263{ GE_CMD_LKC3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 },264265{ GE_CMD_LKS0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 },266{ GE_CMD_LKS1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 },267{ GE_CMD_LKS2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 },268{ GE_CMD_LKS3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 },269270{ GE_CMD_LKO0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 },271{ GE_CMD_LKO1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 },272{ GE_CMD_LKO2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 },273{ GE_CMD_LKO3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 },274275{ GE_CMD_LAC0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 },276{ GE_CMD_LDC0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 },277{ GE_CMD_LSC0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 },278{ GE_CMD_LAC1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 },279{ GE_CMD_LDC1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 },280{ GE_CMD_LSC1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 },281{ GE_CMD_LAC2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 },282{ GE_CMD_LDC2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 },283{ GE_CMD_LSC2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 },284{ GE_CMD_LAC3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 },285{ GE_CMD_LDC3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 },286{ GE_CMD_LSC3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 },287288// Ignored commands289{ GE_CMD_TEXFLUSH, FLAG_EXECUTE, 0, &GPUCommonHW::Execute_TexFlush },290{ GE_CMD_TEXSYNC, 0 },291292// These are just nop or part of other later commands.293{ GE_CMD_NOP, 0 },294{ GE_CMD_BASE, 0 },295{ GE_CMD_TRANSFERSRC, 0 },296{ GE_CMD_TRANSFERSRCW, 0 },297{ GE_CMD_TRANSFERDST, 0 },298{ GE_CMD_TRANSFERDSTW, 0 },299{ GE_CMD_TRANSFERSRCPOS, 0 },300{ GE_CMD_TRANSFERDSTPOS, 0 },301{ GE_CMD_TRANSFERSIZE, 0 },302{ GE_CMD_TRANSFERSTART, FLAG_EXECUTE | FLAG_READS_PC, 0, &GPUCommonHW::Execute_BlockTransferStart },303304// We don't use the dither table.305{ GE_CMD_DITH0 },306{ GE_CMD_DITH1 },307{ GE_CMD_DITH2 },308{ GE_CMD_DITH3 },309310// These handle their own flushing.311{ GE_CMD_WORLDMATRIXNUMBER, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommonHW::Execute_WorldMtxNum },312{ GE_CMD_WORLDMATRIXDATA, FLAG_EXECUTE, 0, &GPUCommonHW::Execute_WorldMtxData },313{ GE_CMD_VIEWMATRIXNUMBER, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommonHW::Execute_ViewMtxNum },314{ GE_CMD_VIEWMATRIXDATA, FLAG_EXECUTE, 0, &GPUCommonHW::Execute_ViewMtxData },315{ GE_CMD_PROJMATRIXNUMBER, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommonHW::Execute_ProjMtxNum },316{ GE_CMD_PROJMATRIXDATA, FLAG_EXECUTE, 0, &GPUCommonHW::Execute_ProjMtxData },317{ GE_CMD_TGENMATRIXNUMBER, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommonHW::Execute_TgenMtxNum },318{ GE_CMD_TGENMATRIXDATA, FLAG_EXECUTE, 0, &GPUCommonHW::Execute_TgenMtxData },319{ GE_CMD_BONEMATRIXNUMBER, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommonHW::Execute_BoneMtxNum },320{ GE_CMD_BONEMATRIXDATA, FLAG_EXECUTE, 0, &GPUCommonHW::Execute_BoneMtxData },321322// Vertex Screen/Texture/Color323{ GE_CMD_VSCX },324{ GE_CMD_VSCY },325{ GE_CMD_VSCZ },326{ GE_CMD_VTCS },327{ GE_CMD_VTCT },328{ GE_CMD_VTCQ },329{ GE_CMD_VCV },330{ GE_CMD_VAP, FLAG_EXECUTE, 0, &GPUCommon::Execute_ImmVertexAlphaPrim },331{ GE_CMD_VFC },332{ GE_CMD_VSCV },333334// "Missing" commands (gaps in the sequence)335{ GE_CMD_UNKNOWN_03, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },336{ GE_CMD_UNKNOWN_0D, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },337{ GE_CMD_UNKNOWN_11, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },338{ GE_CMD_UNKNOWN_29, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },339{ GE_CMD_UNKNOWN_34, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },340{ GE_CMD_UNKNOWN_35, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },341{ GE_CMD_UNKNOWN_39, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },342{ GE_CMD_UNKNOWN_4E, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },343{ GE_CMD_UNKNOWN_4F, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },344{ GE_CMD_UNKNOWN_52, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },345{ GE_CMD_UNKNOWN_59, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },346{ GE_CMD_UNKNOWN_5A, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },347{ GE_CMD_UNKNOWN_B6, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },348{ GE_CMD_UNKNOWN_B7, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },349{ GE_CMD_UNKNOWN_D1, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },350{ GE_CMD_UNKNOWN_ED, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },351{ GE_CMD_UNKNOWN_EF, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },352{ GE_CMD_UNKNOWN_FA, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },353{ GE_CMD_UNKNOWN_FB, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },354{ GE_CMD_UNKNOWN_FC, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },355{ GE_CMD_UNKNOWN_FD, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },356{ GE_CMD_UNKNOWN_FE, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown },357// Appears to be debugging related or something? Hit a lot in GoW.358{ GE_CMD_NOP_FF, 0 },359};360361GPUCommonHW::GPUCommonHW(GraphicsContext *gfxCtx, Draw::DrawContext *draw) : GPUCommon(gfxCtx, draw) {362memset(cmdInfo_, 0, sizeof(cmdInfo_));363364// Convert the command table to a faster format, and check for dupes.365std::set<u8> dupeCheck;366for (size_t i = 0; i < ARRAY_SIZE(commonCommandTable); i++) {367const u8 cmd = commonCommandTable[i].cmd;368if (dupeCheck.find(cmd) != dupeCheck.end()) {369ERROR_LOG(Log::G3D, "Command table Dupe: %02x (%i)", (int)cmd, (int)cmd);370} else {371dupeCheck.insert(cmd);372}373cmdInfo_[cmd].flags |= (uint64_t)commonCommandTable[i].flags | (commonCommandTable[i].dirty << 8);374cmdInfo_[cmd].func = commonCommandTable[i].func;375if ((cmdInfo_[cmd].flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) && !cmdInfo_[cmd].func) {376// Can't have FLAG_EXECUTE commands without a function pointer to execute.377Crash();378}379}380// Find commands missing from the table.381for (int i = 0; i < 0xEF; i++) {382if (dupeCheck.find((u8)i) == dupeCheck.end()) {383ERROR_LOG(Log::G3D, "Command missing from table: %02x (%i)", i, i);384}385}386387UpdateCmdInfo();388UpdateMSAALevel(draw);389}390391GPUCommonHW::~GPUCommonHW() {392// Clear features so they're not visible in system info.393gstate_c.SetUseFlags(0);394395// Delete the various common managers.396framebufferManager_->DestroyAllFBOs();397delete framebufferManager_;398delete textureCache_;399if (shaderManager_) {400shaderManager_->ClearShaders();401delete shaderManager_;402}403}404405// Called once per frame. Might also get called during the pause screen406// if "transparent".407void GPUCommonHW::CheckConfigChanged() {408if (configChanged_) {409ClearCacheNextFrame();410gstate_c.SetUseFlags(CheckGPUFeatures());411drawEngineCommon_->NotifyConfigChanged();412textureCache_->NotifyConfigChanged();413framebufferManager_->NotifyConfigChanged();414BuildReportingInfo();415configChanged_ = false;416}417418// Check needed when running tests.419if (framebufferManager_) {420framebufferManager_->CheckPostShaders();421}422}423424void GPUCommonHW::CheckDisplayResized() {425if (displayResized_) {426framebufferManager_->NotifyDisplayResized();427displayResized_ = false;428}429}430431void GPUCommonHW::CheckRenderResized() {432if (renderResized_) {433framebufferManager_->NotifyRenderResized(msaaLevel_);434renderResized_ = false;435}436}437438// Call at the END of the GPU implementation's DeviceLost439void GPUCommonHW::DeviceLost() {440framebufferManager_->DeviceLost();441draw_ = nullptr;442textureCache_->Clear(false);443textureCache_->DeviceLost();444shaderManager_->DeviceLost();445drawEngineCommon_->DeviceLost();446}447448// Call at the start of the GPU implementation's DeviceRestore449void GPUCommonHW::DeviceRestore(Draw::DrawContext *draw) {450draw_ = draw;451displayResized_ = true; // re-check display bounds.452renderResized_ = true;453framebufferManager_->DeviceRestore(draw_);454textureCache_->DeviceRestore(draw_);455shaderManager_->DeviceRestore(draw_);456drawEngineCommon_->DeviceRestore(draw_);457458PPGeSetDrawContext(draw_);459460gstate_c.SetUseFlags(CheckGPUFeatures());461BuildReportingInfo();462UpdateCmdInfo();463}464465void GPUCommonHW::UpdateCmdInfo() {466if (g_Config.bSoftwareSkinning) {467cmdInfo_[GE_CMD_VERTEXTYPE].flags &= ~FLAG_FLUSHBEFOREONCHANGE;468cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommonHW::Execute_VertexTypeSkinning;469} else {470cmdInfo_[GE_CMD_VERTEXTYPE].flags |= FLAG_FLUSHBEFOREONCHANGE;471cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommonHW::Execute_VertexType;472}473474// Reconfigure for light ubershader or not.475for (int i = 0; i < 4; i++) {476if (gstate_c.Use(GPU_USE_LIGHT_UBERSHADER)) {477cmdInfo_[GE_CMD_LIGHTENABLE0 + i].RemoveDirty(DIRTY_VERTEXSHADER_STATE);478cmdInfo_[GE_CMD_LIGHTENABLE0 + i].AddDirty(DIRTY_LIGHT_CONTROL);479cmdInfo_[GE_CMD_LIGHTTYPE0 + i].RemoveDirty(DIRTY_VERTEXSHADER_STATE);480cmdInfo_[GE_CMD_LIGHTTYPE0 + i].AddDirty(DIRTY_LIGHT_CONTROL);481} else {482cmdInfo_[GE_CMD_LIGHTENABLE0 + i].RemoveDirty(DIRTY_LIGHT_CONTROL);483cmdInfo_[GE_CMD_LIGHTENABLE0 + i].AddDirty(DIRTY_VERTEXSHADER_STATE);484cmdInfo_[GE_CMD_LIGHTTYPE0 + i].RemoveDirty(DIRTY_LIGHT_CONTROL);485cmdInfo_[GE_CMD_LIGHTTYPE0 + i].AddDirty(DIRTY_VERTEXSHADER_STATE);486}487}488489if (gstate_c.Use(GPU_USE_LIGHT_UBERSHADER)) {490cmdInfo_[GE_CMD_MATERIALUPDATE].RemoveDirty(DIRTY_VERTEXSHADER_STATE);491cmdInfo_[GE_CMD_MATERIALUPDATE].AddDirty(DIRTY_LIGHT_CONTROL);492} else {493cmdInfo_[GE_CMD_MATERIALUPDATE].RemoveDirty(DIRTY_LIGHT_CONTROL);494cmdInfo_[GE_CMD_MATERIALUPDATE].AddDirty(DIRTY_VERTEXSHADER_STATE);495}496497if (gstate_c.Use(GPU_USE_FRAGMENT_UBERSHADER)) {498// Texfunc controls both texalpha and doubling. The rest is not dynamic yet so can't remove fragment shader dirtying.499cmdInfo_[GE_CMD_TEXFUNC].AddDirty(DIRTY_TEX_ALPHA_MUL);500} else {501cmdInfo_[GE_CMD_TEXFUNC].RemoveDirty(DIRTY_TEX_ALPHA_MUL);502}503}504505void GPUCommonHW::BeginHostFrame() {506GPUCommon::BeginHostFrame();507if (drawEngineCommon_->EverUsedExactEqualDepth() && !sawExactEqualDepth_) {508sawExactEqualDepth_ = true;509gstate_c.SetUseFlags(CheckGPUFeatures());510}511}512513void GPUCommonHW::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {514framebufferManager_->SetDisplayFramebuffer(framebuf, stride, format);515}516517void GPUCommonHW::CheckFlushOp(int cmd, u32 diff) {518const u8 cmdFlags = cmdInfo_[cmd].flags;519if (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE)) {520if (dumpThisFrame_) {521NOTICE_LOG(Log::G3D, "================ FLUSH ================");522}523drawEngineCommon_->DispatchFlush();524}525}526527void GPUCommonHW::PreExecuteOp(u32 op, u32 diff) {528CheckFlushOp(op >> 24, diff);529}530531void GPUCommonHW::CopyDisplayToOutput(bool reallyDirty) {532// Flush anything left over.533drawEngineCommon_->DispatchFlush();534535shaderManager_->DirtyLastShader();536537framebufferManager_->CopyDisplayToOutput(reallyDirty);538539gstate_c.Dirty(DIRTY_TEXTURE_IMAGE);540}541542void GPUCommonHW::DoState(PointerWrap &p) {543GPUCommon::DoState(p);544545// TODO: Some of these things may not be necessary.546// None of these are necessary when saving.547if (p.mode == p.MODE_READ && !PSP_CoreParameter().frozen) {548textureCache_->Clear(true);549drawEngineCommon_->ClearTrackedVertexArrays();550551gstate_c.Dirty(DIRTY_TEXTURE_IMAGE);552framebufferManager_->DestroyAllFBOs();553}554}555556void GPUCommonHW::ClearCacheNextFrame() {557textureCache_->ClearNextFrame();558}559560// Needs to be called on GPU thread, not reporting thread.561void GPUCommonHW::BuildReportingInfo() {562using namespace Draw;563564reportingPrimaryInfo_ = draw_->GetInfoString(InfoField::VENDORSTRING);565reportingFullInfo_ = reportingPrimaryInfo_ + " - " + System_GetProperty(SYSPROP_GPUDRIVER_VERSION) + " - " + draw_->GetInfoString(InfoField::SHADELANGVERSION);566}567568u32 GPUCommonHW::CheckGPUFeatures() const {569u32 features = 0;570if (draw_->GetDeviceCaps().logicOpSupported) {571features |= GPU_USE_LOGIC_OP;572}573if (draw_->GetDeviceCaps().anisoSupported) {574features |= GPU_USE_ANISOTROPY;575}576if (draw_->GetDeviceCaps().textureNPOTFullySupported) {577features |= GPU_USE_TEXTURE_NPOT;578}579if (draw_->GetDeviceCaps().dualSourceBlend) {580if (!g_Config.bVendorBugChecksEnabled || !draw_->GetBugs().Has(Draw::Bugs::DUAL_SOURCE_BLENDING_BROKEN)) {581features |= GPU_USE_DUALSOURCE_BLEND;582}583}584if (draw_->GetDeviceCaps().blendMinMaxSupported) {585features |= GPU_USE_BLEND_MINMAX;586}587588if (draw_->GetDeviceCaps().clipDistanceSupported) {589features |= GPU_USE_CLIP_DISTANCE;590}591592if (draw_->GetDeviceCaps().cullDistanceSupported) {593features |= GPU_USE_CULL_DISTANCE;594}595596if (draw_->GetDeviceCaps().textureDepthSupported) {597features |= GPU_USE_DEPTH_TEXTURE;598}599600if (draw_->GetDeviceCaps().depthClampSupported) {601// Some backends always do GPU_USE_ACCURATE_DEPTH, but it's required for depth clamp.602features |= GPU_USE_DEPTH_CLAMP | GPU_USE_ACCURATE_DEPTH;603}604605bool canClipOrCull = draw_->GetDeviceCaps().clipDistanceSupported || draw_->GetDeviceCaps().cullDistanceSupported;606bool canDiscardVertex = !draw_->GetBugs().Has(Draw::Bugs::BROKEN_NAN_IN_CONDITIONAL);607if ((canClipOrCull || canDiscardVertex) && !g_Config.bDisableRangeCulling) {608// We'll dynamically use the parts that are supported, to reduce artifacts as much as possible.609features |= GPU_USE_VS_RANGE_CULLING;610}611612if (draw_->GetDeviceCaps().framebufferFetchSupported) {613features |= GPU_USE_FRAMEBUFFER_FETCH;614}615616if (draw_->GetShaderLanguageDesc().bitwiseOps && g_Config.bUberShaderVertex) {617features |= GPU_USE_LIGHT_UBERSHADER;618}619620if (PSP_CoreParameter().compat.flags().ClearToRAM) {621features |= GPU_USE_CLEAR_RAM_HACK;622}623624// Even without depth clamp, force accurate depth on for some games that break without it.625if (PSP_CoreParameter().compat.flags().DepthRangeHack) {626features |= GPU_USE_ACCURATE_DEPTH;627}628629// Some backends will turn this off again in the calling function.630if (g_Config.bUberShaderFragment) {631features |= GPU_USE_FRAGMENT_UBERSHADER;632}633634return features;635}636637u32 GPUCommonHW::CheckGPUFeaturesLate(u32 features) const {638// If we already have a 16-bit depth buffer, we don't need to round.639bool prefer24 = draw_->GetDeviceCaps().preferredDepthBufferFormat == Draw::DataFormat::D24_S8;640bool prefer16 = draw_->GetDeviceCaps().preferredDepthBufferFormat == Draw::DataFormat::D16;641if (!prefer16) {642if (sawExactEqualDepth_ && (features & GPU_USE_ACCURATE_DEPTH) != 0 && !PSP_CoreParameter().compat.flags().ForceMaxDepthResolution) {643// Exact equal tests tend to have issues unless we use the PSP's depth range.644// We use 24-bit depth virtually everwhere, the fallback is just for safety.645if (prefer24)646features |= GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT;647else648features |= GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT;649} else if (!g_Config.bHighQualityDepth && (features & GPU_USE_ACCURATE_DEPTH) != 0) {650features |= GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT;651} else if (PSP_CoreParameter().compat.flags().PixelDepthRounding) {652if (prefer24 && (features & GPU_USE_ACCURATE_DEPTH) != 0) {653// Here we can simulate a 16 bit depth buffer by scaling.654// Note that the depth buffer is fixed point, not floating, so dividing by 256 is pretty good.655features |= GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT;656} else {657// Use fragment rounding on where available otherwise.658features |= GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT;659}660} else if (PSP_CoreParameter().compat.flags().VertexDepthRounding) {661features |= GPU_ROUND_DEPTH_TO_16BIT;662}663}664665return features;666}667668void GPUCommonHW::UpdateMSAALevel(Draw::DrawContext *draw) {669int level = g_Config.iMultiSampleLevel;670if (draw && draw->GetDeviceCaps().multiSampleLevelsMask & (1 << level)) {671msaaLevel_ = level;672} else {673// Didn't support the configured level, so revert to 0.674msaaLevel_ = 0;675}676}677678std::vector<std::string> GPUCommonHW::DebugGetShaderIDs(DebugShaderType type) {679switch (type) {680case SHADER_TYPE_VERTEXLOADER:681return drawEngineCommon_->DebugGetVertexLoaderIDs();682case SHADER_TYPE_TEXTURE:683return textureCache_->GetTextureShaderCache()->DebugGetShaderIDs(type);684default:685return shaderManager_->DebugGetShaderIDs(type);686}687}688689std::string GPUCommonHW::DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) {690switch (type) {691case SHADER_TYPE_VERTEXLOADER:692return drawEngineCommon_->DebugGetVertexLoaderString(id, stringType);693case SHADER_TYPE_TEXTURE:694return textureCache_->GetTextureShaderCache()->DebugGetShaderString(id, type, stringType);695default:696return shaderManager_->DebugGetShaderString(id, type, stringType);697}698}699700bool GPUCommonHW::GetCurrentFramebuffer(GPUDebugBuffer &buffer, GPUDebugFramebufferType type, int maxRes) {701u32 fb_address = type == GPU_DBG_FRAMEBUF_RENDER ? (gstate.getFrameBufRawAddress() | 0x04000000) : framebufferManager_->DisplayFramebufAddr();702int fb_stride = type == GPU_DBG_FRAMEBUF_RENDER ? gstate.FrameBufStride() : framebufferManager_->DisplayFramebufStride();703GEBufferFormat format = type == GPU_DBG_FRAMEBUF_RENDER ? gstate_c.framebufFormat : framebufferManager_->DisplayFramebufFormat();704return framebufferManager_->GetFramebuffer(fb_address, fb_stride, format, buffer, maxRes);705}706707bool GPUCommonHW::GetCurrentDepthbuffer(GPUDebugBuffer &buffer) {708u32 fb_address = gstate.getFrameBufRawAddress() | 0x04000000;709int fb_stride = gstate.FrameBufStride();710711u32 z_address = gstate.getDepthBufRawAddress() | 0x04000000;712int z_stride = gstate.DepthBufStride();713714return framebufferManager_->GetDepthbuffer(fb_address, fb_stride, z_address, z_stride, buffer);715}716717bool GPUCommonHW::GetCurrentStencilbuffer(GPUDebugBuffer &buffer) {718u32 fb_address = gstate.getFrameBufRawAddress() | 0x04000000;719int fb_stride = gstate.FrameBufStride();720721return framebufferManager_->GetStencilbuffer(fb_address, fb_stride, buffer);722}723724bool GPUCommonHW::GetOutputFramebuffer(GPUDebugBuffer &buffer) {725// framebufferManager_ can be null here when taking screens in software rendering mode.726// TODO: Actually grab the framebuffer anyway.727return framebufferManager_ ? framebufferManager_->GetOutputFramebuffer(buffer) : false;728}729730std::vector<const VirtualFramebuffer *> GPUCommonHW::GetFramebufferList() const {731return framebufferManager_->GetFramebufferList();732}733734bool GPUCommonHW::GetCurrentClut(GPUDebugBuffer &buffer) {735return textureCache_->GetCurrentClutBuffer(buffer);736}737738bool GPUCommonHW::GetCurrentTexture(GPUDebugBuffer &buffer, int level, bool *isFramebuffer) {739if (!gstate.isTextureMapEnabled()) {740return false;741}742return textureCache_->GetCurrentTextureDebug(buffer, level, isFramebuffer);743}744745void GPUCommonHW::CheckDepthUsage(VirtualFramebuffer *vfb) {746if (!gstate_c.usingDepth) {747bool isReadingDepth = false;748bool isClearingDepth = false;749bool isWritingDepth = false;750if (gstate.isModeClear()) {751isClearingDepth = gstate.isClearModeDepthMask();752isWritingDepth = isClearingDepth;753} else if (gstate.isDepthTestEnabled()) {754isWritingDepth = gstate.isDepthWriteEnabled();755isReadingDepth = gstate.getDepthTestFunction() > GE_COMP_ALWAYS;756}757758if (isWritingDepth || isReadingDepth) {759gstate_c.usingDepth = true;760gstate_c.clearingDepth = isClearingDepth;761vfb->last_frame_depth_render = gpuStats.numFlips;762if (isWritingDepth) {763vfb->last_frame_depth_updated = gpuStats.numFlips;764}765framebufferManager_->SetDepthFrameBuffer(isClearingDepth);766}767}768}769770void GPUCommonHW::InvalidateCache(u32 addr, int size, GPUInvalidationType type) {771if (size > 0)772textureCache_->Invalidate(addr, size, type);773else774textureCache_->InvalidateAll(type);775776if (type != GPU_INVALIDATE_ALL && framebufferManager_->MayIntersectFramebufferColor(addr)) {777// Vempire invalidates (with writeback) after drawing, but before blitting.778// TODO: Investigate whether we can get this to work some other way.779if (type == GPU_INVALIDATE_SAFE) {780framebufferManager_->UpdateFromMemory(addr, size);781}782}783}784785bool GPUCommonHW::FramebufferDirty() {786VirtualFramebuffer *vfb = framebufferManager_->GetDisplayVFB();787if (vfb) {788bool dirty = vfb->dirtyAfterDisplay;789vfb->dirtyAfterDisplay = false;790return dirty;791}792return true;793}794795bool GPUCommonHW::FramebufferReallyDirty() {796VirtualFramebuffer *vfb = framebufferManager_->GetDisplayVFB();797if (vfb) {798bool dirty = vfb->reallyDirtyAfterDisplay;799vfb->reallyDirtyAfterDisplay = false;800return dirty;801}802return true;803}804805void GPUCommonHW::ExecuteOp(u32 op, u32 diff) {806const u8 cmd = op >> 24;807const CommandInfo info = cmdInfo_[cmd];808const u8 cmdFlags = info.flags;809if ((cmdFlags & FLAG_EXECUTE) || (diff && (cmdFlags & FLAG_EXECUTEONCHANGE))) {810(this->*info.func)(op, diff);811} else if (diff) {812uint64_t dirty = info.flags >> 8;813if (dirty)814gstate_c.Dirty(dirty);815}816}817818void GPUCommonHW::FastRunLoop(DisplayList &list) {819PROFILE_THIS_SCOPE("gpuloop");820821if (!Memory::IsValidAddress(list.pc)) {822// We're having some serious problems here, just bail and try to limp along and not crash the app.823downcount = 0;824return;825}826827const CommandInfo *cmdInfo = cmdInfo_;828int dc = downcount;829for (; dc > 0; --dc) {830// We know that display list PCs have the upper nibble == 0 - no need to mask the pointer831const u32 op = *(const u32_le *)(Memory::base + list.pc);832const u32 cmd = op >> 24;833const CommandInfo &info = cmdInfo[cmd];834const u32 diff = op ^ gstate.cmdmem[cmd];835if (diff == 0) {836if (info.flags & FLAG_EXECUTE) {837downcount = dc;838(this->*info.func)(op, diff);839dc = downcount;840}841} else {842uint64_t flags = info.flags;843if (flags & FLAG_FLUSHBEFOREONCHANGE) {844drawEngineCommon_->DispatchFlush();845}846gstate.cmdmem[cmd] = op;847if (flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) {848downcount = dc;849(this->*info.func)(op, diff);850dc = downcount;851} else {852uint64_t dirty = flags >> 8;853if (dirty)854gstate_c.Dirty(dirty);855}856}857list.pc += 4;858}859downcount = 0;860}861862void GPUCommonHW::Execute_VertexType(u32 op, u32 diff) {863if (diff) {864// TODO: We only need to dirty vshader-state here if the output format will be different.865gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE);866867if (diff & GE_VTYPE_THROUGH_MASK) {868// Switching between through and non-through, we need to invalidate a bunch of stuff.869gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE | DIRTY_CULLRANGE);870}871}872}873874void GPUCommonHW::Execute_VertexTypeSkinning(u32 op, u32 diff) {875// Don't flush when weight count changes.876if (diff & ~GE_VTYPE_WEIGHTCOUNT_MASK) {877// Restore and flush878gstate.vertType ^= diff;879Flush();880gstate.vertType ^= diff;881// In this case, we may be doing weights and morphs.882// Update any bone matrix uniforms so it uses them correctly.883if ((op & GE_VTYPE_MORPHCOUNT_MASK) != 0) {884gstate_c.Dirty(gstate_c.deferredVertTypeDirty);885gstate_c.deferredVertTypeDirty = 0;886}887gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE);888}889if (diff & GE_VTYPE_THROUGH_MASK)890gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE | DIRTY_CULLRANGE);891}892893void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {894// This drives all drawing. All other state we just buffer up, then we apply it only895// when it's time to draw. As most PSP games set state redundantly ALL THE TIME, this is a huge optimization.896897PROFILE_THIS_SCOPE("execprim");898899FlushImm();900901// Upper bits are ignored.902GEPrimitiveType prim = static_cast<GEPrimitiveType>((op >> 16) & 7);903SetDrawType(DRAW_PRIM, prim);904905// Discard AA lines as we can't do anything that makes sense with these anyway. The SW plugin might, though.906if (gstate.isAntiAliasEnabled()) {907// Heuristic derived from discussions in #6483 and #12588.908// Discard AA lines in Persona 3 Portable, DOA Paradise and Summon Night 5, while still keeping AA lines in Echochrome.909if ((prim == GE_PRIM_LINE_STRIP || prim == GE_PRIM_LINES) && gstate.getTextureFunction() == GE_TEXFUNC_REPLACE)910return;911}912913// Update cached framebuffer format.914// We store it in the cache so it can be modified for blue-to-alpha, next.915gstate_c.framebufFormat = gstate.FrameBufFormat();916917if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {918ERROR_LOG(Log::G3D, "Bad vertex address %08x!", gstate_c.vertexAddr);919return;920}921922// See the documentation for gstate_c.blueToAlpha.923bool blueToAlpha = false;924if (PSP_CoreParameter().compat.flags().BlueToAlpha) {925if (gstate_c.framebufFormat == GEBufferFormat::GE_FORMAT_565 && gstate.getColorMask() == 0x0FFFFF && !gstate.isLogicOpEnabled()) {926blueToAlpha = true;927gstate_c.framebufFormat = GEBufferFormat::GE_FORMAT_4444;928}929if (blueToAlpha != gstate_c.blueToAlpha) {930gstate_c.blueToAlpha = blueToAlpha;931gstate_c.Dirty(DIRTY_FRAMEBUF | DIRTY_FRAGMENTSHADER_STATE | DIRTY_BLEND_STATE);932}933}934935if (PSP_CoreParameter().compat.flags().SplitFramebufferMargin) {936switch (gstate.vertType & 0xFFFFFF) {937case 0x00800102: // through, u16 uv, u16 pos (used for the framebuffer effect in-game)938case 0x0080011c: // through, 8888 color, s16 pos (used for clearing in the margin of the title screen)939case 0x00000183: // float uv, float pos (used for drawing in the margin of the title screen)940// Need to re-check the framebuffer every one of these draws, to update the split if needed.941gstate_c.Dirty(DIRTY_FRAMEBUF);942}943}944945// This also makes skipping drawing very effective.946VirtualFramebuffer *vfb = framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason);947if (blueToAlpha) {948vfb->usageFlags |= FB_USAGE_BLUE_TO_ALPHA;949}950951if (gstate_c.dirty & DIRTY_VERTEXSHADER_STATE) {952vertexCost_ = EstimatePerVertexCost();953}954955u32 count = op & 0xFFFF;956// Must check this after SetRenderFrameBuffer so we know SKIPDRAW_NON_DISPLAYED_FB.957if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) {958// Rough estimate, not sure what's correct.959cyclesExecuted += vertexCost_ * count;960if (gstate.isModeClear()) {961gpuStats.numClears++;962}963return;964}965966CheckDepthUsage(vfb);967968const void *verts = Memory::GetPointerUnchecked(gstate_c.vertexAddr);969const void *inds = nullptr;970971bool isTriangle = IsTrianglePrim(prim);972973bool canExtend = isTriangle;974u32 vertexType = gstate.vertType;975if ((vertexType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {976u32 indexAddr = gstate_c.indexAddr;977u32 indexSize = (vertexType & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT;978if (!Memory::IsValidRange(indexAddr, count * indexSize)) {979ERROR_LOG(Log::G3D, "Bad index address %08x (%d)!", indexAddr, count);980return;981}982inds = Memory::GetPointerUnchecked(indexAddr);983canExtend = false;984}985986int bytesRead = 0;987gstate_c.UpdateUVScaleOffset();988989// cull mode990int cullMode = gstate.getCullMode();991992uint32_t vertTypeID = GetVertTypeID(vertexType, gstate.getUVGenMode(), g_Config.bSoftwareSkinning);993994#define MAX_CULL_CHECK_COUNT 6995996// For now, turn off culling on platforms where we don't have SIMD bounding box tests, like RISC-V.997#if PPSSPP_ARCH(ARM_NEON) || PPSSPP_ARCH(SSE2)998999#define PASSES_CULLING ((vertexType & (GE_VTYPE_THROUGH_MASK | GE_VTYPE_MORPHCOUNT_MASK | GE_VTYPE_WEIGHT_MASK | GE_VTYPE_IDX_MASK)) || count > MAX_CULL_CHECK_COUNT)10001001#else10021003#define PASSES_CULLING true10041005#endif10061007// If certain conditions are true, do frustum culling.1008bool passCulling = PASSES_CULLING;1009if (!passCulling) {1010// Do software culling.1011if (drawEngineCommon_->TestBoundingBoxFast(verts, count, vertexType)) {1012passCulling = true;1013} else {1014gpuStats.numCulledDraws++;1015}1016}10171018// If the first one in a batch passes, let's assume the whole batch passes.1019// Cuts down on checking, while not losing that much efficiency.1020bool onePassed = false;1021if (passCulling) {1022if (!drawEngineCommon_->SubmitPrim(verts, inds, prim, count, vertTypeID, true, &bytesRead)) {1023canExtend = false;1024}1025onePassed = true;1026} else {1027// Still need to advance bytesRead.1028drawEngineCommon_->SkipPrim(prim, count, vertTypeID, &bytesRead);1029canExtend = false;1030}10311032// After drawing, we advance the vertexAddr (when non indexed) or indexAddr (when indexed).1033// Some games rely on this, they don't bother reloading VADDR and IADDR.1034// The VADDR/IADDR registers are NOT updated.1035AdvanceVerts(vertexType, count, bytesRead);1036int totalVertCount = count;10371038// PRIMs are often followed by more PRIMs. Save some work and submit them immediately.1039const u32_le *start = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4);1040const u32_le *src = start;1041const u32_le *stall = currentList->stall ? (const u32_le *)Memory::GetPointerUnchecked(currentList->stall) : 0;10421043// Optimized submission of sequences of PRIM. Allows us to avoid going through all the mess1044// above for each one. This can be expanded to support additional games that intersperse1045// PRIM commands with other commands. A special case is Earth Defence Force 2 that changes culling mode1046// between each prim, we just change the triangle winding right here to still be able to join draw calls.10471048uint32_t vtypeCheckMask = g_Config.bSoftwareSkinning ? (~GE_VTYPE_WEIGHTCOUNT_MASK) : 0xFFFFFFFF;10491050if (debugRecording_)1051goto bail;10521053while (src != stall) {1054uint32_t data = *src;1055switch (data >> 24) {1056case GE_CMD_PRIM:1057{1058GEPrimitiveType newPrim = static_cast<GEPrimitiveType>((data >> 16) & 7);1059if (IsTrianglePrim(newPrim) != isTriangle)1060goto bail; // Can't join over this boundary. Might as well exit and get this on the next time around.1061// TODO: more efficient updating of verts/inds10621063u32 count = data & 0xFFFF;1064bool clockwise = !gstate.isCullEnabled() || gstate.getCullMode() == cullMode;1065if (canExtend) {1066// Non-indexed draws can be cheaply merged if vertexAddr hasn't changed, that means the vertices1067// are consecutive in memory. We also ignore culling here.1068_dbg_assert_((vertexType & GE_VTYPE_IDX_MASK) == GE_VTYPE_IDX_NONE);1069int commandsExecuted = drawEngineCommon_->ExtendNonIndexedPrim(src, stall, vertTypeID, clockwise, &bytesRead, isTriangle);1070if (!commandsExecuted) {1071goto bail;1072}1073src += commandsExecuted - 1;1074gstate_c.vertexAddr += bytesRead;1075totalVertCount += count;1076break;1077}10781079verts = Memory::GetPointerUnchecked(gstate_c.vertexAddr);1080inds = nullptr;1081if ((vertexType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {1082inds = Memory::GetPointerUnchecked(gstate_c.indexAddr);1083} else {1084// We can extend again after submitting a normal draw.1085canExtend = isTriangle;1086}10871088bool passCulling = onePassed || PASSES_CULLING;1089if (!passCulling) {1090// Do software culling.1091_dbg_assert_((vertexType & GE_VTYPE_IDX_MASK) == GE_VTYPE_IDX_NONE);1092if (drawEngineCommon_->TestBoundingBoxFast(verts, count, vertexType)) {1093passCulling = true;1094} else {1095gpuStats.numCulledDraws++;1096}1097}1098if (passCulling) {1099if (!drawEngineCommon_->SubmitPrim(verts, inds, newPrim, count, vertTypeID, clockwise, &bytesRead)) {1100canExtend = false;1101}1102// As soon as one passes, assume we don't need to check the rest of this batch.1103onePassed = true;1104} else {1105// Still need to advance bytesRead.1106drawEngineCommon_->SkipPrim(newPrim, count, vertTypeID, &bytesRead);1107canExtend = false;1108}1109AdvanceVerts(vertexType, count, bytesRead);1110totalVertCount += count;1111break;1112}1113case GE_CMD_VERTEXTYPE:1114{1115uint32_t diff = data ^ vertexType;1116// don't mask upper bits, vertexType is unmasked1117if (diff) {1118if (diff & vtypeCheckMask)1119goto bail;1120drawEngineCommon_->FlushSkin();1121canExtend = false; // TODO: Might support extending between some vertex types in the future.1122vertexType = data;1123vertTypeID = GetVertTypeID(vertexType, gstate.getUVGenMode(), g_Config.bSoftwareSkinning);1124}1125break;1126}1127case GE_CMD_VADDR:1128{1129gstate.cmdmem[GE_CMD_VADDR] = data;1130uint32_t newAddr = gstate_c.getRelativeAddress(data & 0x00FFFFFF);1131if (gstate_c.vertexAddr != newAddr) {1132canExtend = false;1133gstate_c.vertexAddr = newAddr;1134}1135break;1136}1137case GE_CMD_IADDR:1138gstate.cmdmem[GE_CMD_IADDR] = data;1139gstate_c.indexAddr = gstate_c.getRelativeAddress(data & 0x00FFFFFF);1140break;1141case GE_CMD_OFFSETADDR:1142gstate.cmdmem[GE_CMD_OFFSETADDR] = data;1143gstate_c.offsetAddr = data << 8;1144break;1145case GE_CMD_BASE:1146gstate.cmdmem[GE_CMD_BASE] = data;1147break;1148case GE_CMD_CULLFACEENABLE:1149// Earth Defence Force 21150if (gstate.cmdmem[GE_CMD_CULLFACEENABLE] != data) {1151goto bail;1152}1153break;1154case GE_CMD_CULL:1155// flip face by indices for triangles1156cullMode = data & 1;1157break;1158case GE_CMD_TEXFLUSH:1159case GE_CMD_NOP:1160case GE_CMD_NOP_FF:1161gstate.cmdmem[data >> 24] = data;1162break;1163case GE_CMD_BONEMATRIXNUMBER:1164gstate.cmdmem[GE_CMD_BONEMATRIXNUMBER] = data;1165break;1166case GE_CMD_TEXSCALEU:1167// We don't "dirty-check" - we could avoid getFloat24 and setting canExtend=false, but usually1168// when texscale commands are in line with the prims like this, they actually have an effect1169// and requires us to stop extending strips anyway.1170gstate.cmdmem[GE_CMD_TEXSCALEU] = data;1171gstate_c.uv.uScale = getFloat24(data);1172canExtend = false;1173break;1174case GE_CMD_TEXSCALEV:1175gstate.cmdmem[GE_CMD_TEXSCALEV] = data;1176gstate_c.uv.vScale = getFloat24(data);1177canExtend = false;1178break;1179case GE_CMD_TEXOFFSETU:1180gstate.cmdmem[GE_CMD_TEXOFFSETU] = data;1181gstate_c.uv.uOff = getFloat24(data);1182canExtend = false;1183break;1184case GE_CMD_TEXOFFSETV:1185gstate.cmdmem[GE_CMD_TEXOFFSETV] = data;1186gstate_c.uv.vOff = getFloat24(data);1187canExtend = false;1188break;1189case GE_CMD_TEXLEVEL:1190// Same Gran Turismo hack from Execute_TexLevel1191if ((data & 3) != GE_TEXLEVEL_MODE_AUTO && (0x00FF0000 & data) != 0) {1192goto bail;1193}1194gstate.cmdmem[GE_CMD_TEXLEVEL] = data;1195break;1196case GE_CMD_CALL:1197{1198// A bone matrix probably. If not we bail.1199const u32 target = gstate_c.getRelativeAddress(data & 0x00FFFFFC);1200if ((Memory::ReadUnchecked_U32(target) >> 24) == GE_CMD_BONEMATRIXDATA &&1201(Memory::ReadUnchecked_U32(target + 11 * 4) >> 24) == GE_CMD_BONEMATRIXDATA &&1202(Memory::ReadUnchecked_U32(target + 12 * 4) >> 24) == GE_CMD_RET &&1203(target > currentList->stall || target + 12 * 4 < currentList->stall) &&1204(gstate.boneMatrixNumber & 0x00FFFFFF) <= 96 - 12) {1205drawEngineCommon_->FlushSkin();1206canExtend = false;1207FastLoadBoneMatrix(target);1208} else {1209goto bail;1210}1211break;1212}12131214case GE_CMD_TEXBUFWIDTH0:1215case GE_CMD_TEXADDR0:1216if (data != gstate.cmdmem[data >> 24])1217goto bail;1218break;12191220default:1221// All other commands might need a flush or something, stop this inner loop.1222goto bail;1223}1224src++;1225}12261227bail:1228drawEngineCommon_->FlushSkin();1229gstate.cmdmem[GE_CMD_VERTEXTYPE] = vertexType;1230int cmdCount = src - start;1231// Skip over the commands we just read out manually.1232if (cmdCount > 0) {1233UpdatePC(currentList->pc, currentList->pc + cmdCount * 4);1234currentList->pc += cmdCount * 4;1235// flush back cull mode1236if (cullMode != gstate.getCullMode()) {1237// We rewrote everything to the old cull mode, so flush first.1238drawEngineCommon_->DispatchFlush();12391240// Now update things for next time.1241gstate.cmdmem[GE_CMD_CULL] ^= 1;1242gstate_c.Dirty(DIRTY_RASTER_STATE);1243}1244}12451246int cycles = vertexCost_ * totalVertCount;1247gpuStats.vertexGPUCycles += cycles;1248cyclesExecuted += cycles;1249}12501251void GPUCommonHW::Execute_Bezier(u32 op, u32 diff) {1252// We don't dirty on normal changes anymore as we prescale, but it's needed for splines/bezier.1253gstate_c.framebufFormat = gstate.FrameBufFormat();12541255// This also make skipping drawing very effective.1256VirtualFramebuffer *vfb = framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason);1257if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) {1258// TODO: Should this eat some cycles? Probably yes. Not sure if important.1259return;1260}12611262CheckDepthUsage(vfb);12631264if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {1265ERROR_LOG_REPORT(Log::G3D, "Bad vertex address %08x!", gstate_c.vertexAddr);1266return;1267}12681269const void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr);1270const void *indices = NULL;1271if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {1272if (!Memory::IsValidAddress(gstate_c.indexAddr)) {1273ERROR_LOG_REPORT(Log::G3D, "Bad index address %08x!", gstate_c.indexAddr);1274return;1275}1276indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);1277}12781279if (vertTypeIsSkinningEnabled(gstate.vertType)) {1280DEBUG_LOG_REPORT(Log::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));1281}12821283// Can't flush after setting gstate_c.submitType below since it'll be a mess - it must be done already.1284if (flushOnParams_)1285drawEngineCommon_->DispatchFlush();12861287Spline::BezierSurface surface;1288surface.tess_u = gstate.getPatchDivisionU();1289surface.tess_v = gstate.getPatchDivisionV();1290surface.num_points_u = op & 0xFF;1291surface.num_points_v = (op >> 8) & 0xFF;1292surface.num_patches_u = (surface.num_points_u - 1) / 3;1293surface.num_patches_v = (surface.num_points_v - 1) / 3;1294surface.primType = gstate.getPatchPrimitiveType();1295surface.patchFacing = gstate.patchfacing & 1;12961297SetDrawType(DRAW_BEZIER, PatchPrimToPrim(surface.primType));12981299// We need to dirty UVSCALEOFFSET here because we look at the submit type when setting that uniform.1300gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE | DIRTY_UVSCALEOFFSET);1301if (drawEngineCommon_->CanUseHardwareTessellation(surface.primType)) {1302gstate_c.submitType = SubmitType::HW_BEZIER;1303if (gstate_c.spline_num_points_u != surface.num_points_u) {1304gstate_c.Dirty(DIRTY_BEZIERSPLINE);1305gstate_c.spline_num_points_u = surface.num_points_u;1306}1307} else {1308gstate_c.submitType = SubmitType::BEZIER;1309}13101311int bytesRead = 0;1312gstate_c.UpdateUVScaleOffset();1313drawEngineCommon_->SubmitCurve(control_points, indices, surface, gstate.vertType, &bytesRead, "bezier");13141315gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE | DIRTY_UVSCALEOFFSET);1316gstate_c.submitType = SubmitType::DRAW;13171318// After drawing, we advance pointers - see SubmitPrim which does the same.1319int count = surface.num_points_u * surface.num_points_v;1320AdvanceVerts(gstate.vertType, count, bytesRead);1321}13221323void GPUCommonHW::Execute_Spline(u32 op, u32 diff) {1324// We don't dirty on normal changes anymore as we prescale, but it's needed for splines/bezier.1325gstate_c.framebufFormat = gstate.FrameBufFormat();13261327// This also make skipping drawing very effective.1328VirtualFramebuffer *vfb = framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason);1329if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) {1330// TODO: Should this eat some cycles? Probably yes. Not sure if important.1331return;1332}13331334CheckDepthUsage(vfb);13351336if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {1337ERROR_LOG_REPORT(Log::G3D, "Bad vertex address %08x!", gstate_c.vertexAddr);1338return;1339}13401341const void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr);1342const void *indices = NULL;1343if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {1344if (!Memory::IsValidAddress(gstate_c.indexAddr)) {1345ERROR_LOG_REPORT(Log::G3D, "Bad index address %08x!", gstate_c.indexAddr);1346return;1347}1348indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);1349}13501351if (vertTypeIsSkinningEnabled(gstate.vertType)) {1352DEBUG_LOG_REPORT(Log::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));1353}13541355// Can't flush after setting gstate_c.submitType below since it'll be a mess - it must be done already.1356if (flushOnParams_)1357drawEngineCommon_->DispatchFlush();13581359Spline::SplineSurface surface;1360surface.tess_u = gstate.getPatchDivisionU();1361surface.tess_v = gstate.getPatchDivisionV();1362surface.type_u = (op >> 16) & 0x3;1363surface.type_v = (op >> 18) & 0x3;1364surface.num_points_u = op & 0xFF;1365surface.num_points_v = (op >> 8) & 0xFF;1366surface.num_patches_u = surface.num_points_u - 3;1367surface.num_patches_v = surface.num_points_v - 3;1368surface.primType = gstate.getPatchPrimitiveType();1369surface.patchFacing = gstate.patchfacing & 1;13701371SetDrawType(DRAW_SPLINE, PatchPrimToPrim(surface.primType));13721373// We need to dirty UVSCALEOFFSET here because we look at the submit type when setting that uniform.1374gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE | DIRTY_UVSCALEOFFSET);1375if (drawEngineCommon_->CanUseHardwareTessellation(surface.primType)) {1376gstate_c.submitType = SubmitType::HW_SPLINE;1377if (gstate_c.spline_num_points_u != surface.num_points_u) {1378gstate_c.Dirty(DIRTY_BEZIERSPLINE);1379gstate_c.spline_num_points_u = surface.num_points_u;1380}1381} else {1382gstate_c.submitType = SubmitType::SPLINE;1383}13841385int bytesRead = 0;1386gstate_c.UpdateUVScaleOffset();1387drawEngineCommon_->SubmitCurve(control_points, indices, surface, gstate.vertType, &bytesRead, "spline");13881389gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE | DIRTY_UVSCALEOFFSET);1390gstate_c.submitType = SubmitType::DRAW;13911392// After drawing, we advance pointers - see SubmitPrim which does the same.1393int count = surface.num_points_u * surface.num_points_v;1394AdvanceVerts(gstate.vertType, count, bytesRead);1395}13961397void GPUCommonHW::Execute_BlockTransferStart(u32 op, u32 diff) {1398Flush();13991400PROFILE_THIS_SCOPE("block"); // don't include the flush in the profile, would be misleading.14011402gstate_c.framebufFormat = gstate.FrameBufFormat();14031404// and take appropriate action. This is a block transfer between RAM and VRAM, or vice versa.1405// Can we skip this on SkipDraw?1406DoBlockTransfer(gstate_c.skipDrawReason);1407}14081409void GPUCommonHW::Execute_TexSize0(u32 op, u32 diff) {1410// Render to texture may have overridden the width/height.1411// Don't reset it unless the size is different / the texture has changed.1412if (diff || gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS)) {1413gstate_c.curTextureWidth = gstate.getTextureWidth(0);1414gstate_c.curTextureHeight = gstate.getTextureHeight(0);1415// We will need to reset the texture now.1416gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);1417}1418}14191420void GPUCommonHW::Execute_TexLevel(u32 op, u32 diff) {1421// TODO: If you change the rules here, don't forget to update the inner interpreter in Execute_Prim.1422if (diff == 0xFFFFFFFF)1423return;14241425gstate.texlevel ^= diff;14261427if (diff & 0xFF0000) {1428// Piggyback on this flag for 3D textures.1429gstate_c.Dirty(DIRTY_MIPBIAS);1430}1431if (gstate.getTexLevelMode() != GE_TEXLEVEL_MODE_AUTO && (0x00FF0000 & gstate.texlevel) != 0) {1432Flush();1433}14341435gstate.texlevel ^= diff;14361437gstate_c.Dirty(DIRTY_TEXTURE_PARAMS | DIRTY_FRAGMENTSHADER_STATE);1438}14391440void GPUCommonHW::Execute_LoadClut(u32 op, u32 diff) {1441gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);1442textureCache_->LoadClut(gstate.getClutAddress(), gstate.getClutLoadBytes());1443}144414451446void GPUCommonHW::Execute_WorldMtxNum(u32 op, u32 diff) {1447if (!currentList) {1448gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | (op & 0xF);1449return;1450}14511452// This is almost always followed by GE_CMD_WORLDMATRIXDATA.1453const u32_le *src = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4);1454u32 *dst = (u32 *)(gstate.worldMatrix + (op & 0xF));1455const int end = 12 - (op & 0xF);1456int i = 0;14571458// We must record the individual data commands while debugRecording_.1459bool fastLoad = !debugRecording_ && end > 0;1460// Stalling in the middle of a matrix would be stupid, I doubt this check is necessary.1461if (currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) {1462fastLoad = false;1463}14641465if (fastLoad) {1466while ((src[i] >> 24) == GE_CMD_WORLDMATRIXDATA) {1467const u32 newVal = src[i] << 8;1468if (dst[i] != newVal) {1469Flush();1470dst[i] = newVal;1471gstate_c.Dirty(DIRTY_WORLDMATRIX);1472}1473if (++i >= end) {1474break;1475}1476}1477}14781479const int count = i;1480gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | ((op & 0xF) + count);14811482// Skip over the loaded data, it's done now.1483UpdatePC(currentList->pc, currentList->pc + count * 4);1484currentList->pc += count * 4;1485}14861487void GPUCommonHW::Execute_WorldMtxData(u32 op, u32 diff) {1488// Note: it's uncommon to get here now, see above.1489int num = gstate.worldmtxnum & 0x00FFFFFF;1490u32 newVal = op << 8;1491if (num < 12 && newVal != ((const u32 *)gstate.worldMatrix)[num]) {1492Flush();1493((u32 *)gstate.worldMatrix)[num] = newVal;1494gstate_c.Dirty(DIRTY_WORLDMATRIX);1495}1496num++;1497gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | (num & 0x00FFFFFF);1498gstate.worldmtxdata = GE_CMD_WORLDMATRIXDATA << 24;1499}15001501void GPUCommonHW::Execute_ViewMtxNum(u32 op, u32 diff) {1502if (!currentList) {1503gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | (op & 0xF);1504return;1505}15061507// This is almost always followed by GE_CMD_VIEWMATRIXDATA.1508const u32_le *src = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4);1509u32 *dst = (u32 *)(gstate.viewMatrix + (op & 0xF));1510const int end = 12 - (op & 0xF);1511int i = 0;15121513bool fastLoad = !debugRecording_ && end > 0;1514if (currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) {1515fastLoad = false;1516}15171518if (fastLoad) {1519while ((src[i] >> 24) == GE_CMD_VIEWMATRIXDATA) {1520const u32 newVal = src[i] << 8;1521if (dst[i] != newVal) {1522Flush();1523dst[i] = newVal;1524gstate_c.Dirty(DIRTY_VIEWMATRIX | DIRTY_CULL_PLANES);1525}1526if (++i >= end) {1527break;1528}1529}1530}15311532const int count = i;1533gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | ((op & 0xF) + count);15341535// Skip over the loaded data, it's done now.1536UpdatePC(currentList->pc, currentList->pc + count * 4);1537currentList->pc += count * 4;1538}15391540void GPUCommonHW::Execute_ViewMtxData(u32 op, u32 diff) {1541// Note: it's uncommon to get here now, see above.1542int num = gstate.viewmtxnum & 0x00FFFFFF;1543u32 newVal = op << 8;1544if (num < 12 && newVal != ((const u32 *)gstate.viewMatrix)[num]) {1545Flush();1546((u32 *)gstate.viewMatrix)[num] = newVal;1547gstate_c.Dirty(DIRTY_VIEWMATRIX | DIRTY_CULL_PLANES);1548}1549num++;1550gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | (num & 0x00FFFFFF);1551gstate.viewmtxdata = GE_CMD_VIEWMATRIXDATA << 24;1552}15531554void GPUCommonHW::Execute_ProjMtxNum(u32 op, u32 diff) {1555if (!currentList) {1556gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | (op & 0xF);1557return;1558}15591560// This is almost always followed by GE_CMD_PROJMATRIXDATA.1561const u32_le *src = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4);1562u32 *dst = (u32 *)(gstate.projMatrix + (op & 0xF));1563const int end = 16 - (op & 0xF);1564int i = 0;15651566bool fastLoad = !debugRecording_;1567if (currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) {1568fastLoad = false;1569}15701571if (fastLoad) {1572while ((src[i] >> 24) == GE_CMD_PROJMATRIXDATA) {1573const u32 newVal = src[i] << 8;1574if (dst[i] != newVal) {1575Flush();1576dst[i] = newVal;1577gstate_c.Dirty(DIRTY_PROJMATRIX | DIRTY_CULL_PLANES);1578}1579if (++i >= end) {1580break;1581}1582}1583}15841585const int count = i;1586gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | ((op & 0xF) + count);15871588// Skip over the loaded data, it's done now.1589UpdatePC(currentList->pc, currentList->pc + count * 4);1590currentList->pc += count * 4;1591}15921593void GPUCommonHW::Execute_ProjMtxData(u32 op, u32 diff) {1594// Note: it's uncommon to get here now, see above.1595int num = gstate.projmtxnum & 0x00FFFFFF;1596u32 newVal = op << 8;1597if (num < 16 && newVal != ((const u32 *)gstate.projMatrix)[num]) {1598Flush();1599((u32 *)gstate.projMatrix)[num] = newVal;1600gstate_c.Dirty(DIRTY_PROJMATRIX | DIRTY_CULL_PLANES);1601}1602num++;1603if (num <= 16)1604gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | (num & 0x00FFFFFF);1605gstate.projmtxdata = GE_CMD_PROJMATRIXDATA << 24;1606}16071608void GPUCommonHW::Execute_TgenMtxNum(u32 op, u32 diff) {1609if (!currentList) {1610gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | (op & 0xF);1611return;1612}16131614// This is almost always followed by GE_CMD_TGENMATRIXDATA.1615const u32_le *src = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4);1616u32 *dst = (u32 *)(gstate.tgenMatrix + (op & 0xF));1617const int end = 12 - (op & 0xF);1618int i = 0;16191620bool fastLoad = !debugRecording_ && end > 0;1621if (currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) {1622fastLoad = false;1623}16241625if (fastLoad) {1626while ((src[i] >> 24) == GE_CMD_TGENMATRIXDATA) {1627const u32 newVal = src[i] << 8;1628if (dst[i] != newVal) {1629Flush();1630dst[i] = newVal;1631// We check the matrix to see if we need projection.1632gstate_c.Dirty(DIRTY_TEXMATRIX | DIRTY_FRAGMENTSHADER_STATE);1633}1634if (++i >= end) {1635break;1636}1637}1638}16391640const int count = i;1641gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | ((op & 0xF) + count);16421643// Skip over the loaded data, it's done now.1644UpdatePC(currentList->pc, currentList->pc + count * 4);1645currentList->pc += count * 4;1646}16471648void GPUCommonHW::Execute_TgenMtxData(u32 op, u32 diff) {1649// Note: it's uncommon to get here now, see above.1650int num = gstate.texmtxnum & 0x00FFFFFF;1651u32 newVal = op << 8;1652if (num < 12 && newVal != ((const u32 *)gstate.tgenMatrix)[num]) {1653Flush();1654((u32 *)gstate.tgenMatrix)[num] = newVal;1655gstate_c.Dirty(DIRTY_TEXMATRIX | DIRTY_FRAGMENTSHADER_STATE); // We check the matrix to see if we need projection1656}1657num++;1658gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | (num & 0x00FFFFFF);1659gstate.texmtxdata = GE_CMD_TGENMATRIXDATA << 24;1660}16611662void GPUCommonHW::Execute_BoneMtxNum(u32 op, u32 diff) {1663if (!currentList) {1664gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (op & 0x7F);1665return;1666}16671668// This is almost always followed by GE_CMD_BONEMATRIXDATA.1669const u32_le *src = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4);1670u32 *dst = (u32 *)(gstate.boneMatrix + (op & 0x7F));1671const int end = 12 * 8 - (op & 0x7F);1672int i = 0;16731674bool fastLoad = !debugRecording_ && end > 0;1675if (currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) {1676fastLoad = false;1677}16781679if (fastLoad) {1680// If we can't use software skinning, we have to flush and dirty.1681if (!g_Config.bSoftwareSkinning) {1682while ((src[i] >> 24) == GE_CMD_BONEMATRIXDATA) {1683const u32 newVal = src[i] << 8;1684if (dst[i] != newVal) {1685Flush();1686dst[i] = newVal;1687}1688if (++i >= end) {1689break;1690}1691}16921693const unsigned int numPlusCount = (op & 0x7F) + i;1694for (unsigned int num = op & 0x7F; num < numPlusCount; num += 12) {1695gstate_c.Dirty(DIRTY_BONEMATRIX0 << (num / 12));1696}1697} else {1698while ((src[i] >> 24) == GE_CMD_BONEMATRIXDATA) {1699dst[i] = src[i] << 8;1700if (++i >= end) {1701break;1702}1703}17041705const unsigned int numPlusCount = (op & 0x7F) + i;1706for (unsigned int num = op & 0x7F; num < numPlusCount; num += 12) {1707gstate_c.deferredVertTypeDirty |= DIRTY_BONEMATRIX0 << (num / 12);1708}1709}1710}17111712const int count = i;1713gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | ((op & 0x7F) + count);17141715// Skip over the loaded data, it's done now.1716UpdatePC(currentList->pc, currentList->pc + count * 4);1717currentList->pc += count * 4;1718}17191720void GPUCommonHW::Execute_BoneMtxData(u32 op, u32 diff) {1721// Note: it's uncommon to get here now, see above.1722int num = gstate.boneMatrixNumber & 0x00FFFFFF;1723u32 newVal = op << 8;1724if (num < 96 && newVal != ((const u32 *)gstate.boneMatrix)[num]) {1725// Bone matrices should NOT flush when software skinning is enabled!1726if (!g_Config.bSoftwareSkinning) {1727Flush();1728gstate_c.Dirty(DIRTY_BONEMATRIX0 << (num / 12));1729} else {1730gstate_c.deferredVertTypeDirty |= DIRTY_BONEMATRIX0 << (num / 12);1731}1732((u32 *)gstate.boneMatrix)[num] = newVal;1733}1734num++;1735gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (num & 0x00FFFFFF);1736gstate.boneMatrixData = GE_CMD_BONEMATRIXDATA << 24;1737}17381739void GPUCommonHW::Execute_TexFlush(u32 op, u32 diff) {1740// Games call this when they need the effect of drawing to be visible to texturing.1741// And for a bunch of other reasons, but either way, this is what we need to do.1742// It's possible we could also use this as a hint for the texture cache somehow.1743framebufferManager_->DiscardFramebufferCopy();1744}17451746size_t GPUCommonHW::FormatGPUStatsCommon(char *buffer, size_t size) {1747float vertexAverageCycles = gpuStats.numVertsSubmitted > 0 ? (float)gpuStats.vertexGPUCycles / (float)gpuStats.numVertsSubmitted : 0.0f;1748return snprintf(buffer, size,1749"DL processing time: %0.2f ms, %d drawsync, %d listsync\n"1750"Draw: %d (%d dec, %d culled), flushes %d, clears %d, bbox jumps %d (%d updates)\n"1751"Vertices: %d dec: %d drawn: %d\n"1752"FBOs active: %d (evaluations: %d)\n"1753"Textures: %d, dec: %d, invalidated: %d, hashed: %d kB\n"1754"readbacks %d (%d non-block), upload %d (cached %d), depal %d\n"1755"block transfers: %d\n"1756"replacer: tracks %d references, %d unique textures\n"1757"Cpy: depth %d, color %d, reint %d, blend %d, self %d\n"1758"GPU cycles: %d (%0.1f per vertex)\n%s",1759gpuStats.msProcessingDisplayLists * 1000.0f,1760gpuStats.numDrawSyncs,1761gpuStats.numListSyncs,1762gpuStats.numDrawCalls,1763gpuStats.numVertexDecodes,1764gpuStats.numCulledDraws,1765gpuStats.numFlushes,1766gpuStats.numClears,1767gpuStats.numBBOXJumps,1768gpuStats.numPlaneUpdates,1769gpuStats.numVertsSubmitted,1770gpuStats.numVertsDecoded,1771gpuStats.numUncachedVertsDrawn,1772(int)framebufferManager_->NumVFBs(),1773gpuStats.numFramebufferEvaluations,1774(int)textureCache_->NumLoadedTextures(),1775gpuStats.numTexturesDecoded,1776gpuStats.numTextureInvalidations,1777gpuStats.numTextureDataBytesHashed / 1024,1778gpuStats.numBlockingReadbacks,1779gpuStats.numReadbacks,1780gpuStats.numUploads,1781gpuStats.numCachedUploads,1782gpuStats.numDepal,1783gpuStats.numBlockTransfers,1784gpuStats.numReplacerTrackedTex,1785gpuStats.numCachedReplacedTextures,1786gpuStats.numDepthCopies,1787gpuStats.numColorCopies,1788gpuStats.numReinterpretCopies,1789gpuStats.numCopiesForShaderBlend,1790gpuStats.numCopiesForSelfTex,1791gpuStats.vertexGPUCycles + gpuStats.otherGPUCycles,1792vertexAverageCycles,1793debugRecording_ ? "(debug-recording)" : ""1794);1795}179617971798