diff options
Diffstat (limited to 'src/Ryujinx.Graphics.Texture/SizeCalculator.cs')
| -rw-r--r-- | src/Ryujinx.Graphics.Texture/SizeCalculator.cs | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Texture/SizeCalculator.cs b/src/Ryujinx.Graphics.Texture/SizeCalculator.cs new file mode 100644 index 00000000..5568784f --- /dev/null +++ b/src/Ryujinx.Graphics.Texture/SizeCalculator.cs @@ -0,0 +1,287 @@ +using Ryujinx.Common; +using System; + +using static Ryujinx.Graphics.Texture.BlockLinearConstants; + +namespace Ryujinx.Graphics.Texture +{ + public static class SizeCalculator + { + private const int StrideAlignment = 32; + + private static int Calculate3DOffsetCount(int levels, int depth) + { + int offsetCount = depth; + + while (--levels > 0) + { + depth = Math.Max(1, depth >> 1); + offsetCount += depth; + } + + return offsetCount; + } + + public static SizeInfo GetBlockLinearTextureSize( + int width, + int height, + int depth, + int levels, + int layers, + int blockWidth, + int blockHeight, + int bytesPerPixel, + int gobBlocksInY, + int gobBlocksInZ, + int gobBlocksInTileX, + int gpuLayerSize = 0) + { + bool is3D = depth > 1; + + int layerSize = 0; + + int[] allOffsets = new int[is3D ? Calculate3DOffsetCount(levels, depth) : levels * layers * depth]; + int[] mipOffsets = new int[levels]; + int[] sliceSizes = new int[levels]; + int[] levelSizes = new int[levels]; + + int mipGobBlocksInY = gobBlocksInY; + int mipGobBlocksInZ = gobBlocksInZ; + + int gobWidth = (GobStride / bytesPerPixel) * gobBlocksInTileX; + int gobHeight = gobBlocksInY * GobHeight; + + int depthLevelOffset = 0; + + for (int level = 0; level < levels; level++) + { + int w = Math.Max(1, width >> level); + int h = Math.Max(1, height >> level); + int d = Math.Max(1, depth >> level); + + w = BitUtils.DivRoundUp(w, blockWidth); + h = BitUtils.DivRoundUp(h, blockHeight); + + while (h <= (mipGobBlocksInY >> 1) * GobHeight && mipGobBlocksInY != 1) + { + mipGobBlocksInY >>= 1; + } + + while (d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1) + { + mipGobBlocksInZ >>= 1; + } + + int widthInGobs = BitUtils.DivRoundUp(w * bytesPerPixel, GobStride); + + int alignment = gobBlocksInTileX; + + if (d < gobBlocksInZ || w <= gobWidth || h <= gobHeight) + { + alignment = 1; + } + + widthInGobs = BitUtils.AlignUp(widthInGobs, alignment); + + int totalBlocksOfGobsInZ = BitUtils.DivRoundUp(d, mipGobBlocksInZ); + int totalBlocksOfGobsInY = BitUtils.DivRoundUp(BitUtils.DivRoundUp(h, GobHeight), mipGobBlocksInY); + + int robSize = widthInGobs * mipGobBlocksInY * mipGobBlocksInZ * GobSize; + + if (is3D) + { + int gobSize = mipGobBlocksInY * GobSize; + + int sliceSize = totalBlocksOfGobsInY * widthInGobs * gobSize; + + int baseOffset = layerSize; + + int mask = gobBlocksInZ - 1; + + for (int z = 0; z < d; z++) + { + int zLow = z & mask; + int zHigh = z & ~mask; + + allOffsets[z + depthLevelOffset] = baseOffset + zLow * gobSize + zHigh * sliceSize; + } + } + + mipOffsets[level] = layerSize; + sliceSizes[level] = totalBlocksOfGobsInY * robSize; + levelSizes[level] = totalBlocksOfGobsInZ * sliceSizes[level]; + + layerSize += levelSizes[level]; + + depthLevelOffset += d; + } + + if (layers > 1) + { + layerSize = AlignLayerSize( + layerSize, + height, + depth, + blockHeight, + gobBlocksInY, + gobBlocksInZ, + gobBlocksInTileX); + } + + int totalSize; + + if (layerSize < gpuLayerSize) + { + totalSize = (layers - 1) * gpuLayerSize + layerSize; + layerSize = gpuLayerSize; + } + else + { + totalSize = layerSize * layers; + } + + if (!is3D) + { + for (int layer = 0; layer < layers; layer++) + { + int baseIndex = layer * levels; + int baseOffset = layer * layerSize; + + for (int level = 0; level < levels; level++) + { + allOffsets[baseIndex + level] = baseOffset + mipOffsets[level]; + } + } + } + + return new SizeInfo(mipOffsets, allOffsets, sliceSizes, levelSizes, depth, levels, layerSize, totalSize, is3D); + } + + public static SizeInfo GetLinearTextureSize(int stride, int height, int blockHeight) + { + // Non-2D or mipmapped linear textures are not supported by the Switch GPU, + // so we only need to handle a single case (2D textures without mipmaps). + int totalSize = stride * BitUtils.DivRoundUp(height, blockHeight); + + return new SizeInfo(totalSize); + } + + private static int AlignLayerSize( + int size, + int height, + int depth, + int blockHeight, + int gobBlocksInY, + int gobBlocksInZ, + int gobBlocksInTileX) + { + if (gobBlocksInTileX < 2) + { + height = BitUtils.DivRoundUp(height, blockHeight); + + while (height <= (gobBlocksInY >> 1) * GobHeight && gobBlocksInY != 1) + { + gobBlocksInY >>= 1; + } + + while (depth <= (gobBlocksInZ >> 1) && gobBlocksInZ != 1) + { + gobBlocksInZ >>= 1; + } + + int blockOfGobsSize = gobBlocksInY * gobBlocksInZ * GobSize; + + int sizeInBlockOfGobs = size / blockOfGobsSize; + + if (size != sizeInBlockOfGobs * blockOfGobsSize) + { + size = (sizeInBlockOfGobs + 1) * blockOfGobsSize; + } + } + else + { + int alignment = (gobBlocksInTileX * GobSize) * gobBlocksInY * gobBlocksInZ; + + size = BitUtils.AlignUp(size, alignment); + } + + return size; + } + + public static Size GetBlockLinearAlignedSize( + int width, + int height, + int depth, + int blockWidth, + int blockHeight, + int bytesPerPixel, + int gobBlocksInY, + int gobBlocksInZ, + int gobBlocksInTileX) + { + width = BitUtils.DivRoundUp(width, blockWidth); + height = BitUtils.DivRoundUp(height, blockHeight); + + int gobWidth = (GobStride / bytesPerPixel) * gobBlocksInTileX; + int gobHeight = gobBlocksInY * GobHeight; + + int alignment = gobWidth; + + if (depth < gobBlocksInZ || width <= gobWidth || height <= gobHeight) + { + alignment = GobStride / bytesPerPixel; + } + + // Height has already been divided by block height, so pass it as 1. + (gobBlocksInY, gobBlocksInZ) = GetMipGobBlockSizes(height, depth, 1, gobBlocksInY, gobBlocksInZ); + + int blockOfGobsHeight = gobBlocksInY * GobHeight; + int blockOfGobsDepth = gobBlocksInZ; + + width = BitUtils.AlignUp(width, alignment); + height = BitUtils.AlignUp(height, blockOfGobsHeight); + depth = BitUtils.AlignUp(depth, blockOfGobsDepth); + + return new Size(width, height, depth); + } + + public static Size GetLinearAlignedSize( + int width, + int height, + int blockWidth, + int blockHeight, + int bytesPerPixel) + { + width = BitUtils.DivRoundUp(width, blockWidth); + height = BitUtils.DivRoundUp(height, blockHeight); + + int widthAlignment = StrideAlignment / bytesPerPixel; + + width = BitUtils.AlignUp(width, widthAlignment); + + return new Size(width, height, 1); + } + + public static (int, int) GetMipGobBlockSizes( + int height, + int depth, + int blockHeight, + int gobBlocksInY, + int gobBlocksInZ) + { + height = BitUtils.DivRoundUp(height, blockHeight); + + while (height <= (gobBlocksInY >> 1) * GobHeight && gobBlocksInY != 1) + { + gobBlocksInY >>= 1; + } + + while (depth <= (gobBlocksInZ >> 1) && gobBlocksInZ != 1) + { + gobBlocksInZ >>= 1; + } + + return (gobBlocksInY, gobBlocksInZ); + } + } +}
\ No newline at end of file |
