/ src / Ryujinx.Graphics.Texture / BlockLinearLayout.cs
BlockLinearLayout.cs
  1  using Ryujinx.Common;
  2  using System.Numerics;
  3  using System.Runtime.CompilerServices;
  4  
  5  using static Ryujinx.Graphics.Texture.BlockLinearConstants;
  6  
  7  namespace Ryujinx.Graphics.Texture
  8  {
  9      class BlockLinearLayout
 10      {
 11          private struct RobAndSliceSizes
 12          {
 13              public int RobSize;
 14              public int SliceSize;
 15  
 16              public RobAndSliceSizes(int robSize, int sliceSize)
 17              {
 18                  RobSize = robSize;
 19                  SliceSize = sliceSize;
 20              }
 21          }
 22  
 23          private readonly int _texBpp;
 24  
 25          private readonly int _bhMask;
 26          private readonly int _bdMask;
 27  
 28          private readonly int _bhShift;
 29          private readonly int _bdShift;
 30          private readonly int _bppShift;
 31  
 32          private readonly int _xShift;
 33  
 34          private readonly int _robSize;
 35          private readonly int _sliceSize;
 36  
 37          // Variables for built in iteration.
 38          private int _yPart;
 39          private int _yzPart;
 40          private int _zPart;
 41  
 42          public BlockLinearLayout(
 43              int width,
 44              int height,
 45              int gobBlocksInY,
 46              int gobBlocksInZ,
 47              int bpp)
 48          {
 49              _texBpp = bpp;
 50  
 51              _bppShift = BitOperations.TrailingZeroCount(bpp);
 52  
 53              _bhMask = gobBlocksInY - 1;
 54              _bdMask = gobBlocksInZ - 1;
 55  
 56              _bhShift = BitOperations.TrailingZeroCount(gobBlocksInY);
 57              _bdShift = BitOperations.TrailingZeroCount(gobBlocksInZ);
 58  
 59              _xShift = BitOperations.TrailingZeroCount(GobSize * gobBlocksInY * gobBlocksInZ);
 60  
 61              RobAndSliceSizes rsSizes = GetRobAndSliceSizes(width, height, gobBlocksInY, gobBlocksInZ);
 62  
 63              _robSize = rsSizes.RobSize;
 64              _sliceSize = rsSizes.SliceSize;
 65          }
 66  
 67          private RobAndSliceSizes GetRobAndSliceSizes(int width, int height, int gobBlocksInY, int gobBlocksInZ)
 68          {
 69              int widthInGobs = BitUtils.DivRoundUp(width * _texBpp, GobStride);
 70  
 71              int robSize = GobSize * gobBlocksInY * gobBlocksInZ * widthInGobs;
 72  
 73              int sliceSize = BitUtils.DivRoundUp(height, gobBlocksInY * GobHeight) * robSize;
 74  
 75              return new RobAndSliceSizes(robSize, sliceSize);
 76          }
 77  
 78          [MethodImpl(MethodImplOptions.AggressiveInlining)]
 79          public int GetOffset(int x, int y, int z)
 80          {
 81              return GetOffsetWithLineOffset(x << _bppShift, y, z);
 82          }
 83  
 84          [MethodImpl(MethodImplOptions.AggressiveInlining)]
 85          public int GetOffsetWithLineOffset(int x, int y, int z)
 86          {
 87              int yh = y / GobHeight;
 88  
 89              int offset = (z >> _bdShift) * _sliceSize + (yh >> _bhShift) * _robSize;
 90  
 91              offset += (x / GobStride) << _xShift;
 92  
 93              offset += (yh & _bhMask) * GobSize;
 94  
 95              offset += ((z & _bdMask) * GobSize) << _bhShift;
 96  
 97              offset += ((x & 0x3f) >> 5) << 8;
 98              offset += ((y & 0x07) >> 1) << 6;
 99              offset += ((x & 0x1f) >> 4) << 5;
100              offset += ((y & 0x01) >> 0) << 4;
101              offset += ((x & 0x0f) >> 0) << 0;
102  
103              return offset;
104          }
105  
106          public (int offset, int size) GetRectangleRange(int x, int y, int width, int height)
107          {
108              // Justification:
109              // The 2D offset is a combination of separate x and y parts.
110              // Both components increase with input and never overlap bits.
111              // Therefore for each component, the minimum input value is the lowest that component can go.
112              // Minimum total value is minimum X component + minimum Y component. Similar goes for maximum.
113  
114              int start = GetOffset(x, y, 0);
115              int end = GetOffset(x + width - 1, y + height - 1, 0) + _texBpp; // Cover the last pixel.
116              return (start, end - start);
117          }
118  
119          public bool LayoutMatches(BlockLinearLayout other)
120          {
121              return _robSize == other._robSize &&
122                     _sliceSize == other._sliceSize &&
123                     _texBpp == other._texBpp &&
124                     _bhMask == other._bhMask &&
125                     _bdMask == other._bdMask;
126          }
127  
128          // Functions for built in iteration.
129          // Components of the offset can be updated separately, and combined to save some time.
130  
131          [MethodImpl(MethodImplOptions.AggressiveInlining)]
132          public void SetY(int y)
133          {
134              int yh = y / GobHeight;
135              int offset = (yh >> _bhShift) * _robSize;
136  
137              offset += (yh & _bhMask) * GobSize;
138  
139              offset += ((y & 0x07) >> 1) << 6;
140              offset += ((y & 0x01) >> 0) << 4;
141  
142              _yPart = offset;
143              _yzPart = offset + _zPart;
144          }
145  
146          [MethodImpl(MethodImplOptions.AggressiveInlining)]
147          public void SetZ(int z)
148          {
149              int offset = (z >> _bdShift) * _sliceSize;
150  
151              offset += ((z & _bdMask) * GobSize) << _bhShift;
152  
153              _zPart = offset;
154              _yzPart = offset + _yPart;
155          }
156  
157          /// <summary>
158          /// Optimized conversion for line offset in bytes to an absolute offset. Input x must be divisible by 16.
159          /// </summary>
160          [MethodImpl(MethodImplOptions.AggressiveInlining)]
161          public int GetOffsetWithLineOffset16(int x)
162          {
163              int offset = (x / GobStride) << _xShift;
164  
165              offset += ((x & 0x3f) >> 5) << 8;
166              offset += ((x & 0x1f) >> 4) << 5;
167  
168              return offset + _yzPart;
169          }
170  
171          /// <summary>
172          /// Optimized conversion for line offset in bytes to an absolute offset. Input x must be divisible by 64.
173          /// </summary>
174          [MethodImpl(MethodImplOptions.AggressiveInlining)]
175          public int GetOffsetWithLineOffset64(int x)
176          {
177              int offset = (x / GobStride) << _xShift;
178  
179              return offset + _yzPart;
180          }
181  
182          [MethodImpl(MethodImplOptions.AggressiveInlining)]
183          public int GetOffset(int x)
184          {
185              x <<= _bppShift;
186              int offset = (x / GobStride) << _xShift;
187  
188              offset += ((x & 0x3f) >> 5) << 8;
189              offset += ((x & 0x1f) >> 4) << 5;
190              offset += (x & 0x0f);
191  
192              return offset + _yzPart;
193          }
194      }
195  }