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