using OpenTK.Graphics.OpenGL; using Ryujinx.Graphics.Gal.Texture; using System; namespace Ryujinx.Graphics.Gal.OpenGL { class OGLTexture : IGalTexture { private OGLCachedResource TextureCache; public OGLTexture() { TextureCache = new OGLCachedResource(DeleteTexture); } public void LockCache() { TextureCache.Lock(); } public void UnlockCache() { TextureCache.Unlock(); } private static void DeleteTexture(ImageHandler CachedImage) { GL.DeleteTexture(CachedImage.Handle); } public void Create(long Key, byte[] Data, GalImage Image) { int Handle = GL.GenTexture(); TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Data.Length); GL.BindTexture(TextureTarget.Texture2D, Handle); const int Level = 0; //TODO: Support mipmap textures. const int Border = 0; if (IsCompressedTextureFormat(Image.Format)) { InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format); GL.CompressedTexImage2D( TextureTarget.Texture2D, Level, InternalFmt, Image.Width, Image.Height, Border, Data.Length, Data); } else { if (Image.Format >= GalImageFormat.ASTC_BEGIN && Image.Format <= GalImageFormat.ASTC_END) { int TextureBlockWidth = GetAstcBlockWidth(Image.Format); int TextureBlockHeight = GetAstcBlockHeight(Image.Format); Data = ASTCDecoder.DecodeToRGBA8888( Data, TextureBlockWidth, TextureBlockHeight, 1, Image.Width, Image.Height, 1); Image.Format = GalImageFormat.A8B8G8R8_UNORM_PACK32; } (PixelInternalFormat InternalFormat, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format); GL.TexImage2D( TextureTarget.Texture2D, Level, InternalFormat, Image.Width, Image.Height, Border, Format, Type, Data); } int SwizzleR = (int)OGLEnumConverter.GetTextureSwizzle(Image.XSource); int SwizzleG = (int)OGLEnumConverter.GetTextureSwizzle(Image.YSource); int SwizzleB = (int)OGLEnumConverter.GetTextureSwizzle(Image.ZSource); int SwizzleA = (int)OGLEnumConverter.GetTextureSwizzle(Image.WSource); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleR, SwizzleR); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleG, SwizzleG); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleB, SwizzleB); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, SwizzleA); } public void CreateFb(long Key, long Size, GalImage Image) { if (!TryGetImage(Key, out ImageHandler CachedImage)) { CachedImage = new ImageHandler(); TextureCache.AddOrUpdate(Key, CachedImage, Size); } CachedImage.EnsureSetup(Image); } public bool TryGetImage(long Key, out ImageHandler CachedImage) { if (TextureCache.TryGetValue(Key, out CachedImage)) { return true; } CachedImage = null; return false; } private static int GetAstcBlockWidth(GalImageFormat Format) { switch (Format) { case GalImageFormat.ASTC_4x4_UNORM_BLOCK: return 4; case GalImageFormat.ASTC_5x5_UNORM_BLOCK: return 5; case GalImageFormat.ASTC_6x6_UNORM_BLOCK: return 6; case GalImageFormat.ASTC_8x8_UNORM_BLOCK: return 8; case GalImageFormat.ASTC_10x10_UNORM_BLOCK: return 10; case GalImageFormat.ASTC_12x12_UNORM_BLOCK: return 12; case GalImageFormat.ASTC_5x4_UNORM_BLOCK: return 5; case GalImageFormat.ASTC_6x5_UNORM_BLOCK: return 6; case GalImageFormat.ASTC_8x6_UNORM_BLOCK: return 8; case GalImageFormat.ASTC_10x8_UNORM_BLOCK: return 10; case GalImageFormat.ASTC_12x10_UNORM_BLOCK: return 12; case GalImageFormat.ASTC_8x5_UNORM_BLOCK: return 8; case GalImageFormat.ASTC_10x5_UNORM_BLOCK: return 10; case GalImageFormat.ASTC_10x6_UNORM_BLOCK: return 10; } throw new ArgumentException(nameof(Format)); } private static int GetAstcBlockHeight(GalImageFormat Format) { switch (Format) { case GalImageFormat.ASTC_4x4_UNORM_BLOCK: return 4; case GalImageFormat.ASTC_5x5_UNORM_BLOCK: return 5; case GalImageFormat.ASTC_6x6_UNORM_BLOCK: return 6; case GalImageFormat.ASTC_8x8_UNORM_BLOCK: return 8; case GalImageFormat.ASTC_10x10_UNORM_BLOCK: return 10; case GalImageFormat.ASTC_12x12_UNORM_BLOCK: return 12; case GalImageFormat.ASTC_5x4_UNORM_BLOCK: return 4; case GalImageFormat.ASTC_6x5_UNORM_BLOCK: return 5; case GalImageFormat.ASTC_8x6_UNORM_BLOCK: return 6; case GalImageFormat.ASTC_10x8_UNORM_BLOCK: return 8; case GalImageFormat.ASTC_12x10_UNORM_BLOCK: return 10; case GalImageFormat.ASTC_8x5_UNORM_BLOCK: return 5; case GalImageFormat.ASTC_10x5_UNORM_BLOCK: return 5; case GalImageFormat.ASTC_10x6_UNORM_BLOCK: return 6; } throw new ArgumentException(nameof(Format)); } public bool TryGetCachedTexture(long Key, long DataSize, out GalImage Image) { if (TextureCache.TryGetSize(Key, out long Size) && Size == DataSize) { if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage)) { Image = CachedImage.Image; return true; } } Image = default(GalImage); return false; } public void Bind(long Key, int Index) { if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage)) { GL.ActiveTexture(TextureUnit.Texture0 + Index); GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle); } } public void SetSampler(GalTextureSampler Sampler) { int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU); int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV); int MinFilter = (int)OGLEnumConverter.GetTextureMinFilter(Sampler.MinFilter, Sampler.MipFilter); int MagFilter = (int)OGLEnumConverter.GetTextureMagFilter(Sampler.MagFilter); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, WrapS); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, WrapT); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter); float[] Color = new float[] { Sampler.BorderColor.Red, Sampler.BorderColor.Green, Sampler.BorderColor.Blue, Sampler.BorderColor.Alpha }; GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBorderColor, Color); } private static bool IsCompressedTextureFormat(GalImageFormat Format) { switch (Format) { case GalImageFormat.BC6H_UFLOAT_BLOCK: case GalImageFormat.BC6H_SFLOAT_BLOCK: case GalImageFormat.BC7_UNORM_BLOCK: case GalImageFormat.BC1_RGBA_UNORM_BLOCK: case GalImageFormat.BC2_UNORM_BLOCK: case GalImageFormat.BC3_UNORM_BLOCK: case GalImageFormat.BC4_SNORM_BLOCK: case GalImageFormat.BC4_UNORM_BLOCK: case GalImageFormat.BC5_SNORM_BLOCK: case GalImageFormat.BC5_UNORM_BLOCK: return true; } return false; } } }