aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs
blob: 682f7d671da2fde5f3ad21d52db9f1a8d4dbca3a (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
using Ryujinx.Common;
using System;

namespace Ryujinx.Graphics.Texture
{
    class BlockLinearSwizzle : ISwizzle
    {
        private const int GobWidth  = 64;
        private const int GobHeight = 8;

        private const int GobSize = GobWidth * GobHeight;

        private int _texWidth;
        private int _texHeight;
        private int _texDepth;
        private int _texGobBlockHeight;
        private int _texGobBlockDepth;
        private int _texBpp;

        private int _bhMask;
        private int _bdMask;

        private int _bhShift;
        private int _bdShift;
        private int _bppShift;

        private int _xShift;

        private int _robSize;
        private int _sliceSize;

        private int _baseOffset;

        public BlockLinearSwizzle(
            int width,
            int height,
            int depth,
            int gobBlockHeight,
            int gobBlockDepth,
            int bpp)
        {
            _texWidth          = width;
            _texHeight         = height;
            _texDepth          = depth;
            _texGobBlockHeight = gobBlockHeight;
            _texGobBlockDepth  = gobBlockDepth;
            _texBpp            = bpp;

            _bppShift = BitUtils.CountTrailingZeros32(bpp);

            SetMipLevel(0);
        }

        public void SetMipLevel(int level)
        {
            _baseOffset = GetMipOffset(level);

            int width  = Math.Max(1, _texWidth  >> level);
            int height = Math.Max(1, _texHeight >> level);
            int depth  = Math.Max(1, _texDepth  >> level);

            GobBlockSizes gbSizes = AdjustGobBlockSizes(height, depth);

            _bhMask = gbSizes.Height - 1;
            _bdMask = gbSizes.Depth  - 1;

            _bhShift = BitUtils.CountTrailingZeros32(gbSizes.Height);
            _bdShift = BitUtils.CountTrailingZeros32(gbSizes.Depth);

            _xShift = BitUtils.CountTrailingZeros32(GobSize * gbSizes.Height * gbSizes.Depth);

            RobAndSliceSizes gsSizes = GetRobAndSliceSizes(width, height, gbSizes);

            _robSize   = gsSizes.RobSize;
            _sliceSize = gsSizes.SliceSize;
        }

        public int GetImageSize(int mipsCount)
        {
            int size = GetMipOffset(mipsCount);

            size = (size + 0x1fff) & ~0x1fff;

            return size;
        }

        public int GetMipOffset(int level)
        {
            int totalSize = 0;

            for (int index = 0; index < level; index++)
            {
                int width  = Math.Max(1, _texWidth  >> index);
                int height = Math.Max(1, _texHeight >> index);
                int depth  = Math.Max(1, _texDepth  >> index);

                GobBlockSizes gbSizes = AdjustGobBlockSizes(height, depth);

                RobAndSliceSizes rsSizes = GetRobAndSliceSizes(width, height, gbSizes);

                totalSize += BitUtils.DivRoundUp(depth, gbSizes.Depth) * rsSizes.SliceSize;
            }

            return totalSize;
        }

        private struct GobBlockSizes
        {
            public int Height;
            public int Depth;

            public GobBlockSizes(int gobBlockHeight, int gobBlockDepth)
            {
                Height = gobBlockHeight;
                Depth  = gobBlockDepth;
            }
        }

        private GobBlockSizes AdjustGobBlockSizes(int height, int depth)
        {
            int gobBlockHeight = _texGobBlockHeight;
            int gobBlockDepth  = _texGobBlockDepth;

            int pow2Height = BitUtils.Pow2RoundUp(height);
            int pow2Depth  = BitUtils.Pow2RoundUp(depth);

            while (gobBlockHeight * GobHeight > pow2Height && gobBlockHeight > 1)
            {
                gobBlockHeight >>= 1;
            }

            while (gobBlockDepth > pow2Depth && gobBlockDepth > 1)
            {
                gobBlockDepth >>= 1;
            }

            return new GobBlockSizes(gobBlockHeight, gobBlockDepth);
        }

        private struct RobAndSliceSizes
        {
            public int RobSize;
            public int SliceSize;

            public RobAndSliceSizes(int robSize, int sliceSize)
            {
                RobSize   = robSize;
                SliceSize = sliceSize;
            }
        }

        private RobAndSliceSizes GetRobAndSliceSizes(int width, int height, GobBlockSizes gbSizes)
        {
            int widthInGobs = BitUtils.DivRoundUp(width * _texBpp, GobWidth);

            int robSize = GobSize * gbSizes.Height * gbSizes.Depth * widthInGobs;

            int sliceSize = BitUtils.DivRoundUp(height, gbSizes.Height * GobHeight) * robSize;

            return new RobAndSliceSizes(robSize, sliceSize);
        }

        public int GetSwizzleOffset(int x, int y, int z)
        {
            x <<= _bppShift;

            int yh = y / GobHeight;

            int position = (z >> _bdShift) * _sliceSize + (yh >> _bhShift) * _robSize;

            position += (x / GobWidth) << _xShift;

            position += (yh & _bhMask) * GobSize;

            position += ((z & _bdMask) * GobSize) << _bhShift;

            position += ((x & 0x3f) >> 5) << 8;
            position += ((y & 0x07) >> 1) << 6;
            position += ((x & 0x1f) >> 4) << 5;
            position += ((y & 0x01) >> 0) << 4;
            position += ((x & 0x0f) >> 0) << 0;

            return _baseOffset + position;
        }
    }
}