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 }