2018-04-08 20:17:35 +01:00
|
|
|
using OpenTK.Graphics.OpenGL;
|
2018-09-08 18:51:50 +01:00
|
|
|
using Ryujinx.Graphics.Texture;
|
2018-06-02 04:50:56 +01:00
|
|
|
using System;
|
2018-04-08 20:17:35 +01:00
|
|
|
|
|
|
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
|
|
|
{
|
2018-08-20 02:25:26 +01:00
|
|
|
class OGLTexture : IGalTexture
|
2018-04-08 20:17:35 +01:00
|
|
|
{
|
2018-11-14 22:22:02 +00:00
|
|
|
private const long MaxTextureCacheSize = 768 * 1024 * 1024;
|
|
|
|
|
2018-08-20 02:25:26 +01:00
|
|
|
private OGLCachedResource<ImageHandler> TextureCache;
|
2018-04-08 20:17:35 +01:00
|
|
|
|
2018-10-23 21:59:52 +01:00
|
|
|
public EventHandler<int> TextureDeleted { get; set; }
|
|
|
|
|
2018-04-08 20:17:35 +01:00
|
|
|
public OGLTexture()
|
|
|
|
{
|
2018-11-14 22:22:02 +00:00
|
|
|
TextureCache = new OGLCachedResource<ImageHandler>(DeleteTexture, MaxTextureCacheSize);
|
2018-04-08 20:17:35 +01:00
|
|
|
}
|
|
|
|
|
2018-07-10 03:01:59 +01:00
|
|
|
public void LockCache()
|
|
|
|
{
|
|
|
|
TextureCache.Lock();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void UnlockCache()
|
|
|
|
{
|
|
|
|
TextureCache.Unlock();
|
|
|
|
}
|
|
|
|
|
2018-10-23 21:59:52 +01:00
|
|
|
private void DeleteTexture(ImageHandler CachedImage)
|
2018-04-08 20:17:35 +01:00
|
|
|
{
|
2018-10-23 21:59:52 +01:00
|
|
|
TextureDeleted?.Invoke(this, CachedImage.Handle);
|
|
|
|
|
2018-08-20 02:25:26 +01:00
|
|
|
GL.DeleteTexture(CachedImage.Handle);
|
2018-06-09 01:15:56 +01:00
|
|
|
}
|
2018-04-08 20:17:35 +01:00
|
|
|
|
2018-09-18 05:30:35 +01:00
|
|
|
public void Create(long Key, int Size, GalImage Image)
|
2018-06-09 01:15:56 +01:00
|
|
|
{
|
|
|
|
int Handle = GL.GenTexture();
|
|
|
|
|
2019-02-28 01:12:24 +00:00
|
|
|
TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
|
|
|
|
|
|
|
|
GL.BindTexture(Target, Handle);
|
2018-09-18 05:30:35 +01:00
|
|
|
|
|
|
|
const int Level = 0; //TODO: Support mipmap textures.
|
|
|
|
const int Border = 0;
|
|
|
|
|
|
|
|
TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Size);
|
|
|
|
|
2018-10-17 22:02:23 +01:00
|
|
|
if (ImageUtils.IsCompressed(Image.Format))
|
2018-09-18 05:30:35 +01:00
|
|
|
{
|
2018-10-17 22:02:23 +01:00
|
|
|
throw new InvalidOperationException("Surfaces with compressed formats are not supported!");
|
2018-09-18 05:30:35 +01:00
|
|
|
}
|
|
|
|
|
2018-10-17 22:02:23 +01:00
|
|
|
(PixelInternalFormat InternalFmt,
|
|
|
|
PixelFormat Format,
|
|
|
|
PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format);
|
|
|
|
|
2019-02-28 01:12:24 +00:00
|
|
|
switch (Target)
|
|
|
|
{
|
|
|
|
case TextureTarget.Texture1D:
|
|
|
|
GL.TexImage1D(
|
|
|
|
Target,
|
|
|
|
Level,
|
|
|
|
InternalFmt,
|
|
|
|
Image.Width,
|
|
|
|
Border,
|
|
|
|
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}");
|
|
|
|
}
|
2018-09-18 05:30:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Create(long Key, byte[] Data, GalImage Image)
|
|
|
|
{
|
|
|
|
int Handle = GL.GenTexture();
|
2018-06-09 01:15:56 +01:00
|
|
|
|
2019-02-28 01:12:24 +00:00
|
|
|
TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
|
|
|
|
|
|
|
|
GL.BindTexture(Target, Handle);
|
2018-04-08 20:17:35 +01:00
|
|
|
|
2018-04-13 19:12:58 +01:00
|
|
|
const int Level = 0; //TODO: Support mipmap textures.
|
2018-04-10 20:50:32 +01:00
|
|
|
const int Border = 0;
|
2018-04-08 20:17:35 +01:00
|
|
|
|
2018-09-18 05:30:35 +01:00
|
|
|
TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Data.Length);
|
|
|
|
|
2018-10-17 22:02:23 +01:00
|
|
|
if (ImageUtils.IsCompressed(Image.Format) && !IsAstc(Image.Format))
|
2018-04-08 20:17:35 +01:00
|
|
|
{
|
2018-08-20 02:25:26 +01:00
|
|
|
InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format);
|
2018-04-10 20:50:32 +01:00
|
|
|
|
2019-02-28 01:12:24 +00:00
|
|
|
switch (Target)
|
|
|
|
{
|
|
|
|
case TextureTarget.Texture1D:
|
|
|
|
GL.CompressedTexImage1D(
|
|
|
|
Target,
|
|
|
|
Level,
|
|
|
|
InternalFmt,
|
|
|
|
Image.Width,
|
|
|
|
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}");
|
|
|
|
}
|
2018-04-08 20:17:35 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-09-08 18:51:50 +01:00
|
|
|
//TODO: Use KHR_texture_compression_astc_hdr when available
|
2018-10-17 22:02:23 +01:00
|
|
|
if (IsAstc(Image.Format))
|
2018-06-02 04:50:56 +01:00
|
|
|
{
|
2018-09-18 05:30:35 +01:00
|
|
|
int TextureBlockWidth = ImageUtils.GetBlockWidth(Image.Format);
|
|
|
|
int TextureBlockHeight = ImageUtils.GetBlockHeight(Image.Format);
|
2019-02-28 01:12:24 +00:00
|
|
|
int TextureBlockDepth = ImageUtils.GetBlockDepth(Image.Format);
|
2018-06-09 01:15:56 +01:00
|
|
|
|
|
|
|
Data = ASTCDecoder.DecodeToRGBA8888(
|
|
|
|
Data,
|
|
|
|
TextureBlockWidth,
|
2019-02-28 01:12:24 +00:00
|
|
|
TextureBlockHeight,
|
|
|
|
TextureBlockDepth,
|
2018-08-20 02:25:26 +01:00
|
|
|
Image.Width,
|
2019-02-28 01:12:24 +00:00
|
|
|
Image.Height,
|
|
|
|
Image.Depth);
|
2018-06-09 01:15:56 +01:00
|
|
|
|
2018-11-17 04:10:51 +00:00
|
|
|
Image.Format = GalImageFormat.RGBA8 | (Image.Format & GalImageFormat.TypeMask);
|
2018-06-02 04:50:56 +01:00
|
|
|
}
|
|
|
|
|
2018-09-18 05:30:35 +01:00
|
|
|
(PixelInternalFormat InternalFmt,
|
|
|
|
PixelFormat Format,
|
|
|
|
PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format);
|
2018-04-10 20:50:32 +01:00
|
|
|
|
2019-02-28 01:12:24 +00:00
|
|
|
|
|
|
|
switch (Target)
|
|
|
|
{
|
|
|
|
case TextureTarget.Texture1D:
|
|
|
|
GL.TexImage1D(
|
|
|
|
Target,
|
|
|
|
Level,
|
|
|
|
InternalFmt,
|
|
|
|
Image.Width,
|
|
|
|
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}");
|
|
|
|
}
|
2018-04-08 20:17:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-17 22:02:23 +01:00
|
|
|
private static bool IsAstc(GalImageFormat Format)
|
|
|
|
{
|
|
|
|
Format &= GalImageFormat.FormatMask;
|
|
|
|
|
|
|
|
return Format > GalImageFormat.Astc2DStart && Format < GalImageFormat.Astc2DEnd;
|
|
|
|
}
|
|
|
|
|
2018-09-18 05:30:35 +01:00
|
|
|
public bool TryGetImage(long Key, out GalImage Image)
|
2018-08-20 02:25:26 +01:00
|
|
|
{
|
2018-09-18 05:30:35 +01:00
|
|
|
if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage))
|
2018-08-20 02:25:26 +01:00
|
|
|
{
|
2018-09-18 05:30:35 +01:00
|
|
|
Image = CachedImage.Image;
|
2018-08-20 02:25:26 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-09-18 05:30:35 +01:00
|
|
|
Image = default(GalImage);
|
2018-08-20 02:25:26 +01:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-09-18 05:30:35 +01:00
|
|
|
public bool TryGetImageHandler(long Key, out ImageHandler CachedImage)
|
2018-06-02 04:50:56 +01:00
|
|
|
{
|
2018-09-18 05:30:35 +01:00
|
|
|
if (TextureCache.TryGetValue(Key, out CachedImage))
|
2018-06-09 01:15:56 +01:00
|
|
|
{
|
2018-09-18 05:30:35 +01:00
|
|
|
return true;
|
2018-06-09 01:15:56 +01:00
|
|
|
}
|
|
|
|
|
2018-09-18 05:30:35 +01:00
|
|
|
CachedImage = null;
|
2018-06-09 01:15:56 +01:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-09-18 05:30:35 +01:00
|
|
|
public void Bind(long Key, int Index, GalImage Image)
|
2018-04-08 20:17:35 +01:00
|
|
|
{
|
2018-08-20 02:25:26 +01:00
|
|
|
if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage))
|
2018-06-09 01:15:56 +01:00
|
|
|
{
|
|
|
|
GL.ActiveTexture(TextureUnit.Texture0 + Index);
|
2018-04-08 20:17:35 +01:00
|
|
|
|
2019-02-28 01:12:24 +00:00
|
|
|
TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
|
|
|
|
|
|
|
|
GL.BindTexture(Target, CachedImage.Handle);
|
2018-09-18 05:30:35 +01:00
|
|
|
|
|
|
|
int[] SwizzleRgba = new int[]
|
|
|
|
{
|
|
|
|
(int)OGLEnumConverter.GetTextureSwizzle(Image.XSource),
|
|
|
|
(int)OGLEnumConverter.GetTextureSwizzle(Image.YSource),
|
|
|
|
(int)OGLEnumConverter.GetTextureSwizzle(Image.ZSource),
|
|
|
|
(int)OGLEnumConverter.GetTextureSwizzle(Image.WSource)
|
|
|
|
};
|
|
|
|
|
2019-02-28 01:12:24 +00:00
|
|
|
GL.TexParameter(Target, TextureParameterName.TextureSwizzleRgba, SwizzleRgba);
|
2018-06-09 01:15:56 +01:00
|
|
|
}
|
2018-04-13 19:12:58 +01:00
|
|
|
}
|
2018-04-08 20:17:35 +01:00
|
|
|
|
2019-02-28 01:12:24 +00:00
|
|
|
public void SetSampler(GalImage Image, GalTextureSampler Sampler)
|
2018-04-13 19:12:58 +01:00
|
|
|
{
|
2018-04-08 20:17:35 +01:00
|
|
|
int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU);
|
|
|
|
int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV);
|
2019-02-28 01:12:24 +00:00
|
|
|
int WrapR = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressP);
|
2018-04-08 20:17:35 +01:00
|
|
|
|
|
|
|
int MinFilter = (int)OGLEnumConverter.GetTextureMinFilter(Sampler.MinFilter, Sampler.MipFilter);
|
|
|
|
int MagFilter = (int)OGLEnumConverter.GetTextureMagFilter(Sampler.MagFilter);
|
|
|
|
|
2019-02-28 01:12:24 +00:00
|
|
|
TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
|
2018-04-08 20:17:35 +01:00
|
|
|
|
2019-02-28 01:12:24 +00:00
|
|
|
GL.TexParameter(Target, TextureParameterName.TextureWrapS, WrapS);
|
|
|
|
GL.TexParameter(Target, TextureParameterName.TextureWrapT, WrapT);
|
|
|
|
GL.TexParameter(Target, TextureParameterName.TextureWrapR, WrapR);
|
|
|
|
|
|
|
|
GL.TexParameter(Target, TextureParameterName.TextureMinFilter, MinFilter);
|
|
|
|
GL.TexParameter(Target, TextureParameterName.TextureMagFilter, MagFilter);
|
2018-04-08 20:17:35 +01:00
|
|
|
|
|
|
|
float[] Color = new float[]
|
|
|
|
{
|
|
|
|
Sampler.BorderColor.Red,
|
|
|
|
Sampler.BorderColor.Green,
|
|
|
|
Sampler.BorderColor.Blue,
|
|
|
|
Sampler.BorderColor.Alpha
|
|
|
|
};
|
|
|
|
|
2019-02-28 01:12:24 +00:00
|
|
|
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);
|
|
|
|
}
|
2018-04-08 20:17:35 +01:00
|
|
|
}
|
|
|
|
}
|
2018-07-16 19:09:34 +01:00
|
|
|
}
|