aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs
blob: 225c732e9c8456d392f61ef6f56fe33c22286a63 (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
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.State;

namespace Ryujinx.Graphics.Gpu.Engine
{
    partial class Methods
    {
        enum ConditionalRenderEnabled
        {
            False,
            True,
            Host
        }

        /// <summary>
        /// Checks if draws and clears should be performed, according
        /// to currently set conditional rendering conditions.
        /// </summary>
        /// <param name="state">GPU state</param>
        /// <returns>True if rendering is enabled, false otherwise</returns>
        private ConditionalRenderEnabled GetRenderEnable(GpuState state)
        {
            ConditionState condState = state.Get<ConditionState>(MethodOffset.ConditionState);

            switch (condState.Condition)
            {
                case Condition.Always:
                    return ConditionalRenderEnabled.True;
                case Condition.Never:
                    return ConditionalRenderEnabled.False;
                case Condition.ResultNonZero:
                    return CounterNonZero(condState.Address.Pack());
                case Condition.Equal:
                    return CounterCompare(condState.Address.Pack(), true);
                case Condition.NotEqual:
                    return CounterCompare(condState.Address.Pack(), false);
            }

            Logger.PrintWarning(LogClass.Gpu, $"Invalid conditional render condition \"{condState.Condition}\".");

            return ConditionalRenderEnabled.True;
        }

        /// <summary>
        /// Checks if the counter value at a given GPU memory address is non-zero.
        /// </summary>
        /// <param name="gpuVa">GPU virtual address of the counter value</param>
        /// <returns>True if the value is not zero, false otherwise. Returns host if handling with host conditional rendering</returns>
        private ConditionalRenderEnabled CounterNonZero(ulong gpuVa)
        {
            ICounterEvent evt = _counterCache.FindEvent(gpuVa);

            if (evt == null)
            {
                return ConditionalRenderEnabled.False;
            }

            if (_context.Renderer.Pipeline.TryHostConditionalRendering(evt, 0L, false))
            {
                return ConditionalRenderEnabled.Host;
            }
            else
            {
                evt.Flush();
                return (_context.MemoryAccessor.Read<ulong>(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
            }
        }

        /// <summary>
        /// Checks if the counter at a given GPU memory address passes a specified equality comparison.
        /// </summary>
        /// <param name="gpuVa">GPU virtual address</param>
        /// <param name="isEqual">True to check if the values are equal, false to check if they are not equal</param>
        /// <returns>True if the condition is met, false otherwise. Returns host if handling with host conditional rendering</returns>
        private ConditionalRenderEnabled CounterCompare(ulong gpuVa, bool isEqual)
        {
            ICounterEvent evt = FindEvent(gpuVa);
            ICounterEvent evt2 = FindEvent(gpuVa + 16);

            if (evt == null && evt2 == null)
            {
                return ConditionalRenderEnabled.False;
            }

            bool useHost;

            if (evt != null && evt2 == null)
            {
                useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, _context.MemoryAccessor.Read<ulong>(gpuVa + 16), isEqual);
            }
            else if (evt == null && evt2 != null)
            {
                useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt2, _context.MemoryAccessor.Read<ulong>(gpuVa), isEqual);
            }
            else
            {
                useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, evt2, isEqual);
            }

            if (useHost)
            {
                return ConditionalRenderEnabled.Host;
            }
            else
            {
                evt?.Flush();
                evt2?.Flush();

                ulong x = _context.MemoryAccessor.Read<ulong>(gpuVa);
                ulong y = _context.MemoryAccessor.Read<ulong>(gpuVa + 16);

                return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
            }
        }

        /// <summary>
        /// Tries to find a counter that is supposed to be written at the specified address,
        /// returning the related event.
        /// </summary>
        /// <param name="gpuVa">GPU virtual address where the counter is supposed to be written</param>
        /// <returns>The counter event, or null if not present</returns>
        private ICounterEvent FindEvent(ulong gpuVa)
        {
            return _counterCache.FindEvent(gpuVa);
        }
    }
}