1 // Copyright 2009 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4
5 #include "VideoCommon/BPStructs.h"
6
7 #include <cmath>
8 #include <cstring>
9 #include <string>
10
11 #include <fmt/format.h>
12
13 #include "Common/CommonTypes.h"
14 #include "Common/Logging/Log.h"
15 #include "Core/ConfigManager.h"
16 #include "Core/CoreTiming.h"
17 #include "Core/FifoPlayer/FifoPlayer.h"
18 #include "Core/FifoPlayer/FifoRecorder.h"
19 #include "Core/HW/Memmap.h"
20 #include "Core/HW/VideoInterface.h"
21
22 #include "VideoCommon/BPFunctions.h"
23 #include "VideoCommon/BPMemory.h"
24 #include "VideoCommon/BoundingBox.h"
25 #include "VideoCommon/Fifo.h"
26 #include "VideoCommon/FramebufferManager.h"
27 #include "VideoCommon/GeometryShaderManager.h"
28 #include "VideoCommon/OpcodeDecoding.h"
29 #include "VideoCommon/PerfQueryBase.h"
30 #include "VideoCommon/PixelEngine.h"
31 #include "VideoCommon/PixelShaderManager.h"
32 #include "VideoCommon/RenderBase.h"
33 #include "VideoCommon/TextureCacheBase.h"
34 #include "VideoCommon/TextureDecoder.h"
35 #include "VideoCommon/VertexShaderManager.h"
36 #include "VideoCommon/VideoBackendBase.h"
37 #include "VideoCommon/VideoCommon.h"
38 #include "VideoCommon/VideoConfig.h"
39
40 using namespace BPFunctions;
41
42 static const float s_gammaLUT[] = {1.0f, 1.7f, 2.2f, 1.0f};
43
BPInit()44 void BPInit()
45 {
46 memset(&bpmem, 0, sizeof(bpmem));
47 bpmem.bpMask = 0xFFFFFF;
48 }
49
BPWritten(const BPCmd & bp)50 static void BPWritten(const BPCmd& bp)
51 {
52 /*
53 ----------------------------------------------------------------------------------------------------------------
54 Purpose: Writes to the BP registers
55 Called: At the end of every: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg
56 How It Works: First the pipeline is flushed then update the bpmem with the new value.
57 Some of the BP cases have to call certain functions while others just update the bpmem.
58 some bp cases check the changes variable, because they might not have to be updated all
59 the time
60 NOTE: it seems not all bp cases like checking changes, so calling if (bp.changes == 0 ? false :
61 true)
62 had to be ditched and the games seem to work fine with out it.
63 NOTE2: Yet Another GameCube Documentation calls them Bypass Raster State Registers but possibly
64 completely wrong
65 NOTE3: This controls the register groups: RAS1/2, SU, TF, TEV, C/Z, PEC
66 TODO: Turn into function table. The (future) DisplayList (DL) jit can then call the functions
67 directly,
68 getting rid of dynamic dispatch. Unfortunately, few games use DLs properly - most\
69 just stuff geometry in them and don't put state changes there
70 ----------------------------------------------------------------------------------------------------------------
71 */
72
73 if (((s32*)&bpmem)[bp.address] == bp.newvalue)
74 {
75 if (!(bp.address == BPMEM_TRIGGER_EFB_COPY || bp.address == BPMEM_CLEARBBOX1 ||
76 bp.address == BPMEM_CLEARBBOX2 || bp.address == BPMEM_SETDRAWDONE ||
77 bp.address == BPMEM_PE_TOKEN_ID || bp.address == BPMEM_PE_TOKEN_INT_ID ||
78 bp.address == BPMEM_LOADTLUT0 || bp.address == BPMEM_LOADTLUT1 ||
79 bp.address == BPMEM_TEXINVALIDATE || bp.address == BPMEM_PRELOAD_MODE ||
80 bp.address == BPMEM_CLEAR_PIXEL_PERF))
81 {
82 return;
83 }
84 }
85
86 FlushPipeline();
87
88 ((u32*)&bpmem)[bp.address] = bp.newvalue;
89
90 switch (bp.address)
91 {
92 case BPMEM_GENMODE: // Set the Generation Mode
93 PRIM_LOG("genmode: texgen=%d, col=%d, multisampling=%d, tev=%d, cullmode=%d, ind=%d, zfeeze=%d",
94 (u32)bpmem.genMode.numtexgens, (u32)bpmem.genMode.numcolchans,
95 (u32)bpmem.genMode.multisampling, (u32)bpmem.genMode.numtevstages + 1,
96 (u32)bpmem.genMode.cullmode, (u32)bpmem.genMode.numindstages,
97 (u32)bpmem.genMode.zfreeze);
98
99 if (bp.changes)
100 PixelShaderManager::SetGenModeChanged();
101
102 // Only call SetGenerationMode when cull mode changes.
103 if (bp.changes & 0xC000)
104 SetGenerationMode();
105 return;
106 case BPMEM_IND_MTXA: // Index Matrix Changed
107 case BPMEM_IND_MTXB:
108 case BPMEM_IND_MTXC:
109 case BPMEM_IND_MTXA + 3:
110 case BPMEM_IND_MTXB + 3:
111 case BPMEM_IND_MTXC + 3:
112 case BPMEM_IND_MTXA + 6:
113 case BPMEM_IND_MTXB + 6:
114 case BPMEM_IND_MTXC + 6:
115 if (bp.changes)
116 PixelShaderManager::SetIndMatrixChanged((bp.address - BPMEM_IND_MTXA) / 3);
117 return;
118 case BPMEM_RAS1_SS0: // Index Texture Coordinate Scale 0
119 if (bp.changes)
120 PixelShaderManager::SetIndTexScaleChanged(false);
121 return;
122 case BPMEM_RAS1_SS1: // Index Texture Coordinate Scale 1
123 if (bp.changes)
124 PixelShaderManager::SetIndTexScaleChanged(true);
125 return;
126 // ----------------
127 // Scissor Control
128 // ----------------
129 case BPMEM_SCISSORTL: // Scissor Rectable Top, Left
130 case BPMEM_SCISSORBR: // Scissor Rectable Bottom, Right
131 case BPMEM_SCISSOROFFSET: // Scissor Offset
132 SetScissor();
133 SetViewport();
134 VertexShaderManager::SetViewportChanged();
135 GeometryShaderManager::SetViewportChanged();
136 return;
137 case BPMEM_LINEPTWIDTH: // Line Width
138 GeometryShaderManager::SetLinePtWidthChanged();
139 return;
140 case BPMEM_ZMODE: // Depth Control
141 PRIM_LOG("zmode: test=%u, func=%u, upd=%u", bpmem.zmode.testenable.Value(),
142 bpmem.zmode.func.Value(), bpmem.zmode.updateenable.Value());
143 SetDepthMode();
144 PixelShaderManager::SetZModeControl();
145 return;
146 case BPMEM_BLENDMODE: // Blending Control
147 if (bp.changes & 0xFFFF)
148 {
149 PRIM_LOG("blendmode: en=%u, open=%u, colupd=%u, alphaupd=%u, dst=%u, src=%u, sub=%u, mode=%u",
150 bpmem.blendmode.blendenable.Value(), bpmem.blendmode.logicopenable.Value(),
151 bpmem.blendmode.colorupdate.Value(), bpmem.blendmode.alphaupdate.Value(),
152 bpmem.blendmode.dstfactor.Value(), bpmem.blendmode.srcfactor.Value(),
153 bpmem.blendmode.subtract.Value(), bpmem.blendmode.logicmode.Value());
154
155 SetBlendMode();
156
157 PixelShaderManager::SetBlendModeChanged();
158 }
159 return;
160 case BPMEM_CONSTANTALPHA: // Set Destination Alpha
161 PRIM_LOG("constalpha: alp=%d, en=%d", bpmem.dstalpha.alpha.Value(),
162 bpmem.dstalpha.enable.Value());
163 if (bp.changes)
164 {
165 PixelShaderManager::SetAlpha();
166 PixelShaderManager::SetDestAlphaChanged();
167 }
168 if (bp.changes & 0x100)
169 SetBlendMode();
170 return;
171
172 // This is called when the game is done drawing the new frame (eg: like in DX: Begin(); Draw();
173 // End();)
174 // Triggers an interrupt on the PPC side so that the game knows when the GPU has finished drawing.
175 // Tokens are similar.
176 case BPMEM_SETDRAWDONE:
177 switch (bp.newvalue & 0xFF)
178 {
179 case 0x02:
180 g_texture_cache->FlushEFBCopies();
181 g_framebuffer_manager->InvalidatePeekCache(false);
182 if (!Fifo::UseDeterministicGPUThread())
183 PixelEngine::SetFinish(); // may generate interrupt
184 DEBUG_LOG(VIDEO, "GXSetDrawDone SetPEFinish (value: 0x%02X)", (bp.newvalue & 0xFFFF));
185 return;
186
187 default:
188 WARN_LOG(VIDEO, "GXSetDrawDone ??? (value 0x%02X)", (bp.newvalue & 0xFFFF));
189 return;
190 }
191 return;
192 case BPMEM_PE_TOKEN_ID: // Pixel Engine Token ID
193 g_texture_cache->FlushEFBCopies();
194 g_framebuffer_manager->InvalidatePeekCache(false);
195 if (!Fifo::UseDeterministicGPUThread())
196 PixelEngine::SetToken(static_cast<u16>(bp.newvalue & 0xFFFF), false);
197 DEBUG_LOG(VIDEO, "SetPEToken 0x%04x", (bp.newvalue & 0xFFFF));
198 return;
199 case BPMEM_PE_TOKEN_INT_ID: // Pixel Engine Interrupt Token ID
200 g_texture_cache->FlushEFBCopies();
201 g_framebuffer_manager->InvalidatePeekCache(false);
202 if (!Fifo::UseDeterministicGPUThread())
203 PixelEngine::SetToken(static_cast<u16>(bp.newvalue & 0xFFFF), true);
204 DEBUG_LOG(VIDEO, "SetPEToken + INT 0x%04x", (bp.newvalue & 0xFFFF));
205 return;
206
207 // ------------------------
208 // EFB copy command. This copies a rectangle from the EFB to either RAM in a texture format or to
209 // XFB as YUYV.
210 // It can also optionally clear the EFB while copying from it. To emulate this, we of course copy
211 // first and clear afterwards.
212 case BPMEM_TRIGGER_EFB_COPY: // Copy EFB Region or Render to the XFB or Clear the screen.
213 {
214 // The bottom right is within the rectangle
215 // The values in bpmem.copyTexSrcXY and bpmem.copyTexSrcWH are updated in case 0x49 and 0x4a in
216 // this function
217
218 u32 destAddr = bpmem.copyTexDest << 5;
219 u32 destStride = bpmem.copyMipMapStrideChannels << 5;
220
221 MathUtil::Rectangle<int> srcRect;
222 srcRect.left = static_cast<int>(bpmem.copyTexSrcXY.x);
223 srcRect.top = static_cast<int>(bpmem.copyTexSrcXY.y);
224
225 // Here Width+1 like Height, otherwise some textures are corrupted already since the native
226 // resolution.
227 srcRect.right = static_cast<int>(bpmem.copyTexSrcXY.x + bpmem.copyTexSrcWH.x + 1);
228 srcRect.bottom = static_cast<int>(bpmem.copyTexSrcXY.y + bpmem.copyTexSrcWH.y + 1);
229
230 // Since the copy X and Y coordinates/sizes are 10-bit, the game can configure a copy region up
231 // to 1024x1024. Hardware tests have found that the number of bytes written does not depend on
232 // the configured stride, instead it is based on the size registers, writing beyond the length
233 // of a single row. The data written for the pixels which lie outside the EFB bounds does not
234 // wrap around instead returning different colors based on the pixel format of the EFB. This
235 // suggests it's not based on coordinates, but instead on memory addresses. The effect of a
236 // within-bounds size but out-of-bounds offset (e.g. offset 320,0, size 640,480) are the same.
237
238 // As it would be difficult to emulate the exact behavior of out-of-bounds reads, instead of
239 // writing the junk data, we don't write anything to RAM at all for over-sized copies, and clamp
240 // to the EFB borders for over-offset copies. The arcade virtual console games (e.g. 1942) are
241 // known for configuring these out-of-range copies.
242 int copy_width = srcRect.GetWidth();
243 int copy_height = srcRect.GetHeight();
244 if (srcRect.right > s32(EFB_WIDTH) || srcRect.bottom > s32(EFB_HEIGHT))
245 {
246 WARN_LOG(VIDEO, "Oversized EFB copy: %dx%d (offset %d,%d stride %u)", copy_width, copy_height,
247 srcRect.left, srcRect.top, destStride);
248
249 // Adjust the copy size to fit within the EFB. So that we don't end up with a stretched image,
250 // instead of clamping the source rectangle, we reduce it by the over-sized amount.
251 if (copy_width > s32(EFB_WIDTH))
252 {
253 srcRect.right -= copy_width - EFB_WIDTH;
254 copy_width = EFB_WIDTH;
255 }
256 if (copy_height > s32(EFB_HEIGHT))
257 {
258 srcRect.bottom -= copy_height - EFB_HEIGHT;
259 copy_height = EFB_HEIGHT;
260 }
261 }
262
263 // Check if we are to copy from the EFB or draw to the XFB
264 const UPE_Copy PE_copy = bpmem.triggerEFBCopy;
265 if (PE_copy.copy_to_xfb == 0)
266 {
267 // bpmem.zcontrol.pixel_format to PEControl::Z24 is when the game wants to copy from ZBuffer
268 // (Zbuffer uses 24-bit Format)
269 static constexpr CopyFilterCoefficients::Values filter_coefficients = {
270 {0, 0, 21, 22, 21, 0, 0}};
271 bool is_depth_copy = bpmem.zcontrol.pixel_format == PEControl::Z24;
272 g_texture_cache->CopyRenderTargetToTexture(
273 destAddr, PE_copy.tp_realFormat(), copy_width, copy_height, destStride, is_depth_copy,
274 srcRect, !!PE_copy.intensity_fmt, !!PE_copy.half_scale, 1.0f, 1.0f,
275 bpmem.triggerEFBCopy.clamp_top, bpmem.triggerEFBCopy.clamp_bottom, filter_coefficients);
276 }
277 else
278 {
279 // We should be able to get away with deactivating the current bbox tracking
280 // here. Not sure if there's a better spot to put this.
281 // the number of lines copied is determined by the y scale * source efb height
282 BoundingBox::Disable();
283
284 float yScale;
285 if (PE_copy.scale_invert)
286 yScale = 256.0f / static_cast<float>(bpmem.dispcopyyscale);
287 else
288 yScale = static_cast<float>(bpmem.dispcopyyscale) / 256.0f;
289
290 float num_xfb_lines = 1.0f + bpmem.copyTexSrcWH.y * yScale;
291
292 u32 height = static_cast<u32>(num_xfb_lines);
293
294 DEBUG_LOG(VIDEO,
295 "RenderToXFB: destAddr: %08x | srcRect {%d %d %d %d} | fbWidth: %u | "
296 "fbStride: %u | fbHeight: %u | yScale: %f",
297 destAddr, srcRect.left, srcRect.top, srcRect.right, srcRect.bottom,
298 bpmem.copyTexSrcWH.x + 1, destStride, height, yScale);
299
300 bool is_depth_copy = bpmem.zcontrol.pixel_format == PEControl::Z24;
301 g_texture_cache->CopyRenderTargetToTexture(
302 destAddr, EFBCopyFormat::XFB, copy_width, height, destStride, is_depth_copy, srcRect,
303 false, false, yScale, s_gammaLUT[PE_copy.gamma], bpmem.triggerEFBCopy.clamp_top,
304 bpmem.triggerEFBCopy.clamp_bottom, bpmem.copyfilter.GetCoefficients());
305
306 // This stays in to signal end of a "frame"
307 g_renderer->RenderToXFB(destAddr, srcRect, destStride, height, s_gammaLUT[PE_copy.gamma]);
308
309 if (g_ActiveConfig.bImmediateXFB)
310 {
311 // below div two to convert from bytes to pixels - it expects width, not stride
312 g_renderer->Swap(destAddr, destStride / 2, destStride, height, CoreTiming::GetTicks());
313 }
314 else
315 {
316 if (FifoPlayer::GetInstance().IsRunningWithFakeVideoInterfaceUpdates())
317 {
318 VideoInterface::FakeVIUpdate(destAddr, srcRect.GetWidth(), destStride, height);
319 }
320 }
321 }
322
323 // Clear the rectangular region after copying it.
324 if (PE_copy.clear)
325 {
326 ClearScreen(srcRect);
327 }
328
329 return;
330 }
331 case BPMEM_LOADTLUT0: // This one updates bpmem.tlutXferSrc, no need to do anything here.
332 return;
333 case BPMEM_LOADTLUT1: // Load a Texture Look Up Table
334 {
335 u32 tlutTMemAddr = (bp.newvalue & 0x3FF) << 9;
336 u32 tlutXferCount = (bp.newvalue & 0x1FFC00) >> 5;
337 u32 addr = bpmem.tmem_config.tlut_src << 5;
338
339 // The GameCube ignores the upper bits of this address. Some games (WW, MKDD) set them.
340 if (!SConfig::GetInstance().bWii)
341 addr = addr & 0x01FFFFFF;
342
343 Memory::CopyFromEmu(texMem + tlutTMemAddr, addr, tlutXferCount);
344
345 if (OpcodeDecoder::g_record_fifo_data)
346 FifoRecorder::GetInstance().UseMemory(addr, tlutXferCount, MemoryUpdate::TMEM);
347
348 TextureCacheBase::InvalidateAllBindPoints();
349
350 return;
351 }
352 case BPMEM_FOGRANGE: // Fog Settings Control
353 case BPMEM_FOGRANGE + 1:
354 case BPMEM_FOGRANGE + 2:
355 case BPMEM_FOGRANGE + 3:
356 case BPMEM_FOGRANGE + 4:
357 case BPMEM_FOGRANGE + 5:
358 if (bp.changes)
359 PixelShaderManager::SetFogRangeAdjustChanged();
360 return;
361 case BPMEM_FOGPARAM0:
362 case BPMEM_FOGBMAGNITUDE:
363 case BPMEM_FOGBEXPONENT:
364 case BPMEM_FOGPARAM3:
365 if (bp.changes)
366 PixelShaderManager::SetFogParamChanged();
367 return;
368 case BPMEM_FOGCOLOR: // Fog Color
369 if (bp.changes)
370 PixelShaderManager::SetFogColorChanged();
371 return;
372 case BPMEM_ALPHACOMPARE: // Compare Alpha Values
373 PRIM_LOG("alphacmp: ref0=%d, ref1=%d, comp0=%d, comp1=%d, logic=%d", (int)bpmem.alpha_test.ref0,
374 (int)bpmem.alpha_test.ref1, (int)bpmem.alpha_test.comp0, (int)bpmem.alpha_test.comp1,
375 (int)bpmem.alpha_test.logic);
376 if (bp.changes & 0xFFFF)
377 PixelShaderManager::SetAlpha();
378 if (bp.changes)
379 {
380 PixelShaderManager::SetAlphaTestChanged();
381 SetBlendMode();
382 }
383 return;
384 case BPMEM_BIAS: // BIAS
385 PRIM_LOG("ztex bias=0x%x", bpmem.ztex1.bias.Value());
386 if (bp.changes)
387 PixelShaderManager::SetZTextureBias();
388 return;
389 case BPMEM_ZTEX2: // Z Texture type
390 {
391 if (bp.changes & 3)
392 PixelShaderManager::SetZTextureTypeChanged();
393 if (bp.changes & 12)
394 PixelShaderManager::SetZTextureOpChanged();
395 #if defined(_DEBUG) || defined(DEBUGFAST)
396 const char* pzop[] = {"DISABLE", "ADD", "REPLACE", "?"};
397 const char* pztype[] = {"Z8", "Z16", "Z24", "?"};
398 PRIM_LOG("ztex op=%s, type=%s", pzop[bpmem.ztex2.op], pztype[bpmem.ztex2.type]);
399 #endif
400 }
401 return;
402 // ----------------------------------
403 // Display Copy Filtering Control - GX_SetCopyFilter(u8 aa,u8 sample_pattern[12][2],u8 vf,u8
404 // vfilter[7])
405 // Fields: Destination, Frame2Field, Gamma, Source
406 // ----------------------------------
407 case BPMEM_DISPLAYCOPYFILTER: // if (aa) { use sample_pattern } else { use 666666 }
408 case BPMEM_DISPLAYCOPYFILTER + 1: // if (aa) { use sample_pattern } else { use 666666 }
409 case BPMEM_DISPLAYCOPYFILTER + 2: // if (aa) { use sample_pattern } else { use 666666 }
410 case BPMEM_DISPLAYCOPYFILTER + 3: // if (aa) { use sample_pattern } else { use 666666 }
411 case BPMEM_COPYFILTER0: // if (vf) { use vfilter } else { use 595000 }
412 case BPMEM_COPYFILTER1: // if (vf) { use vfilter } else { use 000015 }
413 return;
414 // -----------------------------------
415 // Interlacing Control
416 // -----------------------------------
417 case BPMEM_FIELDMASK: // GX_SetFieldMask(u8 even_mask,u8 odd_mask)
418 case BPMEM_FIELDMODE: // GX_SetFieldMode(u8 field_mode,u8 half_aspect_ratio)
419 // TODO
420 return;
421 // ----------------------------------------
422 // Unimportant regs (Clock, Perf, ...)
423 // ----------------------------------------
424 case BPMEM_BUSCLOCK0: // TB Bus Clock ?
425 case BPMEM_BUSCLOCK1: // TB Bus Clock ?
426 case BPMEM_PERF0_TRI: // Perf: Triangles
427 case BPMEM_PERF0_QUAD: // Perf: Quads
428 case BPMEM_PERF1: // Perf: Some Clock, Texels, TX, TC
429 return;
430 // ----------------
431 // EFB Copy config
432 // ----------------
433 case BPMEM_EFB_TL: // EFB Source Rect. Top, Left
434 case BPMEM_EFB_BR: // EFB Source Rect. Bottom, Right (w, h - 1)
435 case BPMEM_EFB_ADDR: // EFB Target Address
436 return;
437 // --------------
438 // Clear Config
439 // --------------
440 case BPMEM_CLEAR_AR: // Alpha and Red Components
441 case BPMEM_CLEAR_GB: // Green and Blue Components
442 case BPMEM_CLEAR_Z: // Z Components (24-bit Zbuffer)
443 return;
444 // -------------------------
445 // Bounding Box Control
446 // -------------------------
447 case BPMEM_CLEARBBOX1:
448 case BPMEM_CLEARBBOX2:
449 {
450 const u8 offset = bp.address & 2;
451 BoundingBox::Enable();
452
453 if (g_ActiveConfig.backend_info.bSupportsBBox && g_ActiveConfig.bBBoxEnable)
454 {
455 g_renderer->BBoxWrite(offset, bp.newvalue & 0x3ff);
456 g_renderer->BBoxWrite(offset + 1, bp.newvalue >> 10);
457 }
458 }
459 return;
460 case BPMEM_TEXINVALIDATE:
461 // TODO: Needs some restructuring in TextureCacheBase.
462 TextureCacheBase::InvalidateAllBindPoints();
463 return;
464
465 case BPMEM_ZCOMPARE: // Set the Z-Compare and EFB pixel format
466 OnPixelFormatChange();
467 if (bp.changes & 7)
468 SetBlendMode(); // dual source could be activated by changing to PIXELFMT_RGBA6_Z24
469 PixelShaderManager::SetZModeControl();
470 return;
471
472 case BPMEM_MIPMAP_STRIDE: // MipMap Stride Channel
473 case BPMEM_COPYYSCALE: // Display Copy Y Scale
474
475 /* 24 RID
476 * 21 BC3 - Ind. Tex Stage 3 NTexCoord
477 * 18 BI3 - Ind. Tex Stage 3 NTexMap
478 * 15 BC2 - Ind. Tex Stage 2 NTexCoord
479 * 12 BI2 - Ind. Tex Stage 2 NTexMap
480 * 9 BC1 - Ind. Tex Stage 1 NTexCoord
481 * 6 BI1 - Ind. Tex Stage 1 NTexMap
482 * 3 BC0 - Ind. Tex Stage 0 NTexCoord
483 * 0 BI0 - Ind. Tex Stage 0 NTexMap */
484 case BPMEM_IREF:
485 {
486 if (bp.changes)
487 PixelShaderManager::SetTevIndirectChanged();
488 return;
489 }
490
491 case BPMEM_TEV_KSEL: // Texture Environment Swap Mode Table 0
492 case BPMEM_TEV_KSEL + 1: // Texture Environment Swap Mode Table 1
493 case BPMEM_TEV_KSEL + 2: // Texture Environment Swap Mode Table 2
494 case BPMEM_TEV_KSEL + 3: // Texture Environment Swap Mode Table 3
495 case BPMEM_TEV_KSEL + 4: // Texture Environment Swap Mode Table 4
496 case BPMEM_TEV_KSEL + 5: // Texture Environment Swap Mode Table 5
497 case BPMEM_TEV_KSEL + 6: // Texture Environment Swap Mode Table 6
498 case BPMEM_TEV_KSEL + 7: // Texture Environment Swap Mode Table 7
499 PixelShaderManager::SetTevKSel(bp.address - BPMEM_TEV_KSEL, bp.newvalue);
500 return;
501
502 /* This Register can be used to limit to which bits of BP registers is
503 * actually written to. The mask is only valid for the next BP write,
504 * and will reset itself afterwards. It's handled as a special case in
505 * LoadBPReg. */
506 case BPMEM_BP_MASK:
507
508 case BPMEM_IND_IMASK: // Index Mask ?
509 case BPMEM_REVBITS: // Always set to 0x0F when GX_InitRevBits() is called.
510 return;
511
512 case BPMEM_CLEAR_PIXEL_PERF:
513 // GXClearPixMetric writes 0xAAA here, Sunshine alternates this register between values 0x000
514 // and 0xAAA
515 if (PerfQueryBase::ShouldEmulate())
516 g_perf_query->ResetQuery();
517 return;
518
519 case BPMEM_PRELOAD_ADDR:
520 case BPMEM_PRELOAD_TMEMEVEN:
521 case BPMEM_PRELOAD_TMEMODD: // Used when PRELOAD_MODE is set
522 return;
523
524 case BPMEM_PRELOAD_MODE: // Set to 0 when GX_TexModeSync() is called.
525 // if this is different from 0, manual TMEM management is used (GX_PreloadEntireTexture).
526 if (bp.newvalue != 0)
527 {
528 // TODO: Not quite sure if this is completely correct (likely not)
529 // NOTE: libogc's implementation of GX_PreloadEntireTexture seems flawed, so it's not
530 // necessarily a good reference for RE'ing this feature.
531
532 BPS_TmemConfig& tmem_cfg = bpmem.tmem_config;
533 u32 src_addr = tmem_cfg.preload_addr << 5; // TODO: Should we add mask here on GC?
534 u32 bytes_read = 0;
535 u32 tmem_addr_even = tmem_cfg.preload_tmem_even * TMEM_LINE_SIZE;
536
537 if (tmem_cfg.preload_tile_info.type != 3)
538 {
539 bytes_read = tmem_cfg.preload_tile_info.count * TMEM_LINE_SIZE;
540 if (tmem_addr_even + bytes_read > TMEM_SIZE)
541 bytes_read = TMEM_SIZE - tmem_addr_even;
542
543 Memory::CopyFromEmu(texMem + tmem_addr_even, src_addr, bytes_read);
544 }
545 else // RGBA8 tiles (and CI14, but that might just be stupid libogc!)
546 {
547 u8* src_ptr = Memory::GetPointer(src_addr);
548
549 // AR and GB tiles are stored in separate TMEM banks => can't use a single memcpy for
550 // everything
551 u32 tmem_addr_odd = tmem_cfg.preload_tmem_odd * TMEM_LINE_SIZE;
552
553 for (u32 i = 0; i < tmem_cfg.preload_tile_info.count; ++i)
554 {
555 if (tmem_addr_even + TMEM_LINE_SIZE > TMEM_SIZE ||
556 tmem_addr_odd + TMEM_LINE_SIZE > TMEM_SIZE)
557 break;
558
559 memcpy(texMem + tmem_addr_even, src_ptr + bytes_read, TMEM_LINE_SIZE);
560 memcpy(texMem + tmem_addr_odd, src_ptr + bytes_read + TMEM_LINE_SIZE, TMEM_LINE_SIZE);
561 tmem_addr_even += TMEM_LINE_SIZE;
562 tmem_addr_odd += TMEM_LINE_SIZE;
563 bytes_read += TMEM_LINE_SIZE * 2;
564 }
565 }
566
567 if (OpcodeDecoder::g_record_fifo_data)
568 FifoRecorder::GetInstance().UseMemory(src_addr, bytes_read, MemoryUpdate::TMEM);
569
570 TextureCacheBase::InvalidateAllBindPoints();
571 }
572 return;
573
574 // ---------------------------------------------------
575 // Set the TEV Color
576 // ---------------------------------------------------
577 //
578 // NOTE: Each of these registers actually maps to two variables internally.
579 // There's a bit that specifies which one is currently written to.
580 //
581 // NOTE: Some games write only to the RA register (or only to the BG register).
582 // We may not assume that the unwritten register holds a valid value, hence
583 // both component pairs need to be loaded individually.
584 case BPMEM_TEV_COLOR_RA:
585 case BPMEM_TEV_COLOR_RA + 2:
586 case BPMEM_TEV_COLOR_RA + 4:
587 case BPMEM_TEV_COLOR_RA + 6:
588 {
589 int num = (bp.address >> 1) & 0x3;
590 if (bpmem.tevregs[num].type_ra)
591 {
592 PixelShaderManager::SetTevKonstColor(num, 0, (s32)bpmem.tevregs[num].red);
593 PixelShaderManager::SetTevKonstColor(num, 3, (s32)bpmem.tevregs[num].alpha);
594 }
595 else
596 {
597 PixelShaderManager::SetTevColor(num, 0, (s32)bpmem.tevregs[num].red);
598 PixelShaderManager::SetTevColor(num, 3, (s32)bpmem.tevregs[num].alpha);
599 }
600 return;
601 }
602
603 case BPMEM_TEV_COLOR_BG:
604 case BPMEM_TEV_COLOR_BG + 2:
605 case BPMEM_TEV_COLOR_BG + 4:
606 case BPMEM_TEV_COLOR_BG + 6:
607 {
608 int num = (bp.address >> 1) & 0x3;
609 if (bpmem.tevregs[num].type_bg)
610 {
611 PixelShaderManager::SetTevKonstColor(num, 1, (s32)bpmem.tevregs[num].green);
612 PixelShaderManager::SetTevKonstColor(num, 2, (s32)bpmem.tevregs[num].blue);
613 }
614 else
615 {
616 PixelShaderManager::SetTevColor(num, 1, (s32)bpmem.tevregs[num].green);
617 PixelShaderManager::SetTevColor(num, 2, (s32)bpmem.tevregs[num].blue);
618 }
619 return;
620 }
621
622 default:
623 break;
624 }
625
626 switch (bp.address & 0xFC) // Texture sampler filter
627 {
628 // -------------------------
629 // Texture Environment Order
630 // -------------------------
631 case BPMEM_TREF:
632 case BPMEM_TREF + 4:
633 PixelShaderManager::SetTevOrder(bp.address - BPMEM_TREF, bp.newvalue);
634 return;
635 // ----------------------
636 // Set wrap size
637 // ----------------------
638 case BPMEM_SU_SSIZE: // Matches BPMEM_SU_TSIZE too
639 case BPMEM_SU_SSIZE + 4:
640 case BPMEM_SU_SSIZE + 8:
641 case BPMEM_SU_SSIZE + 12:
642 if (bp.changes)
643 {
644 PixelShaderManager::SetTexCoordChanged((bp.address - BPMEM_SU_SSIZE) >> 1);
645 GeometryShaderManager::SetTexCoordChanged((bp.address - BPMEM_SU_SSIZE) >> 1);
646 }
647 return;
648 // ------------------------
649 // BPMEM_TX_SETMODE0 - (Texture lookup and filtering mode) LOD/BIAS Clamp, MaxAnsio, LODBIAS,
650 // DiagLoad, Min Filter, Mag Filter, Wrap T, S
651 // BPMEM_TX_SETMODE1 - (LOD Stuff) - Max LOD, Min LOD
652 // ------------------------
653 case BPMEM_TX_SETMODE0: // (0x90 for linear)
654 case BPMEM_TX_SETMODE0_4:
655 TextureCacheBase::InvalidateAllBindPoints();
656 return;
657
658 case BPMEM_TX_SETMODE1:
659 case BPMEM_TX_SETMODE1_4:
660 TextureCacheBase::InvalidateAllBindPoints();
661 return;
662 // --------------------------------------------
663 // BPMEM_TX_SETIMAGE0 - Texture width, height, format
664 // BPMEM_TX_SETIMAGE1 - even LOD address in TMEM - Image Type, Cache Height, Cache Width, TMEM
665 // Offset
666 // BPMEM_TX_SETIMAGE2 - odd LOD address in TMEM - Cache Height, Cache Width, TMEM Offset
667 // BPMEM_TX_SETIMAGE3 - Address of Texture in main memory
668 // --------------------------------------------
669 case BPMEM_TX_SETIMAGE0:
670 case BPMEM_TX_SETIMAGE0_4:
671 case BPMEM_TX_SETIMAGE1:
672 case BPMEM_TX_SETIMAGE1_4:
673 case BPMEM_TX_SETIMAGE2:
674 case BPMEM_TX_SETIMAGE2_4:
675 case BPMEM_TX_SETIMAGE3:
676 case BPMEM_TX_SETIMAGE3_4:
677 TextureCacheBase::InvalidateAllBindPoints();
678 return;
679 // -------------------------------
680 // Set a TLUT
681 // BPMEM_TX_SETTLUT - Format, TMEM Offset (offset of TLUT from start of TMEM high bank > > 5)
682 // -------------------------------
683 case BPMEM_TX_SETTLUT:
684 case BPMEM_TX_SETTLUT_4:
685 TextureCacheBase::InvalidateAllBindPoints();
686 return;
687
688 default:
689 break;
690 }
691
692 switch (bp.address & 0xF0)
693 {
694 // --------------
695 // Indirect Tev
696 // --------------
697 case BPMEM_IND_CMD:
698 PixelShaderManager::SetTevIndirectChanged();
699 return;
700 // --------------------------------------------------
701 // Set Color/Alpha of a Tev
702 // BPMEM_TEV_COLOR_ENV - Dest, Shift, Clamp, Sub, Bias, Sel A, Sel B, Sel C, Sel D
703 // BPMEM_TEV_ALPHA_ENV - Dest, Shift, Clamp, Sub, Bias, Sel A, Sel B, Sel C, Sel D, T Swap, R Swap
704 // --------------------------------------------------
705 case BPMEM_TEV_COLOR_ENV: // Texture Environment 1
706 case BPMEM_TEV_COLOR_ENV + 16:
707 PixelShaderManager::SetTevCombiner((bp.address - BPMEM_TEV_COLOR_ENV) >> 1,
708 (bp.address - BPMEM_TEV_COLOR_ENV) & 1, bp.newvalue);
709 return;
710 default:
711 break;
712 }
713
714 WARN_LOG(VIDEO, "Unknown BP opcode: address = 0x%08x value = 0x%08x", bp.address, bp.newvalue);
715 }
716
717 // Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg()
LoadBPReg(u32 value0)718 void LoadBPReg(u32 value0)
719 {
720 int regNum = value0 >> 24;
721 int oldval = ((u32*)&bpmem)[regNum];
722 int newval = (oldval & ~bpmem.bpMask) | (value0 & bpmem.bpMask);
723 int changes = (oldval ^ newval) & 0xFFFFFF;
724
725 BPCmd bp = {regNum, changes, newval};
726
727 // Reset the mask register if we're not trying to set it ourselves.
728 if (regNum != BPMEM_BP_MASK)
729 bpmem.bpMask = 0xFFFFFF;
730
731 BPWritten(bp);
732 }
733
LoadBPRegPreprocess(u32 value0)734 void LoadBPRegPreprocess(u32 value0)
735 {
736 int regNum = value0 >> 24;
737 // masking could hypothetically be a problem
738 u32 newval = value0 & 0xffffff;
739 switch (regNum)
740 {
741 case BPMEM_SETDRAWDONE:
742 if ((newval & 0xff) == 0x02)
743 PixelEngine::SetFinish();
744 break;
745 case BPMEM_PE_TOKEN_ID:
746 PixelEngine::SetToken(newval & 0xffff, false);
747 break;
748 case BPMEM_PE_TOKEN_INT_ID: // Pixel Engine Interrupt Token ID
749 PixelEngine::SetToken(newval & 0xffff, true);
750 break;
751 }
752 }
753
GetBPRegInfo(const u8 * data,std::string * name,std::string * desc)754 void GetBPRegInfo(const u8* data, std::string* name, std::string* desc)
755 {
756 const char* no_yes[2] = {"No", "Yes"};
757
758 u8 cmd = data[0];
759 u32 cmddata = Common::swap32(data) & 0xFFFFFF;
760 switch (cmd)
761 {
762 // Macro to set the register name and make sure it was written correctly via compile time assertion
763 #define SetRegName(reg) \
764 *name = #reg; \
765 (void)(reg);
766
767 case BPMEM_GENMODE: // 0x00
768 SetRegName(BPMEM_GENMODE);
769 // TODO: Description
770 break;
771
772 case BPMEM_DISPLAYCOPYFILTER: // 0x01
773 // TODO: This is actually the sample pattern used for copies from an antialiased EFB
774 SetRegName(BPMEM_DISPLAYCOPYFILTER);
775 // TODO: Description
776 break;
777
778 case 0x02: // 0x02
779 case 0x03: // 0x03
780 case 0x04: // 0x04
781 // TODO: same as BPMEM_DISPLAYCOPYFILTER
782 break;
783
784 case BPMEM_IND_MTXA: // 0x06
785 case BPMEM_IND_MTXA + 3:
786 case BPMEM_IND_MTXA + 6:
787 SetRegName(BPMEM_IND_MTXA);
788 // TODO: Description
789 break;
790
791 case BPMEM_IND_MTXB: // 0x07
792 case BPMEM_IND_MTXB + 3:
793 case BPMEM_IND_MTXB + 6:
794 SetRegName(BPMEM_IND_MTXB);
795 // TODO: Descriptio
796 break;
797
798 case BPMEM_IND_MTXC: // 0x08
799 case BPMEM_IND_MTXC + 3:
800 case BPMEM_IND_MTXC + 6:
801 SetRegName(BPMEM_IND_MTXC);
802 // TODO: Description
803 break;
804
805 case BPMEM_IND_IMASK: // 0x0F
806 SetRegName(BPMEM_IND_IMASK);
807 // TODO: Description
808 break;
809
810 case BPMEM_IND_CMD: // 0x10
811 case BPMEM_IND_CMD + 1:
812 case BPMEM_IND_CMD + 2:
813 case BPMEM_IND_CMD + 3:
814 case BPMEM_IND_CMD + 4:
815 case BPMEM_IND_CMD + 5:
816 case BPMEM_IND_CMD + 6:
817 case BPMEM_IND_CMD + 7:
818 case BPMEM_IND_CMD + 8:
819 case BPMEM_IND_CMD + 9:
820 case BPMEM_IND_CMD + 10:
821 case BPMEM_IND_CMD + 11:
822 case BPMEM_IND_CMD + 12:
823 case BPMEM_IND_CMD + 13:
824 case BPMEM_IND_CMD + 14:
825 case BPMEM_IND_CMD + 15:
826 SetRegName(BPMEM_IND_CMD);
827 // TODO: Description
828 break;
829
830 case BPMEM_SCISSORTL: // 0x20
831 SetRegName(BPMEM_SCISSORTL);
832 // TODO: Description
833 break;
834
835 case BPMEM_SCISSORBR: // 0x21
836 SetRegName(BPMEM_SCISSORBR);
837 // TODO: Description
838 break;
839
840 case BPMEM_LINEPTWIDTH: // 0x22
841 SetRegName(BPMEM_LINEPTWIDTH);
842 // TODO: Description
843 break;
844
845 case BPMEM_PERF0_TRI: // 0x23
846 SetRegName(BPMEM_PERF0_TRI);
847 // TODO: Description
848 break;
849
850 case BPMEM_PERF0_QUAD: // 0x24
851 SetRegName(BPMEM_PERF0_QUAD);
852 // TODO: Description
853 break;
854
855 case BPMEM_RAS1_SS0: // 0x25
856 SetRegName(BPMEM_RAS1_SS0);
857 // TODO: Description
858 break;
859
860 case BPMEM_RAS1_SS1: // 0x26
861 SetRegName(BPMEM_RAS1_SS1);
862 // TODO: Description
863 break;
864
865 case BPMEM_IREF: // 0x27
866 SetRegName(BPMEM_IREF);
867 // TODO: Description
868 break;
869
870 case BPMEM_TREF: // 0x28
871 case BPMEM_TREF + 1:
872 case BPMEM_TREF + 2:
873 case BPMEM_TREF + 3:
874 case BPMEM_TREF + 4:
875 case BPMEM_TREF + 5:
876 case BPMEM_TREF + 6:
877 case BPMEM_TREF + 7:
878 SetRegName(BPMEM_TREF);
879 // TODO: Description
880 break;
881
882 case BPMEM_SU_SSIZE: // 0x30
883 case BPMEM_SU_SSIZE + 2:
884 case BPMEM_SU_SSIZE + 4:
885 case BPMEM_SU_SSIZE + 6:
886 case BPMEM_SU_SSIZE + 8:
887 case BPMEM_SU_SSIZE + 10:
888 case BPMEM_SU_SSIZE + 12:
889 case BPMEM_SU_SSIZE + 14:
890 SetRegName(BPMEM_SU_SSIZE);
891 // TODO: Description
892 break;
893
894 case BPMEM_SU_TSIZE: // 0x31
895 case BPMEM_SU_TSIZE + 2:
896 case BPMEM_SU_TSIZE + 4:
897 case BPMEM_SU_TSIZE + 6:
898 case BPMEM_SU_TSIZE + 8:
899 case BPMEM_SU_TSIZE + 10:
900 case BPMEM_SU_TSIZE + 12:
901 case BPMEM_SU_TSIZE + 14:
902 SetRegName(BPMEM_SU_TSIZE);
903 // TODO: Description
904 break;
905
906 case BPMEM_ZMODE: // 0x40
907 SetRegName(BPMEM_ZMODE);
908 // TODO: Description
909 break;
910
911 case BPMEM_BLENDMODE: // 0x41
912 {
913 SetRegName(BPMEM_BLENDMODE);
914 BlendMode mode;
915 mode.hex = cmddata;
916 const char* dstfactors[] = {"0", "1", "src_color", "1-src_color",
917 "src_alpha", "1-src_alpha", "dst_alpha", "1-dst_alpha"};
918 const char* srcfactors[] = {"0", "1", "dst_color", "1-dst_color",
919 "src_alpha", "1-src_alpha", "dst_alpha", "1-dst_alpha"};
920 const char* logicmodes[] = {"0", "s & d", "s & ~d", "s", "~s & d", "d",
921 "s ^ d", "s | d", "~(s | d)", "~(s ^ d)", "~d", "s | ~d",
922 "~s", "~s | d", "~(s & d)", "1"};
923 *desc =
924 fmt::format("Enable: {}\n"
925 "Logic ops: {}\n"
926 "Dither: {}\n"
927 "Color write: {}\n"
928 "Alpha write: {}\n"
929 "Dest factor: {}\n"
930 "Source factor: {}\n"
931 "Subtract: {}\n"
932 "Logic mode: {}\n",
933 no_yes[mode.blendenable], no_yes[mode.logicopenable], no_yes[mode.dither],
934 no_yes[mode.colorupdate], no_yes[mode.alphaupdate], dstfactors[mode.dstfactor],
935 srcfactors[mode.srcfactor], no_yes[mode.subtract], logicmodes[mode.logicmode]);
936 }
937 break;
938
939 case BPMEM_CONSTANTALPHA: // 0x42
940 SetRegName(BPMEM_CONSTANTALPHA);
941 // TODO: Description
942 break;
943
944 case BPMEM_ZCOMPARE: // 0x43
945 {
946 SetRegName(BPMEM_ZCOMPARE);
947 PEControl config;
948 config.hex = cmddata;
949 const char* pixel_formats[] = {"RGB8_Z24", "RGBA6_Z24", "RGB565_Z16", "Z24",
950 "Y8", "U8", "V8", "YUV420"};
951 const char* zformats[] = {
952 "linear", "compressed (near)", "compressed (mid)", "compressed (far)",
953 "inv linear", "compressed (inv near)", "compressed (inv mid)", "compressed (inv far)"};
954 *desc = fmt::format("EFB pixel format: {}\n"
955 "Depth format: {}\n"
956 "Early depth test: {}\n",
957 pixel_formats[config.pixel_format], zformats[config.zformat],
958 no_yes[config.early_ztest]);
959 }
960 break;
961
962 case BPMEM_FIELDMASK: // 0x44
963 SetRegName(BPMEM_FIELDMASK);
964 // TODO: Description
965 break;
966
967 case BPMEM_SETDRAWDONE: // 0x45
968 SetRegName(BPMEM_SETDRAWDONE);
969 // TODO: Description
970 break;
971
972 case BPMEM_BUSCLOCK0: // 0x46
973 SetRegName(BPMEM_BUSCLOCK0);
974 // TODO: Description
975 break;
976
977 case BPMEM_PE_TOKEN_ID: // 0x47
978 SetRegName(BPMEM_PE_TOKEN_ID);
979 // TODO: Description
980 break;
981
982 case BPMEM_PE_TOKEN_INT_ID: // 0x48
983 SetRegName(BPMEM_PE_TOKEN_INT_ID);
984 // TODO: Description
985 break;
986
987 case BPMEM_EFB_TL: // 0x49
988 {
989 SetRegName(BPMEM_EFB_TL);
990 X10Y10 left_top;
991 left_top.hex = cmddata;
992 *desc = fmt::format("Left: {}\nTop: {}", u32(left_top.x), u32(left_top.y));
993 }
994 break;
995
996 case BPMEM_EFB_BR: // 0x4A
997 {
998 // TODO: Misleading name, should be BPMEM_EFB_WH instead
999 SetRegName(BPMEM_EFB_BR);
1000 X10Y10 width_height;
1001 width_height.hex = cmddata;
1002 *desc = fmt::format("Width: {}\nHeight: {}", width_height.x + 1, width_height.y + 1);
1003 }
1004 break;
1005
1006 case BPMEM_EFB_ADDR: // 0x4B
1007 SetRegName(BPMEM_EFB_ADDR);
1008 *desc = fmt::format("Target address (32 byte aligned): 0x{:06X}", cmddata << 5);
1009 break;
1010
1011 case BPMEM_MIPMAP_STRIDE: // 0x4D
1012 SetRegName(BPMEM_MIPMAP_STRIDE);
1013 // TODO: Description
1014 break;
1015
1016 case BPMEM_COPYYSCALE: // 0x4E
1017 SetRegName(BPMEM_COPYYSCALE);
1018 *desc = fmt::format("Scaling factor (XFB copy only): 0x{:X} ({} or inverted {})", cmddata,
1019 static_cast<float>(cmddata) / 256.f, 256.f / static_cast<float>(cmddata));
1020 break;
1021
1022 case BPMEM_CLEAR_AR: // 0x4F
1023 SetRegName(BPMEM_CLEAR_AR);
1024 *desc = fmt::format("Alpha: 0x{:02X}\nRed: 0x{:02X}", (cmddata & 0xFF00) >> 8, cmddata & 0xFF);
1025 break;
1026
1027 case BPMEM_CLEAR_GB: // 0x50
1028 SetRegName(BPMEM_CLEAR_GB);
1029 *desc = fmt::format("Green: 0x{:02X}\nBlue: 0x{:02X}", (cmddata & 0xFF00) >> 8, cmddata & 0xFF);
1030 break;
1031
1032 case BPMEM_CLEAR_Z: // 0x51
1033 SetRegName(BPMEM_CLEAR_Z);
1034 *desc = fmt::format("Z value: 0x{:06X}", cmddata);
1035 break;
1036
1037 case BPMEM_TRIGGER_EFB_COPY: // 0x52
1038 {
1039 SetRegName(BPMEM_TRIGGER_EFB_COPY);
1040 UPE_Copy copy;
1041 copy.Hex = cmddata;
1042 *desc = fmt::format(
1043 "Clamping: {}\n"
1044 "Converting from RGB to YUV: {}\n"
1045 "Target pixel format: 0x{:X}\n"
1046 "Gamma correction: {}\n"
1047 "Mipmap filter: {}\n"
1048 "Vertical scaling: {}\n"
1049 "Clear: {}\n"
1050 "Frame to field: 0x{:01X}\n"
1051 "Copy to XFB: {}\n"
1052 "Intensity format: {}\n"
1053 "Automatic color conversion: {}",
1054 (copy.clamp_top && copy.clamp_bottom) ?
1055 "Top and Bottom" :
1056 (copy.clamp_top) ? "Top only" : (copy.clamp_bottom) ? "Bottom only" : "None",
1057 no_yes[copy.yuv], static_cast<int>(copy.tp_realFormat()),
1058 (copy.gamma == 0) ?
1059 "1.0" :
1060 (copy.gamma == 1) ? "1.7" : (copy.gamma == 2) ? "2.2" : "Invalid value 0x3?",
1061 no_yes[copy.half_scale], no_yes[copy.scale_invert], no_yes[copy.clear],
1062 static_cast<u32>(copy.frame_to_field), no_yes[copy.copy_to_xfb], no_yes[copy.intensity_fmt],
1063 no_yes[copy.auto_conv]);
1064 }
1065 break;
1066
1067 case BPMEM_COPYFILTER0: // 0x53
1068 SetRegName(BPMEM_COPYFILTER0);
1069 // TODO: Description
1070 break;
1071
1072 case BPMEM_COPYFILTER1: // 0x54
1073 SetRegName(BPMEM_COPYFILTER1);
1074 // TODO: Description
1075 break;
1076
1077 case BPMEM_CLEARBBOX1: // 0x55
1078 SetRegName(BPMEM_CLEARBBOX1);
1079 // TODO: Description
1080 break;
1081
1082 case BPMEM_CLEARBBOX2: // 0x56
1083 SetRegName(BPMEM_CLEARBBOX2);
1084 // TODO: Description
1085 break;
1086
1087 case BPMEM_CLEAR_PIXEL_PERF: // 0x57
1088 SetRegName(BPMEM_CLEAR_PIXEL_PERF);
1089 // TODO: Description
1090 break;
1091
1092 case BPMEM_REVBITS: // 0x58
1093 SetRegName(BPMEM_REVBITS);
1094 // TODO: Description
1095 break;
1096
1097 case BPMEM_SCISSOROFFSET: // 0x59
1098 SetRegName(BPMEM_SCISSOROFFSET);
1099 // TODO: Description
1100 break;
1101
1102 case BPMEM_PRELOAD_ADDR: // 0x60
1103 SetRegName(BPMEM_PRELOAD_ADDR);
1104 // TODO: Description
1105 break;
1106
1107 case BPMEM_PRELOAD_TMEMEVEN: // 0x61
1108 SetRegName(BPMEM_PRELOAD_TMEMEVEN);
1109 // TODO: Description
1110 break;
1111
1112 case BPMEM_PRELOAD_TMEMODD: // 0x62
1113 SetRegName(BPMEM_PRELOAD_TMEMODD);
1114 // TODO: Description
1115 break;
1116
1117 case BPMEM_PRELOAD_MODE: // 0x63
1118 SetRegName(BPMEM_PRELOAD_MODE);
1119 // TODO: Description
1120 break;
1121
1122 case BPMEM_LOADTLUT0: // 0x64
1123 SetRegName(BPMEM_LOADTLUT0);
1124 // TODO: Description
1125 break;
1126
1127 case BPMEM_LOADTLUT1: // 0x65
1128 SetRegName(BPMEM_LOADTLUT1);
1129 // TODO: Description
1130 break;
1131
1132 case BPMEM_TEXINVALIDATE: // 0x66
1133 SetRegName(BPMEM_TEXINVALIDATE);
1134 // TODO: Description
1135 break;
1136
1137 case BPMEM_PERF1: // 0x67
1138 SetRegName(BPMEM_PERF1);
1139 // TODO: Description
1140 break;
1141
1142 case BPMEM_FIELDMODE: // 0x68
1143 SetRegName(BPMEM_FIELDMODE);
1144 // TODO: Description
1145 break;
1146
1147 case BPMEM_BUSCLOCK1: // 0x69
1148 SetRegName(BPMEM_BUSCLOCK1);
1149 // TODO: Description
1150 break;
1151
1152 case BPMEM_TX_SETMODE0: // 0x80
1153 case BPMEM_TX_SETMODE0 + 1:
1154 case BPMEM_TX_SETMODE0 + 2:
1155 case BPMEM_TX_SETMODE0 + 3:
1156 SetRegName(BPMEM_TX_SETMODE0);
1157 // TODO: Description
1158 break;
1159
1160 case BPMEM_TX_SETMODE1: // 0x84
1161 case BPMEM_TX_SETMODE1 + 1:
1162 case BPMEM_TX_SETMODE1 + 2:
1163 case BPMEM_TX_SETMODE1 + 3:
1164 SetRegName(BPMEM_TX_SETMODE1);
1165 // TODO: Description
1166 break;
1167
1168 case BPMEM_TX_SETIMAGE0: // 0x88
1169 case BPMEM_TX_SETIMAGE0 + 1:
1170 case BPMEM_TX_SETIMAGE0 + 2:
1171 case BPMEM_TX_SETIMAGE0 + 3:
1172 case BPMEM_TX_SETIMAGE0_4: // 0xA8
1173 case BPMEM_TX_SETIMAGE0_4 + 1:
1174 case BPMEM_TX_SETIMAGE0_4 + 2:
1175 case BPMEM_TX_SETIMAGE0_4 + 3:
1176 {
1177 SetRegName(BPMEM_TX_SETIMAGE0);
1178 int texnum =
1179 (cmd < BPMEM_TX_SETIMAGE0_4) ? cmd - BPMEM_TX_SETIMAGE0 : cmd - BPMEM_TX_SETIMAGE0_4 + 4;
1180 TexImage0 teximg;
1181 teximg.hex = cmddata;
1182 *desc = fmt::format("Texture Unit: {}\n"
1183 "Width: {}\n"
1184 "Height: {}\n"
1185 "Format: {:x}\n",
1186 texnum, u32(teximg.width) + 1, u32(teximg.height) + 1, u32(teximg.format));
1187 }
1188 break;
1189
1190 case BPMEM_TX_SETIMAGE1: // 0x8C
1191 case BPMEM_TX_SETIMAGE1 + 1:
1192 case BPMEM_TX_SETIMAGE1 + 2:
1193 case BPMEM_TX_SETIMAGE1 + 3:
1194 case BPMEM_TX_SETIMAGE1_4: // 0xAC
1195 case BPMEM_TX_SETIMAGE1_4 + 1:
1196 case BPMEM_TX_SETIMAGE1_4 + 2:
1197 case BPMEM_TX_SETIMAGE1_4 + 3:
1198 {
1199 SetRegName(BPMEM_TX_SETIMAGE1);
1200 int texnum =
1201 (cmd < BPMEM_TX_SETIMAGE1_4) ? cmd - BPMEM_TX_SETIMAGE1 : cmd - BPMEM_TX_SETIMAGE1_4 + 4;
1202 TexImage1 teximg;
1203 teximg.hex = cmddata;
1204 *desc = fmt::format("Texture Unit: {}\n"
1205 "Even TMEM Offset: {:x}\n"
1206 "Even TMEM Width: {}\n"
1207 "Even TMEM Height: {}\n"
1208 "Cache is manually managed: {}\n",
1209 texnum, u32(teximg.tmem_even), u32(teximg.cache_width),
1210 u32(teximg.cache_height), no_yes[teximg.image_type]);
1211 }
1212 break;
1213
1214 case BPMEM_TX_SETIMAGE2: // 0x90
1215 case BPMEM_TX_SETIMAGE2 + 1:
1216 case BPMEM_TX_SETIMAGE2 + 2:
1217 case BPMEM_TX_SETIMAGE2 + 3:
1218 case BPMEM_TX_SETIMAGE2_4: // 0xB0
1219 case BPMEM_TX_SETIMAGE2_4 + 1:
1220 case BPMEM_TX_SETIMAGE2_4 + 2:
1221 case BPMEM_TX_SETIMAGE2_4 + 3:
1222 {
1223 SetRegName(BPMEM_TX_SETIMAGE2);
1224 int texnum =
1225 (cmd < BPMEM_TX_SETIMAGE2_4) ? cmd - BPMEM_TX_SETIMAGE2 : cmd - BPMEM_TX_SETIMAGE2_4 + 4;
1226 TexImage2 teximg;
1227 teximg.hex = cmddata;
1228 *desc = fmt::format("Texture Unit: {}\n"
1229 "Odd TMEM Offset: {:x}\n"
1230 "Odd TMEM Width: {}\n"
1231 "Odd TMEM Height: {}\n",
1232 texnum, u32(teximg.tmem_odd), u32(teximg.cache_width),
1233 u32(teximg.cache_height));
1234 }
1235 break;
1236
1237 case BPMEM_TX_SETIMAGE3: // 0x94
1238 case BPMEM_TX_SETIMAGE3 + 1:
1239 case BPMEM_TX_SETIMAGE3 + 2:
1240 case BPMEM_TX_SETIMAGE3 + 3:
1241 case BPMEM_TX_SETIMAGE3_4: // 0xB4
1242 case BPMEM_TX_SETIMAGE3_4 + 1:
1243 case BPMEM_TX_SETIMAGE3_4 + 2:
1244 case BPMEM_TX_SETIMAGE3_4 + 3:
1245 {
1246 SetRegName(BPMEM_TX_SETIMAGE3);
1247 int texnum =
1248 (cmd < BPMEM_TX_SETIMAGE3_4) ? cmd - BPMEM_TX_SETIMAGE3 : cmd - BPMEM_TX_SETIMAGE3_4 + 4;
1249 TexImage3 teximg;
1250 teximg.hex = cmddata;
1251 *desc = fmt::format("Texture {} source address (32 byte aligned): 0x{:06X}", texnum,
1252 teximg.image_base << 5);
1253 }
1254 break;
1255
1256 case BPMEM_TX_SETTLUT: // 0x98
1257 case BPMEM_TX_SETTLUT + 1:
1258 case BPMEM_TX_SETTLUT + 2:
1259 case BPMEM_TX_SETTLUT + 3:
1260 case BPMEM_TX_SETTLUT_4: // 0xB8
1261 case BPMEM_TX_SETTLUT_4 + 1:
1262 case BPMEM_TX_SETTLUT_4 + 2:
1263 case BPMEM_TX_SETTLUT_4 + 3:
1264 SetRegName(BPMEM_TX_SETTLUT);
1265 // TODO: Description
1266 break;
1267
1268 case BPMEM_TEV_COLOR_ENV: // 0xC0
1269 case BPMEM_TEV_COLOR_ENV + 2:
1270 case BPMEM_TEV_COLOR_ENV + 4:
1271 case BPMEM_TEV_COLOR_ENV + 8:
1272 case BPMEM_TEV_COLOR_ENV + 10:
1273 case BPMEM_TEV_COLOR_ENV + 12:
1274 case BPMEM_TEV_COLOR_ENV + 14:
1275 case BPMEM_TEV_COLOR_ENV + 16:
1276 case BPMEM_TEV_COLOR_ENV + 18:
1277 case BPMEM_TEV_COLOR_ENV + 20:
1278 case BPMEM_TEV_COLOR_ENV + 22:
1279 case BPMEM_TEV_COLOR_ENV + 24:
1280 case BPMEM_TEV_COLOR_ENV + 26:
1281 case BPMEM_TEV_COLOR_ENV + 28:
1282 case BPMEM_TEV_COLOR_ENV + 30:
1283 {
1284 SetRegName(BPMEM_TEV_COLOR_ENV);
1285 TevStageCombiner::ColorCombiner cc;
1286 cc.hex = cmddata;
1287 const char* tevin[] = {
1288 "prev.rgb", "prev.aaa", "c0.rgb", "c0.aaa", "c1.rgb", "c1.aaa", "c2.rgb", "c2.aaa",
1289 "tex.rgb", "tex.aaa", "ras.rgb", "ras.aaa", "ONE", "HALF", "konst.rgb", "ZERO",
1290 };
1291 const char* tevbias[] = {"0", "+0.5", "-0.5", "compare"};
1292 const char* tevop[] = {"add", "sub"};
1293 const char* tevscale[] = {"1", "2", "4", "0.5"};
1294 const char* tevout[] = {"prev.rgb", "c0.rgb", "c1.rgb", "c2.rgb"};
1295 *desc = fmt::format("Tev stage: {}\n"
1296 "a: {}\n"
1297 "b: {}\n"
1298 "c: {}\n"
1299 "d: {}\n"
1300 "Bias: {}\n"
1301 "Op: {}\n"
1302 "Clamp: {}\n"
1303 "Scale factor: {}\n"
1304 "Dest: {}\n",
1305 (data[0] - BPMEM_TEV_COLOR_ENV) / 2, tevin[cc.a], tevin[cc.b], tevin[cc.c],
1306 tevin[cc.d], tevbias[cc.bias], tevop[cc.op], no_yes[cc.clamp],
1307 tevscale[cc.shift], tevout[cc.dest]);
1308 break;
1309 }
1310
1311 case BPMEM_TEV_ALPHA_ENV: // 0xC1
1312 case BPMEM_TEV_ALPHA_ENV + 2:
1313 case BPMEM_TEV_ALPHA_ENV + 4:
1314 case BPMEM_TEV_ALPHA_ENV + 6:
1315 case BPMEM_TEV_ALPHA_ENV + 8:
1316 case BPMEM_TEV_ALPHA_ENV + 10:
1317 case BPMEM_TEV_ALPHA_ENV + 12:
1318 case BPMEM_TEV_ALPHA_ENV + 14:
1319 case BPMEM_TEV_ALPHA_ENV + 16:
1320 case BPMEM_TEV_ALPHA_ENV + 18:
1321 case BPMEM_TEV_ALPHA_ENV + 20:
1322 case BPMEM_TEV_ALPHA_ENV + 22:
1323 case BPMEM_TEV_ALPHA_ENV + 24:
1324 case BPMEM_TEV_ALPHA_ENV + 26:
1325 case BPMEM_TEV_ALPHA_ENV + 28:
1326 case BPMEM_TEV_ALPHA_ENV + 30:
1327 {
1328 SetRegName(BPMEM_TEV_ALPHA_ENV);
1329 TevStageCombiner::AlphaCombiner ac;
1330 ac.hex = cmddata;
1331 const char* tevin[] = {
1332 "prev", "c0", "c1", "c2", "tex", "ras", "konst", "ZERO",
1333 };
1334 const char* tevbias[] = {"0", "+0.5", "-0.5", "compare"};
1335 const char* tevop[] = {"add", "sub"};
1336 const char* tevscale[] = {"1", "2", "4", "0.5"};
1337 const char* tevout[] = {"prev", "c0", "c1", "c2"};
1338 *desc = fmt::format("Tev stage: {}\n"
1339 "a: {}\n"
1340 "b: {}\n"
1341 "c: {}\n"
1342 "d: {}\n"
1343 "Bias: {}\n"
1344 "Op: {}\n"
1345 "Clamp: {}\n"
1346 "Scale factor: {}\n"
1347 "Dest: {}\n"
1348 "Ras sel: {}\n"
1349 "Tex sel: {}\n",
1350 (data[0] - BPMEM_TEV_ALPHA_ENV) / 2, tevin[ac.a], tevin[ac.b], tevin[ac.c],
1351 tevin[ac.d], tevbias[ac.bias], tevop[ac.op], no_yes[ac.clamp],
1352 tevscale[ac.shift], tevout[ac.dest], ac.rswap.Value(), ac.tswap.Value());
1353 break;
1354 }
1355
1356 case BPMEM_TEV_COLOR_RA: // 0xE0
1357 case BPMEM_TEV_COLOR_RA + 2: // 0xE2
1358 case BPMEM_TEV_COLOR_RA + 4: // 0xE4
1359 case BPMEM_TEV_COLOR_RA + 6: // 0xE6
1360 SetRegName(BPMEM_TEV_COLOR_RA);
1361 // TODO: Description
1362 break;
1363
1364 case BPMEM_TEV_COLOR_BG: // 0xE1
1365 case BPMEM_TEV_COLOR_BG + 2: // 0xE3
1366 case BPMEM_TEV_COLOR_BG + 4: // 0xE5
1367 case BPMEM_TEV_COLOR_BG + 6: // 0xE7
1368 SetRegName(BPMEM_TEV_COLOR_BG);
1369 // TODO: Description
1370 break;
1371
1372 case BPMEM_FOGRANGE: // 0xE8
1373 case BPMEM_FOGRANGE + 1:
1374 case BPMEM_FOGRANGE + 2:
1375 case BPMEM_FOGRANGE + 3:
1376 case BPMEM_FOGRANGE + 4:
1377 case BPMEM_FOGRANGE + 5:
1378 SetRegName(BPMEM_FOGRANGE);
1379 // TODO: Description
1380 break;
1381
1382 case BPMEM_FOGPARAM0: // 0xEE
1383 SetRegName(BPMEM_FOGPARAM0);
1384 // TODO: Description
1385 break;
1386
1387 case BPMEM_FOGBMAGNITUDE: // 0xEF
1388 SetRegName(BPMEM_FOGBMAGNITUDE);
1389 // TODO: Description
1390 break;
1391
1392 case BPMEM_FOGBEXPONENT: // 0xF0
1393 SetRegName(BPMEM_FOGBEXPONENT);
1394 // TODO: Description
1395 break;
1396
1397 case BPMEM_FOGPARAM3: // 0xF1
1398 SetRegName(BPMEM_FOGPARAM3);
1399 // TODO: Description
1400 break;
1401
1402 case BPMEM_FOGCOLOR: // 0xF2
1403 SetRegName(BPMEM_FOGCOLOR);
1404 // TODO: Description
1405 break;
1406
1407 case BPMEM_ALPHACOMPARE: // 0xF3
1408 {
1409 SetRegName(BPMEM_ALPHACOMPARE);
1410 AlphaTest test;
1411 test.hex = cmddata;
1412 const char* functions[] = {"NEVER", "LESS", "EQUAL", "LEQUAL",
1413 "GREATER", "NEQUAL", "GEQUAL", "ALWAYS"};
1414 const char* logic[] = {"AND", "OR", "XOR", "XNOR"};
1415 *desc = fmt::format("Test 1: {} (ref: 0x{:02x})\n"
1416 "Test 2: {} (ref: 0x{:02x})\n"
1417 "Logic: {}\n",
1418 functions[test.comp0], test.ref0.Value(), functions[test.comp1],
1419 test.ref1.Value(), logic[test.logic]);
1420 break;
1421 }
1422
1423 case BPMEM_BIAS: // 0xF4
1424 SetRegName(BPMEM_BIAS);
1425 // TODO: Description
1426 break;
1427
1428 case BPMEM_ZTEX2: // 0xF5
1429 SetRegName(BPMEM_ZTEX2);
1430 // TODO: Description
1431 break;
1432
1433 case BPMEM_TEV_KSEL: // 0xF6
1434 case BPMEM_TEV_KSEL + 1:
1435 case BPMEM_TEV_KSEL + 2:
1436 case BPMEM_TEV_KSEL + 3:
1437 case BPMEM_TEV_KSEL + 4:
1438 case BPMEM_TEV_KSEL + 5:
1439 case BPMEM_TEV_KSEL + 6:
1440 case BPMEM_TEV_KSEL + 7:
1441 SetRegName(BPMEM_TEV_KSEL);
1442 // TODO: Description
1443 break;
1444
1445 #undef SetRegName
1446 }
1447 }
1448
1449 // Called when loading a saved state.
BPReload()1450 void BPReload()
1451 {
1452 // restore anything that goes straight to the renderer.
1453 // let's not risk actually replaying any writes.
1454 // note that PixelShaderManager is already covered since it has its own DoState.
1455 SetGenerationMode();
1456 SetScissor();
1457 SetViewport();
1458 SetDepthMode();
1459 SetBlendMode();
1460 OnPixelFormatChange();
1461 }
1462