1 // Copyright 2016 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "VideoCommon/RenderState.h"
6 #include <algorithm>
7 #include <array>
8 #include "VideoCommon/SamplerCommon.h"
9 #include "VideoCommon/TextureConfig.h"
10 
Generate(const BPMemory & bp,PrimitiveType primitive_type)11 void RasterizationState::Generate(const BPMemory& bp, PrimitiveType primitive_type)
12 {
13   cullmode = bp.genMode.cullmode;
14   primitive = primitive_type;
15 
16   // Back-face culling should be disabled for points/lines.
17   if (primitive_type != PrimitiveType::Triangles && primitive_type != PrimitiveType::TriangleStrip)
18     cullmode = GenMode::CULL_NONE;
19 }
20 
operator =(const RasterizationState & rhs)21 RasterizationState& RasterizationState::operator=(const RasterizationState& rhs)
22 {
23   hex = rhs.hex;
24   return *this;
25 }
26 
operator =(const FramebufferState & rhs)27 FramebufferState& FramebufferState::operator=(const FramebufferState& rhs)
28 {
29   hex = rhs.hex;
30   return *this;
31 }
32 
Generate(const BPMemory & bp)33 void DepthState::Generate(const BPMemory& bp)
34 {
35   testenable = bp.zmode.testenable.Value();
36   updateenable = bp.zmode.updateenable.Value();
37   func = bp.zmode.func.Value();
38 }
39 
operator =(const DepthState & rhs)40 DepthState& DepthState::operator=(const DepthState& rhs)
41 {
42   hex = rhs.hex;
43   return *this;
44 }
45 
46 // If the framebuffer format has no alpha channel, it is assumed to
47 // ONE on blending. As the backends may emulate this framebuffer
48 // configuration with an alpha channel, we just drop all references
49 // to the destination alpha channel.
RemoveDstAlphaUsage(BlendMode::BlendFactor factor)50 static BlendMode::BlendFactor RemoveDstAlphaUsage(BlendMode::BlendFactor factor)
51 {
52   switch (factor)
53   {
54   case BlendMode::DSTALPHA:
55     return BlendMode::ONE;
56   case BlendMode::INVDSTALPHA:
57     return BlendMode::ZERO;
58   default:
59     return factor;
60   }
61 }
62 
63 // We separate the blending parameter for rgb and alpha. For blending
64 // the alpha component, CLR and ALPHA are indentical. So just always
65 // use ALPHA as this makes it easier for the backends to use the second
66 // alpha value of dual source blending.
RemoveSrcColorUsage(BlendMode::BlendFactor factor)67 static BlendMode::BlendFactor RemoveSrcColorUsage(BlendMode::BlendFactor factor)
68 {
69   switch (factor)
70   {
71   case BlendMode::SRCCLR:
72     return BlendMode::SRCALPHA;
73   case BlendMode::INVSRCCLR:
74     return BlendMode::INVSRCALPHA;
75   default:
76     return factor;
77   }
78 }
79 
80 // Same as RemoveSrcColorUsage, but because of the overlapping enum,
81 // this must be written as another function.
RemoveDstColorUsage(BlendMode::BlendFactor factor)82 static BlendMode::BlendFactor RemoveDstColorUsage(BlendMode::BlendFactor factor)
83 {
84   switch (factor)
85   {
86   case BlendMode::DSTCLR:
87     return BlendMode::DSTALPHA;
88   case BlendMode::INVDSTCLR:
89     return BlendMode::INVDSTALPHA;
90   default:
91     return factor;
92   }
93 }
94 
Generate(const BPMemory & bp)95 void BlendingState::Generate(const BPMemory& bp)
96 {
97   // Start with everything disabled.
98   hex = 0;
99 
100   bool target_has_alpha = bp.zcontrol.pixel_format == PEControl::RGBA6_Z24;
101   bool alpha_test_may_success = bp.alpha_test.TestResult() != AlphaTest::FAIL;
102 
103   colorupdate = bp.blendmode.colorupdate && alpha_test_may_success;
104   alphaupdate = bp.blendmode.alphaupdate && target_has_alpha && alpha_test_may_success;
105   dstalpha = bp.dstalpha.enable && alphaupdate;
106   usedualsrc = true;
107 
108   // The subtract bit has the highest priority
109   if (bp.blendmode.subtract)
110   {
111     blendenable = true;
112     subtractAlpha = subtract = true;
113     srcfactoralpha = srcfactor = BlendMode::ONE;
114     dstfactoralpha = dstfactor = BlendMode::ONE;
115 
116     if (dstalpha)
117     {
118       subtractAlpha = false;
119       srcfactoralpha = BlendMode::ONE;
120       dstfactoralpha = BlendMode::ZERO;
121     }
122   }
123 
124   // The blendenable bit has the middle priority
125   else if (bp.blendmode.blendenable)
126   {
127     blendenable = true;
128     srcfactor = bp.blendmode.srcfactor;
129     dstfactor = bp.blendmode.dstfactor;
130     if (!target_has_alpha)
131     {
132       // uses ONE instead of DSTALPHA
133       srcfactor = RemoveDstAlphaUsage(srcfactor);
134       dstfactor = RemoveDstAlphaUsage(dstfactor);
135     }
136     // replaces SRCCLR with SRCALPHA and DSTCLR with DSTALPHA, it is important to
137     // use the dst function for the src factor and vice versa
138     srcfactoralpha = RemoveDstColorUsage(srcfactor);
139     dstfactoralpha = RemoveSrcColorUsage(dstfactor);
140 
141     if (dstalpha)
142     {
143       srcfactoralpha = BlendMode::ONE;
144       dstfactoralpha = BlendMode::ZERO;
145     }
146   }
147 
148   // The logicop bit has the lowest priority
149   else if (bp.blendmode.logicopenable)
150   {
151     if (bp.blendmode.logicmode == BlendMode::NOOP)
152     {
153       // Fast path for Kirby's Return to Dreamland, they use it with dstAlpha.
154       colorupdate = false;
155       alphaupdate = alphaupdate && dstalpha;
156     }
157     else
158     {
159       logicopenable = true;
160       logicmode = bp.blendmode.logicmode;
161 
162       if (dstalpha)
163       {
164         // TODO: Not supported by backends.
165       }
166     }
167   }
168 }
169 
ApproximateLogicOpWithBlending()170 void BlendingState::ApproximateLogicOpWithBlending()
171 {
172   // Any of these which use SRC as srcFactor or DST as dstFactor won't be correct.
173   // This is because the two are aliased to one another (see the enum).
174   struct LogicOpApproximation
175   {
176     bool subtract;
177     BlendMode::BlendFactor srcfactor;
178     BlendMode::BlendFactor dstfactor;
179   };
180   static constexpr std::array<LogicOpApproximation, 16> approximations = {{
181       {false, BlendMode::ZERO, BlendMode::ZERO},            // CLEAR
182       {false, BlendMode::DSTCLR, BlendMode::ZERO},          // AND
183       {true, BlendMode::ONE, BlendMode::INVSRCCLR},         // AND_REVERSE
184       {false, BlendMode::ONE, BlendMode::ZERO},             // COPY
185       {true, BlendMode::DSTCLR, BlendMode::ONE},            // AND_INVERTED
186       {false, BlendMode::ZERO, BlendMode::ONE},             // NOOP
187       {false, BlendMode::INVDSTCLR, BlendMode::INVSRCCLR},  // XOR
188       {false, BlendMode::INVDSTCLR, BlendMode::ONE},        // OR
189       {false, BlendMode::INVSRCCLR, BlendMode::INVDSTCLR},  // NOR
190       {false, BlendMode::INVSRCCLR, BlendMode::ZERO},       // EQUIV
191       {false, BlendMode::INVDSTCLR, BlendMode::INVDSTCLR},  // INVERT
192       {false, BlendMode::ONE, BlendMode::INVDSTALPHA},      // OR_REVERSE
193       {false, BlendMode::INVSRCCLR, BlendMode::INVSRCCLR},  // COPY_INVERTED
194       {false, BlendMode::INVSRCCLR, BlendMode::ONE},        // OR_INVERTED
195       {false, BlendMode::INVDSTCLR, BlendMode::INVSRCCLR},  // NAND
196       {false, BlendMode::ONE, BlendMode::ONE},              // SET
197   }};
198 
199   logicopenable = false;
200   blendenable = true;
201   subtract = approximations[logicmode].subtract;
202   srcfactor = approximations[logicmode].srcfactor;
203   dstfactor = approximations[logicmode].dstfactor;
204 }
205 
operator =(const BlendingState & rhs)206 BlendingState& BlendingState::operator=(const BlendingState& rhs)
207 {
208   hex = rhs.hex;
209   return *this;
210 }
211 
Generate(const BPMemory & bp,u32 index)212 void SamplerState::Generate(const BPMemory& bp, u32 index)
213 {
214   const FourTexUnits& tex = bpmem.tex[index / 4];
215   const TexMode0& tm0 = tex.texMode0[index % 4];
216   const TexMode1& tm1 = tex.texMode1[index % 4];
217 
218   // GX can configure the mip filter to none. However, D3D and Vulkan can't express this in their
219   // sampler states. Therefore, we set the min/max LOD to zero if this option is used.
220   min_filter = (tm0.min_filter & 4) != 0 ? Filter::Linear : Filter::Point;
221   mipmap_filter = (tm0.min_filter & 3) == TexMode0::TEXF_LINEAR ? Filter::Linear : Filter::Point;
222   mag_filter = tm0.mag_filter != 0 ? Filter::Linear : Filter::Point;
223 
224   // If mipmaps are disabled, clamp min/max lod
225   max_lod = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm1.max_lod : 0;
226   min_lod = std::min(max_lod.Value(), static_cast<u64>(tm1.min_lod));
227   lod_bias = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm0.lod_bias * (256 / 32) : 0;
228 
229   // Address modes
230   static constexpr std::array<AddressMode, 4> address_modes = {
231       {AddressMode::Clamp, AddressMode::Repeat, AddressMode::MirroredRepeat, AddressMode::Repeat}};
232   wrap_u = address_modes[tm0.wrap_s];
233   wrap_v = address_modes[tm0.wrap_t];
234   anisotropic_filtering = 0;
235 }
236 
operator =(const SamplerState & rhs)237 SamplerState& SamplerState::operator=(const SamplerState& rhs)
238 {
239   hex = rhs.hex;
240   return *this;
241 }
242 
243 namespace RenderState
244 {
GetInvalidRasterizationState()245 RasterizationState GetInvalidRasterizationState()
246 {
247   RasterizationState state;
248   state.hex = UINT32_C(0xFFFFFFFF);
249   return state;
250 }
251 
GetNoCullRasterizationState(PrimitiveType primitive)252 RasterizationState GetNoCullRasterizationState(PrimitiveType primitive)
253 {
254   RasterizationState state = {};
255   state.cullmode = GenMode::CULL_NONE;
256   state.primitive = primitive;
257   return state;
258 }
259 
GetCullBackFaceRasterizationState(PrimitiveType primitive)260 RasterizationState GetCullBackFaceRasterizationState(PrimitiveType primitive)
261 {
262   RasterizationState state = {};
263   state.cullmode = GenMode::CULL_BACK;
264   state.primitive = primitive;
265   return state;
266 }
267 
GetInvalidDepthState()268 DepthState GetInvalidDepthState()
269 {
270   DepthState state;
271   state.hex = UINT32_C(0xFFFFFFFF);
272   return state;
273 }
274 
GetNoDepthTestingDepthState()275 DepthState GetNoDepthTestingDepthState()
276 {
277   DepthState state = {};
278   state.testenable = false;
279   state.updateenable = false;
280   state.func = ZMode::ALWAYS;
281   return state;
282 }
283 
GetAlwaysWriteDepthState()284 DepthState GetAlwaysWriteDepthState()
285 {
286   DepthState state = {};
287   state.testenable = true;
288   state.updateenable = true;
289   state.func = ZMode::ALWAYS;
290   return state;
291 }
292 
GetInvalidBlendingState()293 BlendingState GetInvalidBlendingState()
294 {
295   BlendingState state;
296   state.hex = UINT32_C(0xFFFFFFFF);
297   return state;
298 }
299 
GetNoBlendingBlendState()300 BlendingState GetNoBlendingBlendState()
301 {
302   BlendingState state = {};
303   state.usedualsrc = false;
304   state.blendenable = false;
305   state.srcfactor = BlendMode::ONE;
306   state.srcfactoralpha = BlendMode::ONE;
307   state.dstfactor = BlendMode::ZERO;
308   state.dstfactoralpha = BlendMode::ZERO;
309   state.logicopenable = false;
310   state.colorupdate = true;
311   state.alphaupdate = true;
312   return state;
313 }
314 
GetNoColorWriteBlendState()315 BlendingState GetNoColorWriteBlendState()
316 {
317   BlendingState state = {};
318   state.usedualsrc = false;
319   state.blendenable = false;
320   state.srcfactor = BlendMode::ONE;
321   state.srcfactoralpha = BlendMode::ONE;
322   state.dstfactor = BlendMode::ZERO;
323   state.dstfactoralpha = BlendMode::ZERO;
324   state.logicopenable = false;
325   state.colorupdate = false;
326   state.alphaupdate = false;
327   return state;
328 }
329 
GetInvalidSamplerState()330 SamplerState GetInvalidSamplerState()
331 {
332   SamplerState state;
333   state.hex = UINT64_C(0xFFFFFFFFFFFFFFFF);
334   return state;
335 }
336 
GetPointSamplerState()337 SamplerState GetPointSamplerState()
338 {
339   SamplerState state = {};
340   state.min_filter = SamplerState::Filter::Point;
341   state.mag_filter = SamplerState::Filter::Point;
342   state.mipmap_filter = SamplerState::Filter::Point;
343   state.wrap_u = SamplerState::AddressMode::Clamp;
344   state.wrap_v = SamplerState::AddressMode::Clamp;
345   state.min_lod = 0;
346   state.max_lod = 255;
347   state.lod_bias = 0;
348   state.anisotropic_filtering = false;
349   return state;
350 }
351 
GetLinearSamplerState()352 SamplerState GetLinearSamplerState()
353 {
354   SamplerState state = {};
355   state.min_filter = SamplerState::Filter::Linear;
356   state.mag_filter = SamplerState::Filter::Linear;
357   state.mipmap_filter = SamplerState::Filter::Linear;
358   state.wrap_u = SamplerState::AddressMode::Clamp;
359   state.wrap_v = SamplerState::AddressMode::Clamp;
360   state.min_lod = 0;
361   state.max_lod = 255;
362   state.lod_bias = 0;
363   state.anisotropic_filtering = false;
364   return state;
365 }
366 
GetColorFramebufferState(AbstractTextureFormat format)367 FramebufferState GetColorFramebufferState(AbstractTextureFormat format)
368 {
369   FramebufferState state = {};
370   state.color_texture_format = format;
371   state.depth_texture_format = AbstractTextureFormat::Undefined;
372   state.per_sample_shading = false;
373   state.samples = 1;
374   return state;
375 }
376 
GetRGBA8FramebufferState()377 FramebufferState GetRGBA8FramebufferState()
378 {
379   return GetColorFramebufferState(AbstractTextureFormat::RGBA8);
380 }
381 
382 }  // namespace RenderState
383