Ryujinx/Ryujinx.Graphics/Gal/OpenGL/OglTexture.cs
2019-03-18 09:55:02 +11:00

406 lines
15 KiB
C#

using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.Texture;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OglTexture : IGalTexture
{
private const long MaxTextureCacheSize = 768 * 1024 * 1024;
private OglCachedResource<ImageHandler> _textureCache;
public EventHandler<int> TextureDeleted { get; set; }
public OglTexture()
{
_textureCache = new OglCachedResource<ImageHandler>(DeleteTexture, MaxTextureCacheSize);
}
public void LockCache()
{
_textureCache.Lock();
}
public void UnlockCache()
{
_textureCache.Unlock();
}
private void DeleteTexture(ImageHandler cachedImage)
{
TextureDeleted?.Invoke(this, cachedImage.Handle);
GL.DeleteTexture(cachedImage.Handle);
}
public void Create(long key, int size, GalImage image)
{
int handle = GL.GenTexture();
TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget);
GL.BindTexture(target, handle);
const int level = 0; //TODO: Support mipmap textures.
const int border = 0;
_textureCache.AddOrUpdate(key, new ImageHandler(handle, image), (uint)size);
if (ImageUtils.IsCompressed(image.Format))
{
throw new InvalidOperationException("Surfaces with compressed formats are not supported!");
}
(PixelInternalFormat internalFmt,
PixelFormat format,
PixelType type) = OglEnumConverter.GetImageFormat(image.Format);
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}");
}
}
public void Create(long key, byte[] data, GalImage image)
{
int handle = GL.GenTexture();
TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget);
GL.BindTexture(target, handle);
const int level = 0; //TODO: Support mipmap textures.
const int border = 0;
_textureCache.AddOrUpdate(key, new ImageHandler(handle, image), (uint)data.Length);
if (ImageUtils.IsCompressed(image.Format) && !IsAstc(image.Format))
{
InternalFormat internalFmt = OglEnumConverter.GetCompressedImageFormat(image.Format);
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;
// Cube map arrays are just 2D texture arrays with 6 entries
// per cube map so we can handle them in the same way
case TextureTarget.TextureCubeMapArray:
case TextureTarget.Texture2DArray:
GL.CompressedTexImage3D(
target,
level,
internalFmt,
image.Width,
image.Height,
image.LayerCount,
border,
data.Length,
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.CompressedTexImage2D(
TextureTarget.TextureCubeMapPositiveX + Face,
level,
internalFmt,
image.Width,
image.Height,
border,
faceSize,
array.Slice(Face * faceSize, faceSize).ToArray());
}
break;
default:
throw new NotImplementedException($"Unsupported texture target type: {target}");
}
}
else
{
//TODO: Use KHR_texture_compression_astc_hdr when available
if (IsAstc(image.Format))
{
int textureBlockWidth = ImageUtils.GetBlockWidth(image.Format);
int textureBlockHeight = ImageUtils.GetBlockHeight(image.Format);
int textureBlockDepth = ImageUtils.GetBlockDepth(image.Format);
data = AstcDecoder.DecodeToRgba8888(
data,
textureBlockWidth,
textureBlockHeight,
textureBlockDepth,
image.Width,
image.Height,
image.Depth);
image.Format = GalImageFormat.Rgba8 | (image.Format & GalImageFormat.TypeMask);
}
(PixelInternalFormat internalFmt,
PixelFormat format,
PixelType type) = OglEnumConverter.GetImageFormat(image.Format);
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;
// Cube map arrays are just 2D texture arrays with 6 entries
// per cube map so we can handle them in the same way
case TextureTarget.TextureCubeMapArray:
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}");
}
}
}
private static bool IsAstc(GalImageFormat format)
{
format &= GalImageFormat.FormatMask;
return format > GalImageFormat.Astc2DStart && format < GalImageFormat.Astc2DEnd;
}
public bool TryGetImage(long key, out GalImage image)
{
if (_textureCache.TryGetValue(key, out ImageHandler cachedImage))
{
image = cachedImage.Image;
return true;
}
image = default(GalImage);
return false;
}
public bool TryGetImageHandler(long key, out ImageHandler cachedImage)
{
if (_textureCache.TryGetValue(key, out cachedImage))
{
return true;
}
cachedImage = null;
return false;
}
public void Bind(long key, int index, GalImage image)
{
if (_textureCache.TryGetValue(key, out ImageHandler cachedImage))
{
GL.ActiveTexture(TextureUnit.Texture0 + index);
TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget);
GL.BindTexture(target, cachedImage.Handle);
int[] swizzleRgba = new int[]
{
(int)OglEnumConverter.GetTextureSwizzle(image.XSource),
(int)OglEnumConverter.GetTextureSwizzle(image.YSource),
(int)OglEnumConverter.GetTextureSwizzle(image.ZSource),
(int)OglEnumConverter.GetTextureSwizzle(image.WSource)
};
GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba);
}
}
public void SetSampler(GalImage image, GalTextureSampler sampler)
{
int wrapS = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressU);
int wrapT = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressV);
int wrapR = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressP);
int minFilter = (int)OglEnumConverter.GetTextureMinFilter(sampler.MinFilter, sampler.MipFilter);
int magFilter = (int)OglEnumConverter.GetTextureMagFilter(sampler.MagFilter);
TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget);
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);
float[] color = new float[]
{
sampler.BorderColor.Red,
sampler.BorderColor.Green,
sampler.BorderColor.Blue,
sampler.BorderColor.Alpha
};
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);
}
}
}
}