Initial non 2D textures support (#525)

* Initial non 2D textures support

- Shaders still need to be changed
- Some types aren't yet implemented

* Start implementing texture instructions suffixes

Fix wrong texture type with cube and TEXS

Also support array textures in TEX and TEX.B

Clean up TEX and TEXS coords managment

Fix TEXS.LL with non-2d textures

Implement TEX.AOFFI

Get the right arguments for TEX, TEXS and TLDS

Also, store suffix operands in appropriate values to support multiple
suffix combinaisons

* Support depth in read/writeTexture

Also support WrapR and detect mipmap

* Proper cube map textures support + fix TEXS.LZ

* Implement depth compare

* some code clean up

* Implement CubeMap textures in OGLTexture.Create

* Implement TLD4 and TLD4S

* Add Texture 1D support

* updates comments

* fix some code style issues

* Fix some nits + rename some things to be less confusing

* Remove GetSuffix local functions

* AOFFI => AOffI

* TextureType => GalTextureTarget

* finish renaming TextureType to TextureTarget

* Disable LL, LZ and LB support in the decompiler

This needs more work at the GL level (GLSL implementation should be
right)

* Revert "Disable LL, LZ and LB support in the decompiler"

This reverts commit 64536c3d9f673645faff3152838d1413c3203395.

* Fix TEXS ARRAY_2D index

* ImageFormat depth should be 1 for all image format

* Fix shader build issues with sampler1DShadow and texture

* Fix DC & AOFFI combinaison with TEX/TEXS

* Support AOFFI with TLD4 and TLD4S

* Fix shader compilation error for TLD4.AOFFI with no DC

* Fix binding isuses on the 2d copy engine

TODO: support 2d array copy

* Support 2D array copy operation in the 2D engine

This make every copy right in the GPU side.
Thie CPU copy probably needs to be updated

* Implement GetGpuSize + fix somes issues with 2d engine copies

TODO: mipmap level in it

* Don't throw an exception in the layer handling

* Fix because of rebase

* Reject 2d layers of non textures in 2d copy engine

* Add 3D textures and mipmap support on BlockLinearSwizzle

* Fix naming on new BitUtils methods

* gpu cache: Make sure to invalidate textures that doesn't have the same target

* Add the concept of layer count for array instead of using depth

Also cleanup GetGpuSize as Swizzle can compute the size with mipmap

* Support multi layer with mip map in ReadTexture

* Add more check for cache invalidation & remove cubemap and cubemap array code for now

Also fix compressed 2d array

* Fix texelFetchOffset shader build error

* Start looking into cube map again

Also add some way to log write in register in engines

* fix write register log levles

* Remove debug logs in WriteRegister

* Disable AOFFI support on non NVIDIA drivers

* Fix code align
This commit is contained in:
Thomas Guillemard 2019-02-28 02:12:24 +01:00 committed by jduncanator
parent 81aa50feb0
commit 884b4e5fd3
39 changed files with 2084 additions and 370 deletions

View File

@ -34,6 +34,11 @@ namespace Ryujinx.Common
return value & -(long)size; return value & -(long)size;
} }
public static int DivRoundUp(int value, int dividend)
{
return (value + dividend - 1) / dividend;
}
public static ulong DivRoundUp(ulong value, uint dividend) public static ulong DivRoundUp(ulong value, uint dividend)
{ {
return (value + dividend - 1) / dividend; return (value + dividend - 1) / dividend;
@ -44,6 +49,24 @@ namespace Ryujinx.Common
return (value + dividend - 1) / dividend; return (value + dividend - 1) / dividend;
} }
public static int Pow2RoundUp(int value)
{
value--;
value |= (value >> 1);
value |= (value >> 2);
value |= (value >> 4);
value |= (value >> 8);
value |= (value >> 16);
return ++value;
}
public static int Pow2RoundDown(int value)
{
return IsPowerOfTwo32(value) ? value : Pow2RoundUp(value) >> 1;
}
public static bool IsPowerOfTwo32(int value) public static bool IsPowerOfTwo32(int value)
{ {
return value != 0 && (value & (value - 1)) == 0; return value != 0 && (value & (value - 1)) == 0;
@ -85,6 +108,18 @@ namespace Ryujinx.Common
return (ulong)count; return (ulong)count;
} }
public static int CountTrailingZeros32(int value)
{
int count = 0;
while (((value >> count) & 1) == 0)
{
count++;
}
return count;
}
public static long ReverseBits64(long value) public static long ReverseBits64(long value)
{ {
return (long)ReverseBits64((ulong)value); return (long)ReverseBits64((ulong)value);

View File

@ -0,0 +1,14 @@
namespace Ryujinx.Graphics
{
public enum DepthCompareFunc
{
Never = 0,
Less = 1,
Equal = 2,
LEqual = 3,
Greater = 4,
NotEqual = 5,
GEqual = 6,
Always = 7
}
}

View File

@ -6,9 +6,15 @@ namespace Ryujinx.Graphics.Gal
{ {
public int Width; public int Width;
public int Height; public int Height;
// FIXME: separate layer and depth
public int Depth;
public int LayerCount;
public int TileWidth; public int TileWidth;
public int GobBlockHeight; public int GobBlockHeight;
public int GobBlockDepth;
public int Pitch; public int Pitch;
public int MaxMipmapLevel;
public GalImageFormat Format; public GalImageFormat Format;
public GalMemoryLayout Layout; public GalMemoryLayout Layout;
@ -16,34 +22,45 @@ namespace Ryujinx.Graphics.Gal
public GalTextureSource YSource; public GalTextureSource YSource;
public GalTextureSource ZSource; public GalTextureSource ZSource;
public GalTextureSource WSource; public GalTextureSource WSource;
public GalTextureTarget TextureTarget;
public GalImage( public GalImage(
int Width, int Width,
int Height, int Height,
int Depth,
int LayerCount,
int TileWidth, int TileWidth,
int GobBlockHeight, int GobBlockHeight,
int GobBlockDepth,
GalMemoryLayout Layout, GalMemoryLayout Layout,
GalImageFormat Format, GalImageFormat Format,
GalTextureSource XSource = GalTextureSource.Red, GalTextureTarget TextureTarget,
GalTextureSource YSource = GalTextureSource.Green, int MaxMipmapLevel = 1,
GalTextureSource ZSource = GalTextureSource.Blue, GalTextureSource XSource = GalTextureSource.Red,
GalTextureSource WSource = GalTextureSource.Alpha) GalTextureSource YSource = GalTextureSource.Green,
GalTextureSource ZSource = GalTextureSource.Blue,
GalTextureSource WSource = GalTextureSource.Alpha)
{ {
this.Width = Width; this.Width = Width;
this.Height = Height; this.Height = Height;
this.LayerCount = LayerCount;
this.Depth = Depth;
this.TileWidth = TileWidth; this.TileWidth = TileWidth;
this.GobBlockHeight = GobBlockHeight; this.GobBlockHeight = GobBlockHeight;
this.GobBlockDepth = GobBlockDepth;
this.Layout = Layout; this.Layout = Layout;
this.Format = Format; this.Format = Format;
this.MaxMipmapLevel = MaxMipmapLevel;
this.XSource = XSource; this.XSource = XSource;
this.YSource = YSource; this.YSource = YSource;
this.ZSource = ZSource; this.ZSource = ZSource;
this.WSource = WSource; this.WSource = WSource;
this.TextureTarget = TextureTarget;
Pitch = ImageUtils.GetPitch(Format, Width); Pitch = ImageUtils.GetPitch(Format, Width);
} }
public bool SizeMatches(GalImage Image) public bool SizeMatches(GalImage Image, bool IgnoreLayer = false)
{ {
if (ImageUtils.GetBytesPerPixel(Format) != if (ImageUtils.GetBytesPerPixel(Format) !=
ImageUtils.GetBytesPerPixel(Image.Format)) ImageUtils.GetBytesPerPixel(Image.Format))
@ -57,7 +74,14 @@ namespace Ryujinx.Graphics.Gal
return false; return false;
} }
return Height == Image.Height; bool Result = Height == Image.Height && Depth == Image.Depth;
if (!IgnoreLayer)
{
Result = Result && LayerCount == Image.LayerCount;
}
return Result;
} }
} }
} }

View File

@ -12,6 +12,9 @@ namespace Ryujinx.Graphics.Gal
public GalColorF BorderColor { get; private set; } public GalColorF BorderColor { get; private set; }
public bool DepthCompare { get; private set; }
public DepthCompareFunc DepthCompareFunc { get; private set; }
public GalTextureSampler( public GalTextureSampler(
GalTextureWrap AddressU, GalTextureWrap AddressU,
GalTextureWrap AddressV, GalTextureWrap AddressV,
@ -19,7 +22,9 @@ namespace Ryujinx.Graphics.Gal
GalTextureFilter MinFilter, GalTextureFilter MinFilter,
GalTextureFilter MagFilter, GalTextureFilter MagFilter,
GalTextureMipFilter MipFilter, GalTextureMipFilter MipFilter,
GalColorF BorderColor) GalColorF BorderColor,
bool DepthCompare,
DepthCompareFunc DepthCompareFunc)
{ {
this.AddressU = AddressU; this.AddressU = AddressU;
this.AddressV = AddressV; this.AddressV = AddressV;
@ -28,6 +33,9 @@ namespace Ryujinx.Graphics.Gal
this.MagFilter = MagFilter; this.MagFilter = MagFilter;
this.MipFilter = MipFilter; this.MipFilter = MipFilter;
this.BorderColor = BorderColor; this.BorderColor = BorderColor;
this.DepthCompare = DepthCompare;
this.DepthCompareFunc = DepthCompareFunc;
} }
} }
} }

View File

@ -0,0 +1,15 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalTextureTarget
{
OneD = 0,
TwoD = 1,
ThreeD = 2,
CubeMap = 3,
OneDArray = 4,
TwoDArray = 5,
OneDBuffer = 6,
TwoDNoMipMap = 7,
CubeArray = 8,
}
}

View File

@ -25,16 +25,20 @@ namespace Ryujinx.Graphics.Gal
void Render(); void Render();
void Copy( void Copy(
long SrcKey, GalImage SrcImage,
long DstKey, GalImage DstImage,
int SrcX0, long SrcKey,
int SrcY0, long DstKey,
int SrcX1, int SrcLayer,
int SrcY1, int DstLayer,
int DstX0, int SrcX0,
int DstY0, int SrcY0,
int DstX1, int SrcX1,
int DstY1); int SrcY1,
int DstX0,
int DstY0,
int DstX1,
int DstY1);
void Reinterpret(long Key, GalImage NewImage); void Reinterpret(long Key, GalImage NewImage);
} }

View File

@ -13,6 +13,6 @@ namespace Ryujinx.Graphics.Gal
void Bind(long Key, int Index, GalImage Image); void Bind(long Key, int Index, GalImage Image);
void SetSampler(GalTextureSampler Sampler); void SetSampler(GalImage Image, GalTextureSampler Sampler);
} }
} }

View File

@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public int Width => Image.Width; public int Width => Image.Width;
public int Height => Image.Height; public int Height => Image.Height;
public int Depth => Image.Depth;
public GalImageFormat Format => Image.Format; public GalImageFormat Format => Image.Format;

View File

@ -189,6 +189,31 @@ namespace Ryujinx.Graphics.Gal.OpenGL
throw new NotImplementedException($"{Format & GalImageFormat.FormatMask} {Format & GalImageFormat.TypeMask}"); throw new NotImplementedException($"{Format & GalImageFormat.FormatMask} {Format & GalImageFormat.TypeMask}");
} }
public static All GetDepthCompareFunc(DepthCompareFunc DepthCompareFunc)
{
switch (DepthCompareFunc)
{
case DepthCompareFunc.LEqual:
return All.Lequal;
case DepthCompareFunc.GEqual:
return All.Gequal;
case DepthCompareFunc.Less:
return All.Less;
case DepthCompareFunc.Greater:
return All.Greater;
case DepthCompareFunc.Equal:
return All.Equal;
case DepthCompareFunc.NotEqual:
return All.Notequal;
case DepthCompareFunc.Always:
return All.Always;
case DepthCompareFunc.Never:
return All.Never;
default:
throw new ArgumentException(nameof(DepthCompareFunc) + " \"" + DepthCompareFunc + "\" is not valid!");
}
}
public static InternalFormat GetCompressedImageFormat(GalImageFormat Format) public static InternalFormat GetCompressedImageFormat(GalImageFormat Format)
{ {
switch (Format) switch (Format)

View File

@ -9,9 +9,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private static Lazy<bool> s_TextureMirrorClamp = new Lazy<bool>(() => HasExtension("GL_EXT_texture_mirror_clamp")); private static Lazy<bool> s_TextureMirrorClamp = new Lazy<bool>(() => HasExtension("GL_EXT_texture_mirror_clamp"));
private static Lazy<bool> s_ViewportArray = new Lazy<bool>(() => HasExtension("GL_ARB_viewport_array")); private static Lazy<bool> s_ViewportArray = new Lazy<bool>(() => HasExtension("GL_ARB_viewport_array"));
private static Lazy<bool> s_NvidiaDriver = new Lazy<bool>(() => IsNvidiaDriver());
public static bool EnhancedLayouts => s_EnhancedLayouts.Value; public static bool EnhancedLayouts => s_EnhancedLayouts.Value;
public static bool TextureMirrorClamp => s_TextureMirrorClamp.Value; public static bool TextureMirrorClamp => s_TextureMirrorClamp.Value;
public static bool ViewportArray => s_ViewportArray.Value; public static bool ViewportArray => s_ViewportArray.Value;
public static bool NvidiaDrvier => s_NvidiaDriver.Value;
private static bool HasExtension(string Name) private static bool HasExtension(string Name)
{ {
@ -27,5 +30,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return false; return false;
} }
private static bool IsNvidiaDriver() {
return GL.GetString(StringName.Vendor).Equals("NVIDIA Corporation");
}
} }
} }

View File

@ -389,16 +389,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL
} }
public void Copy( public void Copy(
long SrcKey, GalImage SrcImage,
long DstKey, GalImage DstImage,
int SrcX0, long SrcKey,
int SrcY0, long DstKey,
int SrcX1, int SrcLayer,
int SrcY1, int DstLayer,
int DstX0, int SrcX0,
int DstY0, int SrcY0,
int DstX1, int SrcX1,
int DstY1) int SrcY1,
int DstX0,
int DstY0,
int DstX1,
int DstY1)
{ {
if (Texture.TryGetImageHandler(SrcKey, out ImageHandler SrcTex) && if (Texture.TryGetImageHandler(SrcKey, out ImageHandler SrcTex) &&
Texture.TryGetImageHandler(DstKey, out ImageHandler DstTex)) Texture.TryGetImageHandler(DstKey, out ImageHandler DstTex))
@ -425,8 +429,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL
FramebufferAttachment Attachment = GetAttachment(SrcTex); FramebufferAttachment Attachment = GetAttachment(SrcTex);
GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0); if (ImageUtils.IsArray(SrcImage.TextureTarget) && SrcLayer > 0)
GL.FramebufferTexture(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0); {
GL.FramebufferTextureLayer(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0, SrcLayer);
}
else
{
GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0);
}
if (ImageUtils.IsArray(DstImage.TextureTarget) && DstLayer > 0)
{
GL.FramebufferTextureLayer(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0, DstLayer);
}
else
{
GL.FramebufferTexture(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0);
}
BlitFramebufferFilter Filter = BlitFramebufferFilter.Nearest; BlitFramebufferFilter Filter = BlitFramebufferFilter.Nearest;
@ -452,7 +472,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
if (NewImage.Format == OldImage.Format && if (NewImage.Format == OldImage.Format &&
NewImage.Width == OldImage.Width && NewImage.Width == OldImage.Width &&
NewImage.Height == OldImage.Height) NewImage.Height == OldImage.Height &&
NewImage.Depth == OldImage.Depth &&
NewImage.LayerCount == OldImage.LayerCount &&
NewImage.TextureTarget == OldImage.TextureTarget)
{ {
return; return;
} }
@ -477,9 +500,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
(_, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(CachedImage.Format); (_, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(CachedImage.Format);
GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle); TextureTarget Target = ImageUtils.GetTextureTarget(NewImage.TextureTarget);
GL.GetTexImage(TextureTarget.Texture2D, 0, Format, Type, IntPtr.Zero); GL.BindTexture(Target, CachedImage.Handle);
GL.GetTexImage(Target, 0, Format, Type, IntPtr.Zero);
GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyPBO); GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyPBO);

View File

@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
GlslProgram Program; GlslProgram Program;
GlslDecompiler Decompiler = new GlslDecompiler(OGLLimit.MaxUboSize); GlslDecompiler Decompiler = new GlslDecompiler(OGLLimit.MaxUboSize, OGLExtension.NvidiaDrvier);
int ShaderDumpIndex = ShaderDumper.DumpIndex; int ShaderDumpIndex = ShaderDumper.DumpIndex;

View File

@ -38,7 +38,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
int Handle = GL.GenTexture(); int Handle = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, Handle); TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
GL.BindTexture(Target, Handle);
const int Level = 0; //TODO: Support mipmap textures. const int Level = 0; //TODO: Support mipmap textures.
const int Border = 0; const int Border = 0;
@ -54,23 +56,70 @@ namespace Ryujinx.Graphics.Gal.OpenGL
PixelFormat Format, PixelFormat Format,
PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format); PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format);
GL.TexImage2D( switch (Target)
TextureTarget.Texture2D, {
Level, case TextureTarget.Texture1D:
InternalFmt, GL.TexImage1D(
Image.Width, Target,
Image.Height, Level,
Border, InternalFmt,
Format, Image.Width,
Type, Border,
IntPtr.Zero); Format,
Type,
IntPtr.Zero);
break;
case TextureTarget.Texture2D:
GL.TexImage2D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Border,
Format,
Type,
IntPtr.Zero);
break;
case TextureTarget.Texture3D:
GL.TexImage3D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Image.Depth,
Border,
Format,
Type,
IntPtr.Zero);
break;
case TextureTarget.Texture2DArray:
GL.TexImage3D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Image.LayerCount,
Border,
Format,
Type,
IntPtr.Zero);
break;
default:
throw new NotImplementedException($"Unsupported texture target type: {Target}");
}
} }
public void Create(long Key, byte[] Data, GalImage Image) public void Create(long Key, byte[] Data, GalImage Image)
{ {
int Handle = GL.GenTexture(); int Handle = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, Handle); TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
GL.BindTexture(Target, Handle);
const int Level = 0; //TODO: Support mipmap textures. const int Level = 0; //TODO: Support mipmap textures.
const int Border = 0; const int Border = 0;
@ -81,15 +130,56 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format); InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format);
GL.CompressedTexImage2D( switch (Target)
TextureTarget.Texture2D, {
Level, case TextureTarget.Texture1D:
InternalFmt, GL.CompressedTexImage1D(
Image.Width, Target,
Image.Height, Level,
Border, InternalFmt,
Data.Length, Image.Width,
Data); Border,
Data.Length,
Data);
break;
case TextureTarget.Texture2D:
GL.CompressedTexImage2D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Border,
Data.Length,
Data);
break;
case TextureTarget.Texture3D:
GL.CompressedTexImage3D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Image.Depth,
Border,
Data.Length,
Data);
break;
case TextureTarget.Texture2DArray:
GL.CompressedTexImage3D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Image.LayerCount,
Border,
Data.Length,
Data);
break;
default:
throw new NotImplementedException($"Unsupported texture target type: {Target}");
}
} }
else else
{ {
@ -98,13 +188,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
int TextureBlockWidth = ImageUtils.GetBlockWidth(Image.Format); int TextureBlockWidth = ImageUtils.GetBlockWidth(Image.Format);
int TextureBlockHeight = ImageUtils.GetBlockHeight(Image.Format); int TextureBlockHeight = ImageUtils.GetBlockHeight(Image.Format);
int TextureBlockDepth = ImageUtils.GetBlockDepth(Image.Format);
Data = ASTCDecoder.DecodeToRGBA8888( Data = ASTCDecoder.DecodeToRGBA8888(
Data, Data,
TextureBlockWidth, TextureBlockWidth,
TextureBlockHeight, 1, TextureBlockHeight,
TextureBlockDepth,
Image.Width, Image.Width,
Image.Height, 1); Image.Height,
Image.Depth);
Image.Format = GalImageFormat.RGBA8 | (Image.Format & GalImageFormat.TypeMask); Image.Format = GalImageFormat.RGBA8 | (Image.Format & GalImageFormat.TypeMask);
} }
@ -113,16 +206,80 @@ namespace Ryujinx.Graphics.Gal.OpenGL
PixelFormat Format, PixelFormat Format,
PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format); PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format);
GL.TexImage2D(
TextureTarget.Texture2D, switch (Target)
Level, {
InternalFmt, case TextureTarget.Texture1D:
Image.Width, GL.TexImage1D(
Image.Height, Target,
Border, Level,
Format, InternalFmt,
Type, Image.Width,
Data); Border,
Format,
Type,
Data);
break;
case TextureTarget.Texture2D:
GL.TexImage2D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Border,
Format,
Type,
Data);
break;
case TextureTarget.Texture3D:
GL.TexImage3D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Image.Depth,
Border,
Format,
Type,
Data);
break;
case TextureTarget.Texture2DArray:
GL.TexImage3D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Image.LayerCount,
Border,
Format,
Type,
Data);
break;
case TextureTarget.TextureCubeMap:
Span<byte> Array = new Span<byte>(Data);
int FaceSize = ImageUtils.GetSize(Image) / 6;
for (int Face = 0; Face < 6; Face++)
{
GL.TexImage2D(
TextureTarget.TextureCubeMapPositiveX + Face,
Level,
InternalFmt,
Image.Width,
Image.Height,
Border,
Format,
Type,
Array.Slice(Face * FaceSize, FaceSize).ToArray());
}
break;
default:
throw new NotImplementedException($"Unsupported texture target type: {Target}");
}
} }
} }
@ -165,7 +322,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
GL.ActiveTexture(TextureUnit.Texture0 + Index); GL.ActiveTexture(TextureUnit.Texture0 + Index);
GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle); TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
GL.BindTexture(Target, CachedImage.Handle);
int[] SwizzleRgba = new int[] int[] SwizzleRgba = new int[]
{ {
@ -175,23 +334,27 @@ namespace Ryujinx.Graphics.Gal.OpenGL
(int)OGLEnumConverter.GetTextureSwizzle(Image.WSource) (int)OGLEnumConverter.GetTextureSwizzle(Image.WSource)
}; };
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleRgba, SwizzleRgba); GL.TexParameter(Target, TextureParameterName.TextureSwizzleRgba, SwizzleRgba);
} }
} }
public void SetSampler(GalTextureSampler Sampler) public void SetSampler(GalImage Image, GalTextureSampler Sampler)
{ {
int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU); int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU);
int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV); int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV);
int WrapR = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressP);
int MinFilter = (int)OGLEnumConverter.GetTextureMinFilter(Sampler.MinFilter, Sampler.MipFilter); int MinFilter = (int)OGLEnumConverter.GetTextureMinFilter(Sampler.MinFilter, Sampler.MipFilter);
int MagFilter = (int)OGLEnumConverter.GetTextureMagFilter(Sampler.MagFilter); int MagFilter = (int)OGLEnumConverter.GetTextureMagFilter(Sampler.MagFilter);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, WrapS); TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, WrapT);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter); GL.TexParameter(Target, TextureParameterName.TextureWrapS, WrapS);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter); GL.TexParameter(Target, TextureParameterName.TextureWrapT, WrapT);
GL.TexParameter(Target, TextureParameterName.TextureWrapR, WrapR);
GL.TexParameter(Target, TextureParameterName.TextureMinFilter, MinFilter);
GL.TexParameter(Target, TextureParameterName.TextureMagFilter, MagFilter);
float[] Color = new float[] float[] Color = new float[]
{ {
@ -201,7 +364,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Sampler.BorderColor.Alpha Sampler.BorderColor.Alpha
}; };
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBorderColor, Color); GL.TexParameter(Target, TextureParameterName.TextureBorderColor, Color);
if (Sampler.DepthCompare)
{
GL.TexParameter(Target, TextureParameterName.TextureCompareMode, (int)All.CompareRToTexture);
GL.TexParameter(Target, TextureParameterName.TextureCompareFunc, (int)OGLEnumConverter.GetDepthCompareFunc(Sampler.DepthCompareFunc));
}
else
{
GL.TexParameter(Target, TextureParameterName.TextureCompareMode, (int)All.None);
GL.TexParameter(Target, TextureParameterName.TextureCompareFunc, (int)All.Never);
}
} }
} }
} }

View File

@ -1,3 +1,5 @@
using Ryujinx.Graphics.Gal.OpenGL;
using Ryujinx.Graphics.Texture;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -224,6 +226,7 @@ namespace Ryujinx.Graphics.Gal.Shader
if (Op.Inst == ShaderIrInst.Texq || if (Op.Inst == ShaderIrInst.Texq ||
Op.Inst == ShaderIrInst.Texs || Op.Inst == ShaderIrInst.Texs ||
Op.Inst == ShaderIrInst.Tld4 ||
Op.Inst == ShaderIrInst.Txlf) Op.Inst == ShaderIrInst.Txlf)
{ {
int Handle = ((ShaderIrOperImm)Op.OperandC).Value; int Handle = ((ShaderIrOperImm)Op.OperandC).Value;
@ -232,7 +235,25 @@ namespace Ryujinx.Graphics.Gal.Shader
string Name = StagePrefix + TextureName + Index; string Name = StagePrefix + TextureName + Index;
m_Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Handle)); GalTextureTarget TextureTarget;
TextureInstructionSuffix TextureInstructionSuffix;
// TODO: Non 2D texture type for TEXQ?
if (Op.Inst == ShaderIrInst.Texq)
{
TextureTarget = GalTextureTarget.TwoD;
TextureInstructionSuffix = TextureInstructionSuffix.None;
}
else
{
ShaderIrMetaTex Meta = ((ShaderIrMetaTex)Op.MetaData);
TextureTarget = Meta.TextureTarget;
TextureInstructionSuffix = Meta.TextureInstructionSuffix;
}
m_Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Handle, false, 0, 1, TextureTarget, TextureInstructionSuffix));
} }
else if (Op.Inst == ShaderIrInst.Texb) else if (Op.Inst == ShaderIrInst.Texb)
{ {
@ -257,9 +278,10 @@ namespace Ryujinx.Graphics.Gal.Shader
if (HandleSrc != null && HandleSrc is ShaderIrOperCbuf Cbuf) if (HandleSrc != null && HandleSrc is ShaderIrOperCbuf Cbuf)
{ {
ShaderIrMetaTex Meta = ((ShaderIrMetaTex)Op.MetaData);
string Name = StagePrefix + TextureName + "_cb" + Cbuf.Index + "_" + Cbuf.Pos; string Name = StagePrefix + TextureName + "_cb" + Cbuf.Index + "_" + Cbuf.Pos;
m_CbTextures.Add(Op, new ShaderDeclInfo(Name, Cbuf.Pos, true, Cbuf.Index)); m_CbTextures.Add(Op, new ShaderDeclInfo(Name, Cbuf.Pos, true, Cbuf.Index, 1, Meta.TextureTarget, Meta.TextureInstructionSuffix));
} }
else else
{ {

View File

@ -1,3 +1,5 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.Texture;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@ -33,7 +35,9 @@ namespace Ryujinx.Graphics.Gal.Shader
public int MaxUboSize { get; } public int MaxUboSize { get; }
public GlslDecompiler(int MaxUboSize) private bool IsNvidiaDriver;
public GlslDecompiler(int MaxUboSize, bool IsNvidiaDriver)
{ {
InstsExpr = new Dictionary<ShaderIrInst, GetInstExpr>() InstsExpr = new Dictionary<ShaderIrInst, GetInstExpr>()
{ {
@ -103,6 +107,7 @@ namespace Ryujinx.Graphics.Gal.Shader
{ ShaderIrInst.Texb, GetTexbExpr }, { ShaderIrInst.Texb, GetTexbExpr },
{ ShaderIrInst.Texq, GetTexqExpr }, { ShaderIrInst.Texq, GetTexqExpr },
{ ShaderIrInst.Texs, GetTexsExpr }, { ShaderIrInst.Texs, GetTexsExpr },
{ ShaderIrInst.Tld4, GetTld4Expr },
{ ShaderIrInst.Trunc, GetTruncExpr }, { ShaderIrInst.Trunc, GetTruncExpr },
{ ShaderIrInst.Txlf, GetTxlfExpr }, { ShaderIrInst.Txlf, GetTxlfExpr },
{ ShaderIrInst.Utof, GetUtofExpr }, { ShaderIrInst.Utof, GetUtofExpr },
@ -110,6 +115,7 @@ namespace Ryujinx.Graphics.Gal.Shader
}; };
this.MaxUboSize = MaxUboSize / 16; this.MaxUboSize = MaxUboSize / 16;
this.IsNvidiaDriver = IsNvidiaDriver;
} }
public GlslProgram Decompile( public GlslProgram Decompile(
@ -219,14 +225,70 @@ namespace Ryujinx.Graphics.Gal.Shader
} }
} }
private string GetSamplerType(TextureTarget TextureTarget, bool HasShadow)
{
string Result;
switch (TextureTarget)
{
case TextureTarget.Texture1D:
Result = "sampler1D";
break;
case TextureTarget.Texture2D:
Result = "sampler2D";
break;
case TextureTarget.Texture3D:
Result = "sampler3D";
break;
case TextureTarget.TextureCubeMap:
Result = "samplerCube";
break;
case TextureTarget.TextureRectangle:
Result = "sampler2DRect";
break;
case TextureTarget.Texture1DArray:
Result = "sampler1DArray";
break;
case TextureTarget.Texture2DArray:
Result = "sampler2DArray";
break;
case TextureTarget.TextureCubeMapArray:
Result = "samplerCubeArray";
break;
case TextureTarget.TextureBuffer:
Result = "samplerBuffer";
break;
case TextureTarget.Texture2DMultisample:
Result = "sampler2DMS";
break;
case TextureTarget.Texture2DMultisampleArray:
Result = "sampler2DMSArray";
break;
default:
throw new NotSupportedException();
}
if (HasShadow)
Result += "Shadow";
return Result;
}
private void PrintDeclTextures() private void PrintDeclTextures()
{ {
foreach (ShaderDeclInfo DeclInfo in IterateCbTextures()) foreach (ShaderDeclInfo DeclInfo in IterateCbTextures())
{ {
SB.AppendLine("uniform sampler2D " + DeclInfo.Name + ";"); TextureTarget Target = ImageUtils.GetTextureTarget(DeclInfo.TextureTarget);
SB.AppendLine($"// {DeclInfo.TextureSuffix}");
SB.AppendLine("uniform " + GetSamplerType(Target, (DeclInfo.TextureSuffix & TextureInstructionSuffix.DC) != 0) + " " + DeclInfo.Name + ";");
} }
PrintDecls(Decl.Textures, "uniform sampler2D"); foreach (ShaderDeclInfo DeclInfo in Decl.Textures.Values.OrderBy(DeclKeySelector))
{
TextureTarget Target = ImageUtils.GetTextureTarget(DeclInfo.TextureTarget);
SB.AppendLine($"// {DeclInfo.TextureSuffix}");
SB.AppendLine("uniform " + GetSamplerType(Target, (DeclInfo.TextureSuffix & TextureInstructionSuffix.DC) != 0) + " " + DeclInfo.Name + ";");
}
} }
private IEnumerable<ShaderDeclInfo> IterateCbTextures() private IEnumerable<ShaderDeclInfo> IterateCbTextures()
@ -778,6 +840,7 @@ namespace Ryujinx.Graphics.Gal.Shader
case ShaderIrInst.Ipa: case ShaderIrInst.Ipa:
case ShaderIrInst.Texq: case ShaderIrInst.Texq:
case ShaderIrInst.Texs: case ShaderIrInst.Texs:
case ShaderIrInst.Tld4:
case ShaderIrInst.Txlf: case ShaderIrInst.Txlf:
return false; return false;
} }
@ -1124,7 +1187,7 @@ namespace Ryujinx.Graphics.Gal.Shader
string Ch = "rgba".Substring(Meta.Elem, 1); string Ch = "rgba".Substring(Meta.Elem, 1);
return "texture(" + DeclInfo.Name + ", " + Coords + ")." + Ch; return GetTextureOperation(Op, DeclInfo.Name, Coords, Ch);
} }
private string GetTexqExpr(ShaderIrOp Op) private string GetTexqExpr(ShaderIrOp Op)
@ -1157,20 +1220,50 @@ namespace Ryujinx.Graphics.Gal.Shader
string Ch = "rgba".Substring(Meta.Elem, 1); string Ch = "rgba".Substring(Meta.Elem, 1);
return "texture(" + Sampler + ", " + Coords + ")." + Ch; return GetTextureOperation(Op, Sampler, Coords, Ch);
} }
private string GetTxlfExpr(ShaderIrOp Op) private string GetTld4Expr(ShaderIrOp Op)
{ {
ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
string Sampler = GetTexSamplerName(Op); string Sampler = GetTexSamplerName(Op);
string Coords = GetTexSamplerCoords(Op);
string Ch = "rgba".Substring(Meta.Elem, 1);
return GetTextureGatherOperation(Op, Sampler, Coords, Ch);
}
// TODO: support AOFFI on non nvidia drivers
private string GetTxlfExpr(ShaderIrOp Op)
{
// TODO: Support all suffixes
ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
TextureInstructionSuffix Suffix = Meta.TextureInstructionSuffix;
string Sampler = GetTexSamplerName(Op);
string Coords = GetITexSamplerCoords(Op); string Coords = GetITexSamplerCoords(Op);
string Ch = "rgba".Substring(Meta.Elem, 1); string Ch = "rgba".Substring(Meta.Elem, 1);
return "texelFetch(" + Sampler + ", " + Coords + ", 0)." + Ch; string Lod = "0";
if (Meta.LevelOfDetail != null)
{
Lod = GetOperExpr(Op, Meta.LevelOfDetail);
}
if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver)
{
string Offset = GetTextureOffset(Meta, GetOperExpr(Op, Meta.Offset));
return "texelFetchOffset(" + Sampler + ", " + Coords + ", " + Lod + ", " + Offset + ")." + Ch;
}
return "texelFetch(" + Sampler + ", " + Coords + ", " + Lod + ")." + Ch;
} }
private string GetTruncExpr(ShaderIrOp Op) => GetUnaryCall(Op, "trunc"); private string GetTruncExpr(ShaderIrOp Op) => GetUnaryCall(Op, "trunc");
@ -1246,14 +1339,205 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetTexSamplerCoords(ShaderIrOp Op) private string GetTexSamplerCoords(ShaderIrOp Op)
{ {
return "vec2(" + GetOperExpr(Op, Op.OperandA) + ", " + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
GetOperExpr(Op, Op.OperandB) + ")";
bool HasDepth = (Meta.TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0;
int Coords = ImageUtils.GetCoordsCountTextureTarget(Meta.TextureTarget);
bool IsArray = ImageUtils.IsArray(Meta.TextureTarget);
string GetLastArgument(ShaderIrNode Node)
{
string Result = GetOperExpr(Op, Node);
// array index is actually an integer so we need to pass it correctly
if (IsArray)
{
Result = "float(floatBitsToInt(" + Result + "))";
}
return Result;
}
string LastArgument;
string DepthArgument = "";
int VecSize = Coords;
if (HasDepth && Op.Inst != ShaderIrInst.Tld4)
{
VecSize++;
DepthArgument = $", {GetOperExpr(Op, Meta.DepthCompare)}";
}
switch (Coords)
{
case 1:
if (HasDepth)
{
return $"vec3({GetOperExpr(Op, Meta.Coordinates[0])}, 0.0{DepthArgument})";
}
return GetOperExpr(Op, Meta.Coordinates[0]);
case 2:
LastArgument = GetLastArgument(Meta.Coordinates[1]);
return $"vec{VecSize}({GetOperExpr(Op, Meta.Coordinates[0])}, {LastArgument}{DepthArgument})";
case 3:
LastArgument = GetLastArgument(Meta.Coordinates[2]);
return $"vec{VecSize}({GetOperExpr(Op, Meta.Coordinates[0])}, {GetOperExpr(Op, Meta.Coordinates[1])}, {LastArgument}{DepthArgument})";
case 4:
LastArgument = GetLastArgument(Meta.Coordinates[3]);
return $"vec4({GetOperExpr(Op, Meta.Coordinates[0])}, {GetOperExpr(Op, Meta.Coordinates[1])}, {GetOperExpr(Op, Meta.Coordinates[2])}, {LastArgument}){DepthArgument}";
default:
throw new InvalidOperationException();
}
}
private string GetTextureOffset(ShaderIrMetaTex Meta, string Oper, int Shift = 4, int Mask = 0xF)
{
string GetOffset(string Operation, int Index)
{
return $"({Operation} >> {Index * Shift}) & 0x{Mask:x}";
}
int Coords = ImageUtils.GetCoordsCountTextureTarget(Meta.TextureTarget);
if (ImageUtils.IsArray(Meta.TextureTarget))
Coords -= 1;
switch (Coords)
{
case 1:
return GetOffset(Oper, 0);
case 2:
return "ivec2(" + GetOffset(Oper, 0) + ", " + GetOffset(Oper, 1) + ")";
case 3:
return "ivec3(" + GetOffset(Oper, 0) + ", " + GetOffset(Oper, 1) + ", " + GetOffset(Oper, 2) + ")";
case 4:
return "ivec4(" + GetOffset(Oper, 0) + ", " + GetOffset(Oper, 1) + ", " + GetOffset(Oper, 2) + ", " + GetOffset(Oper, 3) + ")";
default:
throw new InvalidOperationException();
}
}
// TODO: support AOFFI on non nvidia drivers
private string GetTextureGatherOperation(ShaderIrOp Op, string Sampler, string Coords, string Ch)
{
ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
TextureInstructionSuffix Suffix = Meta.TextureInstructionSuffix;
string ChString = "." + Ch;
string Comp = Meta.Component.ToString();
if ((Suffix & TextureInstructionSuffix.DC) != 0)
{
Comp = GetOperExpr(Op, Meta.DepthCompare);
}
if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver)
{
string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))", 8, 0x3F);
if ((Suffix & TextureInstructionSuffix.DC) != 0)
{
return "textureGatherOffset(" + Sampler + ", " + Coords + ", " + Comp + ", " + Offset + ")" + ChString;
}
return "textureGatherOffset(" + Sampler + ", " + Coords + ", " + Offset + ", " + Comp + ")" + ChString;
}
// TODO: Support PTP
else if ((Suffix & TextureInstructionSuffix.PTP) != 0)
{
throw new NotImplementedException();
}
return "textureGather(" + Sampler + ", " + Coords + ", " + Comp + ")" + ChString;
}
// TODO: support AOFFI on non nvidia drivers
private string GetTextureOperation(ShaderIrOp Op, string Sampler, string Coords, string Ch)
{
ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
TextureInstructionSuffix Suffix = Meta.TextureInstructionSuffix;
string ChString = "." + Ch;
if ((Suffix & TextureInstructionSuffix.DC) != 0)
{
ChString = "";
}
// TODO: Support LBA and LLA
if ((Suffix & TextureInstructionSuffix.LZ) != 0)
{
if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver)
{
string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))");
return "textureLodOffset(" + Sampler + ", " + Coords + ", 0.0, " + Offset + ")" + ChString;
}
return "textureLod(" + Sampler + ", " + Coords + ", 0.0)" + ChString;
}
else if ((Suffix & TextureInstructionSuffix.LB) != 0)
{
if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver)
{
string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))");
return "textureOffset(" + Sampler + ", " + Coords + ", " + Offset + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ")" + ChString;
}
return "texture(" + Sampler + ", " + Coords + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ")" + ChString;
}
else if ((Suffix & TextureInstructionSuffix.LL) != 0)
{
if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver)
{
string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))");
return "textureLodOffset(" + Sampler + ", " + Coords + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ", " + Offset + ")" + ChString;
}
return "textureLod(" + Sampler + ", " + Coords + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ")" + ChString;
}
else if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver)
{
string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))");
return "textureOffset(" + Sampler + ", " + Coords + ", " + Offset + ")" + ChString;
}
else
{
return "texture(" + Sampler + ", " + Coords + ")" + ChString;
}
throw new NotImplementedException($"Texture Suffix {Meta.TextureInstructionSuffix} is not implemented");
} }
private string GetITexSamplerCoords(ShaderIrOp Op) private string GetITexSamplerCoords(ShaderIrOp Op)
{ {
return "ivec2(" + GetOperExpr(Op, Op.OperandA) + ", " + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
GetOperExpr(Op, Op.OperandB) + ")";
switch (ImageUtils.GetCoordsCountTextureTarget(Meta.TextureTarget))
{
case 1:
return GetOperExpr(Op, Meta.Coordinates[0]);
case 2:
return "ivec2(" + GetOperExpr(Op, Meta.Coordinates[0]) + ", " + GetOperExpr(Op, Meta.Coordinates[1]) + ")";
case 3:
return "ivec3(" + GetOperExpr(Op, Meta.Coordinates[0]) + ", " + GetOperExpr(Op, Meta.Coordinates[1]) + ", " + GetOperExpr(Op, Meta.Coordinates[2]) + ")";
default:
throw new InvalidOperationException();
}
} }
private string GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper) private string GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper)
@ -1292,22 +1576,6 @@ namespace Ryujinx.Graphics.Gal.Shader
} }
break; break;
} }
case ShaderIrOperImm Imm:
{
//For integer immediates being used as float,
//it's better (for readability) to just return the float value.
if (DstType == OperType.F32)
{
float Value = BitConverter.Int32BitsToSingle(Imm.Value);
if (!float.IsNaN(Value) && !float.IsInfinity(Value))
{
return GetFloatConst(Value);
}
}
break;
}
} }
switch (DstType) switch (DstType)

View File

@ -1,3 +1,4 @@
using Ryujinx.Graphics.Texture;
using System; using System;
using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper; using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
@ -29,6 +30,75 @@ namespace Ryujinx.Graphics.Gal.Shader
{ RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ } { RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ }
}; };
private static GalTextureTarget TexToTextureTarget(int TexType, bool IsArray)
{
switch (TexType)
{
case 0:
return IsArray ? GalTextureTarget.OneDArray : GalTextureTarget.OneD;
case 2:
return IsArray ? GalTextureTarget.TwoDArray : GalTextureTarget.TwoD;
case 4:
if (IsArray)
throw new InvalidOperationException($"ARRAY bit set on a TEX with 3D texture!");
return GalTextureTarget.ThreeD;
case 6:
return IsArray ? GalTextureTarget.CubeArray : GalTextureTarget.CubeMap;
default:
throw new InvalidOperationException();
}
}
private static GalTextureTarget TexsToTextureTarget(int TexType)
{
switch (TexType)
{
case 0:
return GalTextureTarget.OneD;
case 2:
case 4:
case 6:
case 8:
case 0xa:
case 0xc:
return GalTextureTarget.TwoD;
case 0xe:
case 0x10:
case 0x12:
return GalTextureTarget.TwoDArray;
case 0x14:
case 0x16:
return GalTextureTarget.ThreeD;
case 0x18:
case 0x1a:
return GalTextureTarget.CubeMap;
default:
throw new InvalidOperationException();
}
}
public static GalTextureTarget TldsToTextureTarget(int TexType)
{
switch (TexType)
{
case 0:
case 2:
return GalTextureTarget.OneD;
case 4:
case 8:
case 0xa:
case 0xc:
case 0x18:
return GalTextureTarget.TwoD;
case 0x10:
return GalTextureTarget.TwoDArray;
case 0xe:
return GalTextureTarget.ThreeD;
default:
throw new InvalidOperationException();
}
}
public static void Ld_A(ShaderIrBlock Block, long OpCode, int Position) public static void Ld_A(ShaderIrBlock Block, long OpCode, int Position)
{ {
ShaderIrNode[] Opers = OpCode.Abuf20(); ShaderIrNode[] Opers = OpCode.Abuf20();
@ -132,43 +202,166 @@ namespace Ryujinx.Graphics.Gal.Shader
public static void Tex(ShaderIrBlock Block, long OpCode, int Position) public static void Tex(ShaderIrBlock Block, long OpCode, int Position)
{ {
EmitTex(Block, OpCode, GprHandle: false); TextureInstructionSuffix Suffix;
int RawSuffix = OpCode.Read(0x34, 0x38);
switch (RawSuffix)
{
case 0:
Suffix = TextureInstructionSuffix.None;
break;
case 0x8:
Suffix = TextureInstructionSuffix.LZ;
break;
case 0x10:
Suffix = TextureInstructionSuffix.LB;
break;
case 0x18:
Suffix = TextureInstructionSuffix.LL;
break;
case 0x30:
Suffix = TextureInstructionSuffix.LBA;
break;
case 0x38:
Suffix = TextureInstructionSuffix.LLA;
break;
default:
throw new InvalidOperationException($"Invalid Suffix for TEX instruction {RawSuffix}");
}
bool IsOffset = OpCode.Read(0x36);
if (IsOffset)
Suffix |= TextureInstructionSuffix.AOffI;
EmitTex(Block, OpCode, Suffix, GprHandle: false);
} }
public static void Tex_B(ShaderIrBlock Block, long OpCode, int Position) public static void Tex_B(ShaderIrBlock Block, long OpCode, int Position)
{ {
EmitTex(Block, OpCode, GprHandle: true); TextureInstructionSuffix Suffix;
int RawSuffix = OpCode.Read(0x24, 0xe);
switch (RawSuffix)
{
case 0:
Suffix = TextureInstructionSuffix.None;
break;
case 0x2:
Suffix = TextureInstructionSuffix.LZ;
break;
case 0x4:
Suffix = TextureInstructionSuffix.LB;
break;
case 0x6:
Suffix = TextureInstructionSuffix.LL;
break;
case 0xc:
Suffix = TextureInstructionSuffix.LBA;
break;
case 0xe:
Suffix = TextureInstructionSuffix.LLA;
break;
default:
throw new InvalidOperationException($"Invalid Suffix for TEX.B instruction {RawSuffix}");
}
bool IsOffset = OpCode.Read(0x23);
if (IsOffset)
Suffix |= TextureInstructionSuffix.AOffI;
EmitTex(Block, OpCode, Suffix, GprHandle: true);
} }
private static void EmitTex(ShaderIrBlock Block, long OpCode, bool GprHandle) private static void EmitTex(ShaderIrBlock Block, long OpCode, TextureInstructionSuffix TextureInstructionSuffix, bool GprHandle)
{ {
//TODO: Support other formats. bool IsArray = OpCode.HasArray();
ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[2];
for (int Index = 0; Index < Coords.Length; Index++) GalTextureTarget TextureTarget = TexToTextureTarget(OpCode.Read(28, 6), IsArray);
bool HasDepthCompare = OpCode.Read(0x32);
if (HasDepthCompare)
{
TextureInstructionSuffix |= TextureInstructionSuffix.DC;
}
ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(TextureTarget)];
int IndexExtraCoord = 0;
if (IsArray)
{
IndexExtraCoord++;
Coords[Coords.Length - 1] = OpCode.Gpr8();
}
for (int Index = 0; Index < Coords.Length - IndexExtraCoord; Index++)
{ {
ShaderIrOperGpr CoordReg = OpCode.Gpr8(); ShaderIrOperGpr CoordReg = OpCode.Gpr8();
CoordReg.Index += Index; CoordReg.Index += Index;
CoordReg.Index += IndexExtraCoord;
if (!CoordReg.IsValidRegister) if (!CoordReg.IsValidRegister)
{ {
CoordReg.Index = ShaderIrOperGpr.ZRIndex; CoordReg.Index = ShaderIrOperGpr.ZRIndex;
} }
Coords[Index] = ShaderIrOperGpr.MakeTemporary(Index); Coords[Index] = CoordReg;
Block.AddNode(new ShaderIrAsg(Coords[Index], CoordReg));
} }
int ChMask = OpCode.Read(31, 0xf); int ChMask = OpCode.Read(31, 0xf);
ShaderIrOperGpr LevelOfDetail = null;
ShaderIrOperGpr Offset = null;
ShaderIrOperGpr DepthCompare = null;
// TODO: determine first argument when TEX.B is used
int OperBIndex = GprHandle ? 1 : 0;
if ((TextureInstructionSuffix & TextureInstructionSuffix.LL) != 0 ||
(TextureInstructionSuffix & TextureInstructionSuffix.LB) != 0 ||
(TextureInstructionSuffix & TextureInstructionSuffix.LBA) != 0 ||
(TextureInstructionSuffix & TextureInstructionSuffix.LLA) != 0)
{
LevelOfDetail = OpCode.Gpr20();
LevelOfDetail.Index += OperBIndex;
OperBIndex++;
}
if ((TextureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0)
{
Offset = OpCode.Gpr20();
Offset.Index += OperBIndex;
OperBIndex++;
}
if ((TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0)
{
DepthCompare = OpCode.Gpr20();
DepthCompare.Index += OperBIndex;
OperBIndex++;
}
// ???
ShaderIrNode OperC = GprHandle ShaderIrNode OperC = GprHandle
? (ShaderIrNode)OpCode.Gpr20() ? (ShaderIrNode)OpCode.Gpr20()
: (ShaderIrNode)OpCode.Imm13_36(); : (ShaderIrNode)OpCode.Imm13_36();
ShaderIrInst Inst = GprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs; ShaderIrInst Inst = GprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs;
Coords = CoordsRegistersToTempRegisters(Block, Coords);
int RegInc = 0; int RegInc = 0;
for (int Ch = 0; Ch < 4; Ch++) for (int Ch = 0; Ch < 4; Ch++)
@ -187,9 +380,14 @@ namespace Ryujinx.Graphics.Gal.Shader
continue; continue;
} }
ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch, TextureTarget, TextureInstructionSuffix, Coords)
{
LevelOfDetail = LevelOfDetail,
Offset = Offset,
DepthCompare = DepthCompare
};
ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords[1], OperC, Meta); ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords.Length > 1 ? Coords[1] : null, OperC, Meta);
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op))); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op)));
} }
@ -197,17 +395,238 @@ namespace Ryujinx.Graphics.Gal.Shader
public static void Texs(ShaderIrBlock Block, long OpCode, int Position) public static void Texs(ShaderIrBlock Block, long OpCode, int Position)
{ {
EmitTexs(Block, OpCode, ShaderIrInst.Texs); TextureInstructionSuffix Suffix;
int RawSuffix = OpCode.Read(0x34, 0x1e);
switch (RawSuffix)
{
case 0:
case 0x4:
case 0x10:
case 0x16:
Suffix = TextureInstructionSuffix.LZ;
break;
case 0x6:
case 0x1a:
Suffix = TextureInstructionSuffix.LL;
break;
case 0x8:
Suffix = TextureInstructionSuffix.DC;
break;
case 0x2:
case 0xe:
case 0x14:
case 0x18:
Suffix = TextureInstructionSuffix.None;
break;
case 0xa:
Suffix = TextureInstructionSuffix.LL | TextureInstructionSuffix.DC;
break;
case 0xc:
case 0x12:
Suffix = TextureInstructionSuffix.LZ | TextureInstructionSuffix.DC;
break;
default:
throw new InvalidOperationException($"Invalid Suffix for TEXS instruction {RawSuffix}");
}
GalTextureTarget TextureTarget = TexsToTextureTarget(OpCode.Read(52, 0x1e));
EmitTexs(Block, OpCode, ShaderIrInst.Texs, TextureTarget, Suffix);
} }
public static void Tlds(ShaderIrBlock Block, long OpCode, int Position) public static void Tlds(ShaderIrBlock Block, long OpCode, int Position)
{ {
EmitTexs(Block, OpCode, ShaderIrInst.Txlf); TextureInstructionSuffix Suffix;
int RawSuffix = OpCode.Read(0x34, 0x1e);
switch (RawSuffix)
{
case 0:
case 0x4:
case 0x8:
Suffix = TextureInstructionSuffix.LZ | TextureInstructionSuffix.AOffI;
break;
case 0xc:
Suffix = TextureInstructionSuffix.LZ | TextureInstructionSuffix.MZ;
break;
case 0xe:
case 0x10:
Suffix = TextureInstructionSuffix.LZ;
break;
case 0x2:
case 0xa:
Suffix = TextureInstructionSuffix.LL;
break;
case 0x18:
Suffix = TextureInstructionSuffix.LL | TextureInstructionSuffix.AOffI;
break;
default:
throw new InvalidOperationException($"Invalid Suffix for TLDS instruction {RawSuffix}");
}
GalTextureTarget TextureTarget = TldsToTextureTarget(OpCode.Read(52, 0x1e));
EmitTexs(Block, OpCode, ShaderIrInst.Txlf, TextureTarget, Suffix);
} }
private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) public static void Tld4(ShaderIrBlock Block, long OpCode, int Position)
{ {
//TODO: Support other formats. TextureInstructionSuffix Suffix;
int RawSuffix = OpCode.Read(0x34, 0xc);
switch (RawSuffix)
{
case 0:
Suffix = TextureInstructionSuffix.None;
break;
case 0x4:
Suffix = TextureInstructionSuffix.AOffI;
break;
case 0x8:
Suffix = TextureInstructionSuffix.PTP;
break;
default:
throw new InvalidOperationException($"Invalid Suffix for TLD4 instruction {RawSuffix}");
}
bool IsShadow = OpCode.Read(0x32);
bool IsArray = OpCode.HasArray();
int ChMask = OpCode.Read(31, 0xf);
GalTextureTarget TextureTarget = TexToTextureTarget(OpCode.Read(28, 6), IsArray);
if (IsShadow)
{
Suffix |= TextureInstructionSuffix.DC;
}
EmitTld4(Block, OpCode, TextureTarget, Suffix, ChMask, OpCode.Read(0x38, 0x3), false);
}
public static void Tld4s(ShaderIrBlock Block, long OpCode, int Position)
{
TextureInstructionSuffix Suffix = TextureInstructionSuffix.None;
bool IsOffset = OpCode.Read(0x33);
bool IsShadow = OpCode.Read(0x32);
if (IsOffset)
{
Suffix |= TextureInstructionSuffix.AOffI;
}
if (IsShadow)
{
Suffix |= TextureInstructionSuffix.DC;
}
// TLD4S seems to only support 2D textures with RGBA mask?
EmitTld4(Block, OpCode, GalTextureTarget.TwoD, Suffix, RGBA, OpCode.Read(0x34, 0x3), true);
}
private static void EmitTexs(ShaderIrBlock Block,
long OpCode,
ShaderIrInst Inst,
GalTextureTarget TextureTarget,
TextureInstructionSuffix TextureInstructionSuffix)
{
if (Inst == ShaderIrInst.Txlf && TextureTarget == GalTextureTarget.CubeArray)
{
throw new InvalidOperationException("TLDS instructions cannot use CUBE modifier!");
}
bool IsArray = ImageUtils.IsArray(TextureTarget);
ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(TextureTarget)];
ShaderIrOperGpr OperA = OpCode.Gpr8();
ShaderIrOperGpr OperB = OpCode.Gpr20();
ShaderIrOperGpr SuffixExtra = OpCode.Gpr20();
SuffixExtra.Index += 1;
int CoordStartIndex = 0;
if (IsArray)
{
CoordStartIndex++;
Coords[Coords.Length - 1] = OpCode.Gpr8();
}
switch (Coords.Length - CoordStartIndex)
{
case 1:
Coords[0] = OpCode.Gpr8();
break;
case 2:
Coords[0] = OpCode.Gpr8();
Coords[0].Index += CoordStartIndex;
break;
case 3:
Coords[0] = OpCode.Gpr8();
Coords[0].Index += CoordStartIndex;
Coords[1] = OpCode.Gpr8();
Coords[1].Index += 1 + CoordStartIndex;
break;
default:
throw new NotSupportedException($"{Coords.Length - CoordStartIndex} coords textures aren't supported in TEXS");
}
int OperBIndex = 0;
ShaderIrOperGpr LevelOfDetail = null;
ShaderIrOperGpr Offset = null;
ShaderIrOperGpr DepthCompare = null;
// OperB is always the last value
// Not applicable to 1d textures
if (Coords.Length - CoordStartIndex != 1)
{
Coords[Coords.Length - CoordStartIndex - 1] = OperB;
OperBIndex++;
}
// Encoding of TEXS/TLDS is a bit special and change for 2d textures
// NOTE: OperA seems to hold at best two args.
// On 2D textures, if no suffix need an additional values, Y is stored in OperB, otherwise coords are in OperA and the additional values is in OperB.
if (TextureInstructionSuffix != TextureInstructionSuffix.None && TextureInstructionSuffix != TextureInstructionSuffix.LZ && TextureTarget == GalTextureTarget.TwoD)
{
Coords[Coords.Length - CoordStartIndex - 1] = OpCode.Gpr8();
Coords[Coords.Length - CoordStartIndex - 1].Index += Coords.Length - CoordStartIndex - 1;
OperBIndex--;
}
// TODO: Find what MZ does and what changes about the encoding (Maybe Multisample?)
if ((TextureInstructionSuffix & TextureInstructionSuffix.LL) != 0)
{
LevelOfDetail = OpCode.Gpr20();
LevelOfDetail.Index += OperBIndex;
OperBIndex++;
}
if ((TextureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0)
{
Offset = OpCode.Gpr20();
Offset.Index += OperBIndex;
OperBIndex++;
}
if ((TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0)
{
DepthCompare = OpCode.Gpr20();
DepthCompare.Index += OperBIndex;
OperBIndex++;
}
int LutIndex; int LutIndex;
LutIndex = !OpCode.Gpr0().IsConst ? 1 : 0; LutIndex = !OpCode.Gpr0().IsConst ? 1 : 0;
@ -276,12 +695,7 @@ namespace Ryujinx.Graphics.Gal.Shader
} }
ShaderIrNode OperC = OpCode.Imm13_36(); ShaderIrNode OperC = OpCode.Imm13_36();
Coords = CoordsRegistersToTempRegisters(Block, Coords);
ShaderIrOperGpr Coord0 = ShaderIrOperGpr.MakeTemporary(0);
ShaderIrOperGpr Coord1 = ShaderIrOperGpr.MakeTemporary(1);
Block.AddNode(new ShaderIrAsg(Coord0, OpCode.Gpr8()));
Block.AddNode(new ShaderIrAsg(Coord1, OpCode.Gpr20()));
for (int Ch = 0; Ch < 4; Ch++) for (int Ch = 0; Ch < 4; Ch++)
{ {
@ -290,9 +704,13 @@ namespace Ryujinx.Graphics.Gal.Shader
continue; continue;
} }
ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch, TextureTarget, TextureInstructionSuffix, Coords)
{
ShaderIrOp Op = new ShaderIrOp(Inst, Coord0, Coord1, OperC, Meta); LevelOfDetail = LevelOfDetail,
Offset = Offset,
DepthCompare = DepthCompare
};
ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta);
ShaderIrOperGpr Dst = GetDst(); ShaderIrOperGpr Dst = GetDst();
@ -303,9 +721,156 @@ namespace Ryujinx.Graphics.Gal.Shader
} }
} }
private static void EmitTld4(ShaderIrBlock Block, long OpCode, GalTextureTarget TextureType, TextureInstructionSuffix TextureInstructionSuffix, int ChMask, int Component, bool Scalar)
{
ShaderIrOperGpr OperA = OpCode.Gpr8();
ShaderIrOperGpr OperB = OpCode.Gpr20();
ShaderIrOperImm OperC = OpCode.Imm13_36();
ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(TextureType)];
ShaderIrOperGpr Offset = null;
ShaderIrOperGpr DepthCompare = null;
bool IsArray = ImageUtils.IsArray(TextureType);
int OperBIndex = 0;
if (Scalar)
{
int CoordStartIndex = 0;
if (IsArray)
{
CoordStartIndex++;
Coords[Coords.Length - 1] = OperB;
}
switch (Coords.Length - CoordStartIndex)
{
case 1:
Coords[0] = OpCode.Gpr8();
break;
case 2:
Coords[0] = OpCode.Gpr8();
Coords[0].Index += CoordStartIndex;
break;
case 3:
Coords[0] = OpCode.Gpr8();
Coords[0].Index += CoordStartIndex;
Coords[1] = OpCode.Gpr8();
Coords[1].Index += 1 + CoordStartIndex;
break;
default:
throw new NotSupportedException($"{Coords.Length - CoordStartIndex} coords textures aren't supported in TLD4S");
}
if (Coords.Length - CoordStartIndex != 1)
{
Coords[Coords.Length - CoordStartIndex - 1] = OperB;
OperBIndex++;
}
if (TextureInstructionSuffix != TextureInstructionSuffix.None && TextureType == GalTextureTarget.TwoD)
{
Coords[Coords.Length - CoordStartIndex - 1] = OpCode.Gpr8();
Coords[Coords.Length - CoordStartIndex - 1].Index += Coords.Length - CoordStartIndex - 1;
OperBIndex--;
}
}
else
{
int IndexExtraCoord = 0;
if (IsArray)
{
IndexExtraCoord++;
Coords[Coords.Length - 1] = OpCode.Gpr8();
}
for (int Index = 0; Index < Coords.Length - IndexExtraCoord; Index++)
{
Coords[Index] = OpCode.Gpr8();
Coords[Index].Index += Index;
Coords[Index].Index += IndexExtraCoord;
if (Coords[Index].Index > ShaderIrOperGpr.ZRIndex)
{
Coords[Index].Index = ShaderIrOperGpr.ZRIndex;
}
}
}
if ((TextureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0)
{
Offset = OpCode.Gpr20();
Offset.Index += OperBIndex;
OperBIndex++;
}
if ((TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0)
{
DepthCompare = OpCode.Gpr20();
DepthCompare.Index += OperBIndex;
OperBIndex++;
}
Coords = CoordsRegistersToTempRegisters(Block, Coords);
int RegInc = 0;
for (int Ch = 0; Ch < 4; Ch++)
{
if (!IsChannelUsed(ChMask, Ch))
{
continue;
}
ShaderIrOperGpr Dst = OpCode.Gpr0();
Dst.Index += RegInc++;
if (!Dst.IsValidRegister || Dst.IsConst)
{
continue;
}
ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch, TextureType, TextureInstructionSuffix, Coords)
{
Component = Component,
Offset = Offset,
DepthCompare = DepthCompare
};
ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Tld4, OperA, OperB, OperC, Meta);
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op)));
}
}
private static bool IsChannelUsed(int ChMask, int Ch) private static bool IsChannelUsed(int ChMask, int Ch)
{ {
return (ChMask & (1 << Ch)) != 0; return (ChMask & (1 << Ch)) != 0;
} }
private static ShaderIrOperGpr[] CoordsRegistersToTempRegisters(ShaderIrBlock Block, params ShaderIrOperGpr[] Registers)
{
ShaderIrOperGpr[] Res = new ShaderIrOperGpr[Registers.Length];
for (int Index = 0; Index < Res.Length; Index++)
{
Res[Index] = ShaderIrOperGpr.MakeTemporary(Index);
Block.AddNode(new ShaderIrAsg(Res[Index], Registers[Index]));
}
return Res;
}
} }
} }

View File

@ -19,6 +19,11 @@ namespace Ryujinx.Graphics.Gal.Shader
return ((int)(OpCode >> 20) << 8) >> 8; return ((int)(OpCode >> 20) << 8) >> 8;
} }
private static bool HasArray(this long OpCode)
{
return OpCode.Read(0x1c);
}
private static ShaderIrOperAbuf[] Abuf20(this long OpCode) private static ShaderIrOperAbuf[] Abuf20(this long OpCode)
{ {
int Abuf = OpCode.Read(20, 0x3ff); int Abuf = OpCode.Read(20, 0x3ff);

View File

@ -49,6 +49,7 @@ namespace Ryujinx.Graphics.Gal.Shader
Ipa, Ipa,
Texb, Texb,
Texs, Texs,
Tld4,
Trunc, Trunc,
F_End, F_End,

View File

@ -1,12 +1,24 @@
using Ryujinx.Graphics.Texture;
namespace Ryujinx.Graphics.Gal.Shader namespace Ryujinx.Graphics.Gal.Shader
{ {
class ShaderIrMetaTex : ShaderIrMeta class ShaderIrMetaTex : ShaderIrMeta
{ {
public int Elem { get; private set; } public int Elem { get; private set; }
public GalTextureTarget TextureTarget { get; private set; }
public ShaderIrNode[] Coordinates { get; private set; }
public TextureInstructionSuffix TextureInstructionSuffix { get; private set; }
public ShaderIrOperGpr LevelOfDetail;
public ShaderIrOperGpr Offset;
public ShaderIrOperGpr DepthCompare;
public int Component; // for TLD4(S)
public ShaderIrMetaTex(int Elem) public ShaderIrMetaTex(int Elem, GalTextureTarget TextureTarget, TextureInstructionSuffix TextureInstructionSuffix, params ShaderIrNode[] Coordinates)
{ {
this.Elem = Elem; this.Elem = Elem;
this.TextureTarget = TextureTarget;
this.TextureInstructionSuffix = TextureInstructionSuffix;
this.Coordinates = Coordinates;
} }
} }
} }

View File

@ -122,6 +122,8 @@ namespace Ryujinx.Graphics.Gal.Shader
Set("1101111101001x", ShaderDecode.Texq); Set("1101111101001x", ShaderDecode.Texq);
Set("1101x00xxxxxxx", ShaderDecode.Texs); Set("1101x00xxxxxxx", ShaderDecode.Texs);
Set("1101101xxxxxxx", ShaderDecode.Tlds); Set("1101101xxxxxxx", ShaderDecode.Tlds);
Set("110010xxxx111x", ShaderDecode.Tld4);
Set("1101111100xxxx", ShaderDecode.Tld4s);
Set("01011111xxxxxx", ShaderDecode.Vmad); Set("01011111xxxxxx", ShaderDecode.Vmad);
Set("0100111xxxxxxx", ShaderDecode.Xmad_CR); Set("0100111xxxxxxx", ShaderDecode.Xmad_CR);
Set("0011011x00xxxx", ShaderDecode.Xmad_I); Set("0011011x00xxxx", ShaderDecode.Xmad_I);

View File

@ -1,3 +1,5 @@
using Ryujinx.Graphics.Texture;
namespace Ryujinx.Graphics.Gal namespace Ryujinx.Graphics.Gal
{ {
public class ShaderDeclInfo public class ShaderDeclInfo
@ -9,18 +11,27 @@ namespace Ryujinx.Graphics.Gal
public int Cbuf { get; private set; } public int Cbuf { get; private set; }
public int Size { get; private set; } public int Size { get; private set; }
public GalTextureTarget TextureTarget { get; private set; }
public TextureInstructionSuffix TextureSuffix { get; private set; }
public ShaderDeclInfo( public ShaderDeclInfo(
string Name, string Name,
int Index, int Index,
bool IsCb = false, bool IsCb = false,
int Cbuf = 0, int Cbuf = 0,
int Size = 1) int Size = 1,
GalTextureTarget TextureTarget = GalTextureTarget.TwoD,
TextureInstructionSuffix TextureSuffix = TextureInstructionSuffix.None)
{ {
this.Name = Name; this.Name = Name;
this.Index = Index; this.Index = Index;
this.IsCb = IsCb; this.IsCb = IsCb;
this.Cbuf = Cbuf; this.Cbuf = Cbuf;
this.Size = Size; this.Size = Size;
this.TextureTarget = TextureTarget;
this.TextureSuffix = TextureSuffix;
} }
internal void Enlarge(int NewSize) internal void Enlarge(int NewSize)

View File

@ -1,6 +1,8 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Memory;
using Ryujinx.Graphics.Texture; using Ryujinx.Graphics.Texture;
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Graphics namespace Ryujinx.Graphics
@ -11,6 +13,7 @@ namespace Ryujinx.Graphics
{ {
None, None,
Texture, Texture,
TextureArrayLayer,
ColorBuffer, ColorBuffer,
ZetaBuffer ZetaBuffer
} }
@ -20,6 +23,7 @@ namespace Ryujinx.Graphics
private HashSet<long>[] UploadedKeys; private HashSet<long>[] UploadedKeys;
private Dictionary<long, ImageType> ImageTypes; private Dictionary<long, ImageType> ImageTypes;
private Dictionary<long, int> MirroredTextures;
public GpuResourceManager(NvGpu Gpu) public GpuResourceManager(NvGpu Gpu)
{ {
@ -33,6 +37,7 @@ namespace Ryujinx.Graphics
} }
ImageTypes = new Dictionary<long, ImageType>(); ImageTypes = new Dictionary<long, ImageType>();
MirroredTextures = new Dictionary<long, int>();
} }
public void SendColorBuffer(NvGpuVmm Vmm, long Position, int Attachment, GalImage NewImage) public void SendColorBuffer(NvGpuVmm Vmm, long Position, int Attachment, GalImage NewImage)
@ -70,6 +75,32 @@ namespace Ryujinx.Graphics
ImageTypes[Position] = ImageType.Texture; ImageTypes[Position] = ImageType.Texture;
} }
public bool TryGetTextureLayer(long Position, out int LayerIndex)
{
if (MirroredTextures.TryGetValue(Position, out LayerIndex))
{
ImageType Type = ImageTypes[Position];
// FIXME(thog): I'm actually unsure if we should deny all other image type, gpu testing needs to be done here.
if (Type != ImageType.Texture && Type != ImageType.TextureArrayLayer)
{
LayerIndex = -1;
return false;
}
return true;
}
LayerIndex = -1;
return false;
}
public void SetTextureArrayLayer(long Position, int LayerIndex)
{
ImageTypes[Position] = ImageType.TextureArrayLayer;
MirroredTextures[Position] = LayerIndex;
}
private void PrepareSendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage) private void PrepareSendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage)
{ {
long Size = ImageUtils.GetSize(NewImage); long Size = ImageUtils.GetSize(NewImage);
@ -102,7 +133,7 @@ namespace Ryujinx.Graphics
private bool TryReuse(NvGpuVmm Vmm, long Position, GalImage NewImage) private bool TryReuse(NvGpuVmm Vmm, long Position, GalImage NewImage)
{ {
if (Gpu.Renderer.Texture.TryGetImage(Position, out GalImage CachedImage) && CachedImage.SizeMatches(NewImage)) if (Gpu.Renderer.Texture.TryGetImage(Position, out GalImage CachedImage) && CachedImage.TextureTarget == NewImage.TextureTarget && CachedImage.SizeMatches(NewImage))
{ {
Gpu.Renderer.RenderTarget.Reinterpret(Position, NewImage); Gpu.Renderer.RenderTarget.Reinterpret(Position, NewImage);

View File

@ -1,3 +1,4 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Memory;
using Ryujinx.Graphics.Texture; using Ryujinx.Graphics.Texture;
@ -46,6 +47,8 @@ namespace Ryujinx.Graphics.Graphics3d
bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0; bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0;
int DstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth); int DstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth);
int DstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight); int DstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight);
int DstDepth = ReadRegister(NvGpuEngine2dReg.DstDepth);
int DstLayer = ReadRegister(NvGpuEngine2dReg.DstLayer);
int DstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch); int DstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch);
int DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions); int DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions);
@ -53,6 +56,8 @@ namespace Ryujinx.Graphics.Graphics3d
bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0; bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0;
int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth); int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth);
int SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight); int SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight);
int SrcDepth = ReadRegister(NvGpuEngine2dReg.SrcDepth);
int SrcLayer = ReadRegister(NvGpuEngine2dReg.SrcLayer);
int SrcPitch = ReadRegister(NvGpuEngine2dReg.SrcPitch); int SrcPitch = ReadRegister(NvGpuEngine2dReg.SrcPitch);
int SrcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions); int SrcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions);
@ -82,26 +87,99 @@ namespace Ryujinx.Graphics.Graphics3d
long SrcKey = Vmm.GetPhysicalAddress(SrcAddress); long SrcKey = Vmm.GetPhysicalAddress(SrcAddress);
long DstKey = Vmm.GetPhysicalAddress(DstAddress); long DstKey = Vmm.GetPhysicalAddress(DstAddress);
bool IsSrcLayered = false;
bool IsDstLayered = false;
GalTextureTarget SrcTarget = GalTextureTarget.TwoD;
if (SrcDepth != 0)
{
SrcTarget = GalTextureTarget.TwoDArray;
SrcDepth++;
IsSrcLayered = true;
}
else
{
SrcDepth = 1;
}
GalTextureTarget DstTarget = GalTextureTarget.TwoD;
if (DstDepth != 0)
{
DstTarget = GalTextureTarget.TwoDArray;
DstDepth++;
IsDstLayered = true;
}
else
{
DstDepth = 1;
}
GalImage SrcTexture = new GalImage( GalImage SrcTexture = new GalImage(
SrcWidth, SrcWidth,
SrcHeight, 1, SrcHeight,
SrcBlockHeight, 1, SrcDepth, 1,
SrcBlockHeight, 1,
SrcLayout, SrcLayout,
SrcImgFormat); SrcImgFormat,
SrcTarget);
GalImage DstTexture = new GalImage( GalImage DstTexture = new GalImage(
DstWidth, DstWidth,
DstHeight, 1, DstHeight,
DstBlockHeight, 1, DstDepth, 1,
DstBlockHeight, 1,
DstLayout, DstLayout,
DstImgFormat); DstImgFormat,
DstTarget);
SrcTexture.Pitch = SrcPitch; SrcTexture.Pitch = SrcPitch;
DstTexture.Pitch = DstPitch; DstTexture.Pitch = DstPitch;
long GetLayerOffset(GalImage Image, int Layer)
{
int TargetMipLevel = Image.MaxMipmapLevel <= 1 ? 1 : Image.MaxMipmapLevel - 1;
return ImageUtils.GetLayerOffset(Image, TargetMipLevel) * Layer;
}
int SrcLayerIndex = -1;
if (IsSrcLayered && Gpu.ResourceManager.TryGetTextureLayer(SrcKey, out SrcLayerIndex) && SrcLayerIndex != 0)
{
SrcKey = SrcKey - GetLayerOffset(SrcTexture, SrcLayerIndex);
}
int DstLayerIndex = -1;
if (IsDstLayered && Gpu.ResourceManager.TryGetTextureLayer(DstKey, out DstLayerIndex) && DstLayerIndex != 0)
{
DstKey = DstKey - GetLayerOffset(DstTexture, DstLayerIndex);
}
Gpu.ResourceManager.SendTexture(Vmm, SrcKey, SrcTexture); Gpu.ResourceManager.SendTexture(Vmm, SrcKey, SrcTexture);
Gpu.ResourceManager.SendTexture(Vmm, DstKey, DstTexture); Gpu.ResourceManager.SendTexture(Vmm, DstKey, DstTexture);
if (IsSrcLayered && SrcLayerIndex == -1)
{
for (int Layer = 0; Layer < SrcTexture.LayerCount; Layer++)
{
Gpu.ResourceManager.SetTextureArrayLayer(SrcKey + GetLayerOffset(SrcTexture, Layer), Layer);
}
SrcLayerIndex = 0;
}
if (IsDstLayered && DstLayerIndex == -1)
{
for (int Layer = 0; Layer < DstTexture.LayerCount; Layer++)
{
Gpu.ResourceManager.SetTextureArrayLayer(DstKey + GetLayerOffset(DstTexture, Layer), Layer);
}
DstLayerIndex = 0;
}
int SrcBlitX1 = (int)(SrcBlitX >> 32); int SrcBlitX1 = (int)(SrcBlitX >> 32);
int SrcBlitY1 = (int)(SrcBlitY >> 32); int SrcBlitY1 = (int)(SrcBlitY >> 32);
@ -109,8 +187,12 @@ namespace Ryujinx.Graphics.Graphics3d
int SrcBlitY2 = (int)(SrcBlitY + DstBlitH * BlitDvDy >> 32); int SrcBlitY2 = (int)(SrcBlitY + DstBlitH * BlitDvDy >> 32);
Gpu.Renderer.RenderTarget.Copy( Gpu.Renderer.RenderTarget.Copy(
SrcTexture,
DstTexture,
SrcKey, SrcKey,
DstKey, DstKey,
SrcLayerIndex,
DstLayerIndex,
SrcBlitX1, SrcBlitX1,
SrcBlitY1, SrcBlitY1,
SrcBlitX2, SrcBlitX2,
@ -124,6 +206,8 @@ namespace Ryujinx.Graphics.Graphics3d
//the texture is modified by the guest, however it doesn't //the texture is modified by the guest, however it doesn't
//work when resources that the gpu can write to are copied, //work when resources that the gpu can write to are copied,
//like framebuffers. //like framebuffers.
// FIXME: SUPPORT MULTILAYER CORRECTLY HERE (this will cause weird stuffs on the first layer)
ImageUtils.CopyTexture( ImageUtils.CopyTexture(
Vmm, Vmm,
SrcTexture, SrcTexture,

View File

@ -11,6 +11,7 @@ namespace Ryujinx.Graphics.Graphics3d
DstWidth = 0x86, DstWidth = 0x86,
DstHeight = 0x87, DstHeight = 0x87,
DstAddress = 0x88, DstAddress = 0x88,
DstAddressLow = 0x89,
SrcFormat = 0x8c, SrcFormat = 0x8c,
SrcLinear = 0x8d, SrcLinear = 0x8d,
SrcBlockDimensions = 0x8e, SrcBlockDimensions = 0x8e,
@ -20,6 +21,7 @@ namespace Ryujinx.Graphics.Graphics3d
SrcWidth = 0x92, SrcWidth = 0x92,
SrcHeight = 0x93, SrcHeight = 0x93,
SrcAddress = 0x94, SrcAddress = 0x94,
SrcAddressLow = 0x95,
ClipEnable = 0xa4, ClipEnable = 0xa4,
CopyOperation = 0xab, CopyOperation = 0xab,
BlitControl = 0x223, BlitControl = 0x223,

View File

@ -1,4 +1,5 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Memory;
using Ryujinx.Graphics.Texture; using Ryujinx.Graphics.Texture;
@ -190,7 +191,11 @@ namespace Ryujinx.Graphics.Graphics3d
int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10);
int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10); int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
int BlockDim = ReadRegister(NvGpuEngine3dReg.FrameBufferNBlockDim + FbIndex * 0x10); int ArrayMode = ReadRegister(NvGpuEngine3dReg.FrameBufferNArrayMode + FbIndex * 0x10);
int LayerCount = ArrayMode & 0xFFFF;
int LayerStride = ReadRegister(NvGpuEngine3dReg.FrameBufferNLayerStride + FbIndex * 0x10);
int BaseLayer = ReadRegister(NvGpuEngine3dReg.FrameBufferNBaseLayer + FbIndex * 0x10);
int BlockDim = ReadRegister(NvGpuEngine3dReg.FrameBufferNBlockDim + FbIndex * 0x10);
int GobBlockHeight = 1 << ((BlockDim >> 4) & 7); int GobBlockHeight = 1 << ((BlockDim >> 4) & 7);
@ -210,7 +215,7 @@ namespace Ryujinx.Graphics.Graphics3d
GalImageFormat Format = ImageUtils.ConvertSurface((GalSurfaceFormat)SurfFormat); GalImageFormat Format = ImageUtils.ConvertSurface((GalSurfaceFormat)SurfFormat);
GalImage Image = new GalImage(Width, Height, 1, GobBlockHeight, Layout, Format); GalImage Image = new GalImage(Width, Height, 1, 1, 1, GobBlockHeight, 1, Layout, Format, GalTextureTarget.TwoD);
Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image); Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image);
@ -264,7 +269,8 @@ namespace Ryujinx.Graphics.Graphics3d
GalImageFormat Format = ImageUtils.ConvertZeta((GalZetaFormat)ZetaFormat); GalImageFormat Format = ImageUtils.ConvertZeta((GalZetaFormat)ZetaFormat);
GalImage Image = new GalImage(Width, Height, 1, GobBlockHeight, Layout, Format); // TODO: Support non 2D?
GalImage Image = new GalImage(Width, Height, 1, 1, 1, GobBlockHeight, 1, Layout, Format, GalTextureTarget.TwoD);
Gpu.ResourceManager.SendZetaBuffer(Vmm, Key, Image); Gpu.ResourceManager.SendZetaBuffer(Vmm, Key, Image);
} }
@ -600,7 +606,7 @@ namespace Ryujinx.Graphics.Graphics3d
} }
Gpu.Renderer.Texture.Bind(Key, Index, Image); Gpu.Renderer.Texture.Bind(Key, Index, Image);
Gpu.Renderer.Texture.SetSampler(Sampler); Gpu.Renderer.Texture.SetSampler(Image, Sampler);
} }
} }

View File

@ -2,112 +2,115 @@ namespace Ryujinx.Graphics.Graphics3d
{ {
enum NvGpuEngine3dReg enum NvGpuEngine3dReg
{ {
FrameBufferNAddress = 0x200, FrameBufferNAddress = 0x200,
FrameBufferNWidth = 0x202, FrameBufferNWidth = 0x202,
FrameBufferNHeight = 0x203, FrameBufferNHeight = 0x203,
FrameBufferNFormat = 0x204, FrameBufferNFormat = 0x204,
FrameBufferNBlockDim = 0x205, FrameBufferNBlockDim = 0x205,
ViewportNScaleX = 0x280, FrameBufferNArrayMode = 0x206,
ViewportNScaleY = 0x281, FrameBufferNLayerStride = 0x207,
ViewportNScaleZ = 0x282, FrameBufferNBaseLayer = 0x208,
ViewportNTranslateX = 0x283, ViewportNScaleX = 0x280,
ViewportNTranslateY = 0x284, ViewportNScaleY = 0x281,
ViewportNTranslateZ = 0x285, ViewportNScaleZ = 0x282,
ViewportNHoriz = 0x300, ViewportNTranslateX = 0x283,
ViewportNVert = 0x301, ViewportNTranslateY = 0x284,
DepthRangeNNear = 0x302, ViewportNTranslateZ = 0x285,
DepthRangeNFar = 0x303, ViewportNHoriz = 0x300,
VertexArrayFirst = 0x35d, ViewportNVert = 0x301,
VertexArrayCount = 0x35e, DepthRangeNNear = 0x302,
ClearNColor = 0x360, DepthRangeNFar = 0x303,
ClearDepth = 0x364, VertexArrayFirst = 0x35d,
ClearStencil = 0x368, VertexArrayCount = 0x35e,
ScissorEnable = 0x380, ClearNColor = 0x360,
ScissorHorizontal = 0x381, ClearDepth = 0x364,
ScissorVertical = 0x382, ClearStencil = 0x368,
StencilBackFuncRef = 0x3d5, ScissorEnable = 0x380,
StencilBackMask = 0x3d6, ScissorHorizontal = 0x381,
StencilBackFuncMask = 0x3d7, ScissorVertical = 0x382,
ColorMaskCommon = 0x3e4, StencilBackFuncRef = 0x3d5,
RTSeparateFragData = 0x3eb, StencilBackMask = 0x3d6,
ZetaAddress = 0x3f8, StencilBackFuncMask = 0x3d7,
ZetaFormat = 0x3fa, ColorMaskCommon = 0x3e4,
ZetaBlockDimensions = 0x3fb, RTSeparateFragData = 0x3eb,
ZetaLayerStride = 0x3fc, ZetaAddress = 0x3f8,
VertexAttribNFormat = 0x458, ZetaFormat = 0x3fa,
RTControl = 0x487, ZetaBlockDimensions = 0x3fb,
ZetaHoriz = 0x48a, ZetaLayerStride = 0x3fc,
ZetaVert = 0x48b, VertexAttribNFormat = 0x458,
ZetaArrayMode = 0x48c, RTControl = 0x487,
LinkedTsc = 0x48d, ZetaHoriz = 0x48a,
DepthTestEnable = 0x4b3, ZetaVert = 0x48b,
BlendIndependent = 0x4b9, ZetaArrayMode = 0x48c,
DepthWriteEnable = 0x4ba, LinkedTsc = 0x48d,
DepthTestFunction = 0x4c3, DepthTestEnable = 0x4b3,
BlendSeparateAlpha = 0x4cf, BlendIndependent = 0x4b9,
BlendEquationRgb = 0x4d0, DepthWriteEnable = 0x4ba,
BlendFuncSrcRgb = 0x4d1, DepthTestFunction = 0x4c3,
BlendFuncDstRgb = 0x4d2, BlendSeparateAlpha = 0x4cf,
BlendEquationAlpha = 0x4d3, BlendEquationRgb = 0x4d0,
BlendFuncSrcAlpha = 0x4d4, BlendFuncSrcRgb = 0x4d1,
BlendFuncDstAlpha = 0x4d6, BlendFuncDstRgb = 0x4d2,
BlendEnable = 0x4d7, BlendEquationAlpha = 0x4d3,
IBlendNEnable = 0x4d8, BlendFuncSrcAlpha = 0x4d4,
StencilEnable = 0x4e0, BlendFuncDstAlpha = 0x4d6,
StencilFrontOpFail = 0x4e1, BlendEnable = 0x4d7,
StencilFrontOpZFail = 0x4e2, IBlendNEnable = 0x4d8,
StencilFrontOpZPass = 0x4e3, StencilEnable = 0x4e0,
StencilFrontFuncFunc = 0x4e4, StencilFrontOpFail = 0x4e1,
StencilFrontFuncRef = 0x4e5, StencilFrontOpZFail = 0x4e2,
StencilFrontFuncMask = 0x4e6, StencilFrontOpZPass = 0x4e3,
StencilFrontMask = 0x4e7, StencilFrontFuncFunc = 0x4e4,
ScreenYControl = 0x4eb, StencilFrontFuncRef = 0x4e5,
VertexArrayElemBase = 0x50d, StencilFrontFuncMask = 0x4e6,
VertexArrayInstBase = 0x50e, StencilFrontMask = 0x4e7,
ZetaEnable = 0x54e, ScreenYControl = 0x4eb,
TexHeaderPoolOffset = 0x55d, VertexArrayElemBase = 0x50d,
TexSamplerPoolOffset = 0x557, VertexArrayInstBase = 0x50e,
StencilTwoSideEnable = 0x565, ZetaEnable = 0x54e,
StencilBackOpFail = 0x566, TexHeaderPoolOffset = 0x55d,
StencilBackOpZFail = 0x567, TexSamplerPoolOffset = 0x557,
StencilBackOpZPass = 0x568, StencilTwoSideEnable = 0x565,
StencilBackFuncFunc = 0x569, StencilBackOpFail = 0x566,
FrameBufferSrgb = 0x56e, StencilBackOpZFail = 0x567,
ShaderAddress = 0x582, StencilBackOpZPass = 0x568,
VertexBeginGl = 0x586, StencilBackFuncFunc = 0x569,
PrimRestartEnable = 0x591, FrameBufferSrgb = 0x56e,
PrimRestartIndex = 0x592, ShaderAddress = 0x582,
IndexArrayAddress = 0x5f2, VertexBeginGl = 0x586,
IndexArrayEndAddr = 0x5f4, PrimRestartEnable = 0x591,
IndexArrayFormat = 0x5f6, PrimRestartIndex = 0x592,
IndexBatchFirst = 0x5f7, IndexArrayAddress = 0x5f2,
IndexBatchCount = 0x5f8, IndexArrayEndAddr = 0x5f4,
VertexArrayNInstance = 0x620, IndexArrayFormat = 0x5f6,
CullFaceEnable = 0x646, IndexBatchFirst = 0x5f7,
FrontFace = 0x647, IndexBatchCount = 0x5f8,
CullFace = 0x648, VertexArrayNInstance = 0x620,
ColorMaskN = 0x680, CullFaceEnable = 0x646,
QueryAddress = 0x6c0, FrontFace = 0x647,
QuerySequence = 0x6c2, CullFace = 0x648,
QueryControl = 0x6c3, ColorMaskN = 0x680,
VertexArrayNControl = 0x700, QueryAddress = 0x6c0,
VertexArrayNAddress = 0x701, QuerySequence = 0x6c2,
VertexArrayNDivisor = 0x703, QueryControl = 0x6c3,
IBlendNSeparateAlpha = 0x780, VertexArrayNControl = 0x700,
IBlendNEquationRgb = 0x781, VertexArrayNAddress = 0x701,
IBlendNFuncSrcRgb = 0x782, VertexArrayNDivisor = 0x703,
IBlendNFuncDstRgb = 0x783, IBlendNSeparateAlpha = 0x780,
IBlendNEquationAlpha = 0x784, IBlendNEquationRgb = 0x781,
IBlendNFuncSrcAlpha = 0x785, IBlendNFuncSrcRgb = 0x782,
IBlendNFuncDstAlpha = 0x786, IBlendNFuncDstRgb = 0x783,
VertexArrayNEndAddr = 0x7c0, IBlendNEquationAlpha = 0x784,
ShaderNControl = 0x800, IBlendNFuncSrcAlpha = 0x785,
ShaderNOffset = 0x801, IBlendNFuncDstAlpha = 0x786,
ShaderNMaxGprs = 0x803, VertexArrayNEndAddr = 0x7c0,
ShaderNType = 0x804, ShaderNControl = 0x800,
ConstBufferSize = 0x8e0, ShaderNOffset = 0x801,
ConstBufferAddress = 0x8e1, ShaderNMaxGprs = 0x803,
ConstBufferOffset = 0x8e3, ShaderNType = 0x804,
TextureCbIndex = 0x982 ConstBufferSize = 0x8e0,
ConstBufferAddress = 0x8e1,
ConstBufferOffset = 0x8e3,
TextureCbIndex = 0x982
} }
} }

View File

@ -1,3 +1,4 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Memory;
using Ryujinx.Graphics.Texture; using Ryujinx.Graphics.Texture;
using System.Collections.Generic; using System.Collections.Generic;
@ -125,29 +126,37 @@ namespace Ryujinx.Graphics.Graphics3d
if (SrcLinear) if (SrcLinear)
{ {
SrcSwizzle = new LinearSwizzle(SrcPitch, SrcCpp); SrcSwizzle = new LinearSwizzle(SrcPitch, SrcCpp, SrcSizeX, SrcSizeY);
} }
else else
{ {
SrcSwizzle = new BlockLinearSwizzle(SrcSizeX, SrcCpp, SrcBlockHeight); SrcSwizzle = new BlockLinearSwizzle(
SrcSizeX,
SrcSizeY, 1,
SrcBlockHeight, 1,
SrcCpp);
} }
ISwizzle DstSwizzle; ISwizzle DstSwizzle;
if (DstLinear) if (DstLinear)
{ {
DstSwizzle = new LinearSwizzle(DstPitch, DstCpp); DstSwizzle = new LinearSwizzle(DstPitch, DstCpp, SrcSizeX, SrcSizeY);
} }
else else
{ {
DstSwizzle = new BlockLinearSwizzle(DstSizeX, DstCpp, DstBlockHeight); DstSwizzle = new BlockLinearSwizzle(
DstSizeX,
DstSizeY, 1,
DstBlockHeight, 1,
DstCpp);
} }
for (int Y = 0; Y < YCount; Y++) for (int Y = 0; Y < YCount; Y++)
for (int X = 0; X < XCount; X++) for (int X = 0; X < XCount; X++)
{ {
int SrcOffset = SrcSwizzle.GetSwizzleOffset(SrcPosX + X, SrcPosY + Y); int SrcOffset = SrcSwizzle.GetSwizzleOffset(SrcPosX + X, SrcPosY + Y, 0);
int DstOffset = DstSwizzle.GetSwizzleOffset(DstPosX + X, DstPosY + Y); int DstOffset = DstSwizzle.GetSwizzleOffset(DstPosX + X, DstPosY + Y, 0);
long Src = SrcPA + (uint)SrcOffset; long Src = SrcPA + (uint)SrcOffset;
long Dst = DstPA + (uint)DstOffset; long Dst = DstPA + (uint)DstOffset;

View File

@ -1,3 +1,4 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Memory;
using Ryujinx.Graphics.Texture; using Ryujinx.Graphics.Texture;
using System.Collections.Generic; using System.Collections.Generic;
@ -119,14 +120,17 @@ namespace Ryujinx.Graphics.Graphics3d
} }
else else
{ {
BlockLinearSwizzle Swizzle = new BlockLinearSwizzle(CopyWidth, 1, CopyGobBlockHeight); BlockLinearSwizzle Swizzle = new BlockLinearSwizzle(
CopyWidth,
CopyHeight, 1,
CopyGobBlockHeight, 1, 1);
int SrcOffset = 0; int SrcOffset = 0;
for (int Y = CopyStartY; Y < CopyHeight && SrcOffset < CopySize; Y++) for (int Y = CopyStartY; Y < CopyHeight && SrcOffset < CopySize; Y++)
for (int X = CopyStartX; X < CopyWidth && SrcOffset < CopySize; X++) for (int X = CopyStartX; X < CopyWidth && SrcOffset < CopySize; X++)
{ {
int DstOffset = Swizzle.GetSwizzleOffset(X, Y); int DstOffset = Swizzle.GetSwizzleOffset(X, Y, 0);
Vmm.WriteByte(CopyAddress + DstOffset, Buffer[SrcOffset++]); Vmm.WriteByte(CopyAddress + DstOffset, Buffer[SrcOffset++]);
} }

View File

@ -72,6 +72,7 @@ namespace Ryujinx.Graphics.Texture
if (BlockZ != 1 || Z != 1) if (BlockZ != 1 || Z != 1)
{ {
// TODO: Support 3D textures?
throw new ASTCDecoderException("3D compressed textures unsupported!"); throw new ASTCDecoderException("3D compressed textures unsupported!");
} }

View File

@ -1,51 +1,178 @@
using Ryujinx.Common;
using System; using System;
namespace Ryujinx.Graphics.Texture namespace Ryujinx.Graphics.Texture
{ {
class BlockLinearSwizzle : ISwizzle class BlockLinearSwizzle : ISwizzle
{ {
private int BhShift; private const int GobWidth = 64;
private int BppShift; 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 BhMask;
private int BdMask;
private int BhShift;
private int BdShift;
private int BppShift;
private int XShift; private int XShift;
private int GobStride;
public BlockLinearSwizzle(int Width, int Bpp, int BlockHeight = 16) private int RobSize;
private int SliceSize;
private int BaseOffset;
public BlockLinearSwizzle(
int Width,
int Height,
int Depth,
int GobBlockHeight,
int GobBlockDepth,
int Bpp)
{ {
BhMask = (BlockHeight * 8) - 1; TexWidth = Width;
TexHeight = Height;
TexDepth = Depth;
TexGobBlockHeight = GobBlockHeight;
TexGobBlockDepth = GobBlockDepth;
TexBpp = Bpp;
BhShift = CountLsbZeros(BlockHeight * 8); BppShift = BitUtils.CountTrailingZeros32(Bpp);
BppShift = CountLsbZeros(Bpp);
int WidthInGobs = (int)MathF.Ceiling(Width * Bpp / 64f); SetMipLevel(0);
GobStride = 512 * BlockHeight * WidthInGobs;
XShift = CountLsbZeros(512 * BlockHeight);
} }
private int CountLsbZeros(int Value) public void SetMipLevel(int Level)
{ {
int Count = 0; BaseOffset = GetMipOffset(Level);
while (((Value >> Count) & 1) == 0) 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++)
{ {
Count++; 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 Count; return TotalSize;
} }
public int GetSwizzleOffset(int X, int Y) private struct GobBlockSizes
{
public int Height;
public int Depth;
public GobBlockSizes(int GobBlockHeight, int GobBlockDepth)
{
this.Height = GobBlockHeight;
this.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)
{
this.RobSize = RobSize;
this.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; X <<= BppShift;
int Position = (Y >> BhShift) * GobStride; int YH = Y / GobHeight;
Position += (X >> 6) << XShift; int Position = (Z >> BdShift) * SliceSize + (YH >> BhShift) * RobSize;
Position += ((Y & BhMask) >> 3) << 9; Position += (X / GobWidth) << XShift;
Position += (YH & BhMask) * GobSize;
Position += ((Z & BdMask) * GobSize) << BhShift;
Position += ((X & 0x3f) >> 5) << 8; Position += ((X & 0x3f) >> 5) << 8;
Position += ((Y & 0x07) >> 1) << 6; Position += ((Y & 0x07) >> 1) << 6;
@ -53,7 +180,7 @@ namespace Ryujinx.Graphics.Texture
Position += ((Y & 0x01) >> 0) << 4; Position += ((Y & 0x01) >> 0) << 4;
Position += ((X & 0x0f) >> 0) << 0; Position += ((X & 0x0f) >> 0) << 0;
return Position; return BaseOffset + Position;
} }
} }
} }

View File

@ -2,6 +2,12 @@ namespace Ryujinx.Graphics.Texture
{ {
interface ISwizzle interface ISwizzle
{ {
int GetSwizzleOffset(int X, int Y); int GetSwizzleOffset(int X, int Y, int Z);
void SetMipLevel(int Level);
int GetMipOffset(int Level);
int GetImageSize(int MipsCount);
} }
} }

View File

@ -1,4 +1,6 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Memory;
using System; using System;
@ -23,14 +25,16 @@ namespace Ryujinx.Graphics.Texture
public int BytesPerPixel { get; private set; } public int BytesPerPixel { get; private set; }
public int BlockWidth { get; private set; } public int BlockWidth { get; private set; }
public int BlockHeight { get; private set; } public int BlockHeight { get; private set; }
public int BlockDepth { get; private set; }
public TargetBuffer Target { get; private set; } public TargetBuffer Target { get; private set; }
public ImageDescriptor(int BytesPerPixel, int BlockWidth, int BlockHeight, TargetBuffer Target) public ImageDescriptor(int BytesPerPixel, int BlockWidth, int BlockHeight, int BlockDepth, TargetBuffer Target)
{ {
this.BytesPerPixel = BytesPerPixel; this.BytesPerPixel = BytesPerPixel;
this.BlockWidth = BlockWidth; this.BlockWidth = BlockWidth;
this.BlockHeight = BlockHeight; this.BlockHeight = BlockHeight;
this.BlockDepth = BlockDepth;
this.Target = Target; this.Target = Target;
} }
} }
@ -92,52 +96,52 @@ namespace Ryujinx.Graphics.Texture
private static readonly Dictionary<GalImageFormat, ImageDescriptor> s_ImageTable = private static readonly Dictionary<GalImageFormat, ImageDescriptor> s_ImageTable =
new Dictionary<GalImageFormat, ImageDescriptor>() new Dictionary<GalImageFormat, ImageDescriptor>()
{ {
{ GalImageFormat.RGBA32, new ImageDescriptor(16, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RGBA32, new ImageDescriptor(16, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGBA16, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RGBA16, new ImageDescriptor(8, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RG32, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RG32, new ImageDescriptor(8, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGBX8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RGBX8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGBA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RGBA8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.BGRA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, { GalImageFormat.BGRA8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGB10A2, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RGB10A2, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.R32, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, { GalImageFormat.R32, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGBA4, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RGBA4, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.BptcSfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, { GalImageFormat.BptcSfloat, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) },
{ GalImageFormat.BptcUfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, { GalImageFormat.BptcUfloat, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) },
{ GalImageFormat.BGR5A1, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, { GalImageFormat.BGR5A1, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGB5A1, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RGB5A1, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGB565, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RGB565, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.BGR565, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, { GalImageFormat.BGR565, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.BptcUnorm, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, { GalImageFormat.BptcUnorm, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) },
{ GalImageFormat.RG16, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RG16, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RG8, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RG8, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.R16, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, { GalImageFormat.R16, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.R8, new ImageDescriptor(1, 1, 1, TargetBuffer.Color) }, { GalImageFormat.R8, new ImageDescriptor(1, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.R11G11B10, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, { GalImageFormat.R11G11B10, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.BC1, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) }, { GalImageFormat.BC1, new ImageDescriptor(8, 4, 4, 1, TargetBuffer.Color) },
{ GalImageFormat.BC2, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, { GalImageFormat.BC2, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) },
{ GalImageFormat.BC3, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, { GalImageFormat.BC3, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) },
{ GalImageFormat.BC4, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) }, { GalImageFormat.BC4, new ImageDescriptor(8, 4, 4, 1, TargetBuffer.Color) },
{ GalImageFormat.BC5, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, { GalImageFormat.BC5, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) },
{ GalImageFormat.Astc2D4x4, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, { GalImageFormat.Astc2D4x4, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) },
{ GalImageFormat.Astc2D5x5, new ImageDescriptor(16, 5, 5, TargetBuffer.Color) }, { GalImageFormat.Astc2D5x5, new ImageDescriptor(16, 5, 5, 1, TargetBuffer.Color) },
{ GalImageFormat.Astc2D6x6, new ImageDescriptor(16, 6, 6, TargetBuffer.Color) }, { GalImageFormat.Astc2D6x6, new ImageDescriptor(16, 6, 6, 1, TargetBuffer.Color) },
{ GalImageFormat.Astc2D8x8, new ImageDescriptor(16, 8, 8, TargetBuffer.Color) }, { GalImageFormat.Astc2D8x8, new ImageDescriptor(16, 8, 8, 1, TargetBuffer.Color) },
{ GalImageFormat.Astc2D10x10, new ImageDescriptor(16, 10, 10, TargetBuffer.Color) }, { GalImageFormat.Astc2D10x10, new ImageDescriptor(16, 10, 10, 1, TargetBuffer.Color) },
{ GalImageFormat.Astc2D12x12, new ImageDescriptor(16, 12, 12, TargetBuffer.Color) }, { GalImageFormat.Astc2D12x12, new ImageDescriptor(16, 12, 12, 1, TargetBuffer.Color) },
{ GalImageFormat.Astc2D5x4, new ImageDescriptor(16, 5, 4, TargetBuffer.Color) }, { GalImageFormat.Astc2D5x4, new ImageDescriptor(16, 5, 4, 1, TargetBuffer.Color) },
{ GalImageFormat.Astc2D6x5, new ImageDescriptor(16, 6, 5, TargetBuffer.Color) }, { GalImageFormat.Astc2D6x5, new ImageDescriptor(16, 6, 5, 1, TargetBuffer.Color) },
{ GalImageFormat.Astc2D8x6, new ImageDescriptor(16, 8, 6, TargetBuffer.Color) }, { GalImageFormat.Astc2D8x6, new ImageDescriptor(16, 8, 6, 1, TargetBuffer.Color) },
{ GalImageFormat.Astc2D10x8, new ImageDescriptor(16, 10, 8, TargetBuffer.Color) }, { GalImageFormat.Astc2D10x8, new ImageDescriptor(16, 10, 8, 1, TargetBuffer.Color) },
{ GalImageFormat.Astc2D12x10, new ImageDescriptor(16, 12, 10, TargetBuffer.Color) }, { GalImageFormat.Astc2D12x10, new ImageDescriptor(16, 12, 10, 1, TargetBuffer.Color) },
{ GalImageFormat.Astc2D8x5, new ImageDescriptor(16, 8, 5, TargetBuffer.Color) }, { GalImageFormat.Astc2D8x5, new ImageDescriptor(16, 8, 5, 1, TargetBuffer.Color) },
{ GalImageFormat.Astc2D10x5, new ImageDescriptor(16, 10, 5, TargetBuffer.Color) }, { GalImageFormat.Astc2D10x5, new ImageDescriptor(16, 10, 5, 1, TargetBuffer.Color) },
{ GalImageFormat.Astc2D10x6, new ImageDescriptor(16, 10, 6, TargetBuffer.Color) }, { GalImageFormat.Astc2D10x6, new ImageDescriptor(16, 10, 6, 1, TargetBuffer.Color) },
{ GalImageFormat.D16, new ImageDescriptor(2, 1, 1, TargetBuffer.Depth) }, { GalImageFormat.D16, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Depth) },
{ GalImageFormat.D24, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) }, { GalImageFormat.D24, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Depth) },
{ GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, TargetBuffer.DepthStencil) }, { GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.DepthStencil) },
{ GalImageFormat.D32, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) }, { GalImageFormat.D32, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Depth) },
{ GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, TargetBuffer.DepthStencil) } { GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, 1, TargetBuffer.DepthStencil) }
}; };
public static GalImageFormat ConvertTexture( public static GalImageFormat ConvertTexture(
@ -241,26 +245,37 @@ namespace Ryujinx.Graphics.Texture
ImageDescriptor Desc = GetImageDescriptor(Image.Format); ImageDescriptor Desc = GetImageDescriptor(Image.Format);
(int Width, int Height) = GetImageSizeInBlocks(Image); (int Width, int Height, int Depth) = GetImageSizeInBlocks(Image);
int BytesPerPixel = Desc.BytesPerPixel; int BytesPerPixel = Desc.BytesPerPixel;
//Note: Each row of the texture needs to be aligned to 4 bytes. //Note: Each row of the texture needs to be aligned to 4 bytes.
int Pitch = (Width * BytesPerPixel + 3) & ~3; int Pitch = (Width * BytesPerPixel + 3) & ~3;
byte[] Data = new byte[Height * Pitch];
for (int Y = 0; Y < Height; Y++) int DataLayerSize = Height * Pitch * Depth;
byte[] Data = new byte[DataLayerSize * Image.LayerCount];
int TargetMipLevel = Image.MaxMipmapLevel <= 1 ? 1 : Image.MaxMipmapLevel - 1;
int LayerOffset = ImageUtils.GetLayerOffset(Image, TargetMipLevel);
for (int Layer = 0; Layer < Image.LayerCount; Layer++)
{ {
int OutOffs = Y * Pitch; for (int Z = 0; Z < Depth; Z++)
for (int X = 0; X < Width; X++)
{ {
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); for (int Y = 0; Y < Height; Y++)
{
int OutOffs = (DataLayerSize * Layer) + Y * Pitch + (Z * Width * Height * BytesPerPixel);
CpuMemory.ReadBytes(Position + Offset, Data, OutOffs, BytesPerPixel); for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y, Z);
OutOffs += BytesPerPixel; CpuMemory.ReadBytes(Position + (LayerOffset * Layer) + Offset, Data, OutOffs, BytesPerPixel);
OutOffs += BytesPerPixel;
}
}
} }
} }
@ -273,16 +288,17 @@ namespace Ryujinx.Graphics.Texture
ImageDescriptor Desc = GetImageDescriptor(Image.Format); ImageDescriptor Desc = GetImageDescriptor(Image.Format);
(int Width, int Height) = ImageUtils.GetImageSizeInBlocks(Image); (int Width, int Height, int Depth) = ImageUtils.GetImageSizeInBlocks(Image);
int BytesPerPixel = Desc.BytesPerPixel; int BytesPerPixel = Desc.BytesPerPixel;
int InOffs = 0; int InOffs = 0;
for (int Z = 0; Z < Depth; Z++)
for (int Y = 0; Y < Height; Y++) for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++) for (int X = 0; X < Width; X++)
{ {
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y, Z);
Vmm.Memory.WriteBytes(Position + Offset, Data, InOffs, BytesPerPixel); Vmm.Memory.WriteBytes(Position + Offset, Data, InOffs, BytesPerPixel);
@ -290,6 +306,7 @@ namespace Ryujinx.Graphics.Texture
} }
} }
// TODO: Support non 2D
public static bool CopyTexture( public static bool CopyTexture(
NvGpuVmm Vmm, NvGpuVmm Vmm,
GalImage SrcImage, GalImage SrcImage,
@ -318,8 +335,8 @@ namespace Ryujinx.Graphics.Texture
for (int Y = 0; Y < Height; Y++) for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++) for (int X = 0; X < Width; X++)
{ {
long SrcOffset = (uint)SrcSwizzle.GetSwizzleOffset(SrcX + X, SrcY + Y); long SrcOffset = (uint)SrcSwizzle.GetSwizzleOffset(SrcX + X, SrcY + Y, 0);
long DstOffset = (uint)DstSwizzle.GetSwizzleOffset(DstX + X, DstY + Y); long DstOffset = (uint)DstSwizzle.GetSwizzleOffset(DstX + X, DstY + Y, 0);
byte[] Texel = Vmm.ReadBytes(SrcAddress + SrcOffset, BytesPerPixel); byte[] Texel = Vmm.ReadBytes(SrcAddress + SrcOffset, BytesPerPixel);
@ -333,10 +350,41 @@ namespace Ryujinx.Graphics.Texture
{ {
ImageDescriptor Desc = GetImageDescriptor(Image.Format); ImageDescriptor Desc = GetImageDescriptor(Image.Format);
int ComponentCount = GetCoordsCountTextureTarget(Image.TextureTarget);
if (IsArray(Image.TextureTarget))
ComponentCount--;
int Width = DivRoundUp(Image.Width, Desc.BlockWidth); int Width = DivRoundUp(Image.Width, Desc.BlockWidth);
int Height = DivRoundUp(Image.Height, Desc.BlockHeight); int Height = DivRoundUp(Image.Height, Desc.BlockHeight);
int Depth = DivRoundUp(Image.Depth, Desc.BlockDepth);
return Desc.BytesPerPixel * Width * Height; switch (ComponentCount)
{
case 1:
return Desc.BytesPerPixel * Width * Image.LayerCount;
case 2:
return Desc.BytesPerPixel * Width * Height * Image.LayerCount;
case 3:
return Desc.BytesPerPixel * Width * Height * Depth * Image.LayerCount;
default:
throw new InvalidOperationException($"Invalid component count: {ComponentCount}");
}
}
public static int GetGpuSize(GalImage Image, bool forcePitch = false)
{
return TextureHelper.GetSwizzle(Image).GetImageSize(Image.MaxMipmapLevel) * Image.LayerCount;
}
public static int GetLayerOffset(GalImage Image, int MipLevel)
{
if (MipLevel <= 0)
{
MipLevel = 1;
}
return TextureHelper.GetSwizzle(Image).GetMipOffset(MipLevel);
} }
public static int GetPitch(GalImageFormat Format, int Width) public static int GetPitch(GalImageFormat Format, int Width)
@ -360,6 +408,11 @@ namespace Ryujinx.Graphics.Texture
return GetImageDescriptor(Format).BlockHeight; return GetImageDescriptor(Format).BlockHeight;
} }
public static int GetBlockDepth(GalImageFormat Format)
{
return GetImageDescriptor(Format).BlockDepth;
}
public static int GetAlignedWidth(GalImage Image) public static int GetAlignedWidth(GalImage Image)
{ {
ImageDescriptor Desc = GetImageDescriptor(Image.Format); ImageDescriptor Desc = GetImageDescriptor(Image.Format);
@ -378,12 +431,13 @@ namespace Ryujinx.Graphics.Texture
return (Image.Width + AlignMask) & ~AlignMask; return (Image.Width + AlignMask) & ~AlignMask;
} }
public static (int Width, int Height) GetImageSizeInBlocks(GalImage Image) public static (int Width, int Height, int Depth) GetImageSizeInBlocks(GalImage Image)
{ {
ImageDescriptor Desc = GetImageDescriptor(Image.Format); ImageDescriptor Desc = GetImageDescriptor(Image.Format);
return (DivRoundUp(Image.Width, Desc.BlockWidth), return (DivRoundUp(Image.Width, Desc.BlockWidth),
DivRoundUp(Image.Height, Desc.BlockHeight)); DivRoundUp(Image.Height, Desc.BlockHeight),
DivRoundUp(Image.Depth, Desc.BlockDepth));
} }
public static int GetBytesPerPixel(GalImageFormat Format) public static int GetBytesPerPixel(GalImageFormat Format)
@ -443,5 +497,66 @@ namespace Ryujinx.Graphics.Texture
default: throw new NotImplementedException(((int)Type).ToString()); default: throw new NotImplementedException(((int)Type).ToString());
} }
} }
public static TextureTarget GetTextureTarget(GalTextureTarget GalTextureTarget)
{
switch (GalTextureTarget)
{
case GalTextureTarget.OneD:
return TextureTarget.Texture1D;
case GalTextureTarget.TwoD:
case GalTextureTarget.TwoDNoMipMap:
return TextureTarget.Texture2D;
case GalTextureTarget.ThreeD:
return TextureTarget.Texture3D;
case GalTextureTarget.OneDArray:
return TextureTarget.Texture1DArray;
case GalTextureTarget.OneDBuffer:
return TextureTarget.TextureBuffer;
case GalTextureTarget.TwoDArray:
return TextureTarget.Texture2DArray;
case GalTextureTarget.CubeMap:
return TextureTarget.TextureCubeMap;
case GalTextureTarget.CubeArray:
return TextureTarget.TextureCubeMapArray;
default:
throw new NotSupportedException($"Texture target {GalTextureTarget} currently not supported!");
}
}
public static bool IsArray(GalTextureTarget TextureTarget)
{
switch (TextureTarget)
{
case GalTextureTarget.OneDArray:
case GalTextureTarget.TwoDArray:
case GalTextureTarget.CubeArray:
return true;
default:
return false;
}
}
public static int GetCoordsCountTextureTarget(GalTextureTarget TextureTarget)
{
switch (TextureTarget)
{
case GalTextureTarget.OneD:
return 1;
case GalTextureTarget.OneDArray:
case GalTextureTarget.OneDBuffer:
case GalTextureTarget.TwoD:
case GalTextureTarget.TwoDNoMipMap:
return 2;
case GalTextureTarget.ThreeD:
case GalTextureTarget.TwoDArray:
case GalTextureTarget.CubeMap:
return 3;
case GalTextureTarget.CubeArray:
return 4;
default:
throw new NotImplementedException($"TextureTarget.{TextureTarget} not implemented yet.");
}
}
} }
} }

View File

@ -1,3 +1,5 @@
using System;
namespace Ryujinx.Graphics.Texture namespace Ryujinx.Graphics.Texture
{ {
class LinearSwizzle : ISwizzle class LinearSwizzle : ISwizzle
@ -5,15 +7,39 @@ namespace Ryujinx.Graphics.Texture
private int Pitch; private int Pitch;
private int Bpp; private int Bpp;
public LinearSwizzle(int Pitch, int Bpp) private int SliceSize;
public LinearSwizzle(int Pitch, int Bpp, int Width, int Height)
{ {
this.Pitch = Pitch; this.Pitch = Pitch;
this.Bpp = Bpp; this.Bpp = Bpp;
SliceSize = Width * Height * Bpp;
} }
public int GetSwizzleOffset(int X, int Y) public void SetMipLevel(int Level)
{ {
return X * Bpp + Y * Pitch; throw new NotImplementedException();
}
public int GetMipOffset(int Level)
{
if (Level == 1)
return SliceSize;
throw new NotImplementedException();
}
public int GetImageSize(int MipsCount)
{
int Size = GetMipOffset(MipsCount);
Size = (Size + 0x1fff) & ~0x1fff;
return Size;
}
public int GetSwizzleOffset(int X, int Y, int Z)
{
return Z * SliceSize + X * Bpp + Y * Pitch;
} }
} }
} }

View File

@ -12,6 +12,8 @@ namespace Ryujinx.Graphics.Texture
GalImageFormat Format = GetImageFormat(Tic); GalImageFormat Format = GetImageFormat(Tic);
GalTextureTarget TextureTarget = (GalTextureTarget)((Tic[4] >> 23) & 0xF);
GalTextureSource XSource = (GalTextureSource)((Tic[0] >> 19) & 7); GalTextureSource XSource = (GalTextureSource)((Tic[0] >> 19) & 7);
GalTextureSource YSource = (GalTextureSource)((Tic[0] >> 22) & 7); GalTextureSource YSource = (GalTextureSource)((Tic[0] >> 22) & 7);
GalTextureSource ZSource = (GalTextureSource)((Tic[0] >> 25) & 7); GalTextureSource ZSource = (GalTextureSource)((Tic[0] >> 25) & 7);
@ -19,6 +21,8 @@ namespace Ryujinx.Graphics.Texture
TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7); TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7);
int MaxMipmapLevel = (Tic[3] >> 28) & 0xF + 1;
GalMemoryLayout Layout; GalMemoryLayout Layout;
if (Swizzle == TextureSwizzle.BlockLinear || if (Swizzle == TextureSwizzle.BlockLinear ||
@ -31,22 +35,61 @@ namespace Ryujinx.Graphics.Texture
Layout = GalMemoryLayout.Pitch; Layout = GalMemoryLayout.Pitch;
} }
int BlockHeightLog2 = (Tic[3] >> 3) & 7; int GobBlockHeightLog2 = (Tic[3] >> 3) & 7;
int TileWidthLog2 = (Tic[3] >> 10) & 7; int GobBlockDepthLog2 = (Tic[3] >> 6) & 7;
int TileWidthLog2 = (Tic[3] >> 10) & 7;
int BlockHeight = 1 << BlockHeightLog2; int GobBlockHeight = 1 << GobBlockHeightLog2;
int TileWidth = 1 << TileWidthLog2; int GobBlockDepth = 1 << GobBlockDepthLog2;
int TileWidth = 1 << TileWidthLog2;
int Width = (Tic[4] & 0xffff) + 1; int Width = ((Tic[4] >> 0) & 0xffff) + 1;
int Height = (Tic[5] & 0xffff) + 1; int Height = ((Tic[5] >> 0) & 0xffff) + 1;
int Depth = ((Tic[5] >> 16) & 0x3fff) + 1;
int LayoutCount = 1;
// TODO: check this
if (ImageUtils.IsArray(TextureTarget))
{
LayoutCount = Depth;
Depth = 1;
}
if (TextureTarget == GalTextureTarget.OneD)
{
Height = 1;
}
if (TextureTarget == GalTextureTarget.TwoD || TextureTarget == GalTextureTarget.OneD)
{
Depth = 1;
}
else if (TextureTarget == GalTextureTarget.CubeMap)
{
// FIXME: This is a bit hacky but I guess it's fine for now
LayoutCount = 6;
Depth = 1;
}
else if (TextureTarget == GalTextureTarget.CubeArray)
{
// FIXME: This is a really really hacky but I guess it's fine for now
LayoutCount *= 6;
Depth = 1;
}
GalImage Image = new GalImage( GalImage Image = new GalImage(
Width, Width,
Height, Height,
Depth,
LayoutCount,
TileWidth, TileWidth,
BlockHeight, GobBlockHeight,
GobBlockDepth,
Layout, Layout,
Format, Format,
TextureTarget,
MaxMipmapLevel,
XSource, XSource,
YSource, YSource,
ZSource, ZSource,
@ -68,6 +111,10 @@ namespace Ryujinx.Graphics.Texture
GalTextureWrap AddressV = (GalTextureWrap)((Tsc[0] >> 3) & 7); GalTextureWrap AddressV = (GalTextureWrap)((Tsc[0] >> 3) & 7);
GalTextureWrap AddressP = (GalTextureWrap)((Tsc[0] >> 6) & 7); GalTextureWrap AddressP = (GalTextureWrap)((Tsc[0] >> 6) & 7);
bool DepthCompare = ((Tsc[0] >> 9) & 1) == 1;
DepthCompareFunc DepthCompareFunc = (DepthCompareFunc)((Tsc[0] >> 10) & 7);
GalTextureFilter MagFilter = (GalTextureFilter) ((Tsc[1] >> 0) & 3); GalTextureFilter MagFilter = (GalTextureFilter) ((Tsc[1] >> 0) & 3);
GalTextureFilter MinFilter = (GalTextureFilter) ((Tsc[1] >> 4) & 3); GalTextureFilter MinFilter = (GalTextureFilter) ((Tsc[1] >> 4) & 3);
GalTextureMipFilter MipFilter = (GalTextureMipFilter)((Tsc[1] >> 6) & 3); GalTextureMipFilter MipFilter = (GalTextureMipFilter)((Tsc[1] >> 6) & 3);
@ -85,7 +132,9 @@ namespace Ryujinx.Graphics.Texture
MinFilter, MinFilter,
MagFilter, MagFilter,
MipFilter, MipFilter,
BorderColor); BorderColor,
DepthCompare,
DepthCompareFunc);
} }
private static GalImageFormat GetImageFormat(int[] Tic) private static GalImageFormat GetImageFormat(int[] Tic)

View File

@ -1,4 +1,5 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.Common;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Memory;
@ -9,9 +10,13 @@ namespace Ryujinx.Graphics.Texture
public static ISwizzle GetSwizzle(GalImage Image) public static ISwizzle GetSwizzle(GalImage Image)
{ {
int BlockWidth = ImageUtils.GetBlockWidth (Image.Format); int BlockWidth = ImageUtils.GetBlockWidth (Image.Format);
int BlockHeight = ImageUtils.GetBlockHeight (Image.Format);
int BlockDepth = ImageUtils.GetBlockDepth (Image.Format);
int BytesPerPixel = ImageUtils.GetBytesPerPixel(Image.Format); int BytesPerPixel = ImageUtils.GetBytesPerPixel(Image.Format);
int Width = (Image.Width + (BlockWidth - 1)) / BlockWidth; int Width = BitUtils.DivRoundUp(Image.Width, BlockWidth);
int Height = BitUtils.DivRoundUp(Image.Height, BlockHeight);
int Depth = BitUtils.DivRoundUp(Image.Depth, BlockDepth);
if (Image.Layout == GalMemoryLayout.BlockLinear) if (Image.Layout == GalMemoryLayout.BlockLinear)
{ {
@ -19,11 +24,17 @@ namespace Ryujinx.Graphics.Texture
Width = (Width + AlignMask) & ~AlignMask; Width = (Width + AlignMask) & ~AlignMask;
return new BlockLinearSwizzle(Width, BytesPerPixel, Image.GobBlockHeight); return new BlockLinearSwizzle(
Width,
Height,
Depth,
Image.GobBlockHeight,
Image.GobBlockDepth,
BytesPerPixel);
} }
else else
{ {
return new LinearSwizzle(Image.Pitch, BytesPerPixel); return new LinearSwizzle(Image.Pitch, BytesPerPixel, Width, Height);
} }
} }

View File

@ -0,0 +1,19 @@
using System;
namespace Ryujinx.Graphics.Texture
{
[Flags]
public enum TextureInstructionSuffix
{
None = 0x00, // No Modifier
LZ = 0x02, // Load LOD Zero
LB = 0x08, // Load Bias
LL = 0x10, // Load LOD
LBA = 0x20, // Load Bias with OperA? Auto?
LLA = 0x40, // Load LOD with OperA? Auto?
DC = 0x80, // Depth Compare
AOffI = 0x100, // Offset
MZ = 0x200, // Multisample Zero?
PTP = 0x400 // ???
}
}

View File

@ -216,10 +216,11 @@ namespace Ryujinx.Graphics.VDec
GalImage Image = new GalImage( GalImage Image = new GalImage(
OutputConfig.SurfaceWidth, OutputConfig.SurfaceWidth,
OutputConfig.SurfaceHeight, 1, OutputConfig.SurfaceHeight, 1, 1, 1,
OutputConfig.GobBlockHeight, OutputConfig.GobBlockHeight, 1,
GalMemoryLayout.BlockLinear, GalMemoryLayout.BlockLinear,
GalImageFormat.RGBA8 | GalImageFormat.Unorm); GalImageFormat.RGBA8 | GalImageFormat.Unorm,
GalTextureTarget.TwoD);
ImageUtils.WriteTexture(Vmm, Image, Vmm.GetPhysicalAddress(OutputConfig.SurfaceLumaAddress), Frame.Data); ImageUtils.WriteTexture(Vmm, Image, Vmm.GetPhysicalAddress(OutputConfig.SurfaceLumaAddress), Frame.Data);
} }

View File

@ -1,6 +1,7 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Memory;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS; using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS;
using Ryujinx.HLE.HOS.Services.Nv.NvMap; using Ryujinx.HLE.HOS.Services.Nv.NvMap;
@ -415,9 +416,10 @@ namespace Ryujinx.HLE.HOS.Services.Android
{ {
image = new GalImage( image = new GalImage(
fbWidth, fbWidth,
fbHeight, 1, BlockHeight, fbHeight, 1, 1, 1, BlockHeight, 1,
GalMemoryLayout.BlockLinear, GalMemoryLayout.BlockLinear,
imageFormat); imageFormat,
GalTextureTarget.TwoD);
} }
context.Device.Gpu.ResourceManager.ClearPbCache(); context.Device.Gpu.ResourceManager.ClearPbCache();

View File

@ -13,7 +13,7 @@ namespace Ryujinx.ShaderTools
{ {
if (args.Length == 2) if (args.Length == 2)
{ {
GlslDecompiler Decompiler = new GlslDecompiler(MaxUboSize); GlslDecompiler Decompiler = new GlslDecompiler(MaxUboSize, true);
GalShaderType ShaderType = GalShaderType.Vertex; GalShaderType ShaderType = GalShaderType.Vertex;