261 lines
		
	
	
		
			No EOL
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			261 lines
		
	
	
		
			No EOL
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| 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;
 | |
| 
 | |
|     /// <summary>
 | |
|     /// GPU image presentation window.
 | |
|     /// </summary>
 | |
|     public class Window
 | |
|     {
 | |
|         private readonly GpuContext _context;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Texture presented on the window.
 | |
|         /// </summary>
 | |
|         private struct PresentationTexture
 | |
|         {
 | |
|             /// <summary>
 | |
|             /// Texture cache where the texture might be located.
 | |
|             /// </summary>
 | |
|             public TextureCache Cache { get; }
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Texture information.
 | |
|             /// </summary>
 | |
|             public TextureInfo Info { get; }
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Physical memory locations where the texture data is located.
 | |
|             /// </summary>
 | |
|             public MultiRange Range { get; }
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Texture crop region.
 | |
|             /// </summary>
 | |
|             public ImageCrop Crop { get; }
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Texture acquire callback.
 | |
|             /// </summary>
 | |
|             public Action<GpuContext, object> AcquireCallback { get; }
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Texture release callback.
 | |
|             /// </summary>
 | |
|             public Action<object> ReleaseCallback { get; }
 | |
| 
 | |
|             /// <summary>
 | |
|             /// User defined object, passed to the various callbacks.
 | |
|             /// </summary>
 | |
|             public object UserObj { get; }
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Creates a new instance of the presentation texture.
 | |
|             /// </summary>
 | |
|             /// <param name="cache">Texture cache used to look for the texture to be presented</param>
 | |
|             /// <param name="info">Information of the texture to be presented</param>
 | |
|             /// <param name="range">Physical memory locations where the texture data is located</param>
 | |
|             /// <param name="crop">Texture crop region</param>
 | |
|             /// <param name="acquireCallback">Texture acquire callback</param>
 | |
|             /// <param name="releaseCallback">Texture release callback</param>
 | |
|             /// <param name="userObj">User defined object passed to the release callback, can be used to identify the texture</param>
 | |
|             public PresentationTexture(
 | |
|                 TextureCache               cache,
 | |
|                 TextureInfo                info,
 | |
|                 MultiRange                 range,
 | |
|                 ImageCrop                  crop,
 | |
|                 Action<GpuContext, object> acquireCallback,
 | |
|                 Action<object>             releaseCallback,
 | |
|                 object                     userObj)
 | |
|             {
 | |
|                 Cache           = cache;
 | |
|                 Info            = info;
 | |
|                 Range           = range;
 | |
|                 Crop            = crop;
 | |
|                 AcquireCallback = acquireCallback;
 | |
|                 ReleaseCallback = releaseCallback;
 | |
|                 UserObj         = userObj;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private readonly ConcurrentQueue<PresentationTexture> _frameQueue;
 | |
| 
 | |
|         private int _framesAvailable;
 | |
| 
 | |
|         public bool IsFrameAvailable => _framesAvailable != 0;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a new instance of the GPU presentation window.
 | |
|         /// </summary>
 | |
|         /// <param name="context">GPU emulation context</param>
 | |
|         public Window(GpuContext context)
 | |
|         {
 | |
|             _context = context;
 | |
| 
 | |
|             _frameQueue = new ConcurrentQueue<PresentationTexture>();
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 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.
 | |
|         /// </summary>
 | |
|         /// <param name="pid">Process ID of the process that owns the texture pointed to by <paramref name="address"/></param>
 | |
|         /// <param name="address">CPU virtual address of the texture data</param>
 | |
|         /// <param name="width">Texture width</param>
 | |
|         /// <param name="height">Texture height</param>
 | |
|         /// <param name="stride">Texture stride for linear texture, should be zero otherwise</param>
 | |
|         /// <param name="isLinear">Indicates if the texture is linear, normally false</param>
 | |
|         /// <param name="gobBlocksInY">GOB blocks in the Y direction, for block linear textures</param>
 | |
|         /// <param name="format">Texture format</param>
 | |
|         /// <param name="bytesPerPixel">Texture format bytes per pixel (must match the format)</param>
 | |
|         /// <param name="crop">Texture crop region</param>
 | |
|         /// <param name="acquireCallback">Texture acquire callback</param>
 | |
|         /// <param name="releaseCallback">Texture release callback</param>
 | |
|         /// <param name="userObj">User defined object passed to the release callback</param>
 | |
|         /// <exception cref="ArgumentException">Thrown when <paramref name="pid"/> is invalid</exception>
 | |
|         /// <returns>True if the frame was added to the queue, false otherwise</returns>
 | |
|         public bool EnqueueFrameThreadSafe(
 | |
|             ulong                      pid,
 | |
|             ulong                      address,
 | |
|             int                        width,
 | |
|             int                        height,
 | |
|             int                        stride,
 | |
|             bool                       isLinear,
 | |
|             int                        gobBlocksInY,
 | |
|             Format                     format,
 | |
|             int                        bytesPerPixel,
 | |
|             ImageCrop                  crop,
 | |
|             Action<GpuContext, object> acquireCallback,
 | |
|             Action<object>             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;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Presents a texture on the queue.
 | |
|         /// If the queue is empty, then no texture is presented.
 | |
|         /// </summary>
 | |
|         /// <param name="swapBuffersCallback">Callback method to call when a new texture should be presented on the screen</param>
 | |
|         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, null, pt.Range);
 | |
| 
 | |
|                 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);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Indicate that a frame on the queue is ready to be acquired.
 | |
|         /// </summary>
 | |
|         public void SignalFrameReady()
 | |
|         {
 | |
|             Interlocked.Increment(ref _framesAvailable);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Determine if any frames are available, and decrement the available count if there are.
 | |
|         /// </summary>
 | |
|         /// <returns>True if a frame is available, false otherwise</returns>
 | |
|         public bool ConsumeFrameAvailable()
 | |
|         {
 | |
|             if (Interlocked.CompareExchange(ref _framesAvailable, 0, 0) != 0)
 | |
|             {
 | |
|                 Interlocked.Decrement(ref _framesAvailable);
 | |
| 
 | |
|                 return true;
 | |
|             }
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
| } | 
