From cee712105850ac3385cd0091a923438167433f9f Mon Sep 17 00:00:00 2001 From: TSR Berry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 8 Apr 2023 01:22:00 +0200 Subject: Move solution and projects to src --- src/Ryujinx.Graphics.Gpu/Window.cs | 263 +++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 src/Ryujinx.Graphics.Gpu/Window.cs (limited to 'src/Ryujinx.Graphics.Gpu/Window.cs') diff --git a/src/Ryujinx.Graphics.Gpu/Window.cs b/src/Ryujinx.Graphics.Gpu/Window.cs new file mode 100644 index 00000000..06d0fddf --- /dev/null +++ b/src/Ryujinx.Graphics.Gpu/Window.cs @@ -0,0 +1,263 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Image; +using Ryujinx.Graphics.Texture; +using Ryujinx.Memory.Range; +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace Ryujinx.Graphics.Gpu +{ + using Texture = Image.Texture; + + /// + /// GPU image presentation window. + /// + public class Window + { + private readonly GpuContext _context; + + /// + /// Texture presented on the window. + /// + private readonly struct PresentationTexture + { + /// + /// Texture cache where the texture might be located. + /// + public TextureCache Cache { get; } + + /// + /// Texture information. + /// + public TextureInfo Info { get; } + + /// + /// Physical memory locations where the texture data is located. + /// + public MultiRange Range { get; } + + /// + /// Texture crop region. + /// + public ImageCrop Crop { get; } + + /// + /// Texture acquire callback. + /// + public Action AcquireCallback { get; } + + /// + /// Texture release callback. + /// + public Action ReleaseCallback { get; } + + /// + /// User defined object, passed to the various callbacks. + /// + public object UserObj { get; } + + /// + /// Creates a new instance of the presentation texture. + /// + /// Texture cache used to look for the texture to be presented + /// Information of the texture to be presented + /// Physical memory locations where the texture data is located + /// Texture crop region + /// Texture acquire callback + /// Texture release callback + /// User defined object passed to the release callback, can be used to identify the texture + public PresentationTexture( + TextureCache cache, + TextureInfo info, + MultiRange range, + ImageCrop crop, + Action acquireCallback, + Action releaseCallback, + object userObj) + { + Cache = cache; + Info = info; + Range = range; + Crop = crop; + AcquireCallback = acquireCallback; + ReleaseCallback = releaseCallback; + UserObj = userObj; + } + } + + private readonly ConcurrentQueue _frameQueue; + + private int _framesAvailable; + + public bool IsFrameAvailable => _framesAvailable != 0; + + /// + /// Creates a new instance of the GPU presentation window. + /// + /// GPU emulation context + public Window(GpuContext context) + { + _context = context; + + _frameQueue = new ConcurrentQueue(); + } + + /// + /// Enqueues a frame for presentation. + /// This method is thread safe and can be called from any thread. + /// When the texture is presented and not needed anymore, the release callback is called. + /// It's an error to modify the texture after calling this method, before the release callback is called. + /// + /// Process ID of the process that owns the texture pointed to by + /// CPU virtual address of the texture data + /// Texture width + /// Texture height + /// Texture stride for linear texture, should be zero otherwise + /// Indicates if the texture is linear, normally false + /// GOB blocks in the Y direction, for block linear textures + /// Texture format + /// Texture format bytes per pixel (must match the format) + /// Texture crop region + /// Texture acquire callback + /// Texture release callback + /// User defined object passed to the release callback + /// Thrown when is invalid + /// True if the frame was added to the queue, false otherwise + public bool EnqueueFrameThreadSafe( + ulong pid, + ulong address, + int width, + int height, + int stride, + bool isLinear, + int gobBlocksInY, + Format format, + int bytesPerPixel, + ImageCrop crop, + Action acquireCallback, + Action releaseCallback, + object userObj) + { + if (!_context.PhysicalMemoryRegistry.TryGetValue(pid, out var physicalMemory)) + { + return false; + } + + FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel, 4); + + TextureInfo info = new TextureInfo( + 0UL, + width, + height, + 1, + 1, + 1, + 1, + stride, + isLinear, + gobBlocksInY, + 1, + 1, + Target.Texture2D, + formatInfo); + + int size = SizeCalculator.GetBlockLinearTextureSize( + width, + height, + 1, + 1, + 1, + 1, + 1, + bytesPerPixel, + gobBlocksInY, + 1, + 1).TotalSize; + + MultiRange range = new MultiRange(address, (ulong)size); + + _frameQueue.Enqueue(new PresentationTexture( + physicalMemory.TextureCache, + info, + range, + crop, + acquireCallback, + releaseCallback, + userObj)); + + return true; + } + + /// + /// Presents a texture on the queue. + /// If the queue is empty, then no texture is presented. + /// + /// Callback method to call when a new texture should be presented on the screen + public void Present(Action swapBuffersCallback) + { + _context.AdvanceSequence(); + + if (_frameQueue.TryDequeue(out PresentationTexture pt)) + { + pt.AcquireCallback(_context, pt.UserObj); + + Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, pt.Range); + + pt.Cache.Tick(); + + texture.SynchronizeMemory(); + + ImageCrop crop = pt.Crop; + + if (texture.Info.Width > pt.Info.Width || texture.Info.Height > pt.Info.Height) + { + int top = crop.Top; + int bottom = crop.Bottom; + int left = crop.Left; + int right = crop.Right; + + if (top == 0 && bottom == 0) + { + bottom = Math.Min(texture.Info.Height, pt.Info.Height); + } + + if (left == 0 && right == 0) + { + right = Math.Min(texture.Info.Width, pt.Info.Width); + } + + crop = new ImageCrop(left, right, top, bottom, crop.FlipX, crop.FlipY, crop.IsStretched, crop.AspectRatioX, crop.AspectRatioY); + } + + _context.Renderer.Window.Present(texture.HostTexture, crop, swapBuffersCallback); + + pt.ReleaseCallback(pt.UserObj); + } + } + + /// + /// Indicate that a frame on the queue is ready to be acquired. + /// + public void SignalFrameReady() + { + Interlocked.Increment(ref _framesAvailable); + } + + /// + /// Determine if any frames are available, and decrement the available count if there are. + /// + /// True if a frame is available, false otherwise + public bool ConsumeFrameAvailable() + { + if (Interlocked.CompareExchange(ref _framesAvailable, 0, 0) != 0) + { + Interlocked.Decrement(ref _framesAvailable); + + return true; + } + + return false; + } + } +} \ No newline at end of file -- cgit v1.2.3