Merge Latest Ryujinx (Unstable)

This commit is contained in:
Stossy11 2025-03-08 10:13:40 +11:00
parent aaefc0a9e5
commit 12ab8bc3e2
1237 changed files with 48656 additions and 21399 deletions

View file

@ -30,6 +30,8 @@ namespace Ryujinx.Graphics.OpenGL
_thread.Start();
}
public bool HasContext() => _backgroundContext.HasContext();
private void Run()
{
InBackground = true;

View file

@ -0,0 +1,106 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.OpenGL.Image;
using System;
using static Ryujinx.Graphics.OpenGL.Effects.ShaderHelper;
namespace Ryujinx.Graphics.OpenGL.Effects
{
internal class AreaScalingFilter : IScalingFilter
{
private readonly OpenGLRenderer _renderer;
private int _inputUniform;
private int _outputUniform;
private int _srcX0Uniform;
private int _srcX1Uniform;
private int _srcY0Uniform;
private int _scalingShaderProgram;
private int _srcY1Uniform;
private int _dstX0Uniform;
private int _dstX1Uniform;
private int _dstY0Uniform;
private int _dstY1Uniform;
public float Level { get; set; }
public AreaScalingFilter(OpenGLRenderer renderer)
{
Initialize();
_renderer = renderer;
}
public void Dispose()
{
if (_scalingShaderProgram != 0)
{
GL.DeleteProgram(_scalingShaderProgram);
}
}
private void Initialize()
{
var scalingShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/area_scaling.glsl");
_scalingShaderProgram = CompileProgram(scalingShader, ShaderType.ComputeShader);
_inputUniform = GL.GetUniformLocation(_scalingShaderProgram, "Source");
_outputUniform = GL.GetUniformLocation(_scalingShaderProgram, "imgOutput");
_srcX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX0");
_srcX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX1");
_srcY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY0");
_srcY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY1");
_dstX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX0");
_dstX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX1");
_dstY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY0");
_dstY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY1");
}
public void Run(
TextureView view,
TextureView destinationTexture,
int width,
int height,
Extents2D source,
Extents2D destination)
{
int previousProgram = GL.GetInteger(GetPName.CurrentProgram);
int previousUnit = GL.GetInteger(GetPName.ActiveTexture);
GL.ActiveTexture(TextureUnit.Texture0);
int previousTextureBinding = GL.GetInteger(GetPName.TextureBinding2D);
GL.BindImageTexture(0, destinationTexture.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
int threadGroupWorkRegionDim = 16;
int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
// Scaling pass
GL.UseProgram(_scalingShaderProgram);
view.Bind(0);
GL.Uniform1(_inputUniform, 0);
GL.Uniform1(_outputUniform, 0);
GL.Uniform1(_srcX0Uniform, (float)source.X1);
GL.Uniform1(_srcX1Uniform, (float)source.X2);
GL.Uniform1(_srcY0Uniform, (float)source.Y1);
GL.Uniform1(_srcY1Uniform, (float)source.Y2);
GL.Uniform1(_dstX0Uniform, (float)destination.X1);
GL.Uniform1(_dstX1Uniform, (float)destination.X2);
GL.Uniform1(_dstY0Uniform, (float)destination.Y1);
GL.Uniform1(_dstY1Uniform, (float)destination.Y2);
GL.DispatchCompute(dispatchX, dispatchY, 1);
GL.UseProgram(previousProgram);
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
(_renderer.Pipeline as Pipeline).RestoreImages1And2();
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding);
GL.ActiveTexture((TextureUnit)previousUnit);
}
}
}

View file

@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.OpenGL.Effects
private int _srcY0Uniform;
private int _scalingShaderProgram;
private int _sharpeningShaderProgram;
private float _scale = 1;
private float _sharpeningLevel = 1;
private int _srcY1Uniform;
private int _dstX0Uniform;
private int _dstX1Uniform;
@ -30,10 +30,10 @@ namespace Ryujinx.Graphics.OpenGL.Effects
public float Level
{
get => _scale;
get => _sharpeningLevel;
set
{
_scale = MathF.Max(0.01f, value);
_sharpeningLevel = MathF.Max(0.01f, value);
}
}

View file

@ -1,4 +1,5 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Logging;
namespace Ryujinx.Graphics.OpenGL.Effects
{
@ -6,18 +7,7 @@ namespace Ryujinx.Graphics.OpenGL.Effects
{
public static int CompileProgram(string shaderCode, ShaderType shaderType)
{
var shader = GL.CreateShader(shaderType);
GL.ShaderSource(shader, shaderCode);
GL.CompileShader(shader);
var program = GL.CreateProgram();
GL.AttachShader(program, shader);
GL.LinkProgram(program);
GL.DetachShader(program, shader);
GL.DeleteShader(shader);
return program;
return CompileProgram(new string[] { shaderCode }, shaderType);
}
public static int CompileProgram(string[] shaders, ShaderType shaderType)
@ -26,6 +16,15 @@ namespace Ryujinx.Graphics.OpenGL.Effects
GL.ShaderSource(shader, shaders.Length, shaders, (int[])null);
GL.CompileShader(shader);
GL.GetShader(shader, ShaderParameter.CompileStatus, out int isCompiled);
if (isCompiled == 0)
{
string log = GL.GetShaderInfoLog(shader);
Logger.Error?.Print(LogClass.Gpu, $"Failed to compile effect shader:\n\n{log}\n");
GL.DeleteShader(shader);
return 0;
}
var program = GL.CreateProgram();
GL.AttachShader(program, shader);
GL.LinkProgram(program);

View file

@ -0,0 +1,119 @@
#version 430 core
precision mediump float;
layout (local_size_x = 16, local_size_y = 16) in;
layout(rgba8, binding = 0, location=0) uniform image2D imgOutput;
layout( location=1 ) uniform sampler2D Source;
layout( location=2 ) uniform float srcX0;
layout( location=3 ) uniform float srcX1;
layout( location=4 ) uniform float srcY0;
layout( location=5 ) uniform float srcY1;
layout( location=6 ) uniform float dstX0;
layout( location=7 ) uniform float dstX1;
layout( location=8 ) uniform float dstY0;
layout( location=9 ) uniform float dstY1;
/***** Area Sampling *****/
// By Sam Belliveau and Filippo Tarpini. Public Domain license.
// Effectively a more accurate sharp bilinear filter when upscaling,
// that also works as a mathematically perfect downscale filter.
// https://entropymine.com/imageworsener/pixelmixing/
// https://github.com/obsproject/obs-studio/pull/1715
// https://legacy.imagemagick.org/Usage/filter/
vec4 AreaSampling(vec2 xy)
{
// Determine the sizes of the source and target images.
vec2 source_size = vec2(abs(srcX1 - srcX0), abs(srcY1 - srcY0));
vec2 target_size = vec2(abs(dstX1 - dstX0), abs(dstY1 - dstY0));
vec2 inverted_target_size = vec2(1.0) / target_size;
// Compute the top-left and bottom-right corners of the target pixel box.
vec2 t_beg = floor(xy - vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1));
vec2 t_end = t_beg + vec2(1.0, 1.0);
// Convert the target pixel box to source pixel box.
vec2 beg = t_beg * inverted_target_size * source_size;
vec2 end = t_end * inverted_target_size * source_size;
// Compute the top-left and bottom-right corners of the pixel box.
ivec2 f_beg = ivec2(beg);
ivec2 f_end = ivec2(end);
// Compute how much of the start and end pixels are covered horizontally & vertically.
float area_w = 1.0 - fract(beg.x);
float area_n = 1.0 - fract(beg.y);
float area_e = fract(end.x);
float area_s = fract(end.y);
// Compute the areas of the corner pixels in the pixel box.
float area_nw = area_n * area_w;
float area_ne = area_n * area_e;
float area_sw = area_s * area_w;
float area_se = area_s * area_e;
// Initialize the color accumulator.
vec4 avg_color = vec4(0.0, 0.0, 0.0, 0.0);
// Accumulate corner pixels.
avg_color += area_nw * texelFetch(Source, ivec2(f_beg.x, f_beg.y), 0);
avg_color += area_ne * texelFetch(Source, ivec2(f_end.x, f_beg.y), 0);
avg_color += area_sw * texelFetch(Source, ivec2(f_beg.x, f_end.y), 0);
avg_color += area_se * texelFetch(Source, ivec2(f_end.x, f_end.y), 0);
// Determine the size of the pixel box.
int x_range = int(f_end.x - f_beg.x - 0.5);
int y_range = int(f_end.y - f_beg.y - 0.5);
// Accumulate top and bottom edge pixels.
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x)
{
avg_color += area_n * texelFetch(Source, ivec2(x, f_beg.y), 0);
avg_color += area_s * texelFetch(Source, ivec2(x, f_end.y), 0);
}
// Accumulate left and right edge pixels and all the pixels in between.
for (int y = f_beg.y + 1; y <= f_beg.y + y_range; ++y)
{
avg_color += area_w * texelFetch(Source, ivec2(f_beg.x, y), 0);
avg_color += area_e * texelFetch(Source, ivec2(f_end.x, y), 0);
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x)
{
avg_color += texelFetch(Source, ivec2(x, y), 0);
}
}
// Compute the area of the pixel box that was sampled.
float area_corners = area_nw + area_ne + area_sw + area_se;
float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e);
float area_center = float(x_range) * float(y_range);
// Return the normalized average color.
return avg_color / (area_corners + area_edges + area_center);
}
float insideBox(vec2 v, vec2 bLeft, vec2 tRight) {
vec2 s = step(bLeft, v) - step(tRight, v);
return s.x * s.y;
}
vec2 translateDest(vec2 pos) {
vec2 translatedPos = vec2(pos.x, pos.y);
translatedPos.x = dstX1 < dstX0 ? dstX1 - translatedPos.x : translatedPos.x;
translatedPos.y = dstY0 > dstY1 ? dstY0 + dstY1 - translatedPos.y - 1 : translatedPos.y;
return translatedPos;
}
void main()
{
vec2 bLeft = vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1);
vec2 tRight = vec2(dstX1 > dstX0 ? dstX1 : dstX0, dstY1 > dstY0 ? dstY1 : dstY0);
ivec2 loc = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y);
if (insideBox(loc, bLeft, tRight) == 0) {
imageStore(imgOutput, loc, vec4(0, 0, 0, 1));
return;
}
vec4 outColor = AreaSampling(loc);
imageStore(imgOutput, ivec2(translateDest(loc)), vec4(outColor.rgb, 1));
}

View file

@ -85,4 +85,4 @@ void main() {
CurrFilter(gxy);
gxy.x -= 8u;
CurrFilter(gxy);
}
}

View file

@ -33,7 +33,8 @@ namespace Ryujinx.Graphics.OpenGL.Effects.Smaa
public int Quality
{
get => _quality; set
get => _quality;
set
{
_quality = Math.Clamp(value, 0, _qualities.Length - 1);
}
@ -150,8 +151,8 @@ namespace Ryujinx.Graphics.OpenGL.Effects.Smaa
_areaTexture = new TextureStorage(_renderer, areaInfo);
_searchTexture = new TextureStorage(_renderer, searchInfo);
var areaTexture = EmbeddedResources.Read("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin");
var searchTexture = EmbeddedResources.Read("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin");
var areaTexture = EmbeddedResources.ReadFileToRentedMemory("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin");
var searchTexture = EmbeddedResources.ReadFileToRentedMemory("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin");
var areaView = _areaTexture.CreateDefaultView();
var searchView = _searchTexture.CreateDefaultView();

View file

@ -68,6 +68,7 @@ namespace Ryujinx.Graphics.OpenGL
Add(Format.S8Uint, new FormatInfo(1, false, false, All.StencilIndex8, PixelFormat.StencilIndex, PixelType.UnsignedByte));
Add(Format.D16Unorm, new FormatInfo(1, false, false, All.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort));
Add(Format.S8UintD24Unorm, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248));
Add(Format.X8UintD24Unorm, new FormatInfo(1, false, false, All.DepthComponent24, PixelFormat.DepthComponent, PixelType.UnsignedInt));
Add(Format.D32Float, new FormatInfo(1, false, false, All.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float));
Add(Format.D24UnormS8Uint, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248));
Add(Format.D32FloatS8Uint, new FormatInfo(1, false, false, All.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev));
@ -161,6 +162,7 @@ namespace Ryujinx.Graphics.OpenGL
Add(Format.A1B5G5R5Unorm, new FormatInfo(4, true, false, All.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort5551));
Add(Format.B8G8R8A8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte));
Add(Format.B8G8R8A8Srgb, new FormatInfo(4, false, false, All.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte));
Add(Format.B10G10R10A2Unorm, new FormatInfo(4, false, false, All.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed));
Add(Format.R8Unorm, SizedInternalFormat.R8);
Add(Format.R8Uint, SizedInternalFormat.R8ui);
@ -223,5 +225,17 @@ namespace Ryujinx.Graphics.OpenGL
{
return _tableImage[(int)format];
}
public static bool IsPackedDepthStencil(Format format)
{
return format == Format.D24UnormS8Uint ||
format == Format.D32FloatS8Uint ||
format == Format.S8UintD24Unorm;
}
public static bool IsDepthOnly(Format format)
{
return format == Format.D16Unorm || format == Format.D32Float || format == Format.X8UintD24Unorm;
}
}
}

View file

@ -119,11 +119,11 @@ namespace Ryujinx.Graphics.OpenGL
private static FramebufferAttachment GetAttachment(Format format)
{
if (IsPackedDepthStencilFormat(format))
if (FormatTable.IsPackedDepthStencil(format))
{
return FramebufferAttachment.DepthStencilAttachment;
}
else if (IsDepthOnlyFormat(format))
else if (FormatTable.IsDepthOnly(format))
{
return FramebufferAttachment.DepthAttachment;
}
@ -133,18 +133,6 @@ namespace Ryujinx.Graphics.OpenGL
}
}
private static bool IsPackedDepthStencilFormat(Format format)
{
return format == Format.D24UnormS8Uint ||
format == Format.D32FloatS8Uint ||
format == Format.S8UintD24Unorm;
}
private static bool IsDepthOnlyFormat(Format format)
{
return format == Format.D16Unorm || format == Format.D32Float;
}
public int GetColorLayerCount(int index)
{
return _colors[index]?.Info.GetDepthOrLayers() ?? 0;

View file

@ -7,21 +7,6 @@ namespace Ryujinx.Graphics.OpenGL
{
void MakeCurrent();
// TODO: Support more APIs per platform.
static bool HasContext()
{
if (OperatingSystem.IsWindows())
{
return WGLHelper.GetCurrentContext() != IntPtr.Zero;
}
else if (OperatingSystem.IsLinux())
{
return GLXHelper.GetCurrentContext() != IntPtr.Zero;
}
else
{
return false;
}
}
bool HasContext();
}
}

View file

@ -1,3 +1,4 @@
using Ryujinx.Common.Memory;
using System;
using System.Numerics;
using System.Runtime.InteropServices;
@ -8,9 +9,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
{
static class FormatConverter
{
public unsafe static byte[] ConvertS8D24ToD24S8(ReadOnlySpan<byte> data)
public unsafe static MemoryOwner<byte> ConvertS8D24ToD24S8(ReadOnlySpan<byte> data)
{
byte[] output = new byte[data.Length];
MemoryOwner<byte> outputMemory = MemoryOwner<byte>.Rent(data.Length);
Span<byte> output = outputMemory.Span;
int start = 0;
@ -74,7 +77,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
outSpan[i] = BitOperations.RotateLeft(dataSpan[i], 8);
}
return output;
return outputMemory;
}
public unsafe static byte[] ConvertD24S8ToS8D24(ReadOnlySpan<byte> data)

View file

@ -0,0 +1,63 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.OpenGL.Image
{
class ImageArray : IImageArray
{
private record struct TextureRef
{
public int Handle;
public Format Format;
}
private readonly TextureRef[] _images;
public ImageArray(int size)
{
_images = new TextureRef[size];
}
public void SetImages(int index, ITexture[] images)
{
for (int i = 0; i < images.Length; i++)
{
ITexture image = images[i];
if (image is TextureBase imageBase)
{
_images[index + i].Handle = imageBase.Handle;
_images[index + i].Format = imageBase.Format;
}
else
{
_images[index + i].Handle = 0;
}
}
}
public void Bind(int baseBinding)
{
for (int i = 0; i < _images.Length; i++)
{
if (_images[i].Handle == 0)
{
GL.BindImageTexture(baseBinding + i, 0, 0, true, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
}
else
{
SizedInternalFormat format = FormatTable.GetImageFormat(_images[i].Format);
if (format != 0)
{
GL.BindImageTexture(baseBinding + i, _images[i].Handle, 0, true, 0, TextureAccess.ReadWrite, format);
}
}
}
}
public void Dispose()
{
}
}
}

View file

@ -0,0 +1,56 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.OpenGL.Image
{
class TextureArray : ITextureArray
{
private record struct TextureRef
{
public TextureBase Texture;
public Sampler Sampler;
}
private readonly TextureRef[] _textureRefs;
public TextureArray(int size)
{
_textureRefs = new TextureRef[size];
}
public void SetSamplers(int index, ISampler[] samplers)
{
for (int i = 0; i < samplers.Length; i++)
{
_textureRefs[index + i].Sampler = samplers[i] as Sampler;
}
}
public void SetTextures(int index, ITexture[] textures)
{
for (int i = 0; i < textures.Length; i++)
{
_textureRefs[index + i].Texture = textures[i] as TextureBase;
}
}
public void Bind(int baseBinding)
{
for (int i = 0; i < _textureRefs.Length; i++)
{
if (_textureRefs[i].Texture != null)
{
_textureRefs[i].Texture.Bind(baseBinding + i);
_textureRefs[i].Sampler?.Bind(baseBinding + i);
}
else
{
TextureBase.ClearBinding(baseBinding + i);
}
}
}
public void Dispose()
{
}
}
}

View file

@ -55,22 +55,25 @@ namespace Ryujinx.Graphics.OpenGL.Image
throw new NotImplementedException();
}
public void SetData(IMemoryOwner<byte> data)
/// <inheritdoc/>
public void SetData(MemoryOwner<byte> data)
{
var dataSpan = data.Memory.Span;
var dataSpan = data.Span;
Buffer.SetData(_buffer, _bufferOffset, dataSpan[..Math.Min(dataSpan.Length, _bufferSize)]);
data.Dispose();
}
public void SetData(IMemoryOwner<byte> data, int layer, int level)
/// <inheritdoc/>
public void SetData(MemoryOwner<byte> data, int layer, int level)
{
data.Dispose();
throw new NotSupportedException();
}
public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
/// <inheritdoc/>
public void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
{
data.Dispose();
throw new NotSupportedException();

View file

@ -294,7 +294,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
{
return FramebufferAttachment.DepthStencilAttachment;
}
else if (IsDepthOnly(format))
else if (FormatTable.IsDepthOnly(format))
{
return FramebufferAttachment.DepthAttachment;
}
@ -324,11 +324,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
private static ClearBufferMask GetMask(Format format)
{
if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint || format == Format.S8UintD24Unorm)
if (FormatTable.IsPackedDepthStencil(format))
{
return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit;
}
else if (IsDepthOnly(format))
else if (FormatTable.IsDepthOnly(format))
{
return ClearBufferMask.DepthBufferBit;
}
@ -342,11 +342,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
}
}
private static bool IsDepthOnly(Format format)
{
return format == Format.D16Unorm || format == Format.D32Float;
}
public TextureView BgraSwap(TextureView from)
{
TextureView to = (TextureView)_renderer.CreateTexture(from.Info);

View file

@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
internalFormat = (SizedInternalFormat)format.PixelInternalFormat;
}
int levels = Info.GetLevelsClamped();
int levels = Info.Levels;
switch (Info.Target)
{

View file

@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
pixelInternalFormat = format.PixelInternalFormat;
}
int levels = Info.GetLevelsClamped();
int levels = Info.Levels;
GL.TextureView(
Handle,
@ -268,7 +268,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
public unsafe PinnedSpan<byte> GetData()
{
int size = 0;
int levels = Info.GetLevelsClamped();
int levels = Info.Levels;
for (int level = 0; level < levels; level++)
{
@ -427,7 +427,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
faces = 6;
}
int levels = Info.GetLevelsClamped();
int levels = Info.Levels;
for (int level = 0; level < levels; level++)
{
@ -449,77 +449,61 @@ namespace Ryujinx.Graphics.OpenGL.Image
}
}
public void SetData(ReadOnlySpan<byte> dataSpan)
public void SetData(MemoryOwner<byte> data)
{
if (Format == Format.S8UintD24Unorm)
using (data = EnsureDataFormat(data))
{
dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
}
unsafe
{
fixed (byte* ptr = dataSpan)
unsafe
{
ReadFrom((IntPtr)ptr, dataSpan.Length);
var dataSpan = data.Span;
fixed (byte* ptr = dataSpan)
{
ReadFrom((IntPtr)ptr, dataSpan.Length);
}
}
}
}
public void SetData(IMemoryOwner<byte> data)
public void SetData(MemoryOwner<byte> data, int layer, int level)
{
SetData(data.Memory.Span);
data.Dispose();
}
public void SetData(IMemoryOwner<byte> data, int layer, int level)
{
var dataSpan = data.Memory.Span;
if (Format == Format.S8UintD24Unorm)
using (data = EnsureDataFormat(data))
{
dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
}
unsafe
{
fixed (byte* ptr = dataSpan)
unsafe
{
int width = Math.Max(Info.Width >> level, 1);
int height = Math.Max(Info.Height >> level, 1);
fixed (byte* ptr = data.Span)
{
int width = Math.Max(Info.Width >> level, 1);
int height = Math.Max(Info.Height >> level, 1);
ReadFrom2D((IntPtr)ptr, layer, level, 0, 0, width, height);
ReadFrom2D((IntPtr)ptr, layer, level, 0, 0, width, height);
}
}
}
data.Dispose();
}
public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
public void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
{
var dataSpan = data.Memory.Span;
if (Format == Format.S8UintD24Unorm)
using (data = EnsureDataFormat(data))
{
dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
}
int wInBlocks = BitUtils.DivRoundUp(region.Width, Info.BlockWidth);
int hInBlocks = BitUtils.DivRoundUp(region.Height, Info.BlockHeight);
int wInBlocks = BitUtils.DivRoundUp(region.Width, Info.BlockWidth);
int hInBlocks = BitUtils.DivRoundUp(region.Height, Info.BlockHeight);
unsafe
{
fixed (byte* ptr = dataSpan)
unsafe
{
ReadFrom2D(
(IntPtr)ptr,
layer,
level,
region.X,
region.Y,
region.Width,
region.Height,
BitUtils.AlignUp(wInBlocks * Info.BytesPerPixel, 4) * hInBlocks);
fixed (byte* ptr = data.Span)
{
ReadFrom2D(
(IntPtr)ptr,
layer,
level,
region.X,
region.Y,
region.Width,
region.Height,
BitUtils.AlignUp(wInBlocks * Info.BytesPerPixel, 4) * hInBlocks);
}
}
}
@ -543,6 +527,19 @@ namespace Ryujinx.Graphics.OpenGL.Image
ReadFrom2D(data, layer, level, x, y, width, height, mipSize);
}
private MemoryOwner<byte> EnsureDataFormat(MemoryOwner<byte> data)
{
if (Format == Format.S8UintD24Unorm)
{
using (data)
{
return FormatConverter.ConvertS8D24ToD24S8(data.Span);
}
}
return data;
}
private void ReadFrom2D(IntPtr data, int layer, int level, int x, int y, int width, int height, int mipSize)
{
TextureTarget target = Target.Convert();
@ -724,7 +721,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
int width = Info.Width;
int height = Info.Height;
int depth = Info.Depth;
int levels = Info.GetLevelsClamped();
int levels = Info.Levels;
int offset = 0;

View file

@ -61,7 +61,9 @@ namespace Ryujinx.Graphics.OpenGL
{
BufferCount++;
if (access.HasFlag(GAL.BufferAccess.FlushPersistent))
var memType = access & GAL.BufferAccess.MemoryTypeMask;
if (memType == GAL.BufferAccess.HostMemory)
{
BufferHandle handle = Buffer.CreatePersistent(size);
@ -75,11 +77,6 @@ namespace Ryujinx.Graphics.OpenGL
}
}
public BufferHandle CreateBuffer(int size, GAL.BufferAccess access, BufferHandle storageHint)
{
return CreateBuffer(size, access);
}
public BufferHandle CreateBuffer(nint pointer, int size)
{
throw new NotSupportedException();
@ -90,6 +87,11 @@ namespace Ryujinx.Graphics.OpenGL
throw new NotSupportedException();
}
public IImageArray CreateImageArray(int size, bool isBuffer)
{
return new ImageArray(size);
}
public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info)
{
return new Program(shaders, info.FragmentOutputMap);
@ -112,6 +114,11 @@ namespace Ryujinx.Graphics.OpenGL
}
}
public ITextureArray CreateTextureArray(int size, bool isBuffer)
{
return new TextureArray(size);
}
public void DeleteBuffer(BufferHandle buffer)
{
PersistentBuffers.Unmap(buffer);
@ -121,7 +128,7 @@ namespace Ryujinx.Graphics.OpenGL
public HardwareInfo GetHardwareInfo()
{
return new HardwareInfo(GpuVendor, GpuRenderer);
return new HardwareInfo(GpuVendor, GpuRenderer, GpuVendor); // OpenGL does not provide a driver name, vendor name is closest analogue.
}
public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
@ -138,6 +145,7 @@ namespace Ryujinx.Graphics.OpenGL
return new Capabilities(
api: TargetApi.OpenGL,
vendorName: GpuVendor,
memoryType: SystemMemoryType.BackendManaged,
hasFrontFacingBug: intelWindows,
hasVectorIndexingBug: amdWindows,
needsFragmentOutputSpecialization: false,
@ -151,6 +159,7 @@ namespace Ryujinx.Graphics.OpenGL
supportsBgraFormat: false,
supportsR4G4Format: false,
supportsR4G4B4A4Format: true,
supportsScaledVertexFormats: true,
supportsSnormBufferTextureFormat: false,
supports5BitComponentFormat: true,
supportsSparseBuffer: false,
@ -165,7 +174,8 @@ namespace Ryujinx.Graphics.OpenGL
supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
supportsCubemapView: true,
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
supportsScaledVertexFormats: true,
supportsQuads: HwCapabilities.SupportsQuads,
supportsSeparateSampler: false,
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
supportsShaderFloat64: true,
@ -177,6 +187,12 @@ namespace Ryujinx.Graphics.OpenGL
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
supportsDepthClipControl: true,
uniformBufferSetIndex: 0,
storageBufferSetIndex: 1,
textureSetIndex: 2,
imageSetIndex: 3,
extraSetBaseIndex: 0,
maximumExtraSets: 0,
maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver?
maximumStorageBuffersPerStage: 16,
maximumTexturesPerStage: 32,
@ -186,7 +202,8 @@ namespace Ryujinx.Graphics.OpenGL
shaderSubgroupSize: Constants.MaxSubgroupSize,
storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment,
textureBufferOffsetAlignment: HwCapabilities.TextureBufferOffsetAlignment,
gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0); // Precision is 8 for these vendors on Vulkan.
gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0, // Precision is 8 for these vendors on Vulkan.
maximumGpuMemory: 0);
}
public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
@ -248,7 +265,7 @@ namespace Ryujinx.Graphics.OpenGL
{
// alwaysBackground is ignored, since we cannot switch from the current context.
if (IOpenGLContext.HasContext())
if (_window.BackgroundContext.HasContext())
{
action(); // We have a context already - use that (assuming it is the main one).
}

View file

@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.OpenGL
private readonly Vector4<int>[] _fpIsBgra = new Vector4<int>[SupportBuffer.FragmentIsBgraCount];
private readonly (TextureBase, Format)[] _images;
private readonly TextureBase[] _images;
private TextureBase _unit0Texture;
private Sampler _unit0Sampler;
@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.OpenGL
_fragmentOutputMap = uint.MaxValue;
_componentMasks = uint.MaxValue;
_images = new (TextureBase, Format)[SavedImages];
_images = new TextureBase[SavedImages];
_tfbs = new BufferHandle[Constants.MaxTransformFeedbackBuffers];
_tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers];
@ -935,11 +935,11 @@ namespace Ryujinx.Graphics.OpenGL
SetFrontFace(_frontFace = frontFace.Convert());
}
public void SetImage(int binding, ITexture texture, Format imageFormat)
public void SetImage(ShaderStage stage, int binding, ITexture texture)
{
if ((uint)binding < SavedImages)
{
_images[binding] = (texture as TextureBase, imageFormat);
_images[binding] = texture as TextureBase;
}
if (texture == null)
@ -950,7 +950,7 @@ namespace Ryujinx.Graphics.OpenGL
TextureBase texBase = (TextureBase)texture;
SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat);
SizedInternalFormat format = FormatTable.GetImageFormat(texBase.Format);
if (format != 0)
{
@ -958,6 +958,16 @@ namespace Ryujinx.Graphics.OpenGL
}
}
public void SetImageArray(ShaderStage stage, int binding, IImageArray array)
{
(array as ImageArray).Bind(binding);
}
public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array)
{
throw new NotSupportedException("OpenGL does not support descriptor sets.");
}
public void SetIndexBuffer(BufferRange buffer, IndexType type)
{
_elementsType = type.Convert();
@ -1117,7 +1127,7 @@ namespace Ryujinx.Graphics.OpenGL
prg.Bind();
}
if (prg.HasFragmentShader && _fragmentOutputMap != (uint)prg.FragmentOutputMap)
if (_fragmentOutputMap != (uint)prg.FragmentOutputMap)
{
_fragmentOutputMap = (uint)prg.FragmentOutputMap;
@ -1302,6 +1312,15 @@ namespace Ryujinx.Graphics.OpenGL
}
}
public void SetTextureArray(ShaderStage stage, int binding, ITextureArray array)
{
(array as TextureArray).Bind(binding);
}
public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array)
{
throw new NotSupportedException("OpenGL does not support descriptor sets.");
}
public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
{
@ -1603,11 +1622,11 @@ namespace Ryujinx.Graphics.OpenGL
{
for (int i = 0; i < SavedImages; i++)
{
(TextureBase texBase, Format imageFormat) = _images[i];
TextureBase texBase = _images[i];
if (texBase != null)
{
SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat);
SizedInternalFormat format = FormatTable.GetImageFormat(texBase.Format);
if (format != 0)
{

View file

@ -30,7 +30,6 @@ namespace Ryujinx.Graphics.OpenGL
private ProgramLinkStatus _status = ProgramLinkStatus.Incomplete;
private int[] _shaderHandles;
public bool HasFragmentShader;
public int FragmentOutputMap { get; }
public Program(ShaderSource[] shaders, int fragmentOutputMap)
@ -40,6 +39,7 @@ namespace Ryujinx.Graphics.OpenGL
GL.ProgramParameter(Handle, ProgramParameterName.ProgramBinaryRetrievableHint, 1);
_shaderHandles = new int[shaders.Length];
bool hasFragmentShader = false;
for (int index = 0; index < shaders.Length; index++)
{
@ -47,7 +47,7 @@ namespace Ryujinx.Graphics.OpenGL
if (shader.Stage == ShaderStage.Fragment)
{
HasFragmentShader = true;
hasFragmentShader = true;
}
int shaderHandle = GL.CreateShader(shader.Stage.Convert());
@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.OpenGL
GL.LinkProgram(Handle);
FragmentOutputMap = fragmentOutputMap;
FragmentOutputMap = hasFragmentShader ? fragmentOutputMap : 0;
}
public Program(ReadOnlySpan<byte> code, bool hasFragmentShader, int fragmentOutputMap)
@ -91,8 +91,7 @@ namespace Ryujinx.Graphics.OpenGL
}
}
HasFragmentShader = hasFragmentShader;
FragmentOutputMap = fragmentOutputMap;
FragmentOutputMap = hasFragmentShader ? fragmentOutputMap : 0;
}
public void Bind()

View file

@ -21,6 +21,7 @@
<EmbeddedResource Include="Effects\Shaders\ffx_fsr1.h" />
<EmbeddedResource Include="Effects\Shaders\ffx_a.h" />
<EmbeddedResource Include="Effects\Shaders\fsr_scaling.glsl" />
<EmbeddedResource Include="Effects\Shaders\area_scaling.glsl" />
</ItemGroup>
<ItemGroup>

View file

@ -373,6 +373,16 @@ namespace Ryujinx.Graphics.OpenGL
_isLinear = false;
_scalingFilter.Level = _scalingFilterLevel;
RecreateUpscalingTexture();
break;
case ScalingFilter.Area:
if (_scalingFilter is not AreaScalingFilter)
{
_scalingFilter?.Dispose();
_scalingFilter = new AreaScalingFilter(_renderer);
}
_isLinear = false;
RecreateUpscalingTexture();
break;
}