1 // Copyright 2009 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "VideoCommon/BPFunctions.h"
6 
7 #include <algorithm>
8 
9 #include "Common/CommonTypes.h"
10 #include "Common/Logging/Log.h"
11 
12 #include "VideoCommon/AbstractFramebuffer.h"
13 #include "VideoCommon/BPMemory.h"
14 #include "VideoCommon/FramebufferManager.h"
15 #include "VideoCommon/RenderBase.h"
16 #include "VideoCommon/RenderState.h"
17 #include "VideoCommon/VertexManagerBase.h"
18 #include "VideoCommon/VideoCommon.h"
19 #include "VideoCommon/VideoConfig.h"
20 #include "VideoCommon/XFMemory.h"
21 
22 namespace BPFunctions
23 {
24 // ----------------------------------------------
25 // State translation lookup tables
26 // Reference: Yet Another GameCube Documentation
27 // ----------------------------------------------
28 
FlushPipeline()29 void FlushPipeline()
30 {
31   g_vertex_manager->Flush();
32 }
33 
SetGenerationMode()34 void SetGenerationMode()
35 {
36   g_vertex_manager->SetRasterizationStateChanged();
37 }
38 
SetScissor()39 void SetScissor()
40 {
41   /* NOTE: the minimum value here for the scissor rect and offset is -342.
42    * GX internally adds on an offset of 342 to both the offset and scissor
43    * coords to ensure that the register was always unsigned.
44    *
45    * The code that was here before tried to "undo" this offset, but
46    * since we always take the difference, the +342 added to both
47    * sides cancels out. */
48 
49   /* The scissor offset is always even, so to save space, the scissor offset
50    * register is scaled down by 2. So, if somebody calls
51    * GX_SetScissorBoxOffset(20, 20); the registers will be set to 10, 10. */
52   const int xoff = bpmem.scissorOffset.x * 2;
53   const int yoff = bpmem.scissorOffset.y * 2;
54 
55   MathUtil::Rectangle<int> native_rc(bpmem.scissorTL.x - xoff, bpmem.scissorTL.y - yoff,
56                                      bpmem.scissorBR.x - xoff + 1, bpmem.scissorBR.y - yoff + 1);
57   native_rc.ClampUL(0, 0, EFB_WIDTH, EFB_HEIGHT);
58 
59   auto target_rc = g_renderer->ConvertEFBRectangle(native_rc);
60   auto converted_rc =
61       g_renderer->ConvertFramebufferRectangle(target_rc, g_renderer->GetCurrentFramebuffer());
62   g_renderer->SetScissorRect(converted_rc);
63 }
64 
SetViewport()65 void SetViewport()
66 {
67   int scissor_x_off = bpmem.scissorOffset.x * 2;
68   int scissor_y_off = bpmem.scissorOffset.y * 2;
69   float x = g_renderer->EFBToScaledXf(xfmem.viewport.xOrig - xfmem.viewport.wd - scissor_x_off);
70   float y = g_renderer->EFBToScaledYf(xfmem.viewport.yOrig + xfmem.viewport.ht - scissor_y_off);
71 
72   float width = g_renderer->EFBToScaledXf(2.0f * xfmem.viewport.wd);
73   float height = g_renderer->EFBToScaledYf(-2.0f * xfmem.viewport.ht);
74   float min_depth = (xfmem.viewport.farZ - xfmem.viewport.zRange) / 16777216.0f;
75   float max_depth = xfmem.viewport.farZ / 16777216.0f;
76   if (width < 0.f)
77   {
78     x += width;
79     width *= -1;
80   }
81   if (height < 0.f)
82   {
83     y += height;
84     height *= -1;
85   }
86 
87   // The maximum depth that is written to the depth buffer should never exceed this value.
88   // This is necessary because we use a 2^24 divisor for all our depth values to prevent
89   // floating-point round-trip errors. However the console GPU doesn't ever write a value
90   // to the depth buffer that exceeds 2^24 - 1.
91   constexpr float GX_MAX_DEPTH = 16777215.0f / 16777216.0f;
92   if (!g_ActiveConfig.backend_info.bSupportsDepthClamp)
93   {
94     // There's no way to support oversized depth ranges in this situation. Let's just clamp the
95     // range to the maximum value supported by the console GPU and hope for the best.
96     min_depth = std::clamp(min_depth, 0.0f, GX_MAX_DEPTH);
97     max_depth = std::clamp(max_depth, 0.0f, GX_MAX_DEPTH);
98   }
99 
100   if (g_renderer->UseVertexDepthRange())
101   {
102     // We need to ensure depth values are clamped the maximum value supported by the console GPU.
103     // Taking into account whether the depth range is inverted or not.
104     if (xfmem.viewport.zRange < 0.0f && g_ActiveConfig.backend_info.bSupportsReversedDepthRange)
105     {
106       min_depth = GX_MAX_DEPTH;
107       max_depth = 0.0f;
108     }
109     else
110     {
111       min_depth = 0.0f;
112       max_depth = GX_MAX_DEPTH;
113     }
114   }
115 
116   float near_depth, far_depth;
117   if (g_ActiveConfig.backend_info.bSupportsReversedDepthRange)
118   {
119     // Set the reversed depth range.
120     near_depth = max_depth;
121     far_depth = min_depth;
122   }
123   else
124   {
125     // We use an inverted depth range here to apply the Reverse Z trick.
126     // This trick makes sure we match the precision provided by the 1:0
127     // clipping depth range on the hardware.
128     near_depth = 1.0f - max_depth;
129     far_depth = 1.0f - min_depth;
130   }
131 
132   // Clamp to size if oversized not supported. Required for D3D.
133   if (!g_ActiveConfig.backend_info.bSupportsOversizedViewports)
134   {
135     const float max_width = static_cast<float>(g_renderer->GetCurrentFramebuffer()->GetWidth());
136     const float max_height = static_cast<float>(g_renderer->GetCurrentFramebuffer()->GetHeight());
137     x = std::clamp(x, 0.0f, max_width - 1.0f);
138     y = std::clamp(y, 0.0f, max_height - 1.0f);
139     width = std::clamp(width, 1.0f, max_width - x);
140     height = std::clamp(height, 1.0f, max_height - y);
141   }
142 
143   // Lower-left flip.
144   if (g_ActiveConfig.backend_info.bUsesLowerLeftOrigin)
145     y = static_cast<float>(g_renderer->GetCurrentFramebuffer()->GetHeight()) - y - height;
146 
147   g_renderer->SetViewport(x, y, width, height, near_depth, far_depth);
148 }
149 
SetDepthMode()150 void SetDepthMode()
151 {
152   g_vertex_manager->SetDepthStateChanged();
153 }
154 
SetBlendMode()155 void SetBlendMode()
156 {
157   g_vertex_manager->SetBlendingStateChanged();
158 }
159 
160 /* Explanation of the magic behind ClearScreen:
161   There's numerous possible formats for the pixel data in the EFB.
162   However, in the HW accelerated backends we're always using RGBA8
163   for the EFB format, which causes some problems:
164   - We're using an alpha channel although the game doesn't
165   - If the actual EFB format is RGBA6_Z24 or R5G6B5_Z16, we are using more bits per channel than the
166   native HW
167 
168   To properly emulate the above points, we're doing the following:
169   (1)
170     - disable alpha channel writing of any kind of rendering if the actual EFB format doesn't use an
171   alpha channel
172     - NOTE: Always make sure that the EFB has been cleared to an alpha value of 0xFF in this case!
173     - Same for color channels, these need to be cleared to 0x00 though.
174   (2)
175     - convert the RGBA8 color to RGBA6/RGB8/RGB565 and convert it to RGBA8 again
176     - convert the Z24 depth value to Z16 and back to Z24
177 */
ClearScreen(const MathUtil::Rectangle<int> & rc)178 void ClearScreen(const MathUtil::Rectangle<int>& rc)
179 {
180   bool colorEnable = (bpmem.blendmode.colorupdate != 0);
181   bool alphaEnable = (bpmem.blendmode.alphaupdate != 0);
182   bool zEnable = (bpmem.zmode.updateenable != 0);
183   auto pixel_format = bpmem.zcontrol.pixel_format;
184 
185   // (1): Disable unused color channels
186   if (pixel_format == PEControl::RGB8_Z24 || pixel_format == PEControl::RGB565_Z16 ||
187       pixel_format == PEControl::Z24)
188   {
189     alphaEnable = false;
190   }
191 
192   if (colorEnable || alphaEnable || zEnable)
193   {
194     u32 color = (bpmem.clearcolorAR << 16) | bpmem.clearcolorGB;
195     u32 z = bpmem.clearZValue;
196 
197     // (2) drop additional accuracy
198     if (pixel_format == PEControl::RGBA6_Z24)
199     {
200       color = RGBA8ToRGBA6ToRGBA8(color);
201     }
202     else if (pixel_format == PEControl::RGB565_Z16)
203     {
204       color = RGBA8ToRGB565ToRGBA8(color);
205       z = Z24ToZ16ToZ24(z);
206     }
207     g_renderer->ClearScreen(rc, colorEnable, alphaEnable, zEnable, color, z);
208   }
209 }
210 
OnPixelFormatChange()211 void OnPixelFormatChange()
212 {
213   // TODO : Check for Z compression format change
214   // When using 16bit Z, the game may enable a special compression format which we need to handle
215   // If we don't, Z values will be completely screwed up, currently only Star Wars:RS2 uses that.
216 
217   /*
218    * When changing the EFB format, the pixel data won't get converted to the new format but stays
219    * the same.
220    * Since we are always using an RGBA8 buffer though, this causes issues in some games.
221    * Thus, we reinterpret the old EFB data with the new format here.
222    */
223   if (!g_ActiveConfig.bEFBEmulateFormatChanges)
224     return;
225 
226   auto old_format = g_renderer->GetPrevPixelFormat();
227   auto new_format = bpmem.zcontrol.pixel_format;
228   g_renderer->StorePixelFormat(new_format);
229 
230   DEBUG_LOG(VIDEO, "pixelfmt: pixel=%d, zc=%d", static_cast<int>(new_format),
231             static_cast<int>(bpmem.zcontrol.zformat));
232 
233   // no need to reinterpret pixel data in these cases
234   if (new_format == old_format || old_format == PEControl::INVALID_FMT)
235     return;
236 
237   // Check for pixel format changes
238   switch (old_format)
239   {
240   case PEControl::RGB8_Z24:
241   case PEControl::Z24:
242   {
243     // Z24 and RGB8_Z24 are treated equal, so just return in this case
244     if (new_format == PEControl::RGB8_Z24 || new_format == PEControl::Z24)
245       return;
246 
247     if (new_format == PEControl::RGBA6_Z24)
248     {
249       g_renderer->ReinterpretPixelData(EFBReinterpretType::RGB8ToRGBA6);
250       return;
251     }
252     else if (new_format == PEControl::RGB565_Z16)
253     {
254       g_renderer->ReinterpretPixelData(EFBReinterpretType::RGB8ToRGB565);
255       return;
256     }
257   }
258   break;
259 
260   case PEControl::RGBA6_Z24:
261   {
262     if (new_format == PEControl::RGB8_Z24 || new_format == PEControl::Z24)
263     {
264       g_renderer->ReinterpretPixelData(EFBReinterpretType::RGBA6ToRGB8);
265       return;
266     }
267     else if (new_format == PEControl::RGB565_Z16)
268     {
269       g_renderer->ReinterpretPixelData(EFBReinterpretType::RGBA6ToRGB565);
270       return;
271     }
272   }
273   break;
274 
275   case PEControl::RGB565_Z16:
276   {
277     if (new_format == PEControl::RGB8_Z24 || new_format == PEControl::Z24)
278     {
279       g_renderer->ReinterpretPixelData(EFBReinterpretType::RGB565ToRGB8);
280       return;
281     }
282     else if (new_format == PEControl::RGBA6_Z24)
283     {
284       g_renderer->ReinterpretPixelData(EFBReinterpretType::RGB565ToRGBA6);
285       return;
286     }
287   }
288   break;
289 
290   default:
291     break;
292   }
293 
294   ERROR_LOG(VIDEO, "Unhandled EFB format change: %d to %d", static_cast<int>(old_format),
295             static_cast<int>(new_format));
296 }
297 
SetInterlacingMode(const BPCmd & bp)298 void SetInterlacingMode(const BPCmd& bp)
299 {
300   // TODO
301   switch (bp.address)
302   {
303   case BPMEM_FIELDMODE:
304   {
305     // SDK always sets bpmem.lineptwidth.lineaspect via BPMEM_LINEPTWIDTH
306     // just before this cmd
307     const char* action[] = {"don't adjust", "adjust"};
308     DEBUG_LOG(VIDEO, "BPMEM_FIELDMODE texLOD:%s lineaspect:%s", action[bpmem.fieldmode.texLOD],
309               action[bpmem.lineptwidth.lineaspect]);
310   }
311   break;
312   case BPMEM_FIELDMASK:
313   {
314     // Determines if fields will be written to EFB (always computed)
315     const char* action[] = {"skip", "write"};
316     DEBUG_LOG(VIDEO, "BPMEM_FIELDMASK even:%s odd:%s", action[bpmem.fieldmask.even],
317               action[bpmem.fieldmask.odd]);
318   }
319   break;
320   default:
321     ERROR_LOG(VIDEO, "SetInterlacingMode default");
322     break;
323   }
324 }
325 };  // namespace BPFunctions
326