diff --git a/Ryujinx.Graphics.GAL/IWindow.cs b/Ryujinx.Graphics.GAL/IWindow.cs
index 3d5130c29d..369f7b9a67 100644
--- a/Ryujinx.Graphics.GAL/IWindow.cs
+++ b/Ryujinx.Graphics.GAL/IWindow.cs
@@ -2,11 +2,7 @@ namespace Ryujinx.Graphics.GAL
 {
     public interface IWindow
     {
-        void Present();
-
-        void QueueTexture(ITexture texture, ImageCrop crop, object context);
-
-        void RegisterTextureReleaseCallback(TextureReleaseCallback callback);
+        void Present(ITexture texture, ImageCrop crop);
 
         void SetSize(int width, int height);
     }
diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
index 1832a5e353..19e6799328 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
@@ -76,8 +76,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
             state.RegisterCallback(MethodOffset.UniformBufferBindFragment,       UniformBufferBindFragment);
         }
 
-        public Image.Texture GetTexture(ulong address) => _textureManager.Find2(address);
-
         private void UpdateState(GpuState state)
         {
             // Shaders must be the first one to be updated if modified, because
diff --git a/Ryujinx.Graphics.Gpu/GpuContext.cs b/Ryujinx.Graphics.Gpu/GpuContext.cs
index b52e4e497a..51961522a0 100644
--- a/Ryujinx.Graphics.Gpu/GpuContext.cs
+++ b/Ryujinx.Graphics.Gpu/GpuContext.cs
@@ -1,9 +1,6 @@
 using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.GAL.Texture;
 using Ryujinx.Graphics.Gpu.Engine;
-using Ryujinx.Graphics.Gpu.Image;
 using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Gpu.State;
 using System;
 
 namespace Ryujinx.Graphics.Gpu
@@ -24,6 +21,8 @@ namespace Ryujinx.Graphics.Gpu
 
         public DmaPusher DmaPusher { get; }
 
+        public Window Window { get; }
+
         internal int SequenceNumber { get; private set; }
 
         private Lazy<Capabilities> _caps;
@@ -44,6 +43,8 @@ namespace Ryujinx.Graphics.Gpu
 
             DmaPusher = new DmaPusher(this);
 
+            Window = new Window(this);
+
             _caps = new Lazy<Capabilities>(GetCapabilities);
         }
 
@@ -52,37 +53,6 @@ namespace Ryujinx.Graphics.Gpu
             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()
         {
             return Renderer.GetCapabilities();
diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs
index 28ad3f7798..120abe3fd9 100644
--- a/Ryujinx.Graphics.Gpu/Image/Texture.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs
@@ -335,18 +335,6 @@ namespace Ryujinx.Graphics.Gpu.Image
                 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;
         }
 
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
index 045c2ed947..413ba3272d 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         private ITexture[] _rtHostColors;
         private ITexture   _rtHostDs;
 
-        private ConcurrentRangeList<Texture> _textures;
+        private RangeList<Texture> _textures;
 
         private AutoDeleteCache _cache;
 
@@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Gpu.Image
 
             _rtHostColors = new ITexture[Constants.TotalRenderTargets];
 
-            _textures = new ConcurrentRangeList<Texture>();
+            _textures = new RangeList<Texture>();
 
             _cache = new AutoDeleteCache();
         }
@@ -561,28 +561,6 @@ namespace Ryujinx.Graphics.Gpu.Image
                 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()
         {
             foreach (Texture texture in _cache)
diff --git a/Ryujinx.Graphics.Gpu/Memory/ConcurrentRangeList.cs b/Ryujinx.Graphics.Gpu/Memory/ConcurrentRangeList.cs
deleted file mode 100644
index 6d7d1ce21b..0000000000
--- a/Ryujinx.Graphics.Gpu/Memory/ConcurrentRangeList.cs
+++ /dev/null
@@ -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;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Window.cs b/Ryujinx.Graphics.Gpu/Window.cs
new file mode 100644
index 0000000000..a3dc3b26c2
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Window.cs
@@ -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);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs
index 3fe83dd1c3..cf520ed441 100644
--- a/Ryujinx.Graphics.OpenGL/Window.cs
+++ b/Ryujinx.Graphics.OpenGL/Window.cs
@@ -1,7 +1,6 @@
 using OpenTK.Graphics.OpenGL;
 using Ryujinx.Graphics.GAL;
 using System;
-using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.OpenGL
 {
@@ -13,53 +12,19 @@ namespace Ryujinx.Graphics.OpenGL
         private int _width;
         private int _height;
 
-        private int _resizeWidth;
-        private int _resizeHeight;
-
-        private bool _sizeChanged;
-
-        private object _resizeLocker;
-
-        private int _blitFramebufferHandle;
         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()
         {
             _width  = NativeWidth;
             _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 oldDrawFramebufferHandle = GL.GetInteger(GetPName.DrawFramebufferBinding);
@@ -67,52 +32,10 @@ namespace Ryujinx.Graphics.OpenGL
             GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0);
             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(
                 FramebufferTarget.ReadFramebuffer,
                 FramebufferAttachment.ColorAttachment0,
-                texture.Handle,
+                view.Handle,
                 0);
 
             GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
@@ -124,7 +47,7 @@ namespace Ryujinx.Graphics.OpenGL
             if (crop.Left == 0 && crop.Right == 0)
             {
                 srcX0 = 0;
-                srcX1 = texture.Width;
+                srcX1 = view.Width;
             }
             else
             {
@@ -135,7 +58,7 @@ namespace Ryujinx.Graphics.OpenGL
             if (crop.Top == 0 && crop.Bottom == 0)
             {
                 srcY0 = 0;
-                srcY1 = texture.Height;
+                srcY1 = view.Height;
             }
             else
             {
@@ -173,126 +96,25 @@ namespace Ryujinx.Graphics.OpenGL
             GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle);
             GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle);
 
-            texture.Release();
-
-            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;
+            GL.Enable(EnableCap.FramebufferSrgb);
         }
 
         public void SetSize(int width, int height)
         {
-            lock (_resizeLocker)
-            {
-                _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;
+            _width  = width;
+            _height = height;
         }
 
         private int GetCopyFramebufferHandleLazy()
         {
             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)
             {
                 handle = GL.GenFramebuffer();
 
                 _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;
         }
diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NvFlinger.cs
index 50b55ee068..fa79817754 100644
--- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NvFlinger.cs
+++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NvFlinger.cs
@@ -1,7 +1,7 @@
 using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu;
 using Ryujinx.HLE.HOS.Kernel.Threading;
-using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
 using System;
 using System.Collections.Generic;
@@ -295,11 +295,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 
             _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);
 
             int bytesPerPixel =
@@ -310,7 +305,20 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 
             // 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,
                 fbWidth,
                 fbHeight,
@@ -318,24 +326,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                 false,
                 gobBlocksInY,
                 format,
-                bytesPerPixel);
-
-            _renderer.Window.RegisterTextureReleaseCallback(ReleaseBuffer);
-
-            ImageCrop imageCrop = new ImageCrop(
-                crop.Left,
-                crop.Right,
-                crop.Top,
-                crop.Bottom,
-                flipX,
-                flipY);
-
-            _renderer.Window.QueueTexture(texture, imageCrop, slot);
+                bytesPerPixel,
+                crop,
+                ReleaseBuffer,
+                slot);
         }
 
-        private void ReleaseBuffer(object context)
+        private void ReleaseBuffer(object slot)
         {
-            ReleaseBuffer((int)context);
+            ReleaseBuffer((int)slot);
         }
 
         private void ReleaseBuffer(int slot)
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index e5a5f27361..9df1d7571a 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -122,6 +122,11 @@ namespace Ryujinx.HLE
             Gpu.DmaPusher.DispatchCalls();
         }
 
+        public void PresentFrame(Action swapBuffersCallback)
+        {
+            Gpu.Window.Present(swapBuffersCallback);
+        }
+
         internal void Unload()
         {
             FileSystem.Dispose();
diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs
index 6d1060ee62..1cce6a1f76 100644
--- a/Ryujinx/Ui/GLScreen.cs
+++ b/Ryujinx/Ui/GLScreen.cs
@@ -294,7 +294,7 @@ namespace Ryujinx.Ui
 
         private new void RenderFrame()
         {
-            _renderer.Window.Present();
+            _device.PresentFrame(SwapBuffers);
 
             _device.Statistics.RecordSystemFrameTime();
 
@@ -312,8 +312,6 @@ namespace Ryujinx.Ui
 
             _titleEvent = true;
 
-            SwapBuffers();
-
             _device.System.SignalVsync();
 
             _device.VsyncEvent.Set();