Initial work
This commit is contained in:
1380
Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs
Normal file
1380
Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs
Normal file
File diff suppressed because it is too large
Load Diff
9
Ryujinx.Graphics.Texture/Astc/AstcDecoderException.cs
Normal file
9
Ryujinx.Graphics.Texture/Astc/AstcDecoderException.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture.Astc
|
||||
{
|
||||
public class AstcDecoderException : Exception
|
||||
{
|
||||
public AstcDecoderException(string exMsg) : base(exMsg) { }
|
||||
}
|
||||
}
|
138
Ryujinx.Graphics.Texture/Astc/AstcPixel.cs
Normal file
138
Ryujinx.Graphics.Texture/Astc/AstcPixel.cs
Normal file
@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture.Astc
|
||||
{
|
||||
class AstcPixel
|
||||
{
|
||||
public short R { get; set; }
|
||||
public short G { get; set; }
|
||||
public short B { get; set; }
|
||||
public short A { get; set; }
|
||||
|
||||
byte[] _bitDepth = new byte[4];
|
||||
|
||||
public AstcPixel(short a, short r, short g, short b)
|
||||
{
|
||||
A = a;
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
_bitDepth[i] = 8;
|
||||
}
|
||||
|
||||
public void ClampByte()
|
||||
{
|
||||
R = Math.Min(Math.Max(R, (short)0), (short)255);
|
||||
G = Math.Min(Math.Max(G, (short)0), (short)255);
|
||||
B = Math.Min(Math.Max(B, (short)0), (short)255);
|
||||
A = Math.Min(Math.Max(A, (short)0), (short)255);
|
||||
}
|
||||
|
||||
public short GetComponent(int index)
|
||||
{
|
||||
switch(index)
|
||||
{
|
||||
case 0: return A;
|
||||
case 1: return R;
|
||||
case 2: return G;
|
||||
case 3: return B;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void SetComponent(int index, int value)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
A = (short)value;
|
||||
break;
|
||||
case 1:
|
||||
R = (short)value;
|
||||
break;
|
||||
case 2:
|
||||
G = (short)value;
|
||||
break;
|
||||
case 3:
|
||||
B = (short)value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeBitDepth(byte[] depth)
|
||||
{
|
||||
for (int i = 0; i< 4; i++)
|
||||
{
|
||||
int value = ChangeBitDepth(GetComponent(i), _bitDepth[i], depth[i]);
|
||||
|
||||
SetComponent(i, value);
|
||||
_bitDepth[i] = depth[i];
|
||||
}
|
||||
}
|
||||
|
||||
short ChangeBitDepth(short value, byte oldDepth, byte newDepth)
|
||||
{
|
||||
Debug.Assert(newDepth <= 8);
|
||||
Debug.Assert(oldDepth <= 8);
|
||||
|
||||
if (oldDepth == newDepth)
|
||||
{
|
||||
// Do nothing
|
||||
return value;
|
||||
}
|
||||
else if (oldDepth == 0 && newDepth != 0)
|
||||
{
|
||||
return (short)((1 << newDepth) - 1);
|
||||
}
|
||||
else if (newDepth > oldDepth)
|
||||
{
|
||||
return (short)BitArrayStream.Replicate(value, oldDepth, newDepth);
|
||||
}
|
||||
else
|
||||
{
|
||||
// oldDepth > newDepth
|
||||
if (newDepth == 0)
|
||||
{
|
||||
return 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte bitsWasted = (byte)(oldDepth - newDepth);
|
||||
short tempValue = value;
|
||||
|
||||
tempValue = (short)((tempValue + (1 << (bitsWasted - 1))) >> bitsWasted);
|
||||
tempValue = Math.Min(Math.Max((short)0, tempValue), (short)((1 << newDepth) - 1));
|
||||
|
||||
return (byte)(tempValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Pack()
|
||||
{
|
||||
AstcPixel newPixel = new AstcPixel(A, R, G, B);
|
||||
byte[] eightBitDepth = { 8, 8, 8, 8 };
|
||||
|
||||
newPixel.ChangeBitDepth(eightBitDepth);
|
||||
|
||||
return (byte)newPixel.A << 24 |
|
||||
(byte)newPixel.B << 16 |
|
||||
(byte)newPixel.G << 8 |
|
||||
(byte)newPixel.R << 0;
|
||||
}
|
||||
|
||||
// Adds more precision to the blue channel as described
|
||||
// in C.2.14
|
||||
public static AstcPixel BlueContract(int a, int r, int g, int b)
|
||||
{
|
||||
return new AstcPixel((short)(a),
|
||||
(short)((r + b) >> 1),
|
||||
(short)((g + b) >> 1),
|
||||
(short)(b));
|
||||
}
|
||||
}
|
||||
}
|
121
Ryujinx.Graphics.Texture/Astc/BitArrayStream.cs
Normal file
121
Ryujinx.Graphics.Texture/Astc/BitArrayStream.cs
Normal file
@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture.Astc
|
||||
{
|
||||
public class BitArrayStream
|
||||
{
|
||||
public BitArray BitsArray;
|
||||
|
||||
public int Position { get; private set; }
|
||||
|
||||
public BitArrayStream(BitArray bitArray)
|
||||
{
|
||||
BitsArray = bitArray;
|
||||
Position = 0;
|
||||
}
|
||||
|
||||
public short ReadBits(int length)
|
||||
{
|
||||
int retValue = 0;
|
||||
for (int i = Position; i < Position + length; i++)
|
||||
{
|
||||
if (BitsArray[i])
|
||||
{
|
||||
retValue |= 1 << (i - Position);
|
||||
}
|
||||
}
|
||||
|
||||
Position += length;
|
||||
return (short)retValue;
|
||||
}
|
||||
|
||||
public int ReadBits(int start, int end)
|
||||
{
|
||||
int retValue = 0;
|
||||
for (int i = start; i <= end; i++)
|
||||
{
|
||||
if (BitsArray[i])
|
||||
{
|
||||
retValue |= 1 << (i - start);
|
||||
}
|
||||
}
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
public int ReadBit(int index)
|
||||
{
|
||||
return Convert.ToInt32(BitsArray[index]);
|
||||
}
|
||||
|
||||
public void WriteBits(int value, int length)
|
||||
{
|
||||
for (int i = Position; i < Position + length; i++)
|
||||
{
|
||||
BitsArray[i] = ((value >> (i - Position)) & 1) != 0;
|
||||
}
|
||||
|
||||
Position += length;
|
||||
}
|
||||
|
||||
public byte[] ToByteArray()
|
||||
{
|
||||
byte[] retArray = new byte[(BitsArray.Length + 7) / 8];
|
||||
BitsArray.CopyTo(retArray, 0);
|
||||
return retArray;
|
||||
}
|
||||
|
||||
public static int Replicate(int value, int numberBits, int toBit)
|
||||
{
|
||||
if (numberBits == 0) return 0;
|
||||
if (toBit == 0) return 0;
|
||||
|
||||
int tempValue = value & ((1 << numberBits) - 1);
|
||||
int retValue = tempValue;
|
||||
int resLength = numberBits;
|
||||
|
||||
while (resLength < toBit)
|
||||
{
|
||||
int comp = 0;
|
||||
if (numberBits > toBit - resLength)
|
||||
{
|
||||
int newShift = toBit - resLength;
|
||||
comp = numberBits - newShift;
|
||||
numberBits = newShift;
|
||||
}
|
||||
retValue <<= numberBits;
|
||||
retValue |= tempValue >> comp;
|
||||
resLength += numberBits;
|
||||
}
|
||||
return retValue;
|
||||
}
|
||||
|
||||
public static int PopCnt(int number)
|
||||
{
|
||||
int counter;
|
||||
for (counter = 0; number != 0; counter++)
|
||||
{
|
||||
number &= number - 1;
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
public static void Swap<T>(ref T lhs, ref T rhs)
|
||||
{
|
||||
T temp = lhs;
|
||||
lhs = rhs;
|
||||
rhs = temp;
|
||||
}
|
||||
|
||||
// Transfers a bit as described in C.2.14
|
||||
public static void BitTransferSigned(ref int a, ref int b)
|
||||
{
|
||||
b >>= 1;
|
||||
b |= a & 0x80;
|
||||
a >>= 1;
|
||||
a &= 0x3F;
|
||||
if ((a & 0x20) != 0) a -= 0x40;
|
||||
}
|
||||
}
|
||||
}
|
269
Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs
Normal file
269
Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs
Normal file
@ -0,0 +1,269 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture.Astc
|
||||
{
|
||||
public struct IntegerEncoded
|
||||
{
|
||||
public enum EIntegerEncoding
|
||||
{
|
||||
JustBits,
|
||||
Quint,
|
||||
Trit
|
||||
}
|
||||
|
||||
EIntegerEncoding _encoding;
|
||||
public int NumberBits { get; private set; }
|
||||
public int BitValue { get; private set; }
|
||||
public int TritValue { get; private set; }
|
||||
public int QuintValue { get; private set; }
|
||||
|
||||
public IntegerEncoded(EIntegerEncoding encoding, int numBits)
|
||||
{
|
||||
_encoding = encoding;
|
||||
NumberBits = numBits;
|
||||
BitValue = 0;
|
||||
TritValue = 0;
|
||||
QuintValue = 0;
|
||||
}
|
||||
|
||||
public bool MatchesEncoding(IntegerEncoded other)
|
||||
{
|
||||
return _encoding == other._encoding && NumberBits == other.NumberBits;
|
||||
}
|
||||
|
||||
public EIntegerEncoding GetEncoding()
|
||||
{
|
||||
return _encoding;
|
||||
}
|
||||
|
||||
public int GetBitLength(int numberVals)
|
||||
{
|
||||
int totalBits = NumberBits * numberVals;
|
||||
if (_encoding == EIntegerEncoding.Trit)
|
||||
{
|
||||
totalBits += (numberVals * 8 + 4) / 5;
|
||||
}
|
||||
else if (_encoding == EIntegerEncoding.Quint)
|
||||
{
|
||||
totalBits += (numberVals * 7 + 2) / 3;
|
||||
}
|
||||
return totalBits;
|
||||
}
|
||||
|
||||
public static IntegerEncoded CreateEncoding(int maxVal)
|
||||
{
|
||||
while (maxVal > 0)
|
||||
{
|
||||
int check = maxVal + 1;
|
||||
|
||||
// Is maxVal a power of two?
|
||||
if ((check & (check - 1)) == 0)
|
||||
{
|
||||
return new IntegerEncoded(EIntegerEncoding.JustBits, BitArrayStream.PopCnt(maxVal));
|
||||
}
|
||||
|
||||
// Is maxVal of the type 3*2^n - 1?
|
||||
if ((check % 3 == 0) && ((check / 3) & ((check / 3) - 1)) == 0)
|
||||
{
|
||||
return new IntegerEncoded(EIntegerEncoding.Trit, BitArrayStream.PopCnt(check / 3 - 1));
|
||||
}
|
||||
|
||||
// Is maxVal of the type 5*2^n - 1?
|
||||
if ((check % 5 == 0) && ((check / 5) & ((check / 5) - 1)) == 0)
|
||||
{
|
||||
return new IntegerEncoded(EIntegerEncoding.Quint, BitArrayStream.PopCnt(check / 5 - 1));
|
||||
}
|
||||
|
||||
// Apparently it can't be represented with a bounded integer sequence...
|
||||
// just iterate.
|
||||
maxVal--;
|
||||
}
|
||||
|
||||
return new IntegerEncoded(EIntegerEncoding.JustBits, 0);
|
||||
}
|
||||
|
||||
public static void DecodeTritBlock(
|
||||
BitArrayStream bitStream,
|
||||
List<IntegerEncoded> listIntegerEncoded,
|
||||
int numberBitsPerValue)
|
||||
{
|
||||
// Implement the algorithm in section C.2.12
|
||||
int[] m = new int[5];
|
||||
int[] t = new int[5];
|
||||
int T;
|
||||
|
||||
// Read the trit encoded block according to
|
||||
// table C.2.14
|
||||
m[0] = bitStream.ReadBits(numberBitsPerValue);
|
||||
T = bitStream.ReadBits(2);
|
||||
m[1] = bitStream.ReadBits(numberBitsPerValue);
|
||||
T |= bitStream.ReadBits(2) << 2;
|
||||
m[2] = bitStream.ReadBits(numberBitsPerValue);
|
||||
T |= bitStream.ReadBits(1) << 4;
|
||||
m[3] = bitStream.ReadBits(numberBitsPerValue);
|
||||
T |= bitStream.ReadBits(2) << 5;
|
||||
m[4] = bitStream.ReadBits(numberBitsPerValue);
|
||||
T |= bitStream.ReadBits(1) << 7;
|
||||
|
||||
int c = 0;
|
||||
|
||||
BitArrayStream tb = new BitArrayStream(new BitArray(new int[] { T }));
|
||||
if (tb.ReadBits(2, 4) == 7)
|
||||
{
|
||||
c = (tb.ReadBits(5, 7) << 2) | tb.ReadBits(0, 1);
|
||||
t[4] = t[3] = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = tb.ReadBits(0, 4);
|
||||
if (tb.ReadBits(5, 6) == 3)
|
||||
{
|
||||
t[4] = 2;
|
||||
t[3] = tb.ReadBit(7);
|
||||
}
|
||||
else
|
||||
{
|
||||
t[4] = tb.ReadBit(7);
|
||||
t[3] = tb.ReadBits(5, 6);
|
||||
}
|
||||
}
|
||||
|
||||
BitArrayStream cb = new BitArrayStream(new BitArray(new int[] { c }));
|
||||
if (cb.ReadBits(0, 1) == 3)
|
||||
{
|
||||
t[2] = 2;
|
||||
t[1] = cb.ReadBit(4);
|
||||
t[0] = (cb.ReadBit(3) << 1) | (cb.ReadBit(2) & ~cb.ReadBit(3));
|
||||
}
|
||||
else if (cb.ReadBits(2, 3) == 3)
|
||||
{
|
||||
t[2] = 2;
|
||||
t[1] = 2;
|
||||
t[0] = cb.ReadBits(0, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
t[2] = cb.ReadBit(4);
|
||||
t[1] = cb.ReadBits(2, 3);
|
||||
t[0] = (cb.ReadBit(1) << 1) | (cb.ReadBit(0) & ~cb.ReadBit(1));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Trit, numberBitsPerValue)
|
||||
{
|
||||
BitValue = m[i],
|
||||
TritValue = t[i]
|
||||
};
|
||||
listIntegerEncoded.Add(intEncoded);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DecodeQuintBlock(
|
||||
BitArrayStream bitStream,
|
||||
List<IntegerEncoded> listIntegerEncoded,
|
||||
int numberBitsPerValue)
|
||||
{
|
||||
// Implement the algorithm in section C.2.12
|
||||
int[] m = new int[3];
|
||||
int[] qa = new int[3];
|
||||
int q;
|
||||
|
||||
// Read the trit encoded block according to
|
||||
// table C.2.15
|
||||
m[0] = bitStream.ReadBits(numberBitsPerValue);
|
||||
q = bitStream.ReadBits(3);
|
||||
m[1] = bitStream.ReadBits(numberBitsPerValue);
|
||||
q |= bitStream.ReadBits(2) << 3;
|
||||
m[2] = bitStream.ReadBits(numberBitsPerValue);
|
||||
q |= bitStream.ReadBits(2) << 5;
|
||||
|
||||
BitArrayStream qb = new BitArrayStream(new BitArray(new int[] { q }));
|
||||
if (qb.ReadBits(1, 2) == 3 && qb.ReadBits(5, 6) == 0)
|
||||
{
|
||||
qa[0] = qa[1] = 4;
|
||||
qa[2] = (qb.ReadBit(0) << 2) | ((qb.ReadBit(4) & ~qb.ReadBit(0)) << 1) | (qb.ReadBit(3) & ~qb.ReadBit(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
int c = 0;
|
||||
if (qb.ReadBits(1, 2) == 3)
|
||||
{
|
||||
qa[2] = 4;
|
||||
c = (qb.ReadBits(3, 4) << 3) | ((~qb.ReadBits(5, 6) & 3) << 1) | qb.ReadBit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
qa[2] = qb.ReadBits(5, 6);
|
||||
c = qb.ReadBits(0, 4);
|
||||
}
|
||||
|
||||
BitArrayStream cb = new BitArrayStream(new BitArray(new int[] { c }));
|
||||
if (cb.ReadBits(0, 2) == 5)
|
||||
{
|
||||
qa[1] = 4;
|
||||
qa[0] = cb.ReadBits(3, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
qa[1] = cb.ReadBits(3, 4);
|
||||
qa[0] = cb.ReadBits(0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Quint, numberBitsPerValue)
|
||||
{
|
||||
BitValue = m[i],
|
||||
QuintValue = qa[i]
|
||||
};
|
||||
listIntegerEncoded.Add(intEncoded);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DecodeIntegerSequence(
|
||||
List<IntegerEncoded> decodeIntegerSequence,
|
||||
BitArrayStream bitStream,
|
||||
int maxRange,
|
||||
int numberValues)
|
||||
{
|
||||
// Determine encoding parameters
|
||||
IntegerEncoded intEncoded = CreateEncoding(maxRange);
|
||||
|
||||
// Start decoding
|
||||
int numberValuesDecoded = 0;
|
||||
while (numberValuesDecoded < numberValues)
|
||||
{
|
||||
switch (intEncoded.GetEncoding())
|
||||
{
|
||||
case EIntegerEncoding.Quint:
|
||||
{
|
||||
DecodeQuintBlock(bitStream, decodeIntegerSequence, intEncoded.NumberBits);
|
||||
numberValuesDecoded += 3;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EIntegerEncoding.Trit:
|
||||
{
|
||||
DecodeTritBlock(bitStream, decodeIntegerSequence, intEncoded.NumberBits);
|
||||
numberValuesDecoded += 5;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EIntegerEncoding.JustBits:
|
||||
{
|
||||
intEncoded.BitValue = bitStream.ReadBits(intEncoded.NumberBits);
|
||||
decodeIntegerSequence.Add(intEncoded);
|
||||
numberValuesDecoded++;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
Ryujinx.Graphics.Texture/BlockLinearConstants.cs
Normal file
10
Ryujinx.Graphics.Texture/BlockLinearConstants.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
static class BlockLinearConstants
|
||||
{
|
||||
public const int GobStride = 64;
|
||||
public const int GobHeight = 8;
|
||||
|
||||
public const int GobSize = GobStride * GobHeight;
|
||||
}
|
||||
}
|
106
Ryujinx.Graphics.Texture/BlockLinearLayout.cs
Normal file
106
Ryujinx.Graphics.Texture/BlockLinearLayout.cs
Normal file
@ -0,0 +1,106 @@
|
||||
using Ryujinx.Common;
|
||||
using System;
|
||||
|
||||
using static Ryujinx.Graphics.Texture.BlockLinearConstants;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
class BlockLinearLayout
|
||||
{
|
||||
private struct RobAndSliceSizes
|
||||
{
|
||||
public int RobSize;
|
||||
public int SliceSize;
|
||||
|
||||
public RobAndSliceSizes(int robSize, int sliceSize)
|
||||
{
|
||||
RobSize = robSize;
|
||||
SliceSize = sliceSize;
|
||||
}
|
||||
}
|
||||
|
||||
private int _texWidth;
|
||||
private int _texHeight;
|
||||
private int _texDepth;
|
||||
private int _texGobBlocksInY;
|
||||
private int _texGobBlocksInZ;
|
||||
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;
|
||||
|
||||
public BlockLinearLayout(
|
||||
int width,
|
||||
int height,
|
||||
int depth,
|
||||
int gobBlocksInY,
|
||||
int gobBlocksInZ,
|
||||
int bpp)
|
||||
{
|
||||
_texWidth = width;
|
||||
_texHeight = height;
|
||||
_texDepth = depth;
|
||||
_texGobBlocksInY = gobBlocksInY;
|
||||
_texGobBlocksInZ = gobBlocksInZ;
|
||||
_texBpp = bpp;
|
||||
|
||||
_bppShift = BitUtils.CountTrailingZeros32(bpp);
|
||||
|
||||
_bhMask = gobBlocksInY - 1;
|
||||
_bdMask = gobBlocksInZ - 1;
|
||||
|
||||
_bhShift = BitUtils.CountTrailingZeros32(gobBlocksInY);
|
||||
_bdShift = BitUtils.CountTrailingZeros32(gobBlocksInZ);
|
||||
|
||||
_xShift = BitUtils.CountTrailingZeros32(GobSize * gobBlocksInY * gobBlocksInZ);
|
||||
|
||||
RobAndSliceSizes rsSizes = GetRobAndSliceSizes(width, height, gobBlocksInY, gobBlocksInZ);
|
||||
|
||||
_robSize = rsSizes.RobSize;
|
||||
_sliceSize = rsSizes.SliceSize;
|
||||
}
|
||||
|
||||
private RobAndSliceSizes GetRobAndSliceSizes(int width, int height, int gobBlocksInY, int gobBlocksInZ)
|
||||
{
|
||||
int widthInGobs = BitUtils.DivRoundUp(width * _texBpp, GobStride);
|
||||
|
||||
int robSize = GobSize * gobBlocksInY * gobBlocksInZ * widthInGobs;
|
||||
|
||||
int sliceSize = BitUtils.DivRoundUp(height, gobBlocksInY * GobHeight) * robSize;
|
||||
|
||||
return new RobAndSliceSizes(robSize, sliceSize);
|
||||
}
|
||||
|
||||
public int GetOffset(int x, int y, int z)
|
||||
{
|
||||
x <<= _bppShift;
|
||||
|
||||
int yh = y / GobHeight;
|
||||
|
||||
int position = (z >> _bdShift) * _sliceSize + (yh >> _bhShift) * _robSize;
|
||||
|
||||
position += (x / GobStride) << _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 position;
|
||||
}
|
||||
}
|
||||
}
|
167
Ryujinx.Graphics.Texture/LayoutConverter.cs
Normal file
167
Ryujinx.Graphics.Texture/LayoutConverter.cs
Normal file
@ -0,0 +1,167 @@
|
||||
using Ryujinx.Common;
|
||||
using System;
|
||||
|
||||
using static Ryujinx.Graphics.Texture.BlockLinearConstants;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
public static class LayoutConverter
|
||||
{
|
||||
private const int AlignmentSize = 4;
|
||||
|
||||
public static Span<byte> ConvertBlockLinearToLinear(
|
||||
int width,
|
||||
int height,
|
||||
int depth,
|
||||
int levels,
|
||||
int layers,
|
||||
int blockWidth,
|
||||
int blockHeight,
|
||||
int bytesPerPixel,
|
||||
int gobBlocksInY,
|
||||
int gobBlocksInZ,
|
||||
int gobBlocksInTileX,
|
||||
SizeInfo sizeInfo,
|
||||
Span<byte> data)
|
||||
{
|
||||
int outSize = GetTextureSize(
|
||||
width,
|
||||
height,
|
||||
depth,
|
||||
levels,
|
||||
layers,
|
||||
blockWidth,
|
||||
blockHeight,
|
||||
bytesPerPixel);
|
||||
|
||||
Span<byte> output = new byte[outSize];
|
||||
|
||||
int outOffs = 0;
|
||||
|
||||
int wAlignment = gobBlocksInTileX * (GobStride / bytesPerPixel);
|
||||
|
||||
int mipGobBlocksInY = gobBlocksInY;
|
||||
int mipGobBlocksInZ = gobBlocksInZ;
|
||||
|
||||
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 stride = BitUtils.AlignUp(w * bytesPerPixel, AlignmentSize);
|
||||
int wAligned = BitUtils.AlignUp(w, wAlignment);
|
||||
|
||||
BlockLinearLayout layoutConverter = new BlockLinearLayout(
|
||||
wAligned,
|
||||
h,
|
||||
d,
|
||||
mipGobBlocksInY,
|
||||
mipGobBlocksInZ,
|
||||
bytesPerPixel);
|
||||
|
||||
for (int layer = 0; layer < layers; layer++)
|
||||
{
|
||||
int inBaseOffset = layer * sizeInfo.LayerSize + sizeInfo.GetMipOffset(level);
|
||||
|
||||
for (int z = 0; z < d; z++)
|
||||
for (int y = 0; y < h; y++)
|
||||
{
|
||||
for (int x = 0; x < w; x++)
|
||||
{
|
||||
int offset = inBaseOffset + layoutConverter.GetOffset(x, y, z);
|
||||
|
||||
Span<byte> dest = output.Slice(outOffs + x * bytesPerPixel, bytesPerPixel);
|
||||
|
||||
data.Slice(offset, bytesPerPixel).CopyTo(dest);
|
||||
}
|
||||
|
||||
outOffs += stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static Span<byte> ConvertLinearStridedToLinear(
|
||||
int width,
|
||||
int height,
|
||||
int blockWidth,
|
||||
int blockHeight,
|
||||
int stride,
|
||||
int bytesPerPixel,
|
||||
Span<byte> data)
|
||||
{
|
||||
int outOffs = 0;
|
||||
|
||||
int w = width;
|
||||
int h = height;
|
||||
|
||||
w = BitUtils.DivRoundUp(w, blockWidth);
|
||||
h = BitUtils.DivRoundUp(h, blockHeight);
|
||||
|
||||
int outStride = BitUtils.AlignUp(w * bytesPerPixel, AlignmentSize);
|
||||
|
||||
Span<byte> output = new byte[h * outStride];
|
||||
|
||||
for (int y = 0; y < h; y++)
|
||||
{
|
||||
for (int x = 0; x < w; x++)
|
||||
{
|
||||
int offset = y * stride + x * bytesPerPixel;
|
||||
|
||||
Span<byte> dest = output.Slice(outOffs + x * bytesPerPixel, bytesPerPixel);
|
||||
|
||||
data.Slice(offset, bytesPerPixel).CopyTo(dest);
|
||||
}
|
||||
|
||||
outOffs += outStride;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private static int GetTextureSize(
|
||||
int width,
|
||||
int height,
|
||||
int depth,
|
||||
int levels,
|
||||
int layers,
|
||||
int blockWidth,
|
||||
int blockHeight,
|
||||
int bytesPerPixel)
|
||||
{
|
||||
int layerSize = 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);
|
||||
|
||||
int stride = BitUtils.AlignUp(w * bytesPerPixel, AlignmentSize);
|
||||
|
||||
layerSize += stride * h * d;
|
||||
}
|
||||
|
||||
return layerSize * layers;
|
||||
}
|
||||
}
|
||||
}
|
55
Ryujinx.Graphics.Texture/OffsetCalculator.cs
Normal file
55
Ryujinx.Graphics.Texture/OffsetCalculator.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using Ryujinx.Common;
|
||||
|
||||
using static Ryujinx.Graphics.Texture.BlockLinearConstants;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
public class OffsetCalculator
|
||||
{
|
||||
private int _stride;
|
||||
private bool _isLinear;
|
||||
private int _bytesPerPixel;
|
||||
|
||||
private BlockLinearLayout _layoutConverter;
|
||||
|
||||
public OffsetCalculator(
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
bool isLinear,
|
||||
int gobBlocksInY,
|
||||
int bytesPerPixel)
|
||||
{
|
||||
_stride = stride;
|
||||
_isLinear = isLinear;
|
||||
_bytesPerPixel = bytesPerPixel;
|
||||
|
||||
int wAlignment = GobStride / bytesPerPixel;
|
||||
|
||||
int wAligned = BitUtils.AlignUp(width, wAlignment);
|
||||
|
||||
if (!isLinear)
|
||||
{
|
||||
_layoutConverter = new BlockLinearLayout(
|
||||
wAligned,
|
||||
height,
|
||||
1,
|
||||
gobBlocksInY,
|
||||
1,
|
||||
bytesPerPixel);
|
||||
}
|
||||
}
|
||||
|
||||
public int GetOffset(int x, int y)
|
||||
{
|
||||
if (_isLinear)
|
||||
{
|
||||
return x * _bytesPerPixel + y * _stride;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _layoutConverter.GetOffset(x, y, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj
Normal file
11
Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj
Normal file
@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
19
Ryujinx.Graphics.Texture/Size.cs
Normal file
19
Ryujinx.Graphics.Texture/Size.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using Ryujinx.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
public struct Size
|
||||
{
|
||||
public int Width { get; }
|
||||
public int Height { get; }
|
||||
public int Depth { get; }
|
||||
|
||||
public Size(int width, int height, int depth)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
Depth = depth;
|
||||
}
|
||||
}
|
||||
}
|
195
Ryujinx.Graphics.Texture/SizeCalculator.cs
Normal file
195
Ryujinx.Graphics.Texture/SizeCalculator.cs
Normal file
@ -0,0 +1,195 @@
|
||||
using Ryujinx.Common;
|
||||
using System;
|
||||
|
||||
using static Ryujinx.Graphics.Texture.BlockLinearConstants;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
public static class SizeCalculator
|
||||
{
|
||||
private const int StrideAlignment = 32;
|
||||
|
||||
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 layerSize = 0;
|
||||
|
||||
int[] mipOffsets = new int[levels];
|
||||
|
||||
int mipGobBlocksInY = gobBlocksInY;
|
||||
int mipGobBlocksInZ = gobBlocksInZ;
|
||||
|
||||
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.AlignUp(BitUtils.DivRoundUp(w * bytesPerPixel, GobStride), gobBlocksInTileX);
|
||||
|
||||
int totalBlocksOfGobsInZ = BitUtils.DivRoundUp(d, mipGobBlocksInZ);
|
||||
int totalBlocksOfGobsInY = BitUtils.DivRoundUp(BitUtils.DivRoundUp(h, GobHeight), mipGobBlocksInY);
|
||||
|
||||
int robSize = widthInGobs * mipGobBlocksInY * mipGobBlocksInZ * GobSize;
|
||||
|
||||
mipOffsets[level] = layerSize;
|
||||
|
||||
layerSize += totalBlocksOfGobsInZ * totalBlocksOfGobsInY * robSize;
|
||||
}
|
||||
|
||||
layerSize = AlignLayerSize(
|
||||
layerSize,
|
||||
height,
|
||||
depth,
|
||||
blockHeight,
|
||||
gobBlocksInY,
|
||||
gobBlocksInZ);
|
||||
|
||||
int[] allOffsets = new int[levels * layers];
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
int totalSize = layerSize * layers;
|
||||
|
||||
return new SizeInfo(mipOffsets, allOffsets, levels, layerSize, totalSize);
|
||||
}
|
||||
|
||||
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(new int[] { 0 }, new int[] { 0 }, 1, totalSize, totalSize);
|
||||
}
|
||||
|
||||
private static int AlignLayerSize(
|
||||
int size,
|
||||
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;
|
||||
}
|
||||
|
||||
int blockOfGobsSize = gobBlocksInY * gobBlocksInZ * GobSize;
|
||||
|
||||
int sizeInBlockOfGobs = size / blockOfGobsSize;
|
||||
|
||||
if (size != sizeInBlockOfGobs * blockOfGobsSize)
|
||||
{
|
||||
size = (sizeInBlockOfGobs + 1) * blockOfGobsSize;
|
||||
}
|
||||
|
||||
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 = gobBlocksInTileX * (GobStride / bytesPerPixel);
|
||||
|
||||
int blockOfGobsHeight = gobBlocksInY * GobHeight;
|
||||
int blockOfGobsDepth = gobBlocksInZ;
|
||||
|
||||
width = BitUtils.AlignUp(width, gobWidth);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
58
Ryujinx.Graphics.Texture/SizeInfo.cs
Normal file
58
Ryujinx.Graphics.Texture/SizeInfo.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using Ryujinx.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
public struct SizeInfo
|
||||
{
|
||||
private int[] _mipOffsets;
|
||||
private int[] _allOffsets;
|
||||
|
||||
private int _levels;
|
||||
|
||||
public int LayerSize { get; }
|
||||
public int TotalSize { get; }
|
||||
|
||||
public SizeInfo(
|
||||
int[] mipOffsets,
|
||||
int[] allOffsets,
|
||||
int levels,
|
||||
int layerSize,
|
||||
int totalSize)
|
||||
{
|
||||
_mipOffsets = mipOffsets;
|
||||
_allOffsets = allOffsets;
|
||||
_levels = levels;
|
||||
LayerSize = layerSize;
|
||||
TotalSize = totalSize;
|
||||
}
|
||||
|
||||
public int GetMipOffset(int level)
|
||||
{
|
||||
if ((uint)level > _mipOffsets.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(level));
|
||||
}
|
||||
|
||||
return _mipOffsets[level];
|
||||
}
|
||||
|
||||
public bool FindView(int offset, int size, out int firstLayer, out int firstLevel)
|
||||
{
|
||||
int index = Array.BinarySearch(_allOffsets, offset);
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
firstLayer = 0;
|
||||
firstLevel = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
firstLayer = index / _levels;
|
||||
firstLevel = index - (firstLayer * _levels);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user