Improved and simplified window texture presentation

This commit is contained in:
gdk 2019-11-23 23:24:03 -03:00 committed by Thog
parent b2b2e04669
commit 16d88c21fc
11 changed files with 137 additions and 497 deletions

View File

@ -2,11 +2,7 @@ namespace Ryujinx.Graphics.GAL
{ {
public interface IWindow public interface IWindow
{ {
void Present(); void Present(ITexture texture, ImageCrop crop);
void QueueTexture(ITexture texture, ImageCrop crop, object context);
void RegisterTextureReleaseCallback(TextureReleaseCallback callback);
void SetSize(int width, int height); void SetSize(int width, int height);
} }

View File

@ -76,8 +76,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
state.RegisterCallback(MethodOffset.UniformBufferBindFragment, UniformBufferBindFragment); state.RegisterCallback(MethodOffset.UniformBufferBindFragment, UniformBufferBindFragment);
} }
public Image.Texture GetTexture(ulong address) => _textureManager.Find2(address);
private void UpdateState(GpuState state) private void UpdateState(GpuState state)
{ {
// Shaders must be the first one to be updated if modified, because // Shaders must be the first one to be updated if modified, because

View File

@ -1,9 +1,6 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Texture;
using Ryujinx.Graphics.Gpu.Engine; using Ryujinx.Graphics.Gpu.Engine;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.State;
using System; using System;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Graphics.Gpu
@ -24,6 +21,8 @@ namespace Ryujinx.Graphics.Gpu
public DmaPusher DmaPusher { get; } public DmaPusher DmaPusher { get; }
public Window Window { get; }
internal int SequenceNumber { get; private set; } internal int SequenceNumber { get; private set; }
private Lazy<Capabilities> _caps; private Lazy<Capabilities> _caps;
@ -44,6 +43,8 @@ namespace Ryujinx.Graphics.Gpu
DmaPusher = new DmaPusher(this); DmaPusher = new DmaPusher(this);
Window = new Window(this);
_caps = new Lazy<Capabilities>(GetCapabilities); _caps = new Lazy<Capabilities>(GetCapabilities);
} }
@ -52,37 +53,6 @@ namespace Ryujinx.Graphics.Gpu
SequenceNumber++; SequenceNumber++;
} }
public ITexture GetTexture(
ulong address,
int width,
int height,
int stride,
bool isLinear,
int gobBlocksInY,
Format format,
int bytesPerPixel)
{
FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel);
TextureInfo info = new TextureInfo(
address,
width,
height,
1,
1,
1,
1,
stride,
isLinear,
gobBlocksInY,
1,
1,
Target.Texture2D,
formatInfo);
return Methods.GetTexture(address)?.HostTexture;
}
private Capabilities GetCapabilities() private Capabilities GetCapabilities()
{ {
return Renderer.GetCapabilities(); return Renderer.GetCapabilities();

View File

@ -335,18 +335,6 @@ namespace Ryujinx.Graphics.Gpu.Image
return true; return true;
} }
if (_info.FormatInfo.Format == Format.R8G8B8A8Srgb &&
info.FormatInfo.Format == Format.R8G8B8A8Unorm && !strict)
{
return true;
}
if (_info.FormatInfo.Format == Format.R8G8B8A8Unorm &&
info.FormatInfo.Format == Format.R8G8B8A8Srgb && !strict)
{
return true;
}
return _info.FormatInfo.Format == info.FormatInfo.Format; return _info.FormatInfo.Format == info.FormatInfo.Format;
} }

View File

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Image
private ITexture[] _rtHostColors; private ITexture[] _rtHostColors;
private ITexture _rtHostDs; private ITexture _rtHostDs;
private ConcurrentRangeList<Texture> _textures; private RangeList<Texture> _textures;
private AutoDeleteCache _cache; private AutoDeleteCache _cache;
@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_rtHostColors = new ITexture[Constants.TotalRenderTargets]; _rtHostColors = new ITexture[Constants.TotalRenderTargets];
_textures = new ConcurrentRangeList<Texture>(); _textures = new RangeList<Texture>();
_cache = new AutoDeleteCache(); _cache = new AutoDeleteCache();
} }
@ -561,28 +561,6 @@ namespace Ryujinx.Graphics.Gpu.Image
info.SwizzleA); info.SwizzleA);
} }
public Texture Find2(ulong address)
{
Texture[] ts = _textures.FindOverlaps(address, 1);
if (ts.Length == 2)
{
return ts[1];
}
if (ts.Length == 0)
{
ts = _textures.FindOverlaps(address - 1, 2);
}
if (ts.Length == 0)
{
return null;
}
return ts[0];
}
public void Flush() public void Flush()
{ {
foreach (Texture texture in _cache) foreach (Texture texture in _cache)

View File

@ -1,208 +0,0 @@
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Memory
{
class ConcurrentRangeList<T> where T : IRange<T>
{
private List<T> _items;
public ConcurrentRangeList()
{
_items = new List<T>();
}
public void Add(T item)
{
lock (_items)
{
int index = BinarySearch(item.Address);
if (index < 0)
{
index = ~index;
}
_items.Insert(index, item);
}
}
public bool Remove(T item)
{
lock (_items)
{
int index = BinarySearch(item.Address);
if (index >= 0)
{
while (index > 0 && _items[index - 1].Address == item.Address)
{
index--;
}
while (index < _items.Count)
{
if (_items[index].Equals(item))
{
_items.RemoveAt(index);
return true;
}
if (_items[index].Address > item.Address)
{
break;
}
index++;
}
}
}
return false;
}
public T FindFirstOverlap(T item)
{
return FindFirstOverlap(item.Address, item.Size);
}
public T FindFirstOverlap(ulong address, ulong size)
{
lock (_items)
{
int index = BinarySearch(address, size);
if (index < 0)
{
return default(T);
}
return _items[index];
}
}
public T[] FindOverlaps(T item)
{
return FindOverlaps(item.Address, item.Size);
}
public T[] FindOverlaps(ulong address, ulong size)
{
List<T> overlapsList = new List<T>();
ulong endAddress = address + size;
lock (_items)
{
foreach (T item in _items)
{
if (item.Address >= endAddress)
{
break;
}
if (item.OverlapsWith(address, size))
{
overlapsList.Add(item);
}
}
}
return overlapsList.ToArray();
}
public T[] FindOverlaps(ulong address)
{
List<T> overlapsList = new List<T>();
lock (_items)
{
int index = BinarySearch(address);
if (index >= 0)
{
while (index > 0 && _items[index - 1].Address == address)
{
index--;
}
while (index < _items.Count)
{
T overlap = _items[index++];
if (overlap.Address != address)
{
break;
}
overlapsList.Add(overlap);
}
}
}
return overlapsList.ToArray();
}
private int BinarySearch(ulong address)
{
int left = 0;
int right = _items.Count - 1;
while (left <= right)
{
int range = right - left;
int middle = left + (range >> 1);
T item = _items[middle];
if (item.Address == address)
{
return middle;
}
if (address < item.Address)
{
right = middle - 1;
}
else
{
left = middle + 1;
}
}
return ~left;
}
private int BinarySearch(ulong address, ulong size)
{
int left = 0;
int right = _items.Count - 1;
while (left <= right)
{
int range = right - left;
int middle = left + (range >> 1);
T item = _items[middle];
if (item.OverlapsWith(address, size))
{
return middle;
}
if (address < item.Address)
{
right = middle - 1;
}
else
{
left = middle + 1;
}
}
return ~left;
}
}
}

View File

@ -0,0 +1,94 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Texture;
using Ryujinx.Graphics.Gpu.Image;
using System;
using System.Collections.Concurrent;
namespace Ryujinx.Graphics.Gpu
{
public class Window
{
private GpuContext _context;
private struct PresentationTexture
{
public TextureInfo Info { get; }
public ImageCrop Crop { get; }
public Action<object> Callback { get; }
public object UserObj { get; }
public PresentationTexture(
TextureInfo info,
ImageCrop crop,
Action<object> callback,
object userObj)
{
Info = info;
Crop = crop;
Callback = callback;
UserObj = userObj;
}
}
private ConcurrentQueue<PresentationTexture> _frameQueue;
public Window(GpuContext context)
{
_context = context;
_frameQueue = new ConcurrentQueue<PresentationTexture>();
}
public void EnqueueFrameThreadSafe(
ulong address,
int width,
int height,
int stride,
bool isLinear,
int gobBlocksInY,
Format format,
int bytesPerPixel,
ImageCrop crop,
Action<object> callback,
object userObj)
{
FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel);
TextureInfo info = new TextureInfo(
address,
width,
height,
1,
1,
1,
1,
stride,
isLinear,
gobBlocksInY,
1,
1,
Target.Texture2D,
formatInfo);
_frameQueue.Enqueue(new PresentationTexture(info, crop, callback, userObj));
}
public void Present(Action swapBuffersCallback)
{
_context.AdvanceSequence();
if (_frameQueue.TryDequeue(out PresentationTexture pt))
{
Image.Texture texture = _context.Methods.TextureManager.FindOrCreateTexture(pt.Info);
texture.SynchronizeMemory();
_context.Renderer.Window.Present(texture.HostTexture, pt.Crop);
swapBuffersCallback();
pt.Callback(pt.UserObj);
}
}
}
}

View File

@ -1,7 +1,6 @@
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using System; using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.OpenGL namespace Ryujinx.Graphics.OpenGL
{ {
@ -13,53 +12,19 @@ namespace Ryujinx.Graphics.OpenGL
private int _width; private int _width;
private int _height; private int _height;
private int _resizeWidth;
private int _resizeHeight;
private bool _sizeChanged;
private object _resizeLocker;
private int _blitFramebufferHandle;
private int _copyFramebufferHandle; private int _copyFramebufferHandle;
private int _screenTextureHandle;
private TextureReleaseCallback _release;
private struct PresentationTexture
{
public TextureView Texture { get; }
public ImageCrop Crop { get; }
public object Context { get; }
public PresentationTexture(TextureView texture, ImageCrop crop, object context)
{
Texture = texture;
Crop = crop;
Context = context;
}
}
private Queue<PresentationTexture> _textures;
public Window() public Window()
{ {
_width = NativeWidth; _width = NativeWidth;
_height = NativeHeight; _height = NativeHeight;
_resizeLocker = new object();
_textures = new Queue<PresentationTexture>();
} }
public void Present() public void Present(ITexture texture, ImageCrop crop)
{ {
GL.Disable(EnableCap.FramebufferSrgb); TextureView view = (TextureView)texture;
CopyTextureFromQueue(); GL.Disable(EnableCap.FramebufferSrgb);
int oldReadFramebufferHandle = GL.GetInteger(GetPName.ReadFramebufferBinding); int oldReadFramebufferHandle = GL.GetInteger(GetPName.ReadFramebufferBinding);
int oldDrawFramebufferHandle = GL.GetInteger(GetPName.DrawFramebufferBinding); int oldDrawFramebufferHandle = GL.GetInteger(GetPName.DrawFramebufferBinding);
@ -67,52 +32,10 @@ namespace Ryujinx.Graphics.OpenGL
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0);
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetCopyFramebufferHandleLazy()); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetCopyFramebufferHandleLazy());
GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
GL.Clear(ClearBufferMask.ColorBufferBit);
int windowWidth = _width;
int windowHeight = _height;
GL.BlitFramebuffer(
0,
0,
windowWidth,
windowHeight,
0,
0,
windowWidth,
windowHeight,
ClearBufferMask.ColorBufferBit,
BlitFramebufferFilter.Linear);
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle);
GL.Enable(EnableCap.FramebufferSrgb);
}
private void CopyTextureFromQueue()
{
if (!_textures.TryDequeue(out PresentationTexture presentationTexture))
{
return;
}
TextureView texture = presentationTexture.Texture;
ImageCrop crop = presentationTexture.Crop;
object context = presentationTexture.Context;
int oldReadFramebufferHandle = GL.GetInteger(GetPName.ReadFramebufferBinding);
int oldDrawFramebufferHandle = GL.GetInteger(GetPName.DrawFramebufferBinding);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetCopyFramebufferHandleLazy());
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetBlitFramebufferHandleLazy());
GL.FramebufferTexture( GL.FramebufferTexture(
FramebufferTarget.ReadFramebuffer, FramebufferTarget.ReadFramebuffer,
FramebufferAttachment.ColorAttachment0, FramebufferAttachment.ColorAttachment0,
texture.Handle, view.Handle,
0); 0);
GL.ReadBuffer(ReadBufferMode.ColorAttachment0); GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
@ -124,7 +47,7 @@ namespace Ryujinx.Graphics.OpenGL
if (crop.Left == 0 && crop.Right == 0) if (crop.Left == 0 && crop.Right == 0)
{ {
srcX0 = 0; srcX0 = 0;
srcX1 = texture.Width; srcX1 = view.Width;
} }
else else
{ {
@ -135,7 +58,7 @@ namespace Ryujinx.Graphics.OpenGL
if (crop.Top == 0 && crop.Bottom == 0) if (crop.Top == 0 && crop.Bottom == 0)
{ {
srcY0 = 0; srcY0 = 0;
srcY1 = texture.Height; srcY1 = view.Height;
} }
else else
{ {
@ -173,126 +96,25 @@ namespace Ryujinx.Graphics.OpenGL
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle);
texture.Release(); GL.Enable(EnableCap.FramebufferSrgb);
Release(context);
}
public void QueueTexture(ITexture texture, ImageCrop crop, object context)
{
if (texture == null)
{
Release(context);
return;
}
TextureView textureView = (TextureView)texture;
textureView.Acquire();
_textures.Enqueue(new PresentationTexture(textureView, crop, context));
}
public void RegisterTextureReleaseCallback(TextureReleaseCallback callback)
{
_release = callback;
} }
public void SetSize(int width, int height) public void SetSize(int width, int height)
{ {
lock (_resizeLocker) _width = width;
{ _height = height;
_resizeWidth = width;
_resizeHeight = height;
_sizeChanged = true;
}
}
private void Release(object context)
{
if (_release != null)
{
_release(context);
}
}
private int GetBlitFramebufferHandleLazy()
{
int handle = _blitFramebufferHandle;
if (handle == 0)
{
handle = GL.GenFramebuffer();
_blitFramebufferHandle = handle;
}
return handle;
} }
private int GetCopyFramebufferHandleLazy() private int GetCopyFramebufferHandleLazy()
{ {
int handle = _copyFramebufferHandle; int handle = _copyFramebufferHandle;
void GenerateAndBindTexture()
{
int textureHandle = GenerateWindowTexture();
GL.BindFramebuffer(FramebufferTarget.Framebuffer, handle);
GL.FramebufferTexture(
FramebufferTarget.Framebuffer,
FramebufferAttachment.ColorAttachment0,
textureHandle,
0);
_screenTextureHandle = textureHandle;
}
if (handle == 0) if (handle == 0)
{ {
handle = GL.GenFramebuffer(); handle = GL.GenFramebuffer();
_copyFramebufferHandle = handle; _copyFramebufferHandle = handle;
GenerateAndBindTexture();
} }
else if (_sizeChanged)
{
GL.DeleteTexture(_screenTextureHandle);
lock (_resizeLocker)
{
_width = _resizeWidth;
_height = _resizeHeight;
_sizeChanged = false;
}
GenerateAndBindTexture();
}
return handle;
}
private int GenerateWindowTexture()
{
int handle = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, handle);
GL.TexImage2D(
TextureTarget.Texture2D,
0,
PixelInternalFormat.Rgba8,
_width,
_height,
0,
PixelFormat.Rgba,
PixelType.UnsignedByte,
IntPtr.Zero);
return handle; return handle;
} }

View File

@ -1,7 +1,7 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -295,11 +295,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
_bufferQueue[slot].State = BufferState.Acquired; _bufferQueue[slot].State = BufferState.Acquired;
Rect crop = _bufferQueue[slot].Crop;
bool flipX = _bufferQueue[slot].Transform.HasFlag(HalTransform.FlipX);
bool flipY = _bufferQueue[slot].Transform.HasFlag(HalTransform.FlipY);
Format format = ConvertColorFormat(_bufferQueue[slot].Data.Buffer.Surfaces[0].ColorFormat); Format format = ConvertColorFormat(_bufferQueue[slot].Data.Buffer.Surfaces[0].ColorFormat);
int bytesPerPixel = int bytesPerPixel =
@ -310,7 +305,20 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
// Note: Rotation is being ignored. // Note: Rotation is being ignored.
ITexture texture = context.Device.Gpu.GetTexture( Rect cropRect = _bufferQueue[slot].Crop;
bool flipX = _bufferQueue[slot].Transform.HasFlag(HalTransform.FlipX);
bool flipY = _bufferQueue[slot].Transform.HasFlag(HalTransform.FlipY);
ImageCrop crop = new ImageCrop(
cropRect.Left,
cropRect.Right,
cropRect.Top,
cropRect.Bottom,
flipX,
flipY);
context.Device.Gpu.Window.EnqueueFrameThreadSafe(
fbAddr, fbAddr,
fbWidth, fbWidth,
fbHeight, fbHeight,
@ -318,24 +326,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
false, false,
gobBlocksInY, gobBlocksInY,
format, format,
bytesPerPixel); bytesPerPixel,
crop,
_renderer.Window.RegisterTextureReleaseCallback(ReleaseBuffer); ReleaseBuffer,
slot);
ImageCrop imageCrop = new ImageCrop(
crop.Left,
crop.Right,
crop.Top,
crop.Bottom,
flipX,
flipY);
_renderer.Window.QueueTexture(texture, imageCrop, slot);
} }
private void ReleaseBuffer(object context) private void ReleaseBuffer(object slot)
{ {
ReleaseBuffer((int)context); ReleaseBuffer((int)slot);
} }
private void ReleaseBuffer(int slot) private void ReleaseBuffer(int slot)

View File

@ -122,6 +122,11 @@ namespace Ryujinx.HLE
Gpu.DmaPusher.DispatchCalls(); Gpu.DmaPusher.DispatchCalls();
} }
public void PresentFrame(Action swapBuffersCallback)
{
Gpu.Window.Present(swapBuffersCallback);
}
internal void Unload() internal void Unload()
{ {
FileSystem.Dispose(); FileSystem.Dispose();

View File

@ -294,7 +294,7 @@ namespace Ryujinx.Ui
private new void RenderFrame() private new void RenderFrame()
{ {
_renderer.Window.Present(); _device.PresentFrame(SwapBuffers);
_device.Statistics.RecordSystemFrameTime(); _device.Statistics.RecordSystemFrameTime();
@ -312,8 +312,6 @@ namespace Ryujinx.Ui
_titleEvent = true; _titleEvent = true;
SwapBuffers();
_device.System.SignalVsync(); _device.System.SignalVsync();
_device.VsyncEvent.Set(); _device.VsyncEvent.Set();