Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/GPU/Software/SoftGpu.cpp
5669 views
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <set>
19
20
#include "Common/System/Display.h"
21
#include "Common/GPU/OpenGL/GLFeatures.h"
22
23
#include "GPU/GPUState.h"
24
#include "GPU/ge_constants.h"
25
#include "GPU/Common/TextureDecoder.h"
26
#include "Common/Data/Convert/ColorConv.h"
27
#include "Common/GraphicsContext.h"
28
#include "Common/LogReporting.h"
29
#include "Core/Config.h"
30
#include "Core/ConfigValues.h"
31
#include "Core/Core.h"
32
#include "Core/System.h"
33
#include "Core/Debugger/MemBlockInfo.h"
34
#include "Core/MemMap.h"
35
#include "Core/MIPS/MIPS.h"
36
#include "Core/Util/PPGeDraw.h"
37
#include "Common/Profiler/Profiler.h"
38
#include "Common/GPU/thin3d.h"
39
40
#include "GPU/GPUCommon.h"
41
#include "GPU/Software/DrawPixel.h"
42
#include "GPU/Software/Rasterizer.h"
43
#include "GPU/Software/Sampler.h"
44
#include "GPU/Software/SoftGpu.h"
45
#include "GPU/Software/TransformUnit.h"
46
#include "GPU/Common/DrawEngineCommon.h"
47
#include "GPU/Common/PresentationCommon.h"
48
#include "Common/GPU/ShaderTranslation.h"
49
#include "GPU/Common/SplineCommon.h"
50
#include "GPU/Debugger/Record.h"
51
52
const int FB_WIDTH = 480;
53
const int FB_HEIGHT = 272;
54
55
uint8_t clut[1024];
56
FormatBuffer fb;
57
FormatBuffer depthbuf;
58
59
struct CommandInfo {
60
uint64_t flags;
61
SoftGPU::CmdFunc func;
62
};
63
static CommandInfo softgpuCmdInfo[256];
64
65
struct SoftwareCommandTableEntry {
66
uint8_t cmd;
67
uint8_t flags;
68
SoftDirty dirty;
69
SoftGPU::CmdFunc func;
70
};
71
72
// Software uses a different one, because dirty flags and execute funcs are a bit different.
73
const SoftwareCommandTableEntry softgpuCommandTable[] = {
74
{ GE_CMD_OFFSETADDR, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_OffsetAddr },
75
{ GE_CMD_ORIGIN, FLAG_EXECUTE | FLAG_READS_PC, SoftDirty::NONE, &GPUCommon::Execute_Origin },
76
{ GE_CMD_JUMP, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, SoftDirty::NONE, &GPUCommon::Execute_Jump },
77
{ GE_CMD_CALL, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, SoftDirty::NONE, &SoftGPU::Execute_Call },
78
{ GE_CMD_RET, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, SoftDirty::NONE, &GPUCommon::Execute_Ret },
79
{ GE_CMD_END, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, SoftDirty::NONE, &GPUCommon::Execute_End },
80
{ GE_CMD_VADDR, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Vaddr },
81
{ GE_CMD_IADDR, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Iaddr },
82
{ GE_CMD_BJUMP, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, SoftDirty::NONE, &GPUCommon::Execute_BJump },
83
{ GE_CMD_BOUNDINGBOX, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_BoundingBox },
84
85
{ GE_CMD_PRIM, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_Prim },
86
{ GE_CMD_BEZIER, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_Bezier },
87
{ GE_CMD_SPLINE, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_Spline },
88
89
// Vertex type affects a number of things, mainly because of through.
90
{ GE_CMD_VERTEXTYPE, FLAG_EXECUTEONCHANGE, SoftDirty::TRANSFORM_BASIC, &SoftGPU::Execute_VertexType },
91
92
{ GE_CMD_LOADCLUT, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_LoadClut },
93
94
// These two are actually processed in CMD_END, no flush needed.
95
{ GE_CMD_SIGNAL },
96
{ GE_CMD_FINISH },
97
98
// Changes that dirty the framebuffer or depthbuffer pointer/size.
99
{ GE_CMD_FRAMEBUFPTR, FLAG_EXECUTEONCHANGE, SoftDirty::BINNER_RANGE, &SoftGPU::Execute_FramebufPtr },
100
{ GE_CMD_FRAMEBUFWIDTH, FLAG_EXECUTEONCHANGE, SoftDirty::BINNER_RANGE | SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED, &SoftGPU::Execute_FramebufPtr },
101
{ GE_CMD_FRAMEBUFPIXFORMAT, FLAG_EXECUTEONCHANGE, SoftDirty::BINNER_RANGE | SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_STENCIL | SoftDirty::PIXEL_WRITEMASK, &SoftGPU::Execute_FramebufFormat },
102
{ GE_CMD_ZBUFPTR, FLAG_EXECUTEONCHANGE, SoftDirty::BINNER_RANGE, &SoftGPU::Execute_ZbufPtr },
103
{ GE_CMD_ZBUFWIDTH, FLAG_EXECUTEONCHANGE, SoftDirty::BINNER_RANGE | SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED, &SoftGPU::Execute_ZbufPtr },
104
105
{ GE_CMD_FOGCOLOR, 0, SoftDirty::PIXEL_CACHED },
106
{ GE_CMD_FOG1, 0, SoftDirty::TRANSFORM_FOG },
107
{ GE_CMD_FOG2, 0, SoftDirty::TRANSFORM_FOG },
108
109
{ GE_CMD_CLEARMODE, 0, SoftDirty::TRANSFORM_BASIC | SoftDirty::RAST_BASIC | SoftDirty::RAST_TEX | SoftDirty::SAMPLER_BASIC | SoftDirty::SAMPLER_TEXLIST | SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_ALPHA | SoftDirty::PIXEL_STENCIL | SoftDirty::PIXEL_CACHED | SoftDirty::BINNER_RANGE | SoftDirty::BINNER_OVERLAP },
110
{ GE_CMD_TEXTUREMAPENABLE, 0, SoftDirty::SAMPLER_BASIC | SoftDirty::SAMPLER_TEXLIST | SoftDirty::RAST_TEX | SoftDirty::TRANSFORM_BASIC | SoftDirty::BINNER_OVERLAP },
111
{ GE_CMD_FOGENABLE, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED | SoftDirty::TRANSFORM_BASIC | SoftDirty::TRANSFORM_FOG | SoftDirty::TRANSFORM_MATRIX },
112
{ GE_CMD_TEXMODE, 0, SoftDirty::SAMPLER_BASIC | SoftDirty::SAMPLER_TEXLIST | SoftDirty::RAST_TEX },
113
// Currently this doesn't affect any state, but maybe it should.
114
{ GE_CMD_TEXSHADELS },
115
{ GE_CMD_SHADEMODE, 0, SoftDirty::RAST_BASIC },
116
{ GE_CMD_TEXFUNC, 0, SoftDirty::SAMPLER_BASIC },
117
{ GE_CMD_COLORTEST, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED },
118
{ GE_CMD_ALPHATESTENABLE, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_ALPHA },
119
{ GE_CMD_COLORTESTENABLE, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED },
120
{ GE_CMD_COLORTESTMASK, 0, SoftDirty::PIXEL_CACHED },
121
122
{ GE_CMD_REVERSENORMAL, 0, SoftDirty::TRANSFORM_BASIC },
123
{ GE_CMD_LIGHTINGENABLE, 0, SoftDirty::TRANSFORM_BASIC | SoftDirty::TRANSFORM_MATRIX | SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_0 | SoftDirty::LIGHT_1 | SoftDirty::LIGHT_2 | SoftDirty::LIGHT_3 },
124
{ GE_CMD_LIGHTENABLE0, 0, SoftDirty::TRANSFORM_MATRIX | SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_0 },
125
{ GE_CMD_LIGHTENABLE1, 0, SoftDirty::TRANSFORM_MATRIX | SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_1 },
126
{ GE_CMD_LIGHTENABLE2, 0, SoftDirty::TRANSFORM_MATRIX | SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_2 },
127
{ GE_CMD_LIGHTENABLE3, 0, SoftDirty::TRANSFORM_MATRIX | SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_3 },
128
{ GE_CMD_LIGHTTYPE0, 0, SoftDirty::TRANSFORM_MATRIX | SoftDirty::LIGHT_0 },
129
{ GE_CMD_LIGHTTYPE1, 0, SoftDirty::TRANSFORM_MATRIX | SoftDirty::LIGHT_1 },
130
{ GE_CMD_LIGHTTYPE2, 0, SoftDirty::TRANSFORM_MATRIX | SoftDirty::LIGHT_2 },
131
{ GE_CMD_LIGHTTYPE3, 0, SoftDirty::TRANSFORM_MATRIX | SoftDirty::LIGHT_3 },
132
{ GE_CMD_MATERIALUPDATE, 0, SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL },
133
134
{ GE_CMD_LIGHTMODE, 0, SoftDirty::LIGHT_BASIC },
135
136
{ GE_CMD_TEXFILTER, 0, SoftDirty::SAMPLER_BASIC | SoftDirty::SAMPLER_TEXLIST | SoftDirty::RAST_TEX },
137
{ GE_CMD_TEXWRAP, 0, SoftDirty::SAMPLER_BASIC },
138
139
{ GE_CMD_ALPHATEST, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_ALPHA },
140
{ GE_CMD_COLORREF, 0, SoftDirty::PIXEL_CACHED },
141
{ GE_CMD_TEXENVCOLOR, 0, SoftDirty::PIXEL_CACHED },
142
143
// Currently, this is not part of state, just read on vertex processing.
144
{ GE_CMD_CULL },
145
{ GE_CMD_CULLFACEENABLE },
146
147
{ GE_CMD_DITHERENABLE, 0, SoftDirty::PIXEL_BASIC },
148
{ GE_CMD_STENCILOP, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_STENCIL },
149
{ GE_CMD_STENCILTEST, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_STENCIL },
150
{ GE_CMD_STENCILTESTENABLE, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_STENCIL },
151
{ GE_CMD_ALPHABLENDENABLE, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_ALPHA },
152
{ GE_CMD_BLENDMODE, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_ALPHA },
153
{ GE_CMD_BLENDFIXEDA, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_ALPHA | SoftDirty::PIXEL_CACHED },
154
{ GE_CMD_BLENDFIXEDB, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_ALPHA | SoftDirty::PIXEL_CACHED },
155
{ GE_CMD_MASKRGB, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_WRITEMASK },
156
{ GE_CMD_MASKALPHA, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_WRITEMASK },
157
{ GE_CMD_ZTEST, 0, SoftDirty::PIXEL_BASIC },
158
{ GE_CMD_ZTESTENABLE, 0, SoftDirty::PIXEL_BASIC | SoftDirty::BINNER_RANGE | SoftDirty::BINNER_OVERLAP },
159
{ GE_CMD_ZWRITEDISABLE, 0, SoftDirty::PIXEL_BASIC | SoftDirty::BINNER_RANGE | SoftDirty::BINNER_OVERLAP },
160
{ GE_CMD_LOGICOP, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED },
161
{ GE_CMD_LOGICOPENABLE, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED },
162
163
{ GE_CMD_TEXMAPMODE, 0, SoftDirty::TRANSFORM_BASIC | SoftDirty::RAST_TEX },
164
165
// These are read on every SubmitPrim, no need for dirtying or flushing.
166
{ GE_CMD_TEXSCALEU },
167
{ GE_CMD_TEXSCALEV },
168
{ GE_CMD_TEXOFFSETU },
169
{ GE_CMD_TEXOFFSETV },
170
171
{ GE_CMD_TEXSIZE0, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
172
{ GE_CMD_TEXSIZE1, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
173
{ GE_CMD_TEXSIZE2, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
174
{ GE_CMD_TEXSIZE3, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
175
{ GE_CMD_TEXSIZE4, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
176
{ GE_CMD_TEXSIZE5, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
177
{ GE_CMD_TEXSIZE6, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
178
{ GE_CMD_TEXSIZE7, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
179
{ GE_CMD_TEXFORMAT, 0, SoftDirty::SAMPLER_BASIC | SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
180
{ GE_CMD_TEXLEVEL, 0, SoftDirty::RAST_TEX },
181
{ GE_CMD_TEXLODSLOPE, 0, SoftDirty::RAST_TEX },
182
{ GE_CMD_TEXADDR0, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
183
{ GE_CMD_TEXADDR1, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
184
{ GE_CMD_TEXADDR2, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
185
{ GE_CMD_TEXADDR3, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
186
{ GE_CMD_TEXADDR4, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
187
{ GE_CMD_TEXADDR5, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
188
{ GE_CMD_TEXADDR6, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
189
{ GE_CMD_TEXADDR7, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
190
{ GE_CMD_TEXBUFWIDTH0, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
191
{ GE_CMD_TEXBUFWIDTH1, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
192
{ GE_CMD_TEXBUFWIDTH2, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
193
{ GE_CMD_TEXBUFWIDTH3, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
194
{ GE_CMD_TEXBUFWIDTH4, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
195
{ GE_CMD_TEXBUFWIDTH5, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
196
{ GE_CMD_TEXBUFWIDTH6, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
197
{ GE_CMD_TEXBUFWIDTH7, 0, SoftDirty::SAMPLER_TEXLIST | SoftDirty::BINNER_OVERLAP },
198
199
{ GE_CMD_CLUTADDR },
200
{ GE_CMD_CLUTADDRUPPER },
201
{ GE_CMD_CLUTFORMAT, 0, SoftDirty::SAMPLER_BASIC },
202
203
// Morph weights. TODO: Remove precomputation?
204
{ GE_CMD_MORPHWEIGHT0, FLAG_EXECUTEONCHANGE, SoftDirty::NONE, &GPUCommon::Execute_MorphWeight },
205
{ GE_CMD_MORPHWEIGHT1, FLAG_EXECUTEONCHANGE, SoftDirty::NONE, &GPUCommon::Execute_MorphWeight },
206
{ GE_CMD_MORPHWEIGHT2, FLAG_EXECUTEONCHANGE, SoftDirty::NONE, &GPUCommon::Execute_MorphWeight },
207
{ GE_CMD_MORPHWEIGHT3, FLAG_EXECUTEONCHANGE, SoftDirty::NONE, &GPUCommon::Execute_MorphWeight },
208
{ GE_CMD_MORPHWEIGHT4, FLAG_EXECUTEONCHANGE, SoftDirty::NONE, &GPUCommon::Execute_MorphWeight },
209
{ GE_CMD_MORPHWEIGHT5, FLAG_EXECUTEONCHANGE, SoftDirty::NONE, &GPUCommon::Execute_MorphWeight },
210
{ GE_CMD_MORPHWEIGHT6, FLAG_EXECUTEONCHANGE, SoftDirty::NONE, &GPUCommon::Execute_MorphWeight },
211
{ GE_CMD_MORPHWEIGHT7, FLAG_EXECUTEONCHANGE, SoftDirty::NONE, &GPUCommon::Execute_MorphWeight },
212
213
// No state of flushing required for patch parameters, currently.
214
{ GE_CMD_PATCHDIVISION },
215
{ GE_CMD_PATCHPRIMITIVE },
216
{ GE_CMD_PATCHFACING },
217
{ GE_CMD_PATCHCULLENABLE },
218
219
// Can probably ignore this one as we don't support AA lines.
220
{ GE_CMD_ANTIALIASENABLE, 0, SoftDirty::RAST_BASIC },
221
222
// Viewport and offset for positions.
223
{ GE_CMD_OFFSETX, 0, SoftDirty::RAST_OFFSET },
224
{ GE_CMD_OFFSETY, 0, SoftDirty::RAST_OFFSET },
225
{ GE_CMD_VIEWPORTXSCALE, 0, SoftDirty::TRANSFORM_VIEWPORT },
226
{ GE_CMD_VIEWPORTYSCALE, 0, SoftDirty::TRANSFORM_VIEWPORT },
227
{ GE_CMD_VIEWPORTXCENTER, 0, SoftDirty::TRANSFORM_VIEWPORT },
228
{ GE_CMD_VIEWPORTYCENTER, 0, SoftDirty::TRANSFORM_VIEWPORT },
229
{ GE_CMD_VIEWPORTZSCALE, 0, SoftDirty::TRANSFORM_VIEWPORT },
230
{ GE_CMD_VIEWPORTZCENTER, 0, SoftDirty::TRANSFORM_VIEWPORT },
231
{ GE_CMD_DEPTHCLAMPENABLE, 0, SoftDirty::TRANSFORM_BASIC },
232
233
// Z clipping.
234
{ GE_CMD_MINZ, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED },
235
{ GE_CMD_MAXZ, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED },
236
237
// Region doesn't seem to affect scissor or anything.
238
// As long as REGION1 is zero, REGION2 is effectively another scissor.
239
{ GE_CMD_REGION1, 0, SoftDirty::BINNER_RANGE },
240
{ GE_CMD_REGION2, 0, SoftDirty::BINNER_RANGE },
241
242
// Scissor, only used by the binner.
243
{ GE_CMD_SCISSOR1, 0, SoftDirty::BINNER_RANGE },
244
{ GE_CMD_SCISSOR2, 0, SoftDirty::BINNER_RANGE },
245
246
// Lighting base colors.
247
{ GE_CMD_AMBIENTCOLOR, 0, SoftDirty::LIGHT_MATERIAL },
248
{ GE_CMD_AMBIENTALPHA, 0, SoftDirty::LIGHT_MATERIAL },
249
{ GE_CMD_MATERIALDIFFUSE, 0, SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_0 | SoftDirty::LIGHT_1 | SoftDirty::LIGHT_2 | SoftDirty::LIGHT_3 },
250
// Not currently state, but maybe should be.
251
{ GE_CMD_MATERIALEMISSIVE, 0, SoftDirty::NONE },
252
{ GE_CMD_MATERIALAMBIENT, 0, SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_0 | SoftDirty::LIGHT_1 | SoftDirty::LIGHT_2 | SoftDirty::LIGHT_3 },
253
{ GE_CMD_MATERIALALPHA, 0, SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_0 | SoftDirty::LIGHT_1 | SoftDirty::LIGHT_2 | SoftDirty::LIGHT_3 },
254
{ GE_CMD_MATERIALSPECULAR, 0, SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_0 | SoftDirty::LIGHT_1 | SoftDirty::LIGHT_2 | SoftDirty::LIGHT_3 },
255
{ GE_CMD_MATERIALSPECULARCOEF, 0, SoftDirty::LIGHT_BASIC },
256
257
{ GE_CMD_LX0, 0, SoftDirty::LIGHT_0 },
258
{ GE_CMD_LY0, 0, SoftDirty::LIGHT_0 },
259
{ GE_CMD_LZ0, 0, SoftDirty::LIGHT_0 },
260
{ GE_CMD_LX1, 0, SoftDirty::LIGHT_1 },
261
{ GE_CMD_LY1, 0, SoftDirty::LIGHT_1 },
262
{ GE_CMD_LZ1, 0, SoftDirty::LIGHT_1 },
263
{ GE_CMD_LX2, 0, SoftDirty::LIGHT_2 },
264
{ GE_CMD_LY2, 0, SoftDirty::LIGHT_2 },
265
{ GE_CMD_LZ2, 0, SoftDirty::LIGHT_2 },
266
{ GE_CMD_LX3, 0, SoftDirty::LIGHT_3 },
267
{ GE_CMD_LY3, 0, SoftDirty::LIGHT_3 },
268
{ GE_CMD_LZ3, 0, SoftDirty::LIGHT_3 },
269
270
{ GE_CMD_LDX0, 0, SoftDirty::LIGHT_0 },
271
{ GE_CMD_LDY0, 0, SoftDirty::LIGHT_0 },
272
{ GE_CMD_LDZ0, 0, SoftDirty::LIGHT_0 },
273
{ GE_CMD_LDX1, 0, SoftDirty::LIGHT_1 },
274
{ GE_CMD_LDY1, 0, SoftDirty::LIGHT_1 },
275
{ GE_CMD_LDZ1, 0, SoftDirty::LIGHT_1 },
276
{ GE_CMD_LDX2, 0, SoftDirty::LIGHT_2 },
277
{ GE_CMD_LDY2, 0, SoftDirty::LIGHT_2 },
278
{ GE_CMD_LDZ2, 0, SoftDirty::LIGHT_2 },
279
{ GE_CMD_LDX3, 0, SoftDirty::LIGHT_3 },
280
{ GE_CMD_LDY3, 0, SoftDirty::LIGHT_3 },
281
{ GE_CMD_LDZ3, 0, SoftDirty::LIGHT_3 },
282
283
{ GE_CMD_LKA0, 0, SoftDirty::LIGHT_0 },
284
{ GE_CMD_LKB0, 0, SoftDirty::LIGHT_0 },
285
{ GE_CMD_LKC0, 0, SoftDirty::LIGHT_0 },
286
{ GE_CMD_LKA1, 0, SoftDirty::LIGHT_1 },
287
{ GE_CMD_LKB1, 0, SoftDirty::LIGHT_1 },
288
{ GE_CMD_LKC1, 0, SoftDirty::LIGHT_1 },
289
{ GE_CMD_LKA2, 0, SoftDirty::LIGHT_2 },
290
{ GE_CMD_LKB2, 0, SoftDirty::LIGHT_2 },
291
{ GE_CMD_LKC2, 0, SoftDirty::LIGHT_2 },
292
{ GE_CMD_LKA3, 0, SoftDirty::LIGHT_3 },
293
{ GE_CMD_LKB3, 0, SoftDirty::LIGHT_3 },
294
{ GE_CMD_LKC3, 0, SoftDirty::LIGHT_3 },
295
296
{ GE_CMD_LKS0, 0, SoftDirty::LIGHT_0 },
297
{ GE_CMD_LKS1, 0, SoftDirty::LIGHT_1 },
298
{ GE_CMD_LKS2, 0, SoftDirty::LIGHT_2 },
299
{ GE_CMD_LKS3, 0, SoftDirty::LIGHT_3 },
300
301
{ GE_CMD_LKO0, 0, SoftDirty::LIGHT_0 },
302
{ GE_CMD_LKO1, 0, SoftDirty::LIGHT_1 },
303
{ GE_CMD_LKO2, 0, SoftDirty::LIGHT_2 },
304
{ GE_CMD_LKO3, 0, SoftDirty::LIGHT_3 },
305
306
// Specific light colors.
307
{ GE_CMD_LAC0, 0, SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_0 },
308
{ GE_CMD_LDC0, 0, SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_0 },
309
{ GE_CMD_LSC0, 0, SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_0 },
310
{ GE_CMD_LAC1, 0, SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_1 },
311
{ GE_CMD_LDC1, 0, SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_1 },
312
{ GE_CMD_LSC1, 0, SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_1 },
313
{ GE_CMD_LAC2, 0, SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_2 },
314
{ GE_CMD_LDC2, 0, SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_2 },
315
{ GE_CMD_LSC2, 0, SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_2 },
316
{ GE_CMD_LAC3, 0, SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_3 },
317
{ GE_CMD_LDC3, 0, SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_3 },
318
{ GE_CMD_LSC3, 0, SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_3 },
319
320
// These are currently ignored, but might do flushing later.
321
{ GE_CMD_TEXFLUSH },
322
{ GE_CMD_TEXSYNC },
323
324
// These are just nop or part of other later commands.
325
{ GE_CMD_NOP },
326
{ GE_CMD_BASE },
327
{ GE_CMD_TRANSFERSRC },
328
{ GE_CMD_TRANSFERSRCW },
329
{ GE_CMD_TRANSFERDST },
330
{ GE_CMD_TRANSFERDSTW },
331
{ GE_CMD_TRANSFERSRCPOS },
332
{ GE_CMD_TRANSFERDSTPOS },
333
{ GE_CMD_TRANSFERSIZE },
334
335
// This will flush if necessary.
336
{ GE_CMD_TRANSFERSTART, FLAG_EXECUTE | FLAG_READS_PC, SoftDirty::NONE, &SoftGPU::Execute_BlockTransferStart },
337
338
// We cache the dither matrix, but the values affect little.
339
{ GE_CMD_DITH0, 0, SoftDirty::PIXEL_DITHER },
340
{ GE_CMD_DITH1, 0, SoftDirty::PIXEL_DITHER },
341
{ GE_CMD_DITH2, 0, SoftDirty::PIXEL_DITHER },
342
{ GE_CMD_DITH3, 0, SoftDirty::PIXEL_DITHER },
343
344
{ GE_CMD_WORLDMATRIXNUMBER, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_WorldMtxNum },
345
{ GE_CMD_WORLDMATRIXDATA, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_WorldMtxData },
346
{ GE_CMD_VIEWMATRIXNUMBER, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_ViewMtxNum },
347
{ GE_CMD_VIEWMATRIXDATA, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_ViewMtxData },
348
{ GE_CMD_PROJMATRIXNUMBER, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_ProjMtxNum },
349
{ GE_CMD_PROJMATRIXDATA, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_ProjMtxData },
350
// Currently not state.
351
{ GE_CMD_TGENMATRIXNUMBER, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_TgenMtxNum },
352
{ GE_CMD_TGENMATRIXDATA, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_TgenMtxData },
353
{ GE_CMD_BONEMATRIXNUMBER, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_BoneMtxNum },
354
{ GE_CMD_BONEMATRIXDATA, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_BoneMtxData },
355
356
// Vertex Screen/Texture/Color
357
{ GE_CMD_VSCX },
358
{ GE_CMD_VSCY },
359
{ GE_CMD_VSCZ },
360
{ GE_CMD_VTCS },
361
{ GE_CMD_VTCT },
362
{ GE_CMD_VTCQ },
363
{ GE_CMD_VCV },
364
{ GE_CMD_VAP, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_ImmVertexAlphaPrim },
365
{ GE_CMD_VFC },
366
{ GE_CMD_VSCV },
367
368
// "Missing" commands (gaps in the sequence)
369
{ GE_CMD_UNKNOWN_03, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
370
{ GE_CMD_UNKNOWN_0D, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
371
{ GE_CMD_UNKNOWN_11, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
372
{ GE_CMD_UNKNOWN_29, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
373
{ GE_CMD_UNKNOWN_34, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
374
{ GE_CMD_UNKNOWN_35, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
375
{ GE_CMD_UNKNOWN_39, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
376
{ GE_CMD_UNKNOWN_4E, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
377
{ GE_CMD_UNKNOWN_4F, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
378
{ GE_CMD_UNKNOWN_52, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
379
{ GE_CMD_UNKNOWN_59, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
380
{ GE_CMD_UNKNOWN_5A, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
381
{ GE_CMD_UNKNOWN_B6, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
382
{ GE_CMD_UNKNOWN_B7, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
383
{ GE_CMD_UNKNOWN_D1, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
384
{ GE_CMD_UNKNOWN_ED, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
385
{ GE_CMD_UNKNOWN_EF, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
386
{ GE_CMD_UNKNOWN_FA, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
387
{ GE_CMD_UNKNOWN_FB, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
388
{ GE_CMD_UNKNOWN_FC, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
389
{ GE_CMD_UNKNOWN_FD, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
390
{ GE_CMD_UNKNOWN_FE, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Unknown },
391
// Appears to be debugging related or something? Hit a lot in GoW.
392
{ GE_CMD_NOP_FF },
393
};
394
395
SoftGPU::SoftGPU(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
396
: GPUCommon(gfxCtx, draw)
397
{
398
fb.data = Memory::GetPointerWrite(0x44000000); // TODO: correct default address?
399
depthbuf.data = Memory::GetPointerWrite(0x44000000); // TODO: correct default address?
400
401
memset(softgpuCmdInfo, 0, sizeof(softgpuCmdInfo));
402
403
// Convert the command table to a faster format, and check for dupes.
404
std::set<u8> dupeCheck;
405
for (size_t i = 0; i < ARRAY_SIZE(softgpuCommandTable); i++) {
406
const u8 cmd = softgpuCommandTable[i].cmd;
407
if (dupeCheck.find(cmd) != dupeCheck.end()) {
408
ERROR_LOG(Log::G3D, "Command table Dupe: %02x (%i)", (int)cmd, (int)cmd);
409
} else {
410
dupeCheck.insert(cmd);
411
}
412
softgpuCmdInfo[cmd].flags |= (uint64_t)softgpuCommandTable[i].flags | ((uint64_t)softgpuCommandTable[i].dirty << 8);
413
softgpuCmdInfo[cmd].func = softgpuCommandTable[i].func;
414
if ((softgpuCmdInfo[cmd].flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) && !softgpuCmdInfo[cmd].func) {
415
// Can't have FLAG_EXECUTE commands without a function pointer to execute.
416
Crash();
417
}
418
}
419
// Find commands missing from the table.
420
for (int i = 0; i < 0xEF; i++) {
421
if (dupeCheck.find((u8)i) == dupeCheck.end()) {
422
ERROR_LOG(Log::G3D, "Command missing from table: %02x (%i)", i, i);
423
}
424
}
425
426
memset(vramDirty_, (uint8_t)(SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY), sizeof(vramDirty_));
427
// TODO: Is there a default?
428
displayFramebuf_ = 0;
429
displayStride_ = 512;
430
displayFormat_ = GE_FORMAT_8888;
431
432
Rasterizer::Init();
433
Sampler::Init();
434
drawEngine_ = new SoftwareDrawEngine();
435
drawEngine_->SetGPUCommon(this);
436
drawEngine_->Init();
437
drawEngineCommon_ = drawEngine_;
438
439
// Push the initial CLUT buffer in case it's all zero (we push only on change.)
440
if (drawEngine_->transformUnit.IsStarted())
441
drawEngine_->transformUnit.NotifyClutUpdate(clut);
442
443
// No need to flush for simple parameter changes.
444
flushOnParams_ = false;
445
446
if (gfxCtx && draw) {
447
presentation_ = new PresentationCommon(draw_);
448
presentation_->SetLanguage(draw_->GetShaderLanguageDesc().shaderLanguage);
449
presentation_->UpdateDisplaySize(PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight);
450
presentation_->UpdateRenderSize(PSP_CoreParameter().renderWidth, PSP_CoreParameter().renderHeight);
451
}
452
453
NotifyConfigChanged();
454
NotifyDisplayResized();
455
// NotifyRenderResized(); // hopefully not needed here.
456
}
457
458
void SoftGPU::DeviceLost() {
459
if (presentation_)
460
presentation_->DeviceLost();
461
draw_ = nullptr;
462
if (fbTex) {
463
fbTex->Release();
464
fbTex = nullptr;
465
}
466
}
467
468
void SoftGPU::DeviceRestore(Draw::DrawContext *draw) {
469
draw_ = draw;
470
if (presentation_)
471
presentation_->DeviceRestore(draw_);
472
PPGeSetDrawContext(draw_);
473
}
474
475
SoftGPU::~SoftGPU() {
476
if (fbTex) {
477
fbTex->Release();
478
fbTex = nullptr;
479
}
480
481
delete presentation_;
482
delete drawEngine_;
483
484
Sampler::Shutdown();
485
Rasterizer::Shutdown();
486
}
487
488
void SoftGPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
489
// Seems like this can point into RAM, but should be VRAM if not in RAM.
490
displayFramebuf_ = (framebuf & 0xFF000000) == 0 ? 0x44000000 | framebuf : framebuf;
491
displayStride_ = stride;
492
displayFormat_ = format;
493
494
NotifyDisplay(framebuf, stride, format);
495
}
496
497
DSStretch g_DarkStalkerStretch;
498
499
void SoftGPU::ConvertTextureDescFrom16(Draw::TextureDesc &desc, int srcwidth, int srcheight, const uint16_t *overrideData) {
500
// TODO: This should probably be converted in a shader instead..
501
fbTexBuffer_.resize(srcwidth * srcheight);
502
const uint16_t *displayBuffer = overrideData;
503
if (!displayBuffer)
504
displayBuffer = (const uint16_t *)Memory::GetPointer(displayFramebuf_);
505
506
for (int y = 0; y < srcheight; ++y) {
507
u32 *buf_line = &fbTexBuffer_[y * srcwidth];
508
const u16 *fb_line = &displayBuffer[y * displayStride_];
509
510
switch (displayFormat_) {
511
case GE_FORMAT_565:
512
ConvertRGB565ToRGBA8888(buf_line, fb_line, srcwidth);
513
break;
514
515
case GE_FORMAT_5551:
516
ConvertRGBA5551ToRGBA8888(buf_line, fb_line, srcwidth);
517
break;
518
519
case GE_FORMAT_4444:
520
ConvertRGBA4444ToRGBA8888(buf_line, fb_line, srcwidth);
521
break;
522
523
default:
524
ERROR_LOG_REPORT(Log::G3D, "Software: Unexpected framebuffer format: %d", displayFormat_);
525
break;
526
}
527
}
528
529
desc.width = srcwidth;
530
desc.height = srcheight;
531
desc.initData.push_back((uint8_t *)fbTexBuffer_.data());
532
}
533
534
// Copies RGBA8 data from RAM to the currently bound render target.
535
void SoftGPU::CopyToCurrentFboFromDisplayRam(const DisplayLayoutConfig &config, int srcwidth, int srcheight) {
536
if (!draw_ || !presentation_)
537
return;
538
float u0 = 0.0f;
539
float u1;
540
float v0 = 0.0f;
541
float v1 = 1.0f;
542
543
if (fbTex) {
544
fbTex->Release();
545
fbTex = nullptr;
546
}
547
548
// For accuracy, try to handle 0 stride - sometimes used.
549
if (displayStride_ == 0) {
550
srcheight = 1;
551
u1 = 1.0f;
552
} else {
553
u1 = (float)srcwidth / displayStride_;
554
}
555
556
Draw::TextureDesc desc{};
557
desc.type = Draw::TextureType::LINEAR2D;
558
desc.format = Draw::DataFormat::R8G8B8A8_UNORM;
559
desc.depth = 1;
560
desc.mipLevels = 1;
561
desc.tag = "SoftGPU";
562
bool hasImage = true;
563
564
OutputFlags outputFlags = config.iDisplayFilter == SCALE_NEAREST ? OutputFlags::NEAREST : OutputFlags::LINEAR;
565
bool hasPostShader = presentation_ && presentation_->HasPostShader();
566
567
if (PSP_CoreParameter().compat.flags().DarkStalkersPresentHack && displayFormat_ == GE_FORMAT_5551 && g_DarkStalkerStretch != DSStretch::Off) {
568
const u8 *data = Memory::GetPointerWrite(0x04088000);
569
bool fillDesc = true;
570
if (draw_->GetDataFormatSupport(Draw::DataFormat::A1B5G5R5_UNORM_PACK16) & Draw::FMT_TEXTURE) {
571
// The perfect one.
572
desc.format = Draw::DataFormat::A1B5G5R5_UNORM_PACK16;
573
} else if (!hasPostShader && (draw_->GetDataFormatSupport(Draw::DataFormat::A1R5G5B5_UNORM_PACK16) & Draw::FMT_TEXTURE)) {
574
// RB swapped, compensate with a shader.
575
desc.format = Draw::DataFormat::A1R5G5B5_UNORM_PACK16;
576
outputFlags |= OutputFlags::RB_SWIZZLE;
577
} else {
578
ConvertTextureDescFrom16(desc, srcwidth, srcheight, (const uint16_t *)data);
579
fillDesc = false;
580
}
581
if (fillDesc) {
582
desc.width = displayStride_ == 0 ? srcwidth : displayStride_;
583
desc.height = srcheight;
584
desc.initData.push_back(data);
585
}
586
u0 = 64.5f / (float)desc.width;
587
u1 = 447.5f / (float)desc.width;
588
v0 = 16.0f / (float)desc.height;
589
v1 = 240.0f / (float)desc.height;
590
if (g_DarkStalkerStretch == DSStretch::Normal) {
591
outputFlags |= OutputFlags::PILLARBOX;
592
}
593
} else if (!Memory::IsValidAddress(displayFramebuf_) || srcwidth == 0 || srcheight == 0) {
594
hasImage = false;
595
u1 = 1.0f;
596
} else if (displayFormat_ == GE_FORMAT_8888) {
597
const u8 *data = Memory::GetPointer(displayFramebuf_);
598
desc.width = displayStride_ == 0 ? srcwidth : displayStride_;
599
desc.height = srcheight;
600
desc.initData.push_back(data);
601
desc.format = Draw::DataFormat::R8G8B8A8_UNORM;
602
} else if (displayFormat_ == GE_FORMAT_5551) {
603
const u8 *data = Memory::GetPointer(displayFramebuf_);
604
bool fillDesc = true;
605
if (draw_->GetDataFormatSupport(Draw::DataFormat::A1B5G5R5_UNORM_PACK16) & Draw::FMT_TEXTURE) {
606
// The perfect one.
607
desc.format = Draw::DataFormat::A1B5G5R5_UNORM_PACK16;
608
} else if (!hasPostShader && (draw_->GetDataFormatSupport(Draw::DataFormat::A1R5G5B5_UNORM_PACK16) & Draw::FMT_TEXTURE)) {
609
// RB swapped, compensate with a shader.
610
desc.format = Draw::DataFormat::A1R5G5B5_UNORM_PACK16;
611
outputFlags |= OutputFlags::RB_SWIZZLE;
612
} else {
613
ConvertTextureDescFrom16(desc, srcwidth, srcheight);
614
u1 = 1.0f;
615
fillDesc = false;
616
}
617
if (fillDesc) {
618
desc.width = displayStride_ == 0 ? srcwidth : displayStride_;
619
desc.height = srcheight;
620
desc.initData.push_back(data);
621
}
622
} else {
623
ConvertTextureDescFrom16(desc, srcwidth, srcheight);
624
u1 = 1.0f;
625
}
626
if (!hasImage) {
627
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "CopyToCurrentFboFromDisplayRam");
628
presentation_->NotifyPresent();
629
return;
630
}
631
632
fbTex = draw_->CreateTexture(desc);
633
634
switch (GetGPUBackend()) {
635
case GPUBackend::OPENGL:
636
outputFlags |= OutputFlags::BACKBUFFER_FLIPPED;
637
break;
638
case GPUBackend::DIRECT3D11:
639
outputFlags |= OutputFlags::POSITION_FLIPPED;
640
break;
641
case GPUBackend::VULKAN:
642
break;
643
}
644
645
presentation_->SourceTexture(fbTex, desc.width, desc.height);
646
presentation_->RunPostshaderPasses(config, outputFlags, config.iInternalScreenRotation, u0, v0, u1, v1);
647
presentation_->CopyToOutput(config);
648
}
649
650
void SoftGPU::CopyDisplayToOutput(const DisplayLayoutConfig &config) {
651
drawEngine_->transformUnit.Flush(this, "output");
652
// The display always shows 480x272.
653
CopyToCurrentFboFromDisplayRam(config, FB_WIDTH, FB_HEIGHT);
654
MarkDirty(displayFramebuf_, displayStride_, 272, displayFormat_, SoftGPUVRAMDirty::CLEAR);
655
}
656
657
void SoftGPU::BeginHostFrame(const DisplayLayoutConfig &config) {
658
GPUCommon::BeginHostFrame(config);
659
if (presentation_) {
660
presentation_->BeginFrame(config);
661
}
662
}
663
664
bool SoftGPU::PresentedThisFrame() const {
665
return presentation_ ? presentation_->PresentedThisFrame() : false;
666
}
667
668
void SoftGPU::MarkDirty(uint32_t addr, uint32_t stride, uint32_t height, GEBufferFormat fmt, SoftGPUVRAMDirty value) {
669
uint32_t bytes = height * stride * (fmt == GE_FORMAT_8888 ? 4 : 2);
670
MarkDirty(addr, bytes, value);
671
}
672
673
void SoftGPU::MarkDirty(uint32_t addr, uint32_t bytes, SoftGPUVRAMDirty value) {
674
// Only bother tracking if frameskipping.
675
if (g_Config.iFrameSkip == 0)
676
return;
677
if (!Memory::IsVRAMAddress(addr) || !Memory::IsVRAMAddress(addr + bytes - 1))
678
return;
679
if (lastDirtyAddr_ == addr && lastDirtySize_ == bytes && lastDirtyValue_ == value)
680
return;
681
682
uint32_t start = ((addr - PSP_GetVidMemBase()) & 0x001FFFFF) >> 10;
683
uint32_t end = start + ((bytes + 1023) >> 10);
684
if (end > sizeof(vramDirty_)) {
685
end = sizeof(vramDirty_);
686
}
687
if (value == SoftGPUVRAMDirty::CLEAR || value == (SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY)) {
688
memset(vramDirty_ + start, (uint8_t)value, end - start);
689
} else {
690
for (uint32_t i = start; i < end; ++i) {
691
vramDirty_[i] |= (uint8_t)value;
692
}
693
}
694
695
lastDirtyAddr_ = addr;
696
lastDirtySize_ = bytes;
697
lastDirtyValue_ = value;
698
}
699
700
bool SoftGPU::ClearDirty(uint32_t addr, uint32_t stride, uint32_t height, GEBufferFormat fmt, SoftGPUVRAMDirty value) {
701
uint32_t bytes = height * stride * (fmt == GE_FORMAT_8888 ? 4 : 2);
702
return ClearDirty(addr, bytes, value);
703
}
704
705
bool SoftGPU::ClearDirty(uint32_t addr, uint32_t bytes, SoftGPUVRAMDirty value) {
706
if (!Memory::IsVRAMAddress(addr) || !Memory::IsVRAMAddress(addr + bytes - 1))
707
return false;
708
709
uint32_t start = ((addr - PSP_GetVidMemBase()) & 0x001FFFFF) >> 10;
710
uint32_t end = start + ((bytes + 1023) >> 10);
711
bool result = false;
712
for (uint32_t i = start; i < end; ++i) {
713
if (vramDirty_[i] & (uint8_t)value) {
714
result = true;
715
vramDirty_[i] &= ~(uint8_t)value;
716
}
717
}
718
719
lastDirtyAddr_ = 0;
720
lastDirtySize_ = 0;
721
722
return result;
723
}
724
725
void SoftGPU::NotifyRenderResized(const DisplayLayoutConfig &config) {
726
// Force the render params to 480x272 so other things work.
727
if (config.InternalRotationIsPortrait()) {
728
PSP_CoreParameter().renderWidth = 272;
729
PSP_CoreParameter().renderHeight = 480;
730
} else {
731
PSP_CoreParameter().renderWidth = 480;
732
PSP_CoreParameter().renderHeight = 272;
733
}
734
}
735
736
void SoftGPU::NotifyDisplayResized() {
737
displayResized_ = true;
738
}
739
740
void SoftGPU::CheckDisplayResized() {
741
if (displayResized_ && presentation_) {
742
presentation_->UpdateDisplaySize(PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight);
743
presentation_->UpdateRenderSize(PSP_CoreParameter().renderWidth, PSP_CoreParameter().renderHeight);
744
displayResized_ = false;
745
}
746
}
747
748
void SoftGPU::CheckConfigChanged(const DisplayLayoutConfig &config) {
749
if (configChanged_) {
750
drawEngineCommon_->NotifyConfigChanged();
751
BuildReportingInfo();
752
if (presentation_) {
753
presentation_->UpdatePostShader(config);
754
}
755
configChanged_ = false;
756
}
757
}
758
759
void SoftGPU::FastRunLoop(DisplayList &list) {
760
PROFILE_THIS_SCOPE("soft_runloop");
761
const auto *cmdInfo = softgpuCmdInfo;
762
int dc = downcount;
763
SoftDirty dirty = dirtyFlags_;
764
for (; dc > 0; --dc) {
765
u32 op = Memory::ReadUnchecked_U32(list.pc);
766
const u32 cmd = op >> 24;
767
const auto &info = cmdInfo[cmd];
768
const u32 diff = op ^ gstate.cmdmem[cmd];
769
if (diff == 0) {
770
if (info.flags & FLAG_EXECUTE) {
771
downcount = dc;
772
dirtyFlags_ = dirty;
773
(this->*info.func)(op, diff);
774
dirty = dirtyFlags_;
775
dc = downcount;
776
}
777
} else {
778
uint64_t flags = info.flags;
779
gstate.cmdmem[cmd] = op;
780
dirty |= SoftDirty(flags >> 8);
781
if (flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) {
782
downcount = dc;
783
dirtyFlags_ = dirty;
784
(this->*info.func)(op, diff);
785
dirty = dirtyFlags_;
786
dc = downcount;
787
}
788
}
789
list.pc += 4;
790
}
791
downcount = 0;
792
dirtyFlags_ = dirty;
793
}
794
795
bool SoftGPU::IsStarted() {
796
return drawEngine_ && drawEngine_->transformUnit.IsStarted();
797
}
798
799
void SoftGPU::ExecuteOp(u32 op, u32 diff) {
800
const u8 cmd = op >> 24;
801
const auto info = softgpuCmdInfo[cmd];
802
if (diff == 0) {
803
if (info.flags & FLAG_EXECUTE)
804
(this->*info.func)(op, diff);
805
} else {
806
dirtyFlags_ |= SoftDirty(info.flags >> 8);
807
if (info.flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE))
808
(this->*info.func)(op, diff);
809
}
810
}
811
812
void SoftGPU::Execute_BlockTransferStart(u32 op, u32 diff) {
813
u32 srcBasePtr = gstate.getTransferSrcAddress();
814
u32 srcStride = gstate.getTransferSrcStride();
815
816
u32 dstBasePtr = gstate.getTransferDstAddress();
817
u32 dstStride = gstate.getTransferDstStride();
818
819
int srcX = gstate.getTransferSrcX();
820
int srcY = gstate.getTransferSrcY();
821
822
int dstX = gstate.getTransferDstX();
823
int dstY = gstate.getTransferDstY();
824
825
int width = gstate.getTransferWidth();
826
int height = gstate.getTransferHeight();
827
828
int bpp = gstate.getTransferBpp();
829
830
// Use height less one to account for width, which can be greater or less than stride.
831
const uint32_t src = srcBasePtr + (srcY * srcStride + srcX) * bpp;
832
const uint32_t srcSize = (height - 1) * (srcStride + width) * bpp;
833
const uint32_t dst = dstBasePtr + (dstY * dstStride + dstX) * bpp;
834
const uint32_t dstSize = (height - 1) * (dstStride + width) * bpp;
835
836
// Need to flush both source and target, so we overwrite properly.
837
if (Memory::IsValidRange(src, srcSize) && Memory::IsValidRange(dst, dstSize)) {
838
drawEngine_->transformUnit.FlushIfOverlap(this, "blockxfer", false, src, srcStride, width * bpp, height);
839
drawEngine_->transformUnit.FlushIfOverlap(this, "blockxfer", true, dst, dstStride, width * bpp, height);
840
} else {
841
drawEngine_->transformUnit.Flush(this, "blockxfer_wrap");
842
}
843
844
DoBlockTransfer(gstate_c.skipDrawReason);
845
846
// Could theoretically dirty the framebuffer.
847
MarkDirty(dst, dstSize, SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY);
848
}
849
850
void SoftGPU::Execute_Prim(u32 op, u32 diff) {
851
u32 count = op & 0xFFFF;
852
// Upper bits are ignored.
853
GEPrimitiveType prim = static_cast<GEPrimitiveType>((op >> 16) & 7);
854
if (count == 0)
855
return;
856
FlushImm();
857
858
if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {
859
ERROR_LOG_REPORT(Log::G3D, "Software: Bad vertex address %08x!", gstate_c.vertexAddr);
860
return;
861
}
862
863
const void *verts = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
864
const void *indices = NULL;
865
if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
866
if (!Memory::IsValidAddress(gstate_c.indexAddr)) {
867
ERROR_LOG_REPORT(Log::G3D, "Software: Bad index address %08x!", gstate_c.indexAddr);
868
return;
869
}
870
indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);
871
}
872
873
cyclesExecuted += EstimatePerVertexCost() * count;
874
int bytesRead;
875
gstate_c.UpdateUVScaleOffset();
876
drawEngine_->transformUnit.SetDirty(dirtyFlags_);
877
drawEngine_->transformUnit.SubmitPrimitive(verts, indices, prim, count, gstate.vertType, &bytesRead, drawEngine_);
878
dirtyFlags_ = drawEngine_->transformUnit.GetDirty();
879
880
SoftGPUVRAMDirty mark = (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) != 0 ? SoftGPUVRAMDirty::DIRTY : SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY;
881
MarkDirty(gstate.getFrameBufAddress(), gstate.FrameBufStride(), gstate.getRegionY2() + 1, gstate.FrameBufFormat(), mark);
882
883
// After drawing, we advance the vertexAddr (when non indexed) or indexAddr (when indexed).
884
// Some games rely on this, they don't bother reloading VADDR and IADDR.
885
// The VADDR/IADDR registers are NOT updated.
886
AdvanceVerts(gstate.vertType, count, bytesRead);
887
}
888
889
void SoftGPU::Execute_Bezier(u32 op, u32 diff) {
890
// This also make skipping drawing very effective.
891
if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) {
892
// TODO: Should this eat some cycles? Probably yes. Not sure if important.
893
return;
894
}
895
896
if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {
897
ERROR_LOG_REPORT(Log::G3D, "Bad vertex address %08x!", gstate_c.vertexAddr);
898
return;
899
}
900
901
const void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
902
const void *indices = NULL;
903
if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
904
if (!Memory::IsValidAddress(gstate_c.indexAddr)) {
905
ERROR_LOG_REPORT(Log::G3D, "Bad index address %08x!", gstate_c.indexAddr);
906
return;
907
}
908
indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);
909
}
910
911
if ((gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) || vertTypeIsSkinningEnabled(gstate.vertType)) {
912
DEBUG_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));
913
}
914
915
Spline::BezierSurface surface;
916
surface.tess_u = gstate.getPatchDivisionU();
917
surface.tess_v = gstate.getPatchDivisionV();
918
surface.num_points_u = op & 0xFF;
919
surface.num_points_v = (op >> 8) & 0xFF;
920
surface.num_patches_u = (surface.num_points_u - 1) / 3;
921
surface.num_patches_v = (surface.num_points_v - 1) / 3;
922
surface.primType = gstate.getPatchPrimitiveType();
923
surface.patchFacing = gstate.patchfacing & 1;
924
925
SetDrawType(DRAW_BEZIER, PatchPrimToPrim(surface.primType));
926
927
int bytesRead = 0;
928
gstate_c.UpdateUVScaleOffset();
929
drawEngine_->transformUnit.SetDirty(dirtyFlags_);
930
drawEngineCommon_->SubmitCurve(control_points, indices, surface, gstate.vertType, &bytesRead, "bezier");
931
dirtyFlags_ = drawEngine_->transformUnit.GetDirty();
932
933
SoftGPUVRAMDirty mark = (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) != 0 ? SoftGPUVRAMDirty::DIRTY : SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY;
934
MarkDirty(gstate.getFrameBufAddress(), gstate.FrameBufStride(), gstate.getRegionY2() + 1, gstate.FrameBufFormat(), mark);
935
936
// After drawing, we advance pointers - see SubmitPrim which does the same.
937
int count = surface.num_points_u * surface.num_points_v;
938
AdvanceVerts(gstate.vertType, count, bytesRead);
939
}
940
941
void SoftGPU::Execute_Spline(u32 op, u32 diff) {
942
// This also make skipping drawing very effective.
943
if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) {
944
// TODO: Should this eat some cycles? Probably yes. Not sure if important.
945
return;
946
}
947
948
if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {
949
ERROR_LOG_REPORT(Log::G3D, "Bad vertex address %08x!", gstate_c.vertexAddr);
950
return;
951
}
952
953
const void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
954
const void *indices = NULL;
955
if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
956
if (!Memory::IsValidAddress(gstate_c.indexAddr)) {
957
ERROR_LOG_REPORT(Log::G3D, "Bad index address %08x!", gstate_c.indexAddr);
958
return;
959
}
960
indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);
961
}
962
963
if ((gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) || vertTypeIsSkinningEnabled(gstate.vertType)) {
964
DEBUG_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));
965
}
966
967
Spline::SplineSurface surface;
968
surface.tess_u = gstate.getPatchDivisionU();
969
surface.tess_v = gstate.getPatchDivisionV();
970
surface.type_u = (op >> 16) & 0x3;
971
surface.type_v = (op >> 18) & 0x3;
972
surface.num_points_u = op & 0xFF;
973
surface.num_points_v = (op >> 8) & 0xFF;
974
surface.num_patches_u = surface.num_points_u - 3;
975
surface.num_patches_v = surface.num_points_v - 3;
976
surface.primType = gstate.getPatchPrimitiveType();
977
surface.patchFacing = gstate.patchfacing & 1;
978
979
SetDrawType(DRAW_SPLINE, PatchPrimToPrim(surface.primType));
980
981
int bytesRead = 0;
982
gstate_c.UpdateUVScaleOffset();
983
drawEngine_->transformUnit.SetDirty(dirtyFlags_);
984
drawEngineCommon_->SubmitCurve(control_points, indices, surface, gstate.vertType, &bytesRead, "spline");
985
dirtyFlags_ = drawEngine_->transformUnit.GetDirty();
986
987
SoftGPUVRAMDirty mark = (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) != 0 ? SoftGPUVRAMDirty::DIRTY : SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY;
988
MarkDirty(gstate.getFrameBufAddress(), gstate.FrameBufStride(), gstate.getRegionY2() + 1, gstate.FrameBufFormat(), mark);
989
990
// After drawing, we advance pointers - see SubmitPrim which does the same.
991
int count = surface.num_points_u * surface.num_points_v;
992
AdvanceVerts(gstate.vertType, count, bytesRead);
993
}
994
995
void SoftGPU::Execute_LoadClut(u32 op, u32 diff) {
996
u32 clutAddr = gstate.getClutAddress();
997
// Avoid the hack in getClutLoadBytes() to inaccurately allow more palette data.
998
u32 clutTotalBytes = (gstate.getClutLoadBlocks() & 0x3F) * 32;
999
if (clutTotalBytes > 1024)
1000
clutTotalBytes = 1024;
1001
1002
// Might be copying drawing into the CLUT, so flush.
1003
drawEngine_->transformUnit.FlushIfOverlap(this, "loadclut", false, clutAddr, clutTotalBytes, clutTotalBytes, 1);
1004
1005
bool changed = false;
1006
if (Memory::IsValidAddress(clutAddr)) {
1007
u32 validSize = Memory::ClampValidSizeAt(clutAddr, clutTotalBytes);
1008
changed = memcmp(clut, Memory::GetPointerUnchecked(clutAddr), validSize) != 0;
1009
if (changed)
1010
Memory::MemcpyUnchecked(clut, clutAddr, validSize);
1011
if (validSize < clutTotalBytes) {
1012
// Zero out the parts that were outside valid memory.
1013
memset((u8 *)clut + validSize, 0x00, clutTotalBytes - validSize);
1014
changed = true;
1015
}
1016
} else if (clutAddr != 0) {
1017
// Some invalid addresses trigger a crash, others fill with zero. We always fill zero.
1018
DEBUG_LOG(Log::G3D, "Software: Invalid CLUT address, filling with garbage instead of crashing");
1019
memset(clut, 0x00, clutTotalBytes);
1020
changed = true;
1021
}
1022
1023
if (changed)
1024
drawEngine_->transformUnit.NotifyClutUpdate(clut);
1025
dirtyFlags_ |= SoftDirty::SAMPLER_CLUT;
1026
}
1027
1028
void SoftGPU::Execute_FramebufPtr(u32 op, u32 diff) {
1029
// We assume fb.data won't change while we're drawing.
1030
if (diff) {
1031
drawEngine_->transformUnit.Flush(this, "framebuf");
1032
fb.data = Memory::GetPointerWriteUnchecked(gstate.getFrameBufAddress());
1033
}
1034
}
1035
1036
void SoftGPU::Execute_FramebufFormat(u32 op, u32 diff) {
1037
// We should flush, because ranges within bins may change.
1038
if (diff)
1039
drawEngine_->transformUnit.Flush(this, "framebuf");
1040
}
1041
1042
void SoftGPU::Execute_BoundingBox(u32 op, u32 diff) {
1043
gstate_c.Dirty(DIRTY_CULL_PLANES);
1044
GPUCommon::Execute_BoundingBox(op, diff);
1045
}
1046
1047
void SoftGPU::Execute_ZbufPtr(u32 op, u32 diff) {
1048
// We assume depthbuf.data won't change while we're drawing.
1049
if (diff) {
1050
drawEngine_->transformUnit.Flush(this, "depthbuf");
1051
// For the pointer, ignore memory mirrors. This also gives some buffer for draws that go outside.
1052
// TODO: Confirm how wrapping is handled in drawing. Adjust if we ever handle VRAM mirrors more accurately.
1053
depthbuf.data = Memory::GetPointerWrite(gstate.getDepthBufAddress() & 0x041FFFF0);
1054
}
1055
}
1056
1057
void SoftGPU::Execute_VertexType(u32 op, u32 diff) {
1058
if ((diff & GE_VTYPE_THROUGH_MASK) != 0) {
1059
// This affects a lot of things, but some don't matter if it's off - so defer to when it's back on.
1060
dirtyFlags_ |= SoftDirty::RAST_BASIC | SoftDirty::PIXEL_BASIC;
1061
if ((op & GE_VTYPE_THROUGH_MASK) == 0) {
1062
dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX | SoftDirty::TRANSFORM_VIEWPORT | SoftDirty::TRANSFORM_FOG;
1063
dirtyFlags_ |= SoftDirty::LIGHT_BASIC | SoftDirty::LIGHT_MATERIAL | SoftDirty::LIGHT_0 | SoftDirty::LIGHT_1 | SoftDirty::LIGHT_2 | SoftDirty::LIGHT_3;
1064
dirtyFlags_ |= SoftDirty::PIXEL_CACHED;
1065
}
1066
}
1067
}
1068
1069
void SoftGPU::Execute_WorldMtxNum(u32 op, u32 diff) {
1070
// Setting 0xFFFFF0 will reset to 0.
1071
gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | (op & 0xF);
1072
}
1073
1074
void SoftGPU::Execute_ViewMtxNum(u32 op, u32 diff) {
1075
gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | (op & 0xF);
1076
}
1077
1078
void SoftGPU::Execute_ProjMtxNum(u32 op, u32 diff) {
1079
gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | (op & 0xF);
1080
}
1081
1082
void SoftGPU::Execute_TgenMtxNum(u32 op, u32 diff) {
1083
gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | (op & 0xF);
1084
}
1085
1086
void SoftGPU::Execute_BoneMtxNum(u32 op, u32 diff) {
1087
// Setting any bits outside 0x7F are ignored and resets the internal counter.
1088
gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (op & 0x7F);
1089
}
1090
1091
void SoftGPU::Execute_WorldMtxData(u32 op, u32 diff) {
1092
int num = gstate.worldmtxnum & 0x00FFFFFF;
1093
if (num < 12) {
1094
u32 *target = (u32 *)&gstate.worldMatrix[num];
1095
u32 newVal = op << 8;
1096
if (newVal != *target) {
1097
*target = newVal;
1098
dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX;
1099
gstate_c.Dirty(DIRTY_CULL_PLANES);
1100
}
1101
}
1102
1103
// Also update the CPU visible values, which update differently.
1104
u32 *target = &matrixVisible.all[12 * 8 + (num & 0xF)];
1105
*target = op & 0x00FFFFFF;
1106
1107
num++;
1108
gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | (num & 0x00FFFFFF);
1109
gstate.worldmtxdata = GE_CMD_WORLDMATRIXDATA << 24;
1110
}
1111
1112
void SoftGPU::Execute_ViewMtxData(u32 op, u32 diff) {
1113
int num = gstate.viewmtxnum & 0x00FFFFFF;
1114
if (num < 12) {
1115
u32 *target = (u32 *)&gstate.viewMatrix[num];
1116
u32 newVal = op << 8;
1117
if (newVal != *target) {
1118
*target = newVal;
1119
dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX;
1120
gstate_c.Dirty(DIRTY_CULL_PLANES);
1121
}
1122
}
1123
1124
// Also update the CPU visible values, which update differently.
1125
u32 *target = &matrixVisible.all[12 * 8 + 12 + (num & 0xF)];
1126
*target = op & 0x00FFFFFF;
1127
1128
num++;
1129
gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | (num & 0x00FFFFFF);
1130
gstate.viewmtxdata = GE_CMD_VIEWMATRIXDATA << 24;
1131
}
1132
1133
void SoftGPU::Execute_ProjMtxData(u32 op, u32 diff) {
1134
int num = gstate.projmtxnum & 0x00FFFFFF;
1135
if (num < 16) {
1136
u32 *target = (u32 *)&gstate.projMatrix[num];
1137
u32 newVal = op << 8;
1138
if (newVal != *target) {
1139
*target = newVal;
1140
dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX;
1141
gstate_c.Dirty(DIRTY_CULL_PLANES);
1142
}
1143
}
1144
1145
// Also update the CPU visible values, which update differently.
1146
u32 *target = &matrixVisible.all[12 * 8 + 12 + 12 + (num & 0xF)];
1147
*target = op & 0x00FFFFFF;
1148
1149
num++;
1150
gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | (num & 0x00FFFFFF);
1151
gstate.projmtxdata = GE_CMD_PROJMATRIXDATA << 24;
1152
}
1153
1154
void SoftGPU::Execute_TgenMtxData(u32 op, u32 diff) {
1155
int num = gstate.texmtxnum & 0x00FFFFFF;
1156
if (num < 12) {
1157
u32 *target = (u32 *)&gstate.tgenMatrix[num];
1158
u32 newVal = op << 8;
1159
if (newVal != *target) {
1160
*target = newVal;
1161
// This is mainly used in vertex read, but also affects if we enable texture projection.
1162
dirtyFlags_ |= SoftDirty::RAST_TEX;
1163
}
1164
}
1165
1166
// Doesn't wrap to any other matrix.
1167
if ((num & 0xF) < 12) {
1168
matrixVisible.tgen[num & 0xF] = op & 0x00FFFFFF;
1169
}
1170
1171
num++;
1172
gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | (num & 0x00FFFFFF);
1173
gstate.texmtxdata = GE_CMD_TGENMATRIXDATA << 24;
1174
}
1175
1176
void SoftGPU::Execute_BoneMtxData(u32 op, u32 diff) {
1177
int num = gstate.boneMatrixNumber & 0x00FFFFFF;
1178
1179
if (num < 96) {
1180
u32 *target = (u32 *)&gstate.boneMatrix[num];
1181
u32 newVal = op << 8;
1182
// No dirtying, we read bone data during vertex read.
1183
*target = newVal;
1184
}
1185
1186
// Also update the CPU visible values, which update differently.
1187
u32 *target = &matrixVisible.all[(num & 0x7F)];
1188
*target = op & 0x00FFFFFF;
1189
1190
num++;
1191
gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (num & 0x00FFFFFF);
1192
gstate.boneMatrixData = GE_CMD_BONEMATRIXDATA << 24;
1193
}
1194
1195
static void CopyMatrix24(u32_le *result, const u32 *mtx, u32 count, u32 cmdbits) {
1196
for (u32 i = 0; i < count; ++i) {
1197
result[i] = mtx[i] | cmdbits;
1198
}
1199
}
1200
1201
bool SoftGPU::GetMatrix24(GEMatrixType type, u32_le *result, u32 cmdbits) {
1202
switch (type) {
1203
case GE_MTX_BONE0:
1204
case GE_MTX_BONE1:
1205
case GE_MTX_BONE2:
1206
case GE_MTX_BONE3:
1207
case GE_MTX_BONE4:
1208
case GE_MTX_BONE5:
1209
case GE_MTX_BONE6:
1210
case GE_MTX_BONE7:
1211
CopyMatrix24(result, matrixVisible.bone + (type - GE_MTX_BONE0) * 12, 12, cmdbits);
1212
break;
1213
case GE_MTX_TEXGEN:
1214
CopyMatrix24(result, matrixVisible.tgen, 12, cmdbits);
1215
break;
1216
case GE_MTX_WORLD:
1217
CopyMatrix24(result, matrixVisible.world, 12, cmdbits);
1218
break;
1219
case GE_MTX_VIEW:
1220
CopyMatrix24(result, matrixVisible.view, 12, cmdbits);
1221
break;
1222
case GE_MTX_PROJECTION:
1223
CopyMatrix24(result, matrixVisible.proj, 16, cmdbits);
1224
break;
1225
default:
1226
return false;
1227
}
1228
return true;
1229
}
1230
1231
void SoftGPU::ResetMatrices() {
1232
GPUCommon::ResetMatrices();
1233
dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX | SoftDirty::RAST_TEX;
1234
}
1235
1236
void SoftGPU::Execute_ImmVertexAlphaPrim(u32 op, u32 diff) {
1237
GPUCommon::Execute_ImmVertexAlphaPrim(op, diff);
1238
// We won't flush as often as hardware renderers, so we want to flush right away.
1239
FlushImm();
1240
}
1241
1242
void SoftGPU::Execute_Call(u32 op, u32 diff) {
1243
PROFILE_THIS_SCOPE("gpu_call");
1244
1245
const u32 target = gstate_c.getRelativeAddress(op & 0x00FFFFFC);
1246
if (!Memory::IsValidAddress(target)) {
1247
ERROR_LOG(Log::G3D, "CALL to illegal address %08x - ignoring! data=%06x", target, op & 0x00FFFFFF);
1248
if (g_Config.bIgnoreBadMemAccess) {
1249
return;
1250
}
1251
gpuState = GPUSTATE_ERROR;
1252
downcount = 0;
1253
return;
1254
}
1255
1256
const u32 retval = currentList->pc + 4;
1257
if (currentList->stackptr == ARRAY_SIZE(currentList->stack)) {
1258
ERROR_LOG(Log::G3D, "CALL: Stack full!");
1259
} else {
1260
auto &stackEntry = currentList->stack[currentList->stackptr++];
1261
stackEntry.pc = retval;
1262
stackEntry.offsetAddr = gstate_c.offsetAddr;
1263
// The base address is NOT saved/restored for a regular call.
1264
UpdatePC(currentList->pc, target - 4);
1265
currentList->pc = target - 4; // pc will be increased after we return, counteract that
1266
}
1267
}
1268
1269
void SoftGPU::FinishDeferred() {
1270
// Need to flush before going back to CPU, so drawing is appropriately visible.
1271
drawEngine_->transformUnit.Flush(this, "finish");
1272
}
1273
1274
int SoftGPU::ListSync(int listid, int mode) {
1275
// Take this as a cue that we need to finish drawing.
1276
drawEngine_->transformUnit.Flush(this, "listsync");
1277
return GPUCommon::ListSync(listid, mode);
1278
}
1279
1280
u32 SoftGPU::DrawSync(int mode) {
1281
// Take this as a cue that we need to finish drawing.
1282
drawEngine_->transformUnit.Flush(this, "drawsync");
1283
return GPUCommon::DrawSync(mode);
1284
}
1285
1286
void SoftGPU::GetStats(char *buffer, size_t bufsize) {
1287
drawEngine_->transformUnit.GetStats(buffer, bufsize);
1288
}
1289
1290
void SoftGPU::InvalidateCache(u32 addr, int size, GPUInvalidationType type)
1291
{
1292
// Nothing to invalidate.
1293
}
1294
1295
void SoftGPU::PerformWriteFormattedFromMemory(u32 addr, int size, int width, GEBufferFormat format)
1296
{
1297
// Ignore.
1298
}
1299
1300
bool SoftGPU::PerformMemoryCopy(u32 dest, u32 src, int size, GPUCopyFlag flags) {
1301
// Nothing to update.
1302
InvalidateCache(dest, size, GPU_INVALIDATE_HINT);
1303
if (!(flags & GPUCopyFlag::DEBUG_NOTIFIED))
1304
recorder_.NotifyMemcpy(dest, src, size);
1305
// Let's just be safe.
1306
MarkDirty(dest, size, SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY);
1307
return false;
1308
}
1309
1310
bool SoftGPU::PerformMemorySet(u32 dest, u8 v, int size)
1311
{
1312
// Nothing to update.
1313
InvalidateCache(dest, size, GPU_INVALIDATE_HINT);
1314
recorder_.NotifyMemset(dest, v, size);
1315
// Let's just be safe.
1316
MarkDirty(dest, size, SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY);
1317
return false;
1318
}
1319
1320
bool SoftGPU::PerformReadbackToMemory(u32 dest, int size)
1321
{
1322
// Nothing to update.
1323
InvalidateCache(dest, size, GPU_INVALIDATE_HINT);
1324
return false;
1325
}
1326
1327
bool SoftGPU::PerformWriteColorFromMemory(u32 dest, int size)
1328
{
1329
// Nothing to update.
1330
InvalidateCache(dest, size, GPU_INVALIDATE_HINT);
1331
recorder_.NotifyUpload(dest, size);
1332
return false;
1333
}
1334
1335
bool SoftGPU::PerformWriteStencilFromMemory(u32 dest, int size, WriteStencil flags)
1336
{
1337
return false;
1338
}
1339
1340
bool SoftGPU::FramebufferDirty() {
1341
if (g_Config.iFrameSkip != 0) {
1342
return ClearDirty(displayFramebuf_, displayStride_, 272, displayFormat_, SoftGPUVRAMDirty::DIRTY);
1343
}
1344
return true;
1345
}
1346
1347
bool SoftGPU::FramebufferReallyDirty() {
1348
if (g_Config.iFrameSkip != 0) {
1349
return ClearDirty(displayFramebuf_, displayStride_, 272, displayFormat_, SoftGPUVRAMDirty::REALLY_DIRTY);
1350
}
1351
return true;
1352
}
1353
1354
static DrawingCoords GetTargetSize(int stride) {
1355
int w = std::min(stride, std::max(gstate.getRegionX2(), gstate.getScissorX2()) + 1);
1356
int h = std::max(gstate.getRegionY2(), gstate.getScissorY2()) + 1;
1357
if (gstate.getRegionX2() == 1023 && gstate.getRegionY2() == 1023) {
1358
// Some games max out region, but always scissor to an appropriate size.
1359
// Both values always scissor, we just prefer region as it's usually a more stable size.
1360
w = std::max(stride, gstate.getScissorX2() + 1);
1361
h = std::max(272, gstate.getScissorY2() + 1);
1362
}
1363
1364
return DrawingCoords((s16)w, (s16)h);
1365
}
1366
1367
bool SoftGPU::GetCurrentFramebuffer(GPUDebugBuffer &buffer, GPUDebugFramebufferType type, int maxRes) {
1368
int stride = gstate.FrameBufStride();
1369
DrawingCoords size = GetTargetSize(stride);
1370
GEBufferFormat fmt = gstate.FrameBufFormat();
1371
const u8 *src = fb.data;
1372
1373
if (!Memory::IsValidAddress(displayFramebuf_))
1374
return false;
1375
1376
if (type == GPU_DBG_FRAMEBUF_DISPLAY) {
1377
size.x = 480;
1378
size.y = 272;
1379
stride = displayStride_;
1380
fmt = displayFormat_;
1381
src = Memory::GetPointer(displayFramebuf_);
1382
}
1383
1384
buffer.Allocate(size.x, size.y, fmt);
1385
1386
const int depth = fmt == GE_FORMAT_8888 ? 4 : 2;
1387
u8 *dst = buffer.GetData();
1388
const int byteWidth = size.x * depth;
1389
for (int16_t y = 0; y < size.y; ++y) {
1390
memcpy(dst, src, byteWidth);
1391
dst += byteWidth;
1392
src += stride * depth;
1393
}
1394
return true;
1395
}
1396
1397
bool SoftGPU::GetOutputFramebuffer(GPUDebugBuffer &buffer) {
1398
return GetCurrentFramebuffer(buffer, GPU_DBG_FRAMEBUF_DISPLAY, 1);
1399
}
1400
1401
bool SoftGPU::GetCurrentDepthbuffer(GPUDebugBuffer &buffer) {
1402
DrawingCoords size = GetTargetSize(gstate.DepthBufStride());
1403
buffer.Allocate(size.x, size.y, GPU_DBG_FORMAT_16BIT);
1404
1405
const int depth = 2;
1406
const u8 *src = depthbuf.data;
1407
u8 *dst = buffer.GetData();
1408
for (int16_t y = 0; y < size.y; ++y) {
1409
memcpy(dst, src, size.x * depth);
1410
dst += size.x * depth;
1411
src += gstate.DepthBufStride() * depth;
1412
}
1413
return true;
1414
}
1415
1416
static inline u8 GetPixelStencil(GEBufferFormat fmt, int fbStride, int x, int y) {
1417
if (fmt == GE_FORMAT_565) {
1418
// Always treated as 0 for comparison purposes.
1419
return 0;
1420
} else if (fmt == GE_FORMAT_5551) {
1421
return ((fb.Get16(x, y, fbStride) & 0x8000) != 0) ? 0xFF : 0;
1422
} else if (fmt == GE_FORMAT_4444) {
1423
return Convert4To8(fb.Get16(x, y, fbStride) >> 12);
1424
} else {
1425
return fb.Get32(x, y, fbStride) >> 24;
1426
}
1427
}
1428
1429
bool SoftGPU::GetCurrentStencilbuffer(GPUDebugBuffer &buffer) {
1430
DrawingCoords size = GetTargetSize(gstate.FrameBufStride());
1431
buffer.Allocate(size.x, size.y, GPU_DBG_FORMAT_8BIT);
1432
1433
u8 *row = buffer.GetData();
1434
for (int16_t y = 0; y < size.y; ++y) {
1435
for (int16_t x = 0; x < size.x; ++x) {
1436
row[x] = GetPixelStencil(gstate.FrameBufFormat(), gstate.FrameBufStride(), x, y);
1437
}
1438
row += size.x;
1439
}
1440
return true;
1441
}
1442
1443
bool SoftGPU::GetCurrentTexture(GPUDebugBuffer &buffer, int level, bool *isFramebuffer) {
1444
*isFramebuffer = false;
1445
return Rasterizer::GetCurrentTexture(buffer, level);
1446
}
1447
1448
bool SoftGPU::GetCurrentClut(GPUDebugBuffer &buffer) {
1449
const u32 bpp = gstate.getClutPaletteFormat() == GE_CMODE_32BIT_ABGR8888 ? 4 : 2;
1450
const u32 pixels = 1024 / bpp;
1451
1452
buffer.Allocate(pixels, 1, (GEBufferFormat)gstate.getClutPaletteFormat());
1453
memcpy(buffer.GetData(), clut, 1024);
1454
return true;
1455
}
1456
1457
bool SoftGPU::GetCurrentDrawAsDebugVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices) {
1458
gstate_c.UpdateUVScaleOffset();
1459
return drawEngine_->transformUnit.GetCurrentDrawAsDebugVertices(count, vertices, indices);
1460
}
1461
1462
bool SoftGPU::DescribeCodePtr(const u8 *ptr, std::string &name) {
1463
std::string subname;
1464
if (Sampler::DescribeCodePtr(ptr, subname)) {
1465
name = "SamplerJit:" + subname;
1466
return true;
1467
}
1468
if (Rasterizer::DescribeCodePtr(ptr, subname)) {
1469
name = "RasterizerJit:" + subname;
1470
return true;
1471
}
1472
return GPUCommon::DescribeCodePtr(ptr, name);
1473
}
1474
1475