aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs
blob: dbd4efc8f9a0bb70a43658035f39d2a7b27f7466 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Shader;

namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
    /// <summary>
    /// Maintains a "current" specialiation state, and provides a flag to check if it has changed meaningfully.
    /// </summary>
    internal class SpecializationStateUpdater
    {
        private readonly GpuContext _context;
        private GpuChannelGraphicsState _graphics;
        private GpuChannelPoolState _pool;

        private bool _usesDrawParameters;
        private bool _usesTopology;

        private bool _changed;

        /// <summary>
        /// Creates a new instance of the specialization state updater class.
        /// </summary>
        /// <param name="context">GPU context</param>
        public SpecializationStateUpdater(GpuContext context)
        {
            _context = context;
        }

        /// <summary>
        /// Signal that the specialization state has changed.
        /// </summary>
        private void Signal()
        {
            _changed = true;
        }

        /// <summary>
        /// Checks if the specialization state has changed since the last check.
        /// </summary>
        /// <returns>True if it has changed, false otherwise</returns>
        public bool HasChanged()
        {
            if (_changed)
            {
                _changed = false;
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Sets the active shader, clearing the dirty state and recording if certain specializations are noteworthy.
        /// </summary>
        /// <param name="gs">The active shader</param>
        public void SetShader(CachedShaderProgram gs)
        {
            _usesDrawParameters = gs.Shaders[1]?.Info.UsesDrawParameters ?? false;
            _usesTopology = gs.SpecializationState.IsPrimitiveTopologyQueried();

            _changed = false;
        }

        /// <summary>
        /// Get the current graphics state.
        /// </summary>
        /// <returns>GPU graphics state</returns>
        public ref GpuChannelGraphicsState GetGraphicsState()
        {
            return ref _graphics;
        }

        /// <summary>
        /// Get the current pool state.
        /// </summary>
        /// <returns>GPU pool state</returns>
        public ref GpuChannelPoolState GetPoolState()
        {
            return ref _pool;
        }

        /// <summary>
        /// Early Z force enable.
        /// </summary>
        /// <param name="value">The new value</param>
        public void SetEarlyZForce(bool value)
        {
            _graphics.EarlyZForce = value;

            Signal();
        }

        /// <summary>
        /// Primitive topology of current draw.
        /// </summary>
        /// <param name="value">The new value</param>
        public void SetTopology(PrimitiveTopology value)
        {
            if (value != _graphics.Topology)
            {
                _graphics.Topology = value;

                if (_usesTopology)
                {
                    Signal();
                }
            }
        }

        /// <summary>
        /// Tessellation mode.
        /// </summary>
        /// <param name="value">The new value</param>
        public void SetTessellationMode(TessMode value)
        {
            if (value.Packed != _graphics.TessellationMode.Packed)
            {
                _graphics.TessellationMode = value;

                Signal();
            }
        }

        /// <summary>
        /// Updates alpha-to-coverage state, and sets it as changed.
        /// </summary>
        /// <param name="enable">Whether alpha-to-coverage is enabled</param>
        /// <param name="ditherEnable">Whether alpha-to-coverage dithering is enabled</param>
        public void SetAlphaToCoverageEnable(bool enable, bool ditherEnable)
        {
            _graphics.AlphaToCoverageEnable = enable;
            _graphics.AlphaToCoverageDitherEnable = ditherEnable;

            Signal();
        }

        /// <summary>
        /// Indicates whether the viewport transform is disabled.
        /// </summary>
        /// <param name="value">The new value</param>
        public void SetViewportTransformDisable(bool value)
        {
            if (value != _graphics.ViewportTransformDisable)
            {
                _graphics.ViewportTransformDisable = value;

                Signal();
            }
        }

        /// <summary>
        /// Depth mode zero to one or minus one to one.
        /// </summary>
        /// <param name="value">The new value</param>
        public void SetDepthMode(bool value)
        {
            if (value != _graphics.DepthMode)
            {
                _graphics.DepthMode = value;

                Signal();
            }
        }

        /// <summary>
        /// Indicates if the point size is set on the shader or is fixed.
        /// </summary>
        /// <param name="value">The new value</param>
        public void SetProgramPointSizeEnable(bool value)
        {
            if (value != _graphics.ProgramPointSizeEnable)
            {
                _graphics.ProgramPointSizeEnable = value;

                Signal();
            }
        }

        /// <summary>
        /// Point size used if <see cref="SetProgramPointSizeEnable" /> is provided false.
        /// </summary>
        /// <param name="value">The new value</param>
        public void SetPointSize(float value)
        {
            if (value != _graphics.PointSize)
            {
                _graphics.PointSize = value;

                Signal();
            }
        }

        /// <summary>
        /// Updates alpha test specialization state, and sets it as changed.
        /// </summary>
        /// <param name="enable">Whether alpha test is enabled</param>
        /// <param name="reference">The value to compare with the fragment output alpha</param>
        /// <param name="op">The comparison that decides if the fragment should be discarded</param>
        public void SetAlphaTest(bool enable, float reference, CompareOp op)
        {
            _graphics.AlphaTestEnable = enable;
            _graphics.AlphaTestReference = reference;
            _graphics.AlphaTestCompare = op;

            Signal();
        }

        /// <summary>
        /// Updates the type of the vertex attributes consumed by the shader.
        /// </summary>
        /// <param name="state">The new state</param>
        public void SetAttributeTypes(ref Array32<VertexAttribState> state)
        {
            bool changed = false;
            ref Array32<AttributeType> attributeTypes = ref _graphics.AttributeTypes;
            bool mayConvertVtgToCompute = ShaderCache.MayConvertVtgToCompute(ref _context.Capabilities);
            bool supportsScaledFormats = _context.Capabilities.SupportsScaledVertexFormats && !mayConvertVtgToCompute;

            for (int location = 0; location < state.Length; location++)
            {
                VertexAttribType type = state[location].UnpackType();
                VertexAttribSize size = state[location].UnpackSize();

                AttributeType value;

                if (supportsScaledFormats)
                {
                    value = type switch
                    {
                        VertexAttribType.Sint => AttributeType.Sint,
                        VertexAttribType.Uint => AttributeType.Uint,
                        _ => AttributeType.Float,
                    };
                }
                else
                {
                    value = type switch
                    {
                        VertexAttribType.Sint => AttributeType.Sint,
                        VertexAttribType.Uint => AttributeType.Uint,
                        VertexAttribType.Uscaled => AttributeType.Uscaled,
                        VertexAttribType.Sscaled => AttributeType.Sscaled,
                        _ => AttributeType.Float,
                    };
                }

                if (mayConvertVtgToCompute && (size == VertexAttribSize.Rgb10A2 || size == VertexAttribSize.Rg11B10))
                {
                    value |= AttributeType.Packed;

                    if (type == VertexAttribType.Snorm ||
                        type == VertexAttribType.Sint ||
                        type == VertexAttribType.Sscaled)
                    {
                        value |= AttributeType.PackedRgb10A2Signed;
                    }
                }

                if (attributeTypes[location] != value)
                {
                    attributeTypes[location] = value;
                    changed = true;
                }
            }

            if (changed)
            {
                Signal();
            }
        }

        /// <summary>
        /// Updates the type of the outputs produced by the fragment shader based on the current render target state.
        /// </summary>
        /// <param name="rtControl">The render target control register</param>
        /// <param name="state">The color attachment state</param>
        public void SetFragmentOutputTypes(RtControl rtControl, ref Array8<RtColorState> state)
        {
            bool changed = false;
            int count = rtControl.UnpackCount();

            for (int index = 0; index < Constants.TotalRenderTargets; index++)
            {
                int rtIndex = rtControl.UnpackPermutationIndex(index);

                var colorState = state[rtIndex];

                if (index < count && StateUpdater.IsRtEnabled(colorState))
                {
                    Format format = colorState.Format.Convert().Format;

                    AttributeType type = format.IsInteger() ? (format.IsSint() ? AttributeType.Sint : AttributeType.Uint) : AttributeType.Float;

                    if (type != _graphics.FragmentOutputTypes[index])
                    {
                        _graphics.FragmentOutputTypes[index] = type;
                        changed = true;
                    }
                }
            }

            if (changed && _context.Capabilities.NeedsFragmentOutputSpecialization)
            {
                Signal();
            }
        }

        /// <summary>
        /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
        /// </summary>
        /// <param name="value">The new value</param>
        public void SetHasConstantBufferDrawParameters(bool value)
        {
            if (value != _graphics.HasConstantBufferDrawParameters)
            {
                _graphics.HasConstantBufferDrawParameters = value;

                if (_usesDrawParameters)
                {
                    Signal();
                }
            }
        }

        /// <summary>
        /// Indicates that any storage buffer use is unaligned.
        /// </summary>
        /// <param name="value">The new value</param>
        /// <returns>True if the unaligned state changed, false otherwise</returns>
        public bool SetHasUnalignedStorageBuffer(bool value)
        {
            if (value != _graphics.HasUnalignedStorageBuffer)
            {
                _graphics.HasUnalignedStorageBuffer = value;

                Signal();

                return true;
            }

            return false;
        }

        /// <summary>
        /// Sets the GPU pool state.
        /// </summary>
        /// <param name="state">The new state</param>
        public void SetPoolState(GpuChannelPoolState state)
        {
            if (!state.Equals(_pool))
            {
                _pool = state;

                Signal();
            }
        }

        /// <summary>
        /// Sets the dual-source blend enabled state.
        /// </summary>
        /// <param name="enabled">True if blending is enabled and using dual-source blend</param>
        public void SetDualSourceBlendEnabled(bool enabled)
        {
            if (enabled != _graphics.DualSourceBlendEnable)
            {
                _graphics.DualSourceBlendEnable = enabled;

                Signal();
            }
        }

        /// <summary>
        /// Sets the Y negate enabled state.
        /// </summary>
        /// <param name="enabled">True if Y negate of the fragment coordinates is enabled</param>
        public void SetYNegateEnabled(bool enabled)
        {
            if (enabled != _graphics.YNegateEnabled)
            {
                _graphics.YNegateEnabled = enabled;

                Signal();
            }
        }
    }
}