Skip to content

Commit

Permalink
Properly reimplement taking screenshots
Browse files Browse the repository at this point in the history
  • Loading branch information
sa-exe committed Aug 30, 2022
1 parent 149d527 commit 7e59589
Show file tree
Hide file tree
Showing 22 changed files with 372 additions and 225 deletions.
1 change: 1 addition & 0 deletions NitroSharp.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Ascender/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Autosave/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Bezier/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=blit/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=boxblur/@EntryIndexedValue">True</s:Boolean>
Expand Down
8 changes: 8 additions & 0 deletions src/NitroSharp.NsScript/VM/BuiltInFunctionDispatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ private void SetResult(in ConstantValue value)
case BuiltInFunction.WaitText:
WaitText(ref args);
break;
case BuiltInFunction.Draw:
Draw();
break;

case BuiltInFunction.CreateSound:
CreateSound(ref args);
Expand Down Expand Up @@ -311,6 +314,11 @@ private void SetResult(in ConstantValue value)
return result;
}

private void Draw()
{
_impl.Draw();
}

private void Shake(ref ArgConsumer args)
{
EntityQuery query = args.TakeEntityQuery();
Expand Down
2 changes: 2 additions & 0 deletions src/NitroSharp.NsScript/VM/BuiltInFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,5 +171,7 @@ public virtual void DeleteSave(uint slot) { }

public virtual float X360_GetTriggerAxis(XboxTrigger trigger) => 0;
public virtual void Reset() { }

public virtual void Draw() { }
}
}
17 changes: 12 additions & 5 deletions src/NitroSharp/Builtins.Graphics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
using System.Collections.Immutable;
using System.Numerics;
using NitroSharp.Graphics;
using NitroSharp.Graphics.Core;
using NitroSharp.NsScript;
using NitroSharp.NsScript.Primitives;
using NitroSharp.NsScript.VM;
using NitroSharp.Text;
using Veldrid;

namespace NitroSharp
{
Expand Down Expand Up @@ -225,10 +225,12 @@ public override void CreateSprite(
{
if (src is "SCREEN" or "Screen" or "VIDEO" or "Video")
{
Texture screenshotTexture = _renderCtx.CreateFullscreenTexture();
var result = SpriteTexture.FromStandalone(screenshotTexture);
_ctx.Defer(DeferredOperation.CaptureFramebuffer(screenshotTexture));
return result;
GameProcess process = src.Equals("SCREEN", StringComparison.OrdinalIgnoreCase)
? _ctx.ActiveProcess
: _ctx.MainProcess;

PooledTexture texture = _ctx.RenderToTexture(process);
return SpriteTexture.FromPooledTexture(texture);
}

if (_ctx.Content.RequestTexture(src) is { } asset)
Expand Down Expand Up @@ -396,6 +398,11 @@ public override void WaitText(EntityQuery query, TimeSpan timeout)
_ctx.Wait(CurrentThread, WaitCondition.EntityIdle, null, query);
}

public override void Draw()
{
Delay(TimeSpan.FromMicroseconds(1));
}

public override void CreateAlphaMask(
in EntityPath entityPath,
int priority,
Expand Down
7 changes: 5 additions & 2 deletions src/NitroSharp/Content/ContentManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,13 @@ public Texture LoadTexture(string path, bool staging)
return _textureLoader.LoadTexture(stream, staging);
}

public T? TryGet<T>(AssetRef<T> assetRef) where T : class, IDisposable
=> _cache.Get(assetRef.Handle).Asset as T;

public T Get<T>(AssetRef<T> assetRef)
where T : class, IDisposable
{
if (!(_cache.Get(assetRef.Handle).Asset is T loadedAsset))
if (_cache.Get(assetRef.Handle).Asset is not T loadedAsset)
{
throw new InvalidOperationException(
$"BUG: asset '{assetRef.Path}' is missing from the cache."
Expand Down Expand Up @@ -220,7 +223,7 @@ public bool ResolveAssets()
}
}

public Stream OpenStream(string path)
private Stream OpenStream(string path)
{
string fsPath = path;
if (Path.GetDirectoryName(path) is { } dir && Path.GetFileName(path) is { } filename)
Expand Down
2 changes: 1 addition & 1 deletion src/NitroSharp/Entity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ internal static class MouseStateEntities
public static readonly string[] All = { MouseUsual, MouseClick, MouseOver, MouseLeave };

public static bool IsMouseStateEntity(this Entity? entity)
=> entity is { Id.Name: MouseUsual or MouseClick or MouseOver or MouseLeave };
=> entity is { Id.Name: MouseUsual or MouseClick or MouseOver or MouseLeave };
}

internal interface EntityInternal
Expand Down
57 changes: 35 additions & 22 deletions src/NitroSharp/GameContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Threading.Tasks;
using NitroSharp.Content;
using NitroSharp.Graphics;
using NitroSharp.Graphics.Core;
using NitroSharp.Media;
using NitroSharp.NsScript;
using NitroSharp.NsScript.Compiler;
Expand All @@ -31,35 +32,24 @@ internal readonly record struct FrameStamp(long FrameId, long StopwatchTicks)

internal enum DeferredOperationKind
{
CaptureFramebuffer,
SaveGame,
LoadGame
}

internal readonly struct DeferredOperation
{
public DeferredOperationKind Kind { get; private init; }
public Texture? ScreenshotTexture { get; private init; }
public uint? SaveSlot { get; private init; }

public static DeferredOperation CaptureFramebuffer(Texture dstTexture) => new()
{
Kind = DeferredOperationKind.CaptureFramebuffer,
ScreenshotTexture = dstTexture,
SaveSlot = null
};

public static DeferredOperation SaveGame(uint slot) => new()
{
Kind = DeferredOperationKind.SaveGame,
ScreenshotTexture = null,
SaveSlot = slot
};

public static DeferredOperation LoadGame(uint slot) => new()
{
Kind = DeferredOperationKind.LoadGame,
ScreenshotTexture = null,
SaveSlot = slot
};
}
Expand All @@ -74,6 +64,7 @@ public sealed class GameContext : IAsyncDisposable
private (string, MediaStream?) _activeVoice;
private readonly Queue<DeferredOperation> _deferredOperations = new();
private bool _clearFramebuffer;
private FrameStamp _now;

private GameContext(
Logger logger,
Expand Down Expand Up @@ -126,7 +117,7 @@ private GameContext(

internal bool Skipping { get; private set; }
internal bool Advance { get; set; }
internal Texture? LastScreenshot { get; private set; }

internal EntityId FocusedUiElement { get; set; }
internal NsFocusDirection? RequestedFocusChange { get; set; }

Expand All @@ -152,7 +143,6 @@ public static async Task<GameContext> Create(GameWindow window, Config config, G
var saveManager = new GameSaveManager(profile);
var renderContext = new RenderContext(
window,
config,
profile,
gd,
swapchain,
Expand Down Expand Up @@ -473,6 +463,7 @@ private void MainLoop()
// necessary or until the engine is both feature-complete and stable.
private void Tick(FrameStamp framestamp, float dt)
{
_now = framestamp;
RenderContext.BeginFrame(framestamp, _clearFramebuffer);
_clearFramebuffer = true;
InputContext.Update(VM.SystemVariables);
Expand Down Expand Up @@ -518,15 +509,21 @@ private void Tick(FrameStamp framestamp, float dt)
ProcessSounds(world, dt);
RenderFrame(framestamp, world.RenderItems, dt, assetsReady);
HandleInput();
RunDeferredOperations();

if (assetsReady)
{
ActiveProcess.ProcessWaitOperations(this);
}

if (assetsReady)
{
RunDeferredOperations();
}

RenderContext.EndFrame();

try
{
RenderContext.EndFrame();
RenderContext.Present();
}
catch (VeldridException e)
Expand Down Expand Up @@ -556,7 +553,7 @@ private void RenderFrame(

foreach (RenderItem ri in active)
{
ri.Render(RenderContext, assetsReady);
ri.Render(RenderContext);
}

RenderContext.TextureCache.BeginFrame(frameStamp);
Expand Down Expand Up @@ -626,8 +623,7 @@ private void CreateSysProcess(string mainModule)
//_context.SysProcess?.Dispose();
if (SysProcess is null)
{
LastScreenshot ??= RenderContext.CreateFullscreenTexture(staging: true);
RenderContext.CaptureFramebuffer(LastScreenshot);
//LastScreenshot ??= TakeScreenshot(MainProcess);
MainProcess.VmProcess.Suspend();
SysProcess = CreateProcess(VM, mainModule, _fontSettings);
_clearFramebuffer = false;
Expand All @@ -641,10 +637,6 @@ private void RunDeferredOperations()
{
switch (op.Kind)
{
case DeferredOperationKind.CaptureFramebuffer:
Debug.Assert(op.ScreenshotTexture is not null);
RenderContext.CaptureFramebuffer(op.ScreenshotTexture);
break;
case DeferredOperationKind.SaveGame:
Debug.Assert(op.SaveSlot is not null);
SaveManager.Save(this, op.SaveSlot.Value);
Expand All @@ -662,6 +654,27 @@ private void RunDeferredOperations()
}
}

internal PooledTexture RenderToTexture(GameProcess process)
{
return RenderContext.RenderToTexture(batch =>
{
ReadOnlySpan<RenderItem> activeItems = process.World.RenderItems.SortActive();
foreach (RenderItem renderItem in activeItems)
{
renderItem.PerformLayout(this);
}

RenderContext.ResolveGlyphs();

foreach (RenderItem renderItem in activeItems)
{
renderItem.Render(RenderContext, batch);
}

RenderContext.TextureCache.BeginFrame(_now);
});
}

internal void Defer(in DeferredOperation operation)
{
ActiveProcess.VmProcess.Suspend();
Expand Down
23 changes: 23 additions & 0 deletions src/NitroSharp/Graphics/Core/PooledTexture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using Veldrid;

namespace NitroSharp.Graphics.Core;

internal readonly struct PooledTexture : IDisposable
{
private readonly ResourcePool<Texture> _pool;
private readonly Texture _texture;

public PooledTexture(ResourcePool<Texture> pool, Texture texture)
{
_pool = pool;
_texture = texture;
}

public Texture Get() => _texture;

public void Dispose()
{
_pool.Return(_texture);
}
}
19 changes: 15 additions & 4 deletions src/NitroSharp/Graphics/DrawBatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ internal sealed class DrawBatch : IDisposable
{
private readonly RenderContext _ctx;
private CommandList? _commandList;

private bool _began;
private Draw _lastDraw;
private Vector2 _lastAlphaMaskPosition = new(float.NaN);

Expand All @@ -187,13 +187,16 @@ public DrawBatch(RenderContext context)

public void Begin(CommandList commandList, RenderTarget target, RgbaFloat? clearColor)
{
Debug.Assert(!_began);
_commandList = commandList;
commandList.SetFramebuffer(target.Framebuffer);
Target = target;
if (clearColor is { } clear)
{
commandList.ClearColorTarget(0, clear);
}

_began = true;
}

public void UpdateBuffer<T>(GpuBuffer<T> buffer, in T data)
Expand All @@ -218,7 +221,7 @@ public void PushQuad(
QuadShaderResources resources = _ctx.ShaderResources.Quad;
if (alphaMaskPosition != _lastAlphaMaskPosition)
{
Vector4 newValue = new Vector4(alphaMaskPosition, 0, 0);
var newValue = new Vector4(alphaMaskPosition, 0, 0);
UpdateBuffer(resources.AlphaMaskPositionBuffer, newValue);
_lastAlphaMaskPosition = alphaMaskPosition;
}
Expand Down Expand Up @@ -357,7 +360,15 @@ void setResources(uint slot, ResourceSetKey? rsKeyOpt)
_lastDraw = default;
}

public void End() => Flush();
public void Dispose() => Flush();
public void End()
{
Flush();
_began = false;
}

public void Dispose()
{
End();
}
}
}
4 changes: 2 additions & 2 deletions src/NitroSharp/Graphics/Icon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public static Icon Load(RenderContext renderContext, IconPathPattern pathPattern
ContentManager content = renderContext.Content;
ResourceFactory rf = renderContext.ResourceFactory;
Texture? texture = null;
CommandList cl = renderContext.RentCommandList();
CommandList cl = renderContext.CommandListPool.Rent();
cl.Begin();
uint layer = 0;
foreach (string path in pathPattern.EnumeratePaths())
Expand All @@ -80,7 +80,7 @@ public static Icon Load(RenderContext renderContext, IconPathPattern pathPattern
}
cl.End();
renderContext.GraphicsDevice.SubmitCommands(cl);
renderContext.ReturnCommandList(cl);
renderContext.CommandListPool.Return(cl);

Debug.Assert(texture is not null);
return new Icon(texture);
Expand Down
Loading

0 comments on commit 7e59589

Please sign in to comment.