forked from MoonsideGames/MoonWorks
new MoonWorks.Graphics.Fence API
parent
7e18764942
commit
e0f05881b0
|
@ -1 +1 @@
|
||||||
Subproject commit 60a7523fac254d5e2d89185392e8c1afd8581aa9
|
Subproject commit b5325e6d0329eeb35b074091a569a5f679852d28
|
|
@ -1 +1 @@
|
||||||
Subproject commit 859f47f6fa0dfa0f7f941dcced6664fa83736202
|
Subproject commit 3dcd69ff85db80eea51481edd323b42c05993e1a
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MoonWorks.Graphics
|
||||||
|
{
|
||||||
|
internal class FencePool
|
||||||
|
{
|
||||||
|
private GraphicsDevice GraphicsDevice;
|
||||||
|
private Queue<Fence> Fences = new Queue<Fence>();
|
||||||
|
|
||||||
|
public FencePool(GraphicsDevice graphicsDevice)
|
||||||
|
{
|
||||||
|
GraphicsDevice = graphicsDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Fence Obtain()
|
||||||
|
{
|
||||||
|
if (Fences.Count == 0)
|
||||||
|
{
|
||||||
|
return new Fence(GraphicsDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Fences.Dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Return(Fence fence)
|
||||||
|
{
|
||||||
|
Fences.Enqueue(fence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ namespace MoonWorks.Graphics
|
||||||
public bool IsDisposed { get; private set; }
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
private readonly HashSet<WeakReference<GraphicsResource>> resources = new HashSet<WeakReference<GraphicsResource>>();
|
private readonly HashSet<WeakReference<GraphicsResource>> resources = new HashSet<WeakReference<GraphicsResource>>();
|
||||||
|
private FencePool FencePool;
|
||||||
|
|
||||||
public GraphicsDevice(
|
public GraphicsDevice(
|
||||||
Backend preferredBackend,
|
Backend preferredBackend,
|
||||||
|
@ -72,6 +73,8 @@ namespace MoonWorks.Graphics
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FencePool = new FencePool(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ClaimWindow(Window window, PresentMode presentMode)
|
public bool ClaimWindow(Window window, PresentMode presentMode)
|
||||||
|
@ -120,94 +123,174 @@ namespace MoonWorks.Graphics
|
||||||
return new CommandBuffer(this, Refresh.Refresh_AcquireCommandBuffer(Handle));
|
return new CommandBuffer(this, Refresh.Refresh_AcquireCommandBuffer(Handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void Submit(CommandBuffer commandBuffer)
|
/// <summary>
|
||||||
|
/// Submits a command buffer to the GPU for processing.
|
||||||
|
/// </summary>
|
||||||
|
public void Submit(CommandBuffer commandBuffer)
|
||||||
{
|
{
|
||||||
var commandBufferPtrs = stackalloc IntPtr[1];
|
|
||||||
|
|
||||||
commandBufferPtrs[0] = commandBuffer.Handle;
|
|
||||||
|
|
||||||
Refresh.Refresh_Submit(
|
Refresh.Refresh_Submit(
|
||||||
Handle,
|
Handle,
|
||||||
1,
|
commandBuffer.Handle
|
||||||
(IntPtr) commandBufferPtrs
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void Submit(
|
/// <summary>
|
||||||
CommandBuffer commandBufferOne,
|
/// Submits a command buffer to the GPU for processing and acquires a fence associated with the submission.
|
||||||
CommandBuffer commandBufferTwo
|
/// </summary>
|
||||||
) {
|
/// <returns></returns>
|
||||||
var commandBufferPtrs = stackalloc IntPtr[2];
|
public Fence SubmitAndAcquireFence(CommandBuffer commandBuffer)
|
||||||
|
|
||||||
commandBufferPtrs[0] = commandBufferOne.Handle;
|
|
||||||
commandBufferPtrs[1] = commandBufferTwo.Handle;
|
|
||||||
|
|
||||||
Refresh.Refresh_Submit(
|
|
||||||
Handle,
|
|
||||||
2,
|
|
||||||
(IntPtr) commandBufferPtrs
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void Submit(
|
|
||||||
CommandBuffer commandBufferOne,
|
|
||||||
CommandBuffer commandBufferTwo,
|
|
||||||
CommandBuffer commandBufferThree
|
|
||||||
) {
|
|
||||||
var commandBufferPtrs = stackalloc IntPtr[3];
|
|
||||||
|
|
||||||
commandBufferPtrs[0] = commandBufferOne.Handle;
|
|
||||||
commandBufferPtrs[1] = commandBufferTwo.Handle;
|
|
||||||
commandBufferPtrs[2] = commandBufferThree.Handle;
|
|
||||||
|
|
||||||
Refresh.Refresh_Submit(
|
|
||||||
Handle,
|
|
||||||
3,
|
|
||||||
(IntPtr) commandBufferPtrs
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void Submit(
|
|
||||||
CommandBuffer commandBufferOne,
|
|
||||||
CommandBuffer commandBufferTwo,
|
|
||||||
CommandBuffer commandBufferThree,
|
|
||||||
CommandBuffer commandBufferFour
|
|
||||||
) {
|
|
||||||
var commandBufferPtrs = stackalloc IntPtr[4];
|
|
||||||
|
|
||||||
commandBufferPtrs[0] = commandBufferOne.Handle;
|
|
||||||
commandBufferPtrs[1] = commandBufferTwo.Handle;
|
|
||||||
commandBufferPtrs[2] = commandBufferThree.Handle;
|
|
||||||
commandBufferPtrs[3] = commandBufferFour.Handle;
|
|
||||||
|
|
||||||
Refresh.Refresh_Submit(
|
|
||||||
Handle,
|
|
||||||
4,
|
|
||||||
(IntPtr) commandBufferPtrs
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void Submit(params CommandBuffer[] commandBuffers)
|
|
||||||
{
|
{
|
||||||
var commandBufferPtrs = stackalloc IntPtr[commandBuffers.Length];
|
var fenceHandle = Refresh.Refresh_SubmitAndAcquireFence(
|
||||||
|
|
||||||
for (var i = 0; i < commandBuffers.Length; i += 1)
|
|
||||||
{
|
|
||||||
commandBufferPtrs[i] = commandBuffers[i].Handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
Refresh.Refresh_Submit(
|
|
||||||
Handle,
|
Handle,
|
||||||
(uint) commandBuffers.Length,
|
commandBuffer.Handle
|
||||||
(IntPtr) commandBufferPtrs
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var fence = FencePool.Obtain();
|
||||||
|
fence.SetHandle(fenceHandle);
|
||||||
|
|
||||||
|
return fence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wait for the graphics device to become idle.
|
||||||
|
/// </summary>
|
||||||
public void Wait()
|
public void Wait()
|
||||||
{
|
{
|
||||||
Refresh.Refresh_Wait(Handle);
|
Refresh.Refresh_Wait(Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits for the given fence to become signaled.
|
||||||
|
/// </summary>
|
||||||
|
public unsafe void WaitForFences(Fence fence)
|
||||||
|
{
|
||||||
|
var handlePtr = stackalloc nint[1];
|
||||||
|
handlePtr[0] = fence.Handle;
|
||||||
|
|
||||||
|
Refresh.Refresh_WaitForFences(
|
||||||
|
Handle,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
(nint) handlePtr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wait for one or more fences to become signaled.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="waitAll">If true, will wait for all given fences to be signaled.</param>
|
||||||
|
public unsafe void WaitForFences(
|
||||||
|
Fence fenceOne,
|
||||||
|
Fence fenceTwo,
|
||||||
|
bool waitAll
|
||||||
|
) {
|
||||||
|
var handlePtr = stackalloc nint[2];
|
||||||
|
handlePtr[0] = fenceOne.Handle;
|
||||||
|
handlePtr[1] = fenceTwo.Handle;
|
||||||
|
|
||||||
|
Refresh.Refresh_WaitForFences(
|
||||||
|
Handle,
|
||||||
|
Conversions.BoolToByte(waitAll),
|
||||||
|
2,
|
||||||
|
(nint) handlePtr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wait for one or more fences to become signaled.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="waitAll">If true, will wait for all given fences to be signaled.</param>
|
||||||
|
public unsafe void WaitForFences(
|
||||||
|
Fence fenceOne,
|
||||||
|
Fence fenceTwo,
|
||||||
|
Fence fenceThree,
|
||||||
|
bool waitAll
|
||||||
|
) {
|
||||||
|
var handlePtr = stackalloc nint[3];
|
||||||
|
handlePtr[0] = fenceOne.Handle;
|
||||||
|
handlePtr[1] = fenceTwo.Handle;
|
||||||
|
handlePtr[2] = fenceThree.Handle;
|
||||||
|
|
||||||
|
Refresh.Refresh_WaitForFences(
|
||||||
|
Handle,
|
||||||
|
Conversions.BoolToByte(waitAll),
|
||||||
|
3,
|
||||||
|
(nint) handlePtr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wait for one or more fences to become signaled.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="waitAll">If true, will wait for all given fences to be signaled.</param>
|
||||||
|
public unsafe void WaitForFences(
|
||||||
|
Fence fenceOne,
|
||||||
|
Fence fenceTwo,
|
||||||
|
Fence fenceThree,
|
||||||
|
Fence fenceFour,
|
||||||
|
bool waitAll
|
||||||
|
) {
|
||||||
|
var handlePtr = stackalloc nint[4];
|
||||||
|
handlePtr[0] = fenceOne.Handle;
|
||||||
|
handlePtr[1] = fenceTwo.Handle;
|
||||||
|
handlePtr[2] = fenceThree.Handle;
|
||||||
|
handlePtr[3] = fenceFour.Handle;
|
||||||
|
|
||||||
|
Refresh.Refresh_WaitForFences(
|
||||||
|
Handle,
|
||||||
|
Conversions.BoolToByte(waitAll),
|
||||||
|
4,
|
||||||
|
(nint) handlePtr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wait for one or more fences to become signaled.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="waitAll">If true, will wait for all given fences to be signaled.</param>
|
||||||
|
public unsafe void WaitForFences(Fence[] fences, bool waitAll)
|
||||||
|
{
|
||||||
|
var handlePtr = stackalloc nint[fences.Length];
|
||||||
|
|
||||||
|
for (var i = 0; i < fences.Length; i += 1)
|
||||||
|
{
|
||||||
|
handlePtr[i] = fences[i].Handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Refresh.Refresh_WaitForFences(
|
||||||
|
Handle,
|
||||||
|
Conversions.BoolToByte(waitAll),
|
||||||
|
4,
|
||||||
|
(nint) handlePtr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the fence is signaled, indicating that the associated command buffer has finished processing.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="InvalidOperationException">Throws if the fence query indicates that the graphics device has been lost.</exception>
|
||||||
|
public bool QueryFence(Fence fence)
|
||||||
|
{
|
||||||
|
var result = Refresh.Refresh_QueryFence(Handle, fence.Handle);
|
||||||
|
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The graphics device has been lost.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Release reference to an acquired fence, enabling it to be reused.
|
||||||
|
/// </summary>
|
||||||
|
public void ReleaseFence(Fence fence)
|
||||||
|
{
|
||||||
|
Refresh.Refresh_ReleaseFence(Handle, fence.Handle);
|
||||||
|
fence.Handle = IntPtr.Zero;
|
||||||
|
FencePool.Return(fence);
|
||||||
|
}
|
||||||
|
|
||||||
private TextureFormat GetSwapchainFormat(Window window)
|
private TextureFormat GetSwapchainFormat(Window window)
|
||||||
{
|
{
|
||||||
return (TextureFormat) Refresh.Refresh_GetSwapchainFormat(Handle, window.Handle);
|
return (TextureFormat) Refresh.Refresh_GetSwapchainFormat(Handle, window.Handle);
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
using System;
|
||||||
|
using RefreshCS;
|
||||||
|
|
||||||
|
namespace MoonWorks.Graphics
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Fences allow you to track the status of a submitted command buffer.
|
||||||
|
/// You should only acquire a Fence if you will need to track the command buffer.
|
||||||
|
/// You should make sure to call GraphicsDevice.ReleaseFence when done with a Fence to avoid memory growth.
|
||||||
|
///
|
||||||
|
/// The Fence object itself is basically just a wrapper for the Refresh_Fence.
|
||||||
|
/// The internal handle is replaced so that we can pool Fence objects to manage garbage.
|
||||||
|
/// </summary>
|
||||||
|
public class Fence : GraphicsResource
|
||||||
|
{
|
||||||
|
protected override Action<nint, nint> QueueDestroyFunction => Release;
|
||||||
|
|
||||||
|
internal Fence(GraphicsDevice device) : base(device, true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SetHandle(nint handle)
|
||||||
|
{
|
||||||
|
Handle = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Release(nint deviceHandle, nint fenceHandle)
|
||||||
|
{
|
||||||
|
if (fenceHandle != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
// This will only be called if the client forgot to release a handle. Oh no!
|
||||||
|
Refresh.Refresh_ReleaseFence(deviceHandle, fenceHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -592,7 +592,7 @@ namespace MoonWorks.Graphics
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Asynchronously saves RGBA or BGRA pixel data to a file in PNG format.
|
/// Asynchronously saves RGBA or BGRA pixel data to a file in PNG format.
|
||||||
/// Warning: this is expensive!
|
/// Warning: this is expensive and will block to wait for data download from GPU!
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public unsafe void SavePNG(string path)
|
public unsafe void SavePNG(string path)
|
||||||
{
|
{
|
||||||
|
@ -608,14 +608,16 @@ namespace MoonWorks.Graphics
|
||||||
// immediately request the data copy
|
// immediately request the data copy
|
||||||
var commandBuffer = Device.AcquireCommandBuffer();
|
var commandBuffer = Device.AcquireCommandBuffer();
|
||||||
commandBuffer.CopyTextureToBuffer(this, buffer);
|
commandBuffer.CopyTextureToBuffer(this, buffer);
|
||||||
Device.Submit(commandBuffer);
|
var fence = Device.SubmitAndAcquireFence(commandBuffer);
|
||||||
|
|
||||||
var byteCount = buffer.Size;
|
var byteCount = buffer.Size;
|
||||||
|
|
||||||
var pixelsPtr = NativeMemory.Alloc((nuint) byteCount);
|
var pixelsPtr = NativeMemory.Alloc((nuint) byteCount);
|
||||||
var pixelsSpan = new Span<byte>(pixelsPtr, (int) byteCount);
|
var pixelsSpan = new Span<byte>(pixelsPtr, (int) byteCount);
|
||||||
|
|
||||||
Device.Wait(); // make sure the data transfer is done...
|
Device.WaitForFences(fence); // make sure the data transfer is done...
|
||||||
|
Device.ReleaseFence(fence); // and then release the fence
|
||||||
|
|
||||||
buffer.GetData(pixelsSpan);
|
buffer.GetData(pixelsSpan);
|
||||||
|
|
||||||
if (Format == TextureFormat.B8G8R8A8)
|
if (Format == TextureFormat.B8G8R8A8)
|
||||||
|
|
Loading…
Reference in New Issue