1 // Copyright 2008 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4
5 #include "VideoCommon/PixelShaderManager.h"
6
7 #include <iterator>
8
9 #include "Common/ChunkFile.h"
10 #include "Common/CommonTypes.h"
11 #include "VideoCommon/RenderBase.h"
12 #include "VideoCommon/VideoCommon.h"
13 #include "VideoCommon/VideoConfig.h"
14 #include "VideoCommon/XFMemory.h"
15
16 bool PixelShaderManager::s_bFogRangeAdjustChanged;
17 bool PixelShaderManager::s_bViewPortChanged;
18 bool PixelShaderManager::s_bIndirectDirty;
19 bool PixelShaderManager::s_bDestAlphaDirty;
20
21 PixelShaderConstants PixelShaderManager::constants;
22 bool PixelShaderManager::dirty;
23
Init()24 void PixelShaderManager::Init()
25 {
26 constants = {};
27
28 // Init any intial constants which aren't zero when bpmem is zero.
29 s_bFogRangeAdjustChanged = true;
30 s_bViewPortChanged = false;
31
32 SetIndMatrixChanged(0);
33 SetIndMatrixChanged(1);
34 SetIndMatrixChanged(2);
35 SetZTextureTypeChanged();
36 SetTexCoordChanged(0);
37 SetTexCoordChanged(1);
38 SetTexCoordChanged(2);
39 SetTexCoordChanged(3);
40 SetTexCoordChanged(4);
41 SetTexCoordChanged(5);
42 SetTexCoordChanged(6);
43 SetTexCoordChanged(7);
44
45 // fixed Konstants
46 for (int component = 0; component < 4; component++)
47 {
48 constants.konst[0][component] = 255; // 1
49 constants.konst[1][component] = 223; // 7/8
50 constants.konst[2][component] = 191; // 3/4
51 constants.konst[3][component] = 159; // 5/8
52 constants.konst[4][component] = 128; // 1/2
53 constants.konst[5][component] = 96; // 3/8
54 constants.konst[6][component] = 64; // 1/4
55 constants.konst[7][component] = 32; // 1/8
56
57 // Invalid Konstants (reads as zero on hardware)
58 constants.konst[8][component] = 0;
59 constants.konst[9][component] = 0;
60 constants.konst[10][component] = 0;
61 constants.konst[11][component] = 0;
62
63 // Annoyingly, alpha reads zero values for the .rgb colors (offically
64 // defined as invalid)
65 // If it wasn't for this, we could just use one of the first 3 colunms
66 // instead of
67 // wasting an entire 4th column just for alpha.
68 if (component == 3)
69 {
70 constants.konst[12][component] = 0;
71 constants.konst[13][component] = 0;
72 constants.konst[14][component] = 0;
73 constants.konst[15][component] = 0;
74 }
75 }
76
77 dirty = true;
78 }
79
Dirty()80 void PixelShaderManager::Dirty()
81 {
82 // This function is called after a savestate is loaded.
83 // Any constants that can changed based on settings should be re-calculated
84 s_bFogRangeAdjustChanged = true;
85
86 SetEfbScaleChanged(g_renderer->EFBToScaledXf(1), g_renderer->EFBToScaledYf(1));
87 SetFogParamChanged();
88
89 dirty = true;
90 }
91
SetConstants()92 void PixelShaderManager::SetConstants()
93 {
94 if (s_bFogRangeAdjustChanged)
95 {
96 // set by two components, so keep changed flag here
97 // TODO: try to split both registers and move this logic to the shader
98 if (!g_ActiveConfig.bDisableFog && bpmem.fogRange.Base.Enabled == 1)
99 {
100 // bpmem.fogRange.Base.Center : center of the viewport in x axis. observation:
101 // bpmem.fogRange.Base.Center = realcenter + 342;
102 int center = ((u32)bpmem.fogRange.Base.Center) - 342;
103 // normalize center to make calculations easy
104 float ScreenSpaceCenter = center / (2.0f * xfmem.viewport.wd);
105 ScreenSpaceCenter = (ScreenSpaceCenter * 2.0f) - 1.0f;
106 // bpmem.fogRange.K seems to be a table of precalculated coefficients for the adjust factor
107 // observations: bpmem.fogRange.K[0].LO appears to be the lowest value and
108 // bpmem.fogRange.K[4].HI the largest
109 // they always seems to be larger than 256 so my theory is :
110 // they are the coefficients from the center to the border of the screen
111 // so to simplify I use the hi coefficient as K in the shader taking 256 as the scale
112 // TODO: Shouldn't this be EFBToScaledXf?
113 constants.fogf[2] = ScreenSpaceCenter;
114 constants.fogf[3] =
115 static_cast<float>(g_renderer->EFBToScaledX(static_cast<int>(2.0f * xfmem.viewport.wd)));
116
117 for (size_t i = 0, vec_index = 0; i < std::size(bpmem.fogRange.K); i++)
118 {
119 constexpr float scale = 4.0f;
120 constants.fogrange[vec_index / 4][vec_index % 4] = bpmem.fogRange.K[i].GetValue(0) * scale;
121 vec_index++;
122 constants.fogrange[vec_index / 4][vec_index % 4] = bpmem.fogRange.K[i].GetValue(1) * scale;
123 vec_index++;
124 }
125 }
126 else
127 {
128 constants.fogf[2] = 0;
129 constants.fogf[3] = 1;
130 }
131 dirty = true;
132
133 s_bFogRangeAdjustChanged = false;
134 }
135
136 if (s_bViewPortChanged)
137 {
138 constants.zbias[1][0] = (s32)xfmem.viewport.farZ;
139 constants.zbias[1][1] = (s32)xfmem.viewport.zRange;
140 dirty = true;
141 s_bViewPortChanged = false;
142 }
143
144 if (s_bIndirectDirty)
145 {
146 for (int i = 0; i < 4; i++)
147 constants.pack1[i][3] = 0;
148
149 for (u32 i = 0; i < (bpmem.genMode.numtevstages + 1); ++i)
150 {
151 u32 stage = bpmem.tevind[i].bt;
152 if (stage < bpmem.genMode.numindstages)
153 {
154 // We set some extra bits so the ubershader can quickly check if these
155 // features are in use.
156 if (bpmem.tevind[i].IsActive())
157 constants.pack1[stage][3] =
158 bpmem.tevindref.getTexCoord(stage) | bpmem.tevindref.getTexMap(stage) << 8 | 1 << 16;
159 // Note: a tevind of zero just happens to be a passthrough, so no need
160 // to set an extra bit.
161 constants.pack1[i][2] = bpmem.tevind[i].hex; // TODO: This match shadergen, but videosw
162 // will always wrap.
163
164 // The ubershader uses tevind != 0 as a condition whether to calculate texcoords,
165 // even when texture is disabled, instead of the stage < bpmem.genMode.numindstages.
166 // We set an unused bit here to indicate that the stage is active, even if it
167 // is just a pass-through.
168 constants.pack1[i][2] |= 0x80000000;
169 }
170 else
171 {
172 constants.pack1[i][2] = 0;
173 }
174 }
175
176 dirty = true;
177 s_bIndirectDirty = false;
178 }
179
180 if (s_bDestAlphaDirty)
181 {
182 // Destination alpha is only enabled if alpha writes are enabled. Force entire uniform to zero
183 // when disabled.
184 u32 dstalpha = bpmem.blendmode.alphaupdate && bpmem.dstalpha.enable &&
185 bpmem.zcontrol.pixel_format == PEControl::RGBA6_Z24 ?
186 bpmem.dstalpha.hex :
187 0;
188
189 if (constants.dstalpha != dstalpha)
190 {
191 constants.dstalpha = dstalpha;
192 dirty = true;
193 }
194 }
195 }
196
SetTevColor(int index,int component,s32 value)197 void PixelShaderManager::SetTevColor(int index, int component, s32 value)
198 {
199 auto& c = constants.colors[index];
200 c[component] = value;
201 dirty = true;
202
203 PRIM_LOG("tev color%d: %d %d %d %d", index, c[0], c[1], c[2], c[3]);
204 }
205
SetTevKonstColor(int index,int component,s32 value)206 void PixelShaderManager::SetTevKonstColor(int index, int component, s32 value)
207 {
208 auto& c = constants.kcolors[index];
209 c[component] = value;
210 dirty = true;
211
212 // Konst for ubershaders. We build the whole array on cpu so the gpu can do a single indirect
213 // access.
214 if (component != 3) // Alpha doesn't included in the .rgb konsts
215 constants.konst[index + 12][component] = value;
216
217 // .rrrr .gggg .bbbb .aaaa konsts
218 constants.konst[index + 16 + component * 4][0] = value;
219 constants.konst[index + 16 + component * 4][1] = value;
220 constants.konst[index + 16 + component * 4][2] = value;
221 constants.konst[index + 16 + component * 4][3] = value;
222
223 PRIM_LOG("tev konst color%d: %d %d %d %d", index, c[0], c[1], c[2], c[3]);
224 }
225
SetTevOrder(int index,u32 order)226 void PixelShaderManager::SetTevOrder(int index, u32 order)
227 {
228 if (constants.pack2[index][0] != order)
229 {
230 constants.pack2[index][0] = order;
231 dirty = true;
232 }
233 }
234
SetTevKSel(int index,u32 ksel)235 void PixelShaderManager::SetTevKSel(int index, u32 ksel)
236 {
237 if (constants.pack2[index][1] != ksel)
238 {
239 constants.pack2[index][1] = ksel;
240 dirty = true;
241 }
242 }
243
SetTevCombiner(int index,int alpha,u32 combiner)244 void PixelShaderManager::SetTevCombiner(int index, int alpha, u32 combiner)
245 {
246 if (constants.pack1[index][alpha] != combiner)
247 {
248 constants.pack1[index][alpha] = combiner;
249 dirty = true;
250 }
251 }
252
SetTevIndirectChanged()253 void PixelShaderManager::SetTevIndirectChanged()
254 {
255 s_bIndirectDirty = true;
256 }
257
SetAlpha()258 void PixelShaderManager::SetAlpha()
259 {
260 constants.alpha[0] = bpmem.alpha_test.ref0;
261 constants.alpha[1] = bpmem.alpha_test.ref1;
262 constants.alpha[3] = static_cast<s32>(bpmem.dstalpha.alpha);
263 dirty = true;
264 }
265
SetAlphaTestChanged()266 void PixelShaderManager::SetAlphaTestChanged()
267 {
268 // Force alphaTest Uniform to zero if it will always pass.
269 // (set an extra bit to distinguish from "never && never")
270 // TODO: we could optimize this further and check the actual constants,
271 // i.e. "a <= 0" and "a >= 255" will always pass.
272 u32 alpha_test =
273 bpmem.alpha_test.TestResult() != AlphaTest::PASS ? bpmem.alpha_test.hex | 1 << 31 : 0;
274 if (constants.alphaTest != alpha_test)
275 {
276 constants.alphaTest = alpha_test;
277 dirty = true;
278 }
279 }
280
SetDestAlphaChanged()281 void PixelShaderManager::SetDestAlphaChanged()
282 {
283 s_bDestAlphaDirty = true;
284 }
285
SetTexDims(int texmapid,u32 width,u32 height)286 void PixelShaderManager::SetTexDims(int texmapid, u32 width, u32 height)
287 {
288 float rwidth = 1.0f / (width * 128.0f);
289 float rheight = 1.0f / (height * 128.0f);
290
291 // TODO: move this check out to callee. There we could just call this function on texture changes
292 // or better, use textureSize() in glsl
293 if (constants.texdims[texmapid][0] != rwidth || constants.texdims[texmapid][1] != rheight)
294 dirty = true;
295
296 constants.texdims[texmapid][0] = rwidth;
297 constants.texdims[texmapid][1] = rheight;
298 }
299
SetZTextureBias()300 void PixelShaderManager::SetZTextureBias()
301 {
302 constants.zbias[1][3] = bpmem.ztex1.bias;
303 dirty = true;
304 }
305
SetViewportChanged()306 void PixelShaderManager::SetViewportChanged()
307 {
308 s_bViewPortChanged = true;
309 s_bFogRangeAdjustChanged =
310 true; // TODO: Shouldn't be necessary with an accurate fog range adjust implementation
311 }
312
SetEfbScaleChanged(float scalex,float scaley)313 void PixelShaderManager::SetEfbScaleChanged(float scalex, float scaley)
314 {
315 constants.efbscale[0] = 1.0f / scalex;
316 constants.efbscale[1] = 1.0f / scaley;
317 dirty = true;
318 }
319
SetZSlope(float dfdx,float dfdy,float f0)320 void PixelShaderManager::SetZSlope(float dfdx, float dfdy, float f0)
321 {
322 constants.zslope[0] = dfdx;
323 constants.zslope[1] = dfdy;
324 constants.zslope[2] = f0;
325 dirty = true;
326 }
327
SetIndTexScaleChanged(bool high)328 void PixelShaderManager::SetIndTexScaleChanged(bool high)
329 {
330 constants.indtexscale[high][0] = bpmem.texscale[high].ss0;
331 constants.indtexscale[high][1] = bpmem.texscale[high].ts0;
332 constants.indtexscale[high][2] = bpmem.texscale[high].ss1;
333 constants.indtexscale[high][3] = bpmem.texscale[high].ts1;
334 dirty = true;
335 }
336
SetIndMatrixChanged(int matrixidx)337 void PixelShaderManager::SetIndMatrixChanged(int matrixidx)
338 {
339 int scale = ((u32)bpmem.indmtx[matrixidx].col0.s0 << 0) |
340 ((u32)bpmem.indmtx[matrixidx].col1.s1 << 2) |
341 ((u32)bpmem.indmtx[matrixidx].col2.s2 << 4);
342
343 // xyz - static matrix
344 // w - dynamic matrix scale / 128
345 constants.indtexmtx[2 * matrixidx][0] = bpmem.indmtx[matrixidx].col0.ma;
346 constants.indtexmtx[2 * matrixidx][1] = bpmem.indmtx[matrixidx].col1.mc;
347 constants.indtexmtx[2 * matrixidx][2] = bpmem.indmtx[matrixidx].col2.me;
348 constants.indtexmtx[2 * matrixidx][3] = 17 - scale;
349 constants.indtexmtx[2 * matrixidx + 1][0] = bpmem.indmtx[matrixidx].col0.mb;
350 constants.indtexmtx[2 * matrixidx + 1][1] = bpmem.indmtx[matrixidx].col1.md;
351 constants.indtexmtx[2 * matrixidx + 1][2] = bpmem.indmtx[matrixidx].col2.mf;
352 constants.indtexmtx[2 * matrixidx + 1][3] = 17 - scale;
353 dirty = true;
354
355 PRIM_LOG("indmtx%d: scale=%d, mat=(%d %d %d; %d %d %d)", matrixidx, scale,
356 bpmem.indmtx[matrixidx].col0.ma, bpmem.indmtx[matrixidx].col1.mc,
357 bpmem.indmtx[matrixidx].col2.me, bpmem.indmtx[matrixidx].col0.mb,
358 bpmem.indmtx[matrixidx].col1.md, bpmem.indmtx[matrixidx].col2.mf);
359 }
360
SetZTextureTypeChanged()361 void PixelShaderManager::SetZTextureTypeChanged()
362 {
363 switch (bpmem.ztex2.type)
364 {
365 case TEV_ZTEX_TYPE_U8:
366 constants.zbias[0][0] = 0;
367 constants.zbias[0][1] = 0;
368 constants.zbias[0][2] = 0;
369 constants.zbias[0][3] = 1;
370 break;
371 case TEV_ZTEX_TYPE_U16:
372 constants.zbias[0][0] = 1;
373 constants.zbias[0][1] = 0;
374 constants.zbias[0][2] = 0;
375 constants.zbias[0][3] = 256;
376 break;
377 case TEV_ZTEX_TYPE_U24:
378 constants.zbias[0][0] = 65536;
379 constants.zbias[0][1] = 256;
380 constants.zbias[0][2] = 1;
381 constants.zbias[0][3] = 0;
382 break;
383 default:
384 break;
385 }
386 dirty = true;
387 }
388
SetZTextureOpChanged()389 void PixelShaderManager::SetZTextureOpChanged()
390 {
391 constants.ztex_op = bpmem.ztex2.op;
392 dirty = true;
393 }
394
SetTexCoordChanged(u8 texmapid)395 void PixelShaderManager::SetTexCoordChanged(u8 texmapid)
396 {
397 TCoordInfo& tc = bpmem.texcoords[texmapid];
398 constants.texdims[texmapid][2] = (float)(tc.s.scale_minus_1 + 1) * 128.0f;
399 constants.texdims[texmapid][3] = (float)(tc.t.scale_minus_1 + 1) * 128.0f;
400 dirty = true;
401 }
402
SetFogColorChanged()403 void PixelShaderManager::SetFogColorChanged()
404 {
405 if (g_ActiveConfig.bDisableFog)
406 return;
407
408 constants.fogcolor[0] = bpmem.fog.color.r;
409 constants.fogcolor[1] = bpmem.fog.color.g;
410 constants.fogcolor[2] = bpmem.fog.color.b;
411 dirty = true;
412 }
413
SetFogParamChanged()414 void PixelShaderManager::SetFogParamChanged()
415 {
416 if (!g_ActiveConfig.bDisableFog)
417 {
418 constants.fogf[0] = bpmem.fog.GetA();
419 constants.fogf[1] = bpmem.fog.GetC();
420 constants.fogi[1] = bpmem.fog.b_magnitude;
421 constants.fogi[3] = bpmem.fog.b_shift;
422 constants.fogParam3 = bpmem.fog.c_proj_fsel.hex;
423 }
424 else
425 {
426 constants.fogf[0] = 0.f;
427 constants.fogf[1] = 0.f;
428 constants.fogi[1] = 1;
429 constants.fogi[3] = 1;
430 constants.fogParam3 = 0;
431 }
432 dirty = true;
433 }
434
SetFogRangeAdjustChanged()435 void PixelShaderManager::SetFogRangeAdjustChanged()
436 {
437 if (g_ActiveConfig.bDisableFog)
438 return;
439
440 s_bFogRangeAdjustChanged = true;
441
442 if (constants.fogRangeBase != bpmem.fogRange.Base.hex)
443 {
444 constants.fogRangeBase = bpmem.fogRange.Base.hex;
445 dirty = true;
446 }
447 }
448
SetGenModeChanged()449 void PixelShaderManager::SetGenModeChanged()
450 {
451 constants.genmode = bpmem.genMode.hex;
452 s_bIndirectDirty = true;
453 dirty = true;
454 }
455
SetZModeControl()456 void PixelShaderManager::SetZModeControl()
457 {
458 u32 late_ztest = bpmem.UseLateDepthTest();
459 u32 rgba6_format =
460 (bpmem.zcontrol.pixel_format == PEControl::RGBA6_Z24 && !g_ActiveConfig.bForceTrueColor) ? 1 :
461 0;
462 u32 dither = rgba6_format && bpmem.blendmode.dither;
463 if (constants.late_ztest != late_ztest || constants.rgba6_format != rgba6_format ||
464 constants.dither != dither)
465 {
466 constants.late_ztest = late_ztest;
467 constants.rgba6_format = rgba6_format;
468 constants.dither = dither;
469 dirty = true;
470 }
471 s_bDestAlphaDirty = true;
472 }
473
SetBlendModeChanged()474 void PixelShaderManager::SetBlendModeChanged()
475 {
476 u32 dither = constants.rgba6_format && bpmem.blendmode.dither;
477 if (constants.dither != dither)
478 {
479 constants.dither = dither;
480 dirty = true;
481 }
482 BlendingState state = {};
483 state.Generate(bpmem);
484 if (constants.blend_enable != state.blendenable)
485 {
486 constants.blend_enable = state.blendenable;
487 dirty = true;
488 }
489 if (constants.blend_src_factor != state.srcfactor)
490 {
491 constants.blend_src_factor = state.srcfactor;
492 dirty = true;
493 }
494 if (constants.blend_src_factor_alpha != state.srcfactoralpha)
495 {
496 constants.blend_src_factor_alpha = state.srcfactoralpha;
497 dirty = true;
498 }
499 if (constants.blend_dst_factor != state.dstfactor)
500 {
501 constants.blend_dst_factor = state.dstfactor;
502 dirty = true;
503 }
504 if (constants.blend_dst_factor_alpha != state.dstfactoralpha)
505 {
506 constants.blend_dst_factor_alpha = state.dstfactoralpha;
507 dirty = true;
508 }
509 if (constants.blend_subtract != state.subtract)
510 {
511 constants.blend_subtract = state.subtract;
512 dirty = true;
513 }
514 if (constants.blend_subtract_alpha != state.subtractAlpha)
515 {
516 constants.blend_subtract_alpha = state.subtractAlpha;
517 dirty = true;
518 }
519 s_bDestAlphaDirty = true;
520 }
521
SetBoundingBoxActive(bool active)522 void PixelShaderManager::SetBoundingBoxActive(bool active)
523 {
524 const bool enable = active && g_ActiveConfig.bBBoxEnable;
525 if (enable == (constants.bounding_box != 0))
526 return;
527
528 constants.bounding_box = active;
529 dirty = true;
530 }
531
DoState(PointerWrap & p)532 void PixelShaderManager::DoState(PointerWrap& p)
533 {
534 p.Do(s_bFogRangeAdjustChanged);
535 p.Do(s_bViewPortChanged);
536 p.Do(s_bIndirectDirty);
537 p.Do(s_bDestAlphaDirty);
538
539 p.Do(constants);
540
541 if (p.GetMode() == PointerWrap::MODE_READ)
542 {
543 // Fixup the current state from global GPU state
544 // NOTE: This requires that all GPU memory has been loaded already.
545 Dirty();
546 }
547 }
548