aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs
blob: 01ef2f9a0918f2cf210d9090ebc933d134545a47 (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
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Memory;

namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
    /// <summary>
    /// Helper methods used for conditional rendering.
    /// </summary>
    static class ConditionalRendering
    {
        /// <summary>
        /// Checks if draws and clears should be performed, according
        /// to currently set conditional rendering conditions.
        /// </summary>
        /// <param name="context">GPU context</param>
        /// <param name="memoryManager">Memory manager bound to the channel currently executing</param>
        /// <param name="address">Conditional rendering buffer address</param>
        /// <param name="condition">Conditional rendering condition</param>
        /// <returns>True if rendering is enabled, false otherwise</returns>
        public static ConditionalRenderEnabled GetRenderEnable(GpuContext context, MemoryManager memoryManager, GpuVa address, Condition condition)
        {
            switch (condition)
            {
                case Condition.Always:
                    return ConditionalRenderEnabled.True;
                case Condition.Never:
                    return ConditionalRenderEnabled.False;
                case Condition.ResultNonZero:
                    return CounterNonZero(context, memoryManager, address.Pack());
                case Condition.Equal:
                    return CounterCompare(context, memoryManager, address.Pack(), true);
                case Condition.NotEqual:
                    return CounterCompare(context, memoryManager, address.Pack(), false);
            }

            Logger.Warning?.Print(LogClass.Gpu, $"Invalid conditional render condition \"{condition}\".");

            return ConditionalRenderEnabled.True;
        }

        /// <summary>
        /// Checks if the counter value at a given GPU memory address is non-zero.
        /// </summary>
        /// <param name="context">GPU context</param>
        /// <param name="memoryManager">Memory manager bound to the channel currently executing</param>
        /// <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 static ConditionalRenderEnabled CounterNonZero(GpuContext context, MemoryManager memoryManager, ulong gpuVa)
        {
            ICounterEvent evt = memoryManager.CounterCache.FindEvent(gpuVa);

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

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

        /// <summary>
        /// Checks if the counter at a given GPU memory address passes a specified equality comparison.
        /// </summary>
        /// <param name="context">GPU context</param>
        /// <param name="memoryManager">Memory manager bound to the channel currently executing</param>
        /// <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 static ConditionalRenderEnabled CounterCompare(GpuContext context, MemoryManager memoryManager, ulong gpuVa, bool isEqual)
        {
            ICounterEvent evt = FindEvent(memoryManager.CounterCache, gpuVa);
            ICounterEvent evt2 = FindEvent(memoryManager.CounterCache, gpuVa + 16);

            bool useHost;

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

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

                ulong x = memoryManager.Read<ulong>(gpuVa, true);
                ulong y = memoryManager.Read<ulong>(gpuVa + 16, true);

                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="counterCache">GPU counter cache to search on</param>
        /// <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 static ICounterEvent FindEvent(CounterCache counterCache, ulong gpuVa)
        {
            return counterCache.FindEvent(gpuVa);
        }
    }
}