aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs
blob: 06db2a8049051166bf28bcf02fd41b8703e6a4e6 (plain)
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
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using System.Collections.Generic;

using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;

namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
    static class GlobalToStorage
    {
        private const int StorageDescsBaseOffset = 0x44; // In words.

        private const int UbeStorageDescsBaseOffset = 0x84; // In words.
        private const int UbeStorageMaxCount        = 14;

        private const int StorageDescSize = 4; // In words.
        private const int StorageMaxCount = 16;

        private const int StorageDescsSize  = StorageDescSize * StorageMaxCount;

        public static void RunPass(BasicBlock block, ShaderStage stage)
        {
            int sbStart = GetStorageBaseCbOffset(stage);

            int sbEnd = sbStart + StorageDescsSize;

            // This one is only used on compute shaders.
            // Compute shaders uses two separate sets of storage.
            int ubeSbStart = UbeStorageDescsBaseOffset;
            int ubeSbEnd   = UbeStorageDescsBaseOffset + StorageDescSize * UbeStorageMaxCount;

            for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next)
            {
                if (!(node.Value is Operation operation))
                {
                    continue;
                }

                if (operation.Inst == Instruction.LoadGlobal ||
                    operation.Inst == Instruction.StoreGlobal)
                {
                    Operand source = operation.GetSource(0);

                    if (source.AsgOp is Operation asgOperation)
                    {
                        int storageIndex = SearchForStorageBase(asgOperation, sbStart, sbEnd);

                        /*if (storageIndex < 0 && stage == ShaderStage.Compute)
                        {
                            storageIndex = SearchForStorageBase(asgOperation, ubeSbStart, ubeSbEnd);
                        }*/

                        if (storageIndex >= 0)
                        {
                            node = ReplaceGlobalWithStorage(node, storageIndex);
                        }
                    }
                }
            }
        }

        private static LinkedListNode<INode> ReplaceGlobalWithStorage(LinkedListNode<INode> node, int storageIndex)
        {
            Operation operation = (Operation)node.Value;

            Operation storageOp;

            if (operation.Inst == Instruction.LoadGlobal)
            {
                Operand source = operation.GetSource(0);

                storageOp = new Operation(Instruction.LoadStorage, operation.Dest, Const(storageIndex), source);
            }
            else
            {
                Operand src1 = operation.GetSource(0);
                Operand src2 = operation.GetSource(1);

                storageOp = new Operation(Instruction.StoreStorage, null, Const(storageIndex), src1, src2);
            }

            for (int index = 0; index < operation.SourcesCount; index++)
            {
                operation.SetSource(index, null);
            }

            LinkedListNode<INode> oldNode = node;

            node = node.List.AddAfter(node, storageOp);

            node.List.Remove(oldNode);

            return node;
        }

        private static int SearchForStorageBase(Operation operation, int sbStart, int sbEnd)
        {
            Queue<Operation> assignments = new Queue<Operation>();

            assignments.Enqueue(operation);

            while (assignments.TryDequeue(out operation))
            {
                for (int index = 0; index < operation.SourcesCount; index++)
                {
                    Operand source = operation.GetSource(index);

                    if (source.Type == OperandType.ConstantBuffer)
                    {
                        int slot   = source.GetCbufSlot();
                        int offset = source.GetCbufOffset();

                        if (slot == 0 && offset >= sbStart && offset < sbEnd)
                        {
                            int storageIndex = (offset - sbStart) / StorageDescSize;

                            return storageIndex;
                        }
                    }

                    if (source.AsgOp is Operation asgOperation)
                    {
                        assignments.Enqueue(asgOperation);
                    }
                }
            }

            return -1;
        }

        private static int GetStorageBaseCbOffset(ShaderStage stage)
        {
            switch (stage)
            {
                case ShaderStage.Compute:                return StorageDescsBaseOffset + 2 * StorageDescsSize;
                case ShaderStage.Vertex:                 return StorageDescsBaseOffset;
                case ShaderStage.TessellationControl:    return StorageDescsBaseOffset + 1 * StorageDescsSize;
                case ShaderStage.TessellationEvaluation: return StorageDescsBaseOffset + 2 * StorageDescsSize;
                case ShaderStage.Geometry:               return StorageDescsBaseOffset + 3 * StorageDescsSize;
                case ShaderStage.Fragment:               return StorageDescsBaseOffset + 4 * StorageDescsSize;
            }

            return 0;
        }
    }
}