OpenGL: Fix blit from non-multisample to multisample texture (#3596)
* OpenGL: Fix blit from non-multisample to multisample texture * New approach for multisample copy using compute shaders
This commit is contained in:
parent
41790aa743
commit
da75a9a6ea
6 changed files with 329 additions and 156 deletions
|
@ -1,98 +0,0 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.OpenGL.Image
|
|
||||||
{
|
|
||||||
class IntermmediatePool : IDisposable
|
|
||||||
{
|
|
||||||
private readonly OpenGLRenderer _renderer;
|
|
||||||
private readonly List<TextureView> _entries;
|
|
||||||
|
|
||||||
public IntermmediatePool(OpenGLRenderer renderer)
|
|
||||||
{
|
|
||||||
_renderer = renderer;
|
|
||||||
_entries = new List<TextureView>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextureView GetOrCreateWithAtLeast(
|
|
||||||
Target target,
|
|
||||||
int blockWidth,
|
|
||||||
int blockHeight,
|
|
||||||
int bytesPerPixel,
|
|
||||||
Format format,
|
|
||||||
int width,
|
|
||||||
int height,
|
|
||||||
int depth,
|
|
||||||
int levels)
|
|
||||||
{
|
|
||||||
TextureView entry;
|
|
||||||
|
|
||||||
for (int i = 0; i < _entries.Count; i++)
|
|
||||||
{
|
|
||||||
entry = _entries[i];
|
|
||||||
|
|
||||||
if (entry.Target == target && entry.Format == format)
|
|
||||||
{
|
|
||||||
if (entry.Width < width || entry.Height < height || entry.Info.Depth < depth || entry.Info.Levels < levels)
|
|
||||||
{
|
|
||||||
width = Math.Max(width, entry.Width);
|
|
||||||
height = Math.Max(height, entry.Height);
|
|
||||||
depth = Math.Max(depth, entry.Info.Depth);
|
|
||||||
levels = Math.Max(levels, entry.Info.Levels);
|
|
||||||
|
|
||||||
entry.Dispose();
|
|
||||||
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
|
|
||||||
_entries[i] = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
|
|
||||||
_entries.Add(entry);
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TextureView CreateNew(
|
|
||||||
Target target,
|
|
||||||
int blockWidth,
|
|
||||||
int blockHeight,
|
|
||||||
int bytesPerPixel,
|
|
||||||
Format format,
|
|
||||||
int width,
|
|
||||||
int height,
|
|
||||||
int depth,
|
|
||||||
int levels)
|
|
||||||
{
|
|
||||||
return (TextureView)_renderer.CreateTexture(new TextureCreateInfo(
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
depth,
|
|
||||||
levels,
|
|
||||||
1,
|
|
||||||
blockWidth,
|
|
||||||
blockHeight,
|
|
||||||
bytesPerPixel,
|
|
||||||
format,
|
|
||||||
DepthStencilMode.Depth,
|
|
||||||
target,
|
|
||||||
SwizzleComponent.Red,
|
|
||||||
SwizzleComponent.Green,
|
|
||||||
SwizzleComponent.Blue,
|
|
||||||
SwizzleComponent.Alpha), 1f);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
foreach (TextureView entry in _entries)
|
|
||||||
{
|
|
||||||
entry.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_entries.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,8 +9,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
{
|
{
|
||||||
private readonly OpenGLRenderer _renderer;
|
private readonly OpenGLRenderer _renderer;
|
||||||
|
|
||||||
public IntermmediatePool IntermmediatePool { get; }
|
|
||||||
|
|
||||||
private int _srcFramebuffer;
|
private int _srcFramebuffer;
|
||||||
private int _dstFramebuffer;
|
private int _dstFramebuffer;
|
||||||
|
|
||||||
|
@ -20,7 +18,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
public TextureCopy(OpenGLRenderer renderer)
|
public TextureCopy(OpenGLRenderer renderer)
|
||||||
{
|
{
|
||||||
_renderer = renderer;
|
_renderer = renderer;
|
||||||
IntermmediatePool = new IntermmediatePool(renderer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Copy(
|
public void Copy(
|
||||||
|
@ -517,8 +514,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
|
|
||||||
_copyPboHandle = 0;
|
_copyPboHandle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
IntermmediatePool.Dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
276
Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs
Normal file
276
Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs
Normal file
|
@ -0,0 +1,276 @@
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
|
{
|
||||||
|
class TextureCopyMS
|
||||||
|
{
|
||||||
|
private const string ComputeShaderMSToNonMS = @"#version 450 core
|
||||||
|
|
||||||
|
layout (binding = 0, $FORMAT$) uniform uimage2DMS imgIn;
|
||||||
|
layout (binding = 1, $FORMAT$) uniform uimage2D imgOut;
|
||||||
|
|
||||||
|
layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
uvec2 coords = gl_GlobalInvocationID.xy;
|
||||||
|
ivec2 imageSz = imageSize(imgOut);
|
||||||
|
if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int inSamples = imageSamples(imgIn);
|
||||||
|
int samplesInXLog2 = 0;
|
||||||
|
int samplesInYLog2 = 0;
|
||||||
|
switch (inSamples)
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
samplesInXLog2 = 1;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
samplesInXLog2 = 1;
|
||||||
|
samplesInYLog2 = 1;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
samplesInXLog2 = 2;
|
||||||
|
samplesInYLog2 = 1;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
samplesInXLog2 = 2;
|
||||||
|
samplesInYLog2 = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int samplesInX = 1 << samplesInXLog2;
|
||||||
|
int samplesInY = 1 << samplesInYLog2;
|
||||||
|
int sampleIdx = (int(coords.x) & (samplesInX - 1)) | ((int(coords.y) & (samplesInY - 1)) << samplesInXLog2);
|
||||||
|
uvec4 value = imageLoad(imgIn, ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2), sampleIdx);
|
||||||
|
imageStore(imgOut, ivec2(coords), value);
|
||||||
|
}";
|
||||||
|
|
||||||
|
private const string ComputeShaderNonMSToMS = @"#version 450 core
|
||||||
|
|
||||||
|
layout (binding = 0, $FORMAT$) uniform uimage2D imgIn;
|
||||||
|
layout (binding = 1, $FORMAT$) uniform uimage2DMS imgOut;
|
||||||
|
|
||||||
|
layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
uvec2 coords = gl_GlobalInvocationID.xy;
|
||||||
|
ivec2 imageSz = imageSize(imgIn);
|
||||||
|
if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int outSamples = imageSamples(imgOut);
|
||||||
|
int samplesInXLog2 = 0;
|
||||||
|
int samplesInYLog2 = 0;
|
||||||
|
switch (outSamples)
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
samplesInXLog2 = 1;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
samplesInXLog2 = 1;
|
||||||
|
samplesInYLog2 = 1;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
samplesInXLog2 = 2;
|
||||||
|
samplesInYLog2 = 1;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
samplesInXLog2 = 2;
|
||||||
|
samplesInYLog2 = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int samplesInX = 1 << samplesInXLog2;
|
||||||
|
int samplesInY = 1 << samplesInYLog2;
|
||||||
|
int sampleIdx = (int(coords.x) & (samplesInX - 1)) | ((int(coords.y) & (samplesInY - 1)) << samplesInXLog2);
|
||||||
|
uvec4 value = imageLoad(imgIn, ivec2(coords));
|
||||||
|
imageStore(imgOut, ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2), sampleIdx, value);
|
||||||
|
}";
|
||||||
|
|
||||||
|
private readonly OpenGLRenderer _renderer;
|
||||||
|
private int[] _msToNonMSProgramHandles;
|
||||||
|
private int[] _nonMSToMSProgramHandles;
|
||||||
|
|
||||||
|
public TextureCopyMS(OpenGLRenderer renderer)
|
||||||
|
{
|
||||||
|
_renderer = renderer;
|
||||||
|
_msToNonMSProgramHandles = new int[5];
|
||||||
|
_nonMSToMSProgramHandles = new int[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyMSToNonMS(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int depth)
|
||||||
|
{
|
||||||
|
TextureCreateInfo srcInfo = src.Info;
|
||||||
|
TextureCreateInfo dstInfo = dst.Info;
|
||||||
|
|
||||||
|
int srcHandle = CreateViewIfNeeded(src);
|
||||||
|
int dstHandle = CreateViewIfNeeded(dst);
|
||||||
|
|
||||||
|
int dstWidth = dstInfo.Width;
|
||||||
|
int dstHeight = dstInfo.Height;
|
||||||
|
|
||||||
|
GL.UseProgram(GetMSToNonMSShader(srcInfo.BytesPerPixel));
|
||||||
|
|
||||||
|
for (int z = 0; z < depth; z++)
|
||||||
|
{
|
||||||
|
GL.BindImageTexture(0, srcHandle, 0, false, srcLayer + z, TextureAccess.ReadOnly, GetFormat(srcInfo.BytesPerPixel));
|
||||||
|
GL.BindImageTexture(1, dstHandle, 0, false, dstLayer + z, TextureAccess.WriteOnly, GetFormat(dstInfo.BytesPerPixel));
|
||||||
|
|
||||||
|
GL.DispatchCompute((dstWidth + 31) / 32, (dstHeight + 31) / 32, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pipeline pipeline = (Pipeline)_renderer.Pipeline;
|
||||||
|
|
||||||
|
pipeline.RestoreProgram();
|
||||||
|
pipeline.RestoreImages1And2();
|
||||||
|
|
||||||
|
DestroyViewIfNeeded(src, srcHandle);
|
||||||
|
DestroyViewIfNeeded(dst, dstHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyNonMSToMS(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int depth)
|
||||||
|
{
|
||||||
|
TextureCreateInfo srcInfo = src.Info;
|
||||||
|
TextureCreateInfo dstInfo = dst.Info;
|
||||||
|
|
||||||
|
int srcHandle = CreateViewIfNeeded(src);
|
||||||
|
int dstHandle = CreateViewIfNeeded(dst);
|
||||||
|
|
||||||
|
int srcWidth = srcInfo.Width;
|
||||||
|
int srcHeight = srcInfo.Height;
|
||||||
|
|
||||||
|
GL.UseProgram(GetNonMSToMSShader(srcInfo.BytesPerPixel));
|
||||||
|
|
||||||
|
for (int z = 0; z < depth; z++)
|
||||||
|
{
|
||||||
|
GL.BindImageTexture(0, srcHandle, 0, false, srcLayer + z, TextureAccess.ReadOnly, GetFormat(srcInfo.BytesPerPixel));
|
||||||
|
GL.BindImageTexture(1, dstHandle, 0, false, dstLayer + z, TextureAccess.WriteOnly, GetFormat(dstInfo.BytesPerPixel));
|
||||||
|
|
||||||
|
GL.DispatchCompute((srcWidth + 31) / 32, (srcHeight + 31) / 32, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pipeline pipeline = (Pipeline)_renderer.Pipeline;
|
||||||
|
|
||||||
|
pipeline.RestoreProgram();
|
||||||
|
pipeline.RestoreImages1And2();
|
||||||
|
|
||||||
|
DestroyViewIfNeeded(src, srcHandle);
|
||||||
|
DestroyViewIfNeeded(dst, dstHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SizedInternalFormat GetFormat(int bytesPerPixel)
|
||||||
|
{
|
||||||
|
return bytesPerPixel switch
|
||||||
|
{
|
||||||
|
1 => SizedInternalFormat.R8ui,
|
||||||
|
2 => SizedInternalFormat.R16ui,
|
||||||
|
4 => SizedInternalFormat.R32ui,
|
||||||
|
8 => SizedInternalFormat.Rg32ui,
|
||||||
|
16 => SizedInternalFormat.Rgba32ui,
|
||||||
|
_ => throw new ArgumentException($"Invalid bytes per pixel {bytesPerPixel}.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CreateViewIfNeeded(ITextureInfo texture)
|
||||||
|
{
|
||||||
|
// Binding sRGB textures as images doesn't work on NVIDIA,
|
||||||
|
// we need to create and bind a RGBA view for it to work.
|
||||||
|
if (texture.Info.Format == Format.R8G8B8A8Srgb)
|
||||||
|
{
|
||||||
|
int handle = GL.GenTexture();
|
||||||
|
|
||||||
|
GL.TextureView(
|
||||||
|
handle,
|
||||||
|
texture.Info.Target.Convert(),
|
||||||
|
texture.Storage.Handle,
|
||||||
|
PixelInternalFormat.Rgba8,
|
||||||
|
texture.FirstLevel,
|
||||||
|
1,
|
||||||
|
texture.FirstLayer,
|
||||||
|
texture.Info.GetLayers());
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return texture.Handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DestroyViewIfNeeded(ITextureInfo info, int handle)
|
||||||
|
{
|
||||||
|
if (info.Handle != handle)
|
||||||
|
{
|
||||||
|
GL.DeleteTexture(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetMSToNonMSShader(int bytesPerPixel)
|
||||||
|
{
|
||||||
|
return GetShader(ComputeShaderMSToNonMS, _msToNonMSProgramHandles, bytesPerPixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetNonMSToMSShader(int bytesPerPixel)
|
||||||
|
{
|
||||||
|
return GetShader(ComputeShaderNonMSToMS, _nonMSToMSProgramHandles, bytesPerPixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetShader(string code, int[] programHandles, int bytesPerPixel)
|
||||||
|
{
|
||||||
|
int index = BitOperations.Log2((uint)bytesPerPixel);
|
||||||
|
|
||||||
|
if (programHandles[index] == 0)
|
||||||
|
{
|
||||||
|
int csHandle = GL.CreateShader(ShaderType.ComputeShader);
|
||||||
|
|
||||||
|
string format = new[] { "r8ui", "r16ui", "r32ui", "rg32ui", "rgba32ui" }[index];
|
||||||
|
|
||||||
|
GL.ShaderSource(csHandle, code.Replace("$FORMAT$", format));
|
||||||
|
GL.CompileShader(csHandle);
|
||||||
|
|
||||||
|
int programHandle = GL.CreateProgram();
|
||||||
|
|
||||||
|
GL.AttachShader(programHandle, csHandle);
|
||||||
|
GL.LinkProgram(programHandle);
|
||||||
|
GL.DetachShader(programHandle, csHandle);
|
||||||
|
GL.DeleteShader(csHandle);
|
||||||
|
|
||||||
|
GL.GetProgram(programHandle, GetProgramParameterName.LinkStatus, out int status);
|
||||||
|
|
||||||
|
if (status == 0)
|
||||||
|
{
|
||||||
|
throw new Exception(GL.GetProgramInfoLog(programHandle));
|
||||||
|
}
|
||||||
|
|
||||||
|
programHandles[index] = programHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return programHandles[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _msToNonMSProgramHandles.Length; i++)
|
||||||
|
{
|
||||||
|
if (_msToNonMSProgramHandles[i] != 0)
|
||||||
|
{
|
||||||
|
GL.DeleteProgram(_msToNonMSProgramHandles[i]);
|
||||||
|
_msToNonMSProgramHandles[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _nonMSToMSProgramHandles.Length; i++)
|
||||||
|
{
|
||||||
|
if (_nonMSToMSProgramHandles[i] != 0)
|
||||||
|
{
|
||||||
|
GL.DeleteProgram(_nonMSToMSProgramHandles[i]);
|
||||||
|
_nonMSToMSProgramHandles[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -116,28 +116,15 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
{
|
{
|
||||||
TextureView destinationView = (TextureView)destination;
|
TextureView destinationView = (TextureView)destination;
|
||||||
|
|
||||||
if (destinationView.Target.IsMultisample() || Target.IsMultisample())
|
if (!destinationView.Target.IsMultisample() && Target.IsMultisample())
|
||||||
{
|
{
|
||||||
Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
|
int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
|
||||||
Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
|
_renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, 0, firstLayer, layers);
|
||||||
|
}
|
||||||
TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast(
|
else if (destinationView.Target.IsMultisample() && !Target.IsMultisample())
|
||||||
GetIntermmediateTarget(Target),
|
{
|
||||||
Info.BlockWidth,
|
int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
|
||||||
Info.BlockHeight,
|
_renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, 0, firstLayer, layers);
|
||||||
Info.BytesPerPixel,
|
|
||||||
Format,
|
|
||||||
Width,
|
|
||||||
Height,
|
|
||||||
Info.Depth,
|
|
||||||
Info.Levels);
|
|
||||||
|
|
||||||
GL.Disable(EnableCap.FramebufferSrgb);
|
|
||||||
|
|
||||||
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true);
|
|
||||||
_renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, firstLayer, 0, firstLevel);
|
|
||||||
|
|
||||||
GL.Enable(EnableCap.FramebufferSrgb);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -149,28 +136,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
{
|
{
|
||||||
TextureView destinationView = (TextureView)destination;
|
TextureView destinationView = (TextureView)destination;
|
||||||
|
|
||||||
if (destinationView.Target.IsMultisample() || Target.IsMultisample())
|
if (!destinationView.Target.IsMultisample() && Target.IsMultisample())
|
||||||
{
|
{
|
||||||
Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
|
_renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, srcLayer, dstLayer,1);
|
||||||
Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
|
}
|
||||||
|
else if (destinationView.Target.IsMultisample() && !Target.IsMultisample())
|
||||||
TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast(
|
{
|
||||||
GetIntermmediateTarget(Target),
|
_renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, srcLayer, dstLayer, 1);
|
||||||
Info.BlockWidth,
|
|
||||||
Info.BlockHeight,
|
|
||||||
Info.BytesPerPixel,
|
|
||||||
Format,
|
|
||||||
Math.Max(1, Width >> srcLevel),
|
|
||||||
Math.Max(1, Height >> srcLevel),
|
|
||||||
1,
|
|
||||||
1);
|
|
||||||
|
|
||||||
GL.Disable(EnableCap.FramebufferSrgb);
|
|
||||||
|
|
||||||
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true, srcLayer, 0, srcLevel, 0, 1, 1);
|
|
||||||
_renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, dstLayer, 0, dstLevel, 1, 1);
|
|
||||||
|
|
||||||
GL.Enable(EnableCap.FramebufferSrgb);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -178,17 +150,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Target GetIntermmediateTarget(Target srcTarget)
|
|
||||||
{
|
|
||||||
return srcTarget switch
|
|
||||||
{
|
|
||||||
Target.Texture2D => Target.Texture2DMultisample,
|
|
||||||
Target.Texture2DArray => Target.Texture2DMultisampleArray,
|
|
||||||
Target.Texture2DMultisampleArray => Target.Texture2DArray,
|
|
||||||
_ => Target.Texture2D
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
|
public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
|
||||||
{
|
{
|
||||||
_renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter);
|
_renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter);
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
private TextureCopy _textureCopy;
|
private TextureCopy _textureCopy;
|
||||||
private TextureCopy _backgroundTextureCopy;
|
private TextureCopy _backgroundTextureCopy;
|
||||||
internal TextureCopy TextureCopy => BackgroundContextWorker.InBackground ? _backgroundTextureCopy : _textureCopy;
|
internal TextureCopy TextureCopy => BackgroundContextWorker.InBackground ? _backgroundTextureCopy : _textureCopy;
|
||||||
|
internal TextureCopyMS TextureCopyMS { get; }
|
||||||
|
|
||||||
private Sync _sync;
|
private Sync _sync;
|
||||||
|
|
||||||
|
@ -48,6 +49,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
_window = new Window(this);
|
_window = new Window(this);
|
||||||
_textureCopy = new TextureCopy(this);
|
_textureCopy = new TextureCopy(this);
|
||||||
_backgroundTextureCopy = new TextureCopy(this);
|
_backgroundTextureCopy = new TextureCopy(this);
|
||||||
|
TextureCopyMS = new TextureCopyMS(this);
|
||||||
_sync = new Sync();
|
_sync = new Sync();
|
||||||
PersistentBuffers = new PersistentBuffers();
|
PersistentBuffers = new PersistentBuffers();
|
||||||
ResourcePool = new ResourcePool();
|
ResourcePool = new ResourcePool();
|
||||||
|
@ -211,6 +213,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
{
|
{
|
||||||
_textureCopy.Dispose();
|
_textureCopy.Dispose();
|
||||||
_backgroundTextureCopy.Dispose();
|
_backgroundTextureCopy.Dispose();
|
||||||
|
TextureCopyMS.Dispose();
|
||||||
PersistentBuffers.Dispose();
|
PersistentBuffers.Dispose();
|
||||||
ResourcePool.Dispose();
|
ResourcePool.Dispose();
|
||||||
_pipeline.Dispose();
|
_pipeline.Dispose();
|
||||||
|
|
|
@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
{
|
{
|
||||||
class Pipeline : IPipeline, IDisposable
|
class Pipeline : IPipeline, IDisposable
|
||||||
{
|
{
|
||||||
|
private const int SavedImages = 2;
|
||||||
|
|
||||||
private readonly DrawTextureEmulation _drawTexture;
|
private readonly DrawTextureEmulation _drawTexture;
|
||||||
|
|
||||||
internal ulong DrawCount { get; private set; }
|
internal ulong DrawCount { get; private set; }
|
||||||
|
@ -46,6 +48,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
private Vector4<float>[] _renderScale = new Vector4<float>[73];
|
private Vector4<float>[] _renderScale = new Vector4<float>[73];
|
||||||
private int _fragmentScaleCount;
|
private int _fragmentScaleCount;
|
||||||
|
|
||||||
|
private (TextureBase, Format)[] _images;
|
||||||
private TextureBase _unit0Texture;
|
private TextureBase _unit0Texture;
|
||||||
private Sampler _unit0Sampler;
|
private Sampler _unit0Sampler;
|
||||||
|
|
||||||
|
@ -78,6 +81,8 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
_fragmentOutputMap = uint.MaxValue;
|
_fragmentOutputMap = uint.MaxValue;
|
||||||
_componentMasks = uint.MaxValue;
|
_componentMasks = uint.MaxValue;
|
||||||
|
|
||||||
|
_images = new (TextureBase, Format)[SavedImages];
|
||||||
|
|
||||||
var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f };
|
var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f };
|
||||||
new Span<Vector4<float>>(_renderScale).Fill(defaultScale);
|
new Span<Vector4<float>>(_renderScale).Fill(defaultScale);
|
||||||
|
|
||||||
|
@ -907,6 +912,11 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
|
|
||||||
public void SetImage(int binding, ITexture texture, Format imageFormat)
|
public void SetImage(int binding, ITexture texture, Format imageFormat)
|
||||||
{
|
{
|
||||||
|
if ((uint)binding < SavedImages)
|
||||||
|
{
|
||||||
|
_images[binding] = (texture as TextureBase, imageFormat);
|
||||||
|
}
|
||||||
|
|
||||||
if (texture == null)
|
if (texture == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -1608,6 +1618,32 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RestoreProgram()
|
||||||
|
{
|
||||||
|
_program?.Bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RestoreImages1And2()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < SavedImages; i++)
|
||||||
|
{
|
||||||
|
(TextureBase texBase, Format imageFormat) = _images[i];
|
||||||
|
|
||||||
|
if (texBase != null)
|
||||||
|
{
|
||||||
|
SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat);
|
||||||
|
|
||||||
|
if (format != 0)
|
||||||
|
{
|
||||||
|
GL.BindImageTexture(i, texBase.Handle, 0, true, 0, TextureAccess.ReadWrite, format);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.BindImageTexture(i, 0, 0, true, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
|
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
|
||||||
{
|
{
|
||||||
if (value is CounterQueueEvent)
|
if (value is CounterQueueEvent)
|
||||||
|
|
Reference in a new issue