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