Skip to content

Commit ebf5d03

Browse files
[Rendering] WIP on texture read on SDL GPU;
1 parent dbbb36e commit ebf5d03

File tree

8 files changed

+174
-35
lines changed

8 files changed

+174
-35
lines changed

Engine/Staple.Core/Rendering/RenderSystem/Backend/IRendererBackend.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ IShaderProgram CreateShaderVertexFragment(byte[] vertex, byte[] fragment,
6060

6161
ITexture CreateEmptyTexture(int width, int height, TextureFormat format, TextureFlags flags);
6262

63+
void ReadTexture(ITexture texture, Action<byte[]> onComplete);
64+
6365
void Render(RenderState state);
6466

6567
void RenderTransient<T>(Span<T> vertices, VertexLayout layout, Span<ushort> indices, RenderState state) where T : unmanaged;

Engine/Staple.Core/Rendering/RenderSystem/Backend/Impls/SDLGPU/Commands/SDLGPUDestroyTextureCommand.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
using SDL3;
2-
using static Staple.InputAction;
3-
4-
namespace Staple.Internal;
1+
namespace Staple.Internal;
52

63
internal class SDLGPUDestroyTextureCommand(ResourceHandle<Texture> handle) : IRenderCommand
74
{
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using SDL3;
2+
using System;
3+
using static System.Runtime.InteropServices.JavaScript.JSType;
4+
5+
namespace Staple.Internal;
6+
7+
internal class SDLGPUReadTextureCommand(SDLGPUTexture texture, Action<byte[]> onComplete) : IRenderCommand
8+
{
9+
public readonly SDLGPUTexture texture = texture;
10+
public readonly Action<byte[]> onComplete = onComplete;
11+
12+
public void Update(IRendererBackend rendererBackend)
13+
{
14+
if (rendererBackend is not SDLGPURendererBackend backend ||
15+
(texture?.Disposed ?? true) ||
16+
backend.TryGetTexture(texture.handle, out var resource) == false ||
17+
resource.used == false ||
18+
resource.length == 0)
19+
{
20+
return;
21+
}
22+
23+
if (resource.transferBuffer != nint.Zero)
24+
{
25+
SDL.SDL_ReleaseGPUTransferBuffer(backend.device, resource.transferBuffer);
26+
27+
resource.transferBuffer = nint.Zero;
28+
}
29+
30+
var info = new SDL.SDL_GPUTransferBufferCreateInfo()
31+
{
32+
size = (uint)resource.length,
33+
usage = SDL.SDL_GPUTransferBufferUsage.SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD,
34+
};
35+
36+
resource.transferBuffer = SDL.SDL_CreateGPUTransferBuffer(backend.device, in info);
37+
38+
if (resource.transferBuffer == nint.Zero)
39+
{
40+
return;
41+
}
42+
43+
if (backend.renderPass != nint.Zero)
44+
{
45+
backend.FinishPasses();
46+
}
47+
48+
if (backend.copyPass == nint.Zero)
49+
{
50+
backend.copyPass = SDL.SDL_BeginGPUCopyPass(backend.commandBuffer);
51+
}
52+
53+
if (backend.copyPass == nint.Zero)
54+
{
55+
return;
56+
}
57+
58+
var textureInfo = new SDL.SDL_GPUTextureTransferInfo()
59+
{
60+
offset = 0,
61+
pixels_per_row = (uint)resource.width,
62+
rows_per_layer = (uint)resource.height,
63+
transfer_buffer = resource.transferBuffer,
64+
};
65+
66+
var destination = new SDL.SDL_GPUTextureRegion()
67+
{
68+
texture = resource.texture,
69+
w = (uint)resource.width,
70+
h = (uint)resource.height,
71+
d = 1,
72+
};
73+
74+
SDL.SDL_DownloadFromGPUTexture(backend.copyPass, in destination, in textureInfo);
75+
76+
backend.QueueTextureRead(texture, onComplete);
77+
}
78+
}

Engine/Staple.Core/Rendering/RenderSystem/Backend/Impls/SDLGPU/SDLGPURendererBackend+Textures.cs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ internal void ReleaseTextureResource(TextureResource resource)
1212
return;
1313
}
1414

15+
for(var i = readTextureQueue.Count - 1; i >= 0; i--)
16+
{
17+
if (TryGetTexture(readTextureQueue[i].Item1?.handle ?? default, out var r) &&
18+
r == resource)
19+
{
20+
readTextureQueue[i].Item2?.Invoke(null);
21+
22+
readTextureQueue.RemoveAt(i);
23+
}
24+
}
25+
1526
SDL.SDL_WaitForGPUIdle(device);
1627

1728
if (resource.transferBuffer != nint.Zero)
@@ -212,7 +223,7 @@ public ITexture CreateTextureAssetTexture(SerializableTexture asset, TextureFlag
212223
return null;
213224
}
214225

215-
var handle = SDLGPURendererBackend.ReserveTextureResource(textures, texture, asset.width, asset.height, format, flags);
226+
var handle = ReserveTextureResource(textures, texture, asset.width, asset.height, format, flags);
216227

217228
if (handle.IsValid == false)
218229
{
@@ -253,7 +264,7 @@ public ITexture CreatePixelTexture(byte[] data, int width, int height, TextureFo
253264
return null;
254265
}
255266

256-
var handle = SDLGPURendererBackend.ReserveTextureResource(textures, texture, width, height, format, flags);
267+
var handle = ReserveTextureResource(textures, texture, width, height, format, flags);
257268

258269
if(handle.IsValid == false)
259270
{
@@ -294,7 +305,7 @@ public ITexture CreateEmptyTexture(int width, int height, TextureFormat format,
294305
return null;
295306
}
296307

297-
var handle = SDLGPURendererBackend.ReserveTextureResource(textures, texture, width, height, format, flags);
308+
var handle = ReserveTextureResource(textures, texture, width, height, format, flags);
298309

299310
if (handle.IsValid == false)
300311
{
@@ -311,6 +322,34 @@ public void UpdateTexture(ResourceHandle<Texture> handle, Span<byte> data)
311322
AddCommand(new SDLGPUUpdateTextureCommand(handle, data.ToArray()));
312323
}
313324

325+
public void ReadTexture(ITexture texture, Action<byte[]> onComplete)
326+
{
327+
if(texture is not SDLGPUTexture t ||
328+
t.Disposed ||
329+
onComplete == null)
330+
{
331+
onComplete?.Invoke(null);
332+
333+
return;
334+
}
335+
336+
AddCommand(new SDLGPUReadTextureCommand(t, onComplete));
337+
}
338+
339+
internal void QueueTextureRead(SDLGPUTexture texture, Action<byte[]> onComplete)
340+
{
341+
if(texture == null ||
342+
texture.Disposed ||
343+
onComplete == null)
344+
{
345+
onComplete?.Invoke(null);
346+
347+
return;
348+
}
349+
350+
readTextureQueue.Add((texture, onComplete));
351+
}
352+
314353
public static SDL.SDL_GPUTextureType GetTextureType(TextureFlags flags)
315354
{
316355
if (flags.HasFlag(TextureFlags.TextureTypeCube))

Engine/Staple.Core/Rendering/RenderSystem/Backend/Impls/SDLGPU/SDLGPURendererBackend.cs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ public void CreateBuffers()
398398
private readonly Dictionary<TextureFlags, nint> textureSamplers = [];
399399
private bool needsDepthTextureUpdate = false;
400400
private readonly List<IRenderCommand> commands = [];
401+
private readonly List<(SDLGPUTexture, Action<byte[]>)> readTextureQueue = [];
401402

402403
private readonly BufferResource[] vertexBuffers = new BufferResource[ushort.MaxValue - 1];
403404
private readonly BufferResource[] indexBuffers = new BufferResource[ushort.MaxValue - 1];
@@ -710,7 +711,48 @@ public void EndFrame()
710711

711712
if (commandBuffer != nint.Zero)
712713
{
713-
SDL.SDL_SubmitGPUCommandBuffer(commandBuffer);
714+
var fences = new nint[SDL.SDL_SubmitGPUCommandBufferAndAcquireFence(commandBuffer)];
715+
716+
if(SDL.SDL_WaitForGPUFences(device, true, fences.AsSpan(), (uint)fences.Length) == false)
717+
{
718+
Log.Error($"[SDL GPU] Failed to wait for GPU Fences: {SDL.SDL_GetError()}");
719+
}
720+
721+
SDL.SDL_ReleaseGPUFence(device, fences[0]);
722+
723+
for(var i = readTextureQueue.Count - 1; i >= 0; i--)
724+
{
725+
var item = readTextureQueue[i];
726+
727+
readTextureQueue.RemoveAt(i);
728+
729+
if (item.Item1 == null ||
730+
item.Item1.Disposed ||
731+
TryGetTexture(item.Item1.handle, out var resource) == false ||
732+
resource.used == false ||
733+
resource.transferBuffer == nint.Zero)
734+
{
735+
continue;
736+
}
737+
738+
unsafe
739+
{
740+
var buffer = new byte[resource.length];
741+
742+
var map = SDL.SDL_MapGPUTransferBuffer(device, resource.transferBuffer, false);
743+
744+
var from = new Span<byte>((void *)map, buffer.Length);
745+
var to = new Span<byte>(buffer);
746+
747+
from.CopyTo(to);
748+
749+
SDL.SDL_UnmapGPUTransferBuffer(device, resource.transferBuffer);
750+
751+
resource.transferBuffer = nint.Zero;
752+
753+
item.Item2?.Invoke(buffer);
754+
}
755+
}
714756

715757
commandBuffer = nint.Zero;
716758
}

Engine/Staple.Core/Rendering/RenderSystem/RenderSystem+Internal.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ internal class DrawBucket
9090
/// Gets the reset flags for specific video flags
9191
/// </summary>
9292
/// <param name="videoFlags">The video flags to use</param>
93-
/// <returns>The BGFX Reset Flags</returns>
93+
/// <returns>The reset flags</returns>
9494
internal static RenderModeFlags RenderFlags(VideoFlags videoFlags)
9595
{
9696
var resetFlags = RenderModeFlags.None;

Engine/Staple.Core/Rendering/Texture/Texture.cs

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -502,36 +502,17 @@ public static bool PackTextures(RawTextureData[] textureData, int width, int hei
502502
/// Reads back the pixels for a texture. Requires readback on the texture metadata
503503
/// </summary>
504504
/// <param name="completion">The completion block when the data is ready. This is an async operation.</param>
505-
/// <param name="mipLevel">The mip level to read</param>
506505
/// <remarks>Render target textures need to use <see cref="RenderTarget.ReadTexture"/> instead</remarks>
507-
public void ReadPixels(Action<Texture, byte[]> completion, byte mipLevel = 0)
506+
public void ReadPixels(Action<Texture, byte[]> completion)
508507
{
509-
unsafe
508+
if(Disposed)
510509
{
511-
/*
512-
if (handle.Valid == false ||
513-
metadata.readBack == false ||
514-
renderTarget ||
515-
info.storageSize == 0)
516-
{
517-
completion?.Invoke(this, null);
518-
519-
return;
520-
}
521-
522-
var buffer = new byte[info.storageSize];
523-
524-
fixed (void* ptr = buffer)
525-
{
526-
var targetFrame = bgfx.read_texture(handle, ptr, 0);
510+
completion?.Invoke(null, null);
527511

528-
RenderSystem.Instance.QueueFrameCallback(targetFrame, () =>
529-
{
530-
completion?.Invoke(this, buffer);
531-
});
532-
}
533-
*/
512+
return;
534513
}
514+
515+
RenderSystem.Backend.ReadTexture(impl, (result) => completion?.Invoke(this, result));
535516
}
536517

537518
/// <summary>

Engine/Staple.Core/Resources/ResourceManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ internal void Destroy(DestroyMode mode)
303303
}
304304

305305
/// <summary>
306-
/// Attempts to recreate resources (used usually when context is lost and bgfx was restarted)
306+
/// Attempts to recreate resources (used usually when context is lost and rendering was restarted)
307307
/// </summary>
308308
internal void RecreateResources()
309309
{

0 commit comments

Comments
 (0)