Skip to content

Commit cc32dcd

Browse files
committed
Reimplement taking screenshots once more
1 parent 255f481 commit cc32dcd

File tree

7 files changed

+81
-59
lines changed

7 files changed

+81
-59
lines changed

src/NitroSharp.NsScript/VM/NsScriptProcess.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ internal void CommitSuspendThread(NsScriptThread thread, TimeSpan? timeoutOpt)
204204
{
205205
if (!IsRunning) { return; }
206206
thread.SuspensionTime = Ticks;
207-
if (timeoutOpt is TimeSpan timeout)
207+
if (timeoutOpt is { } timeout)
208208
{
209209
thread.SleepTimeout = (long)Math.Round(timeout.TotalSeconds * Stopwatch.Frequency);
210210
}

src/NitroSharp/Builtins.Graphics.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using NitroSharp.NsScript.Primitives;
88
using NitroSharp.NsScript.VM;
99
using NitroSharp.Text;
10+
using Veldrid;
1011

1112
namespace NitroSharp
1213
{
@@ -229,7 +230,8 @@ public override void CreateSprite(
229230
? _ctx.ActiveProcess
230231
: _ctx.MainProcess;
231232

232-
PooledTexture texture = _ctx.RenderToTexture(process);
233+
var texture = new PooledTexture(_renderCtx.OffscreenTexturePool, _renderCtx.OffscreenTexturePool.Rent());
234+
_ctx.Defer(DeferredOperation.TakeScreenshot(process, texture));
233235
return SpriteTexture.FromPooledTexture(texture);
234236
}
235237

src/NitroSharp/GameContext.cs

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ internal readonly record struct FrameStamp(long FrameId, long StopwatchTicks)
3232

3333
internal enum DeferredOperationKind
3434
{
35+
TakeScreenshot,
3536
SaveGame,
3637
LoadGame
3738
}
@@ -40,6 +41,15 @@ internal readonly struct DeferredOperation
4041
{
4142
public DeferredOperationKind Kind { get; private init; }
4243
public uint? SaveSlot { get; private init; }
44+
public GameProcess? ScreenshotProcess { get; private init; }
45+
public PooledTexture? ScreenshotTexture { get; private init; }
46+
47+
public static DeferredOperation TakeScreenshot(GameProcess process, PooledTexture texture) => new()
48+
{
49+
Kind = DeferredOperationKind.TakeScreenshot,
50+
ScreenshotTexture = texture,
51+
ScreenshotProcess = process
52+
};
4353

4454
public static DeferredOperation SaveGame(uint slot) => new()
4555
{
@@ -502,23 +512,19 @@ private void Tick(FrameStamp framestamp, float dt)
502512
bool assetsReady = Content.ResolveAssets();
503513
if (assetsReady)
504514
{
515+
RunDeferredOperations();
505516
world.BeginFrame();
506517
}
507518

508519
ProcessSounds(world, dt);
509-
RenderFrame(framestamp, world.RenderItems, dt, assetsReady);
520+
RenderFrame(world.RenderItems, dt, assetsReady);
510521
HandleInput();
511522

512523
if (assetsReady)
513524
{
514525
ActiveProcess.ProcessWaitOperations(this);
515526
}
516527

517-
if (assetsReady)
518-
{
519-
RunDeferredOperations();
520-
}
521-
522528
RenderContext.EndFrame();
523529

524530
try
@@ -532,7 +538,6 @@ private void Tick(FrameStamp framestamp, float dt)
532538
}
533539

534540
private void RenderFrame(
535-
in FrameStamp frameStamp,
536541
SortableEntityGroupView<RenderItem> renderItems,
537542
float dt,
538543
bool assetsReady)
@@ -554,8 +559,6 @@ private void RenderFrame(
554559
{
555560
ri.Render(RenderContext);
556561
}
557-
558-
RenderContext.TextureCache.BeginFrame(frameStamp);
559562
}
560563

561564
private static void ProcessSounds(World world, float dt)
@@ -622,7 +625,6 @@ private void CreateSysProcess(string mainModule)
622625
//_context.SysProcess?.Dispose();
623626
if (SysProcess is null)
624627
{
625-
//LastScreenshot ??= TakeScreenshot(MainProcess);
626628
MainProcess.VmProcess.Suspend();
627629
SysProcess = CreateProcess(VM, mainModule, _fontSettings);
628630
_clearFramebuffer = false;
@@ -636,6 +638,11 @@ private void RunDeferredOperations()
636638
{
637639
switch (op.Kind)
638640
{
641+
case DeferredOperationKind.TakeScreenshot:
642+
Debug.Assert(op.ScreenshotProcess is not null);
643+
Debug.Assert(op.ScreenshotTexture.HasValue);
644+
RenderToTexture(op.ScreenshotProcess, op.ScreenshotTexture.Value.Get(), fence: null);
645+
break;
639646
case DeferredOperationKind.SaveGame:
640647
Debug.Assert(op.SaveSlot is not null);
641648
SaveManager.Save(this, op.SaveSlot.Value);
@@ -653,25 +660,42 @@ private void RunDeferredOperations()
653660
}
654661
}
655662

656-
internal PooledTexture RenderToTexture(GameProcess process)
663+
internal void RenderToTexture(GameProcess process, Texture texture, Fence? fence)
657664
{
658-
return RenderContext.RenderToTexture(batch =>
665+
RenderContext.EndFrame();
666+
667+
RenderContext.BeginFrame(_now, RenderContext.OffscreenTarget, clear: true);
668+
ReadOnlySpan<RenderItem> activeItems = process.World.RenderItems.SortActive();
669+
foreach (RenderItem renderItem in activeItems)
659670
{
660-
ReadOnlySpan<RenderItem> activeItems = process.World.RenderItems.SortActive();
661-
foreach (RenderItem renderItem in activeItems)
662-
{
663-
renderItem.PerformLayout(this);
664-
}
671+
renderItem.PerformLayout(this);
672+
}
665673

666-
RenderContext.ResolveGlyphs();
674+
RenderContext.ResolveGlyphs();
667675

668-
foreach (RenderItem renderItem in activeItems)
669-
{
670-
renderItem.Render(RenderContext, batch);
671-
}
676+
foreach (RenderItem renderItem in activeItems)
677+
{
678+
renderItem.Render(RenderContext);
679+
}
680+
681+
RenderContext.EndFrame();
682+
683+
CommandList cl = RenderContext.CommandListPool.Rent();
684+
cl.Begin();
685+
cl.CopyTexture(source: RenderContext.OffscreenTarget.ColorTarget, texture);
686+
cl.End();
687+
688+
if (fence is not null)
689+
{
690+
RenderContext.GraphicsDevice.SubmitCommands(cl, fence);
691+
}
692+
else
693+
{
694+
RenderContext.GraphicsDevice.SubmitCommands(cl);
695+
}
672696

673-
RenderContext.TextureCache.BeginFrame(_now);
674-
});
697+
RenderContext.CommandListPool.Return(cl);
698+
RenderContext.BeginFrame(_now, _clearFramebuffer);
675699
}
676700

677701
internal void Defer(in DeferredOperation operation)

src/NitroSharp/Graphics/Core/TextureCache.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,25 +102,22 @@ public TextureCache(
102102
);
103103
}
104104

105+
public bool IsActive => _now.IsValid;
105106
public Texture UvRectTexture => _uvRectCache.Texture;
106107

107-
private FrameStamp DefaultEvictionPolicy => GetEvictionThreshold(
108-
maxFrames: null, maxTimeMs: 5000
109-
);
108+
private FrameStamp DefaultEvictionPolicy
109+
=> GetEvictionThreshold(maxFrames: null, maxTimeMs: 5000);
110110

111-
private FrameStamp AgressiveEvictionPolicy => GetEvictionThreshold(
112-
maxFrames: 1, maxTimeMs: null
113-
);
111+
private FrameStamp AggressiveEvictionPolicy
112+
=> GetEvictionThreshold(maxFrames: 1, maxTimeMs: null);
114113

115114
private ArrayTexture SelectArrayTexture(PixelFormat pixelFormat)
116-
{
117-
return pixelFormat switch
115+
=> pixelFormat switch
118116
{
119117
PixelFormat.R8_G8_B8_A8_UNorm => _rgbaTexture,
120118
PixelFormat.R8_UNorm => _r8Texture,
121119
_ => UnsupportedPixelFormat(pixelFormat)
122120
};
123-
}
124121

125122
public Texture GetCacheTexture(PixelFormat pixelFormat, out bool reallocatedThisFrame)
126123
{
@@ -244,7 +241,7 @@ private CacheEntry DoAllocateEntry(PixelFormat pixelFormat, Size textureSize)
244241

245242
FrameStamp evictionThreshold = arrayTexture.LayerCount < _maxTextureLayers
246243
? DefaultEvictionPolicy
247-
: AgressiveEvictionPolicy;
244+
: AggressiveEvictionPolicy;
248245
EvictOldEntries(evictionThreshold);
249246

250247
entryOpt = TryAllocateEntry(arrayTexture, textureSize);

src/NitroSharp/Graphics/RenderContext.cs

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ internal sealed class RenderContext : IDisposable
3232

3333
private readonly Swapchain _mainSwapchain;
3434
private readonly RenderTarget _swapchainTarget;
35-
private readonly RenderTarget _offscreenTarget;
3635
private readonly ResourcePool<Texture> _offscreenTexturePool;
3736
private readonly DrawBatch _offscreenBatch;
3837

@@ -56,7 +55,7 @@ public RenderContext(
5655
_shaderLibrary = new ShaderLibrary(graphicsDevice);
5756

5857
_swapchainTarget = RenderTarget.Swapchain(graphicsDevice, swapchain.Framebuffer);
59-
_offscreenTarget = new RenderTarget(graphicsDevice, _swapchainTarget.Size);
58+
OffscreenTarget = new RenderTarget(graphicsDevice, _swapchainTarget.Size);
6059
_offscreenTexturePool = new ResourcePool<Texture>(
6160
CreateOffscreenTexture,
6261
x => x.Dispose(),
@@ -153,6 +152,9 @@ public RenderContext(
153152

154153
public DrawBatch MainBatch { get; }
155154

155+
public RenderTarget OffscreenTarget { get; }
156+
public ref readonly ResourcePool<Texture> OffscreenTexturePool => ref _offscreenTexturePool;
157+
156158
public AnimatedIcons Icons { get; }
157159

158160
public SystemVariableLookup SystemVariables { get; }
@@ -205,10 +207,15 @@ private AnimatedIcons LoadIcons(GameProfile gameProfile)
205207
}
206208

207209
public void BeginFrame(in FrameStamp frameStamp, bool clear)
210+
{
211+
BeginFrame(frameStamp, _swapchainTarget, clear);
212+
}
213+
214+
public void BeginFrame(in FrameStamp frameStamp, RenderTarget renderTarget, bool clear)
208215
{
209216
_drawCommands.Begin();
210217
RgbaFloat? clearColor = clear ? RgbaFloat.Black : null;
211-
MainBatch.Begin(_drawCommands, _swapchainTarget, clearColor);
218+
MainBatch.Begin(_drawCommands, renderTarget, clearColor);
212219

213220
_secondaryCommandList.Begin();
214221

@@ -227,18 +234,6 @@ public DrawBatch BeginOffscreenBatch(RenderTarget renderTarget, RgbaFloat? clear
227234
return _offscreenBatch;
228235
}
229236

230-
public PooledTexture RenderToTexture(Action<DrawBatch> renderFunc)
231-
{
232-
using (DrawBatch batch = BeginOffscreenBatch(_offscreenTarget, RgbaFloat.Black))
233-
{
234-
renderFunc(batch);
235-
}
236-
237-
Texture destination = _offscreenTexturePool.Rent();
238-
_secondaryCommandList.CopyTexture(source: _offscreenTarget.ColorTarget, destination);
239-
return new PooledTexture(_offscreenTexturePool, destination);
240-
}
241-
242237
public void ResolveGlyphs()
243238
{
244239
Text.ResolveGlyphs();
@@ -265,7 +260,10 @@ public void EndFrame()
265260
ResourceSetCache.EndFrame();
266261

267262
Text.EndFrame(TransferCommands);
268-
TextureCache.EndFrame(TransferCommands);
263+
if (TextureCache.IsActive)
264+
{
265+
TextureCache.EndFrame(TransferCommands);
266+
}
269267
Quads.End(TransferCommands);
270268
QuadsUV3.End(TransferCommands);
271269
Cubes.End(TransferCommands);
@@ -306,7 +304,7 @@ public void Dispose()
306304
TextureCache.Dispose();
307305
ResourceSetCache.Dispose();
308306
_swapchainTarget.Dispose();
309-
_offscreenTarget.Dispose();
307+
OffscreenTarget.Dispose();
310308
_offscreenTexturePool.Dispose();
311309
_shaderLibrary.Dispose();
312310
_mainSwapchain.Dispose();

src/NitroSharp/Graphics/Shaders/transition.frag

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ layout(set = 1, binding = 2) uniform sampler Sampler;
77
layout(set = 2, binding = 0) uniform FadeAmount
88
{
99
float _FadeAmount;
10-
vec3 _padding;
1110
};
1211

1312
layout(location = 0) in vec4 fs_Color;

src/NitroSharp/Saving/GameSaveManager.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
using System;
22
using System.Buffers;
33
using System.Collections.Generic;
4-
using System.Diagnostics;
54
using System.IO;
65
using System.Linq;
76
using MessagePack;
87
using NitroSharp.Graphics;
9-
using NitroSharp.Graphics.Core;
108
using SixLabors.ImageSharp;
119
using SixLabors.ImageSharp.PixelFormats;
1210
using Veldrid;
@@ -71,7 +69,7 @@ public void Save(GameContext ctx, uint slot)
7169
.Select(x => rc.ReadbackTexture(cl, x))
7270
.ToArray();
7371
cl.End();
74-
Fence fence = rc.ResourceFactory.CreateFence(signaled: false);
72+
using Fence fence = rc.ResourceFactory.CreateFence(signaled: false);
7573
rc.GraphicsDevice.SubmitCommands(cl, fence);
7674
rc.GraphicsDevice.WaitForFence(fence);
7775

@@ -101,13 +99,17 @@ public void Save(GameContext ctx, uint slot)
10199
if (slot != AutosaveSlot)
102100
{
103101
using FileStream thumbStream = File.Create(Path.Combine(saveDir, "thum.npf"));
104-
using PooledTexture gpuScreenshot = ctx.RenderToTexture(ctx.MainProcess);
102+
Texture gpuScreenshot = rc.OffscreenTexturePool.Rent();
103+
fence.Reset();
104+
ctx.RenderToTexture(ctx.MainProcess, gpuScreenshot, fence);
105+
rc.GraphicsDevice.WaitForFence(fence);
105106
cl.Begin();
106-
using Texture screenshot = rc.ReadbackTexture(cl, gpuScreenshot.Get());
107+
using Texture screenshot = rc.ReadbackTexture(cl, gpuScreenshot);
107108
cl.End();
108109
fence.Reset();
109110
rc.GraphicsDevice.SubmitCommands(cl, fence);
110111
rc.GraphicsDevice.WaitForFence(fence);
112+
rc.OffscreenTexturePool.Return(gpuScreenshot);
111113
SaveAsPng(ctx.RenderContext, screenshot, thumbStream, 128, 72);
112114

113115
File.Create(Path.Combine(saveDir, "val.npf")).Close();

0 commit comments

Comments
 (0)