Prevent FIELD redraw flicker
This commit is contained in:
74
engine/Age.Engine.Tests/ScriptPresentationBarrierTests.cs
Normal file
74
engine/Age.Engine.Tests/ScriptPresentationBarrierTests.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Age.Engine.Hosting;
|
||||
using Xunit;
|
||||
|
||||
public class ScriptPresentationBarrierTests
|
||||
{
|
||||
[Fact]
|
||||
public void ScriptBurstWithholdsPresentationUntilExit()
|
||||
{
|
||||
var barrier = new ScriptPresentationBarrier();
|
||||
|
||||
barrier.EnterScript();
|
||||
Assert.False(barrier.TryEnterPresentation());
|
||||
|
||||
barrier.ExitScript();
|
||||
Assert.True(barrier.TryEnterPresentation());
|
||||
barrier.ExitPresentation();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestedScriptsRemainOneAtomicBurst()
|
||||
{
|
||||
var barrier = new ScriptPresentationBarrier();
|
||||
|
||||
barrier.EnterScript();
|
||||
barrier.EnterScript();
|
||||
barrier.ExitScript();
|
||||
Assert.False(barrier.TryEnterPresentation());
|
||||
|
||||
barrier.ExitScript();
|
||||
Assert.True(barrier.TryEnterPresentation());
|
||||
barrier.ExitPresentation();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ServiceBoundaryTemporarilyAllowsPresentation()
|
||||
{
|
||||
var barrier = new ScriptPresentationBarrier();
|
||||
|
||||
barrier.EnterScript();
|
||||
bool suspended = barrier.SuspendScript();
|
||||
Assert.True(suspended);
|
||||
Assert.True(barrier.TryEnterPresentation());
|
||||
barrier.ExitPresentation();
|
||||
|
||||
barrier.ResumeScript(suspended);
|
||||
Assert.False(barrier.TryEnterPresentation());
|
||||
barrier.ExitScript();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenderThreadCannotEnterWhileVmThreadIsRebuilding()
|
||||
{
|
||||
var barrier = new ScriptPresentationBarrier();
|
||||
using var entered = new ManualResetEventSlim();
|
||||
using var release = new ManualResetEventSlim();
|
||||
Task vmBurst = Task.Run(() =>
|
||||
{
|
||||
barrier.EnterScript();
|
||||
entered.Set();
|
||||
release.Wait();
|
||||
barrier.ExitScript();
|
||||
});
|
||||
|
||||
await Task.Run(entered.Wait);
|
||||
Assert.False(barrier.TryEnterPresentation());
|
||||
|
||||
release.Set();
|
||||
await vmBurst;
|
||||
Assert.True(barrier.TryEnterPresentation());
|
||||
barrier.ExitPresentation();
|
||||
}
|
||||
}
|
||||
47
engine/Age.Engine/Hosting/ScriptPresentationBarrier.cs
Normal file
47
engine/Age.Engine/Hosting/ScriptPresentationBarrier.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Age.Engine.Hosting;
|
||||
|
||||
/// <summary>
|
||||
/// Keeps one VM opcode burst atomic with respect to the interactive compositor. The VM owns the
|
||||
/// write side while script code is running; the render thread samples through the read side only
|
||||
/// after the VM reaches a presentation, sleep, or input service boundary.
|
||||
/// </summary>
|
||||
public sealed class ScriptPresentationBarrier
|
||||
{
|
||||
private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.NoRecursion);
|
||||
private int _scriptDepth;
|
||||
|
||||
public void EnterScript()
|
||||
{
|
||||
if (_scriptDepth++ == 0)
|
||||
_lock.EnterWriteLock();
|
||||
}
|
||||
|
||||
public void ExitScript()
|
||||
{
|
||||
if (_scriptDepth <= 0)
|
||||
throw new InvalidOperationException("No script burst is active.");
|
||||
if (--_scriptDepth == 0)
|
||||
_lock.ExitWriteLock();
|
||||
}
|
||||
|
||||
/// <summary>Temporarily expose the completed burst state while a host service is parked.</summary>
|
||||
public bool SuspendScript()
|
||||
{
|
||||
if (_scriptDepth <= 0 || !_lock.IsWriteLockHeld) return false;
|
||||
_lock.ExitWriteLock();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ResumeScript(bool suspended)
|
||||
{
|
||||
if (suspended) _lock.EnterWriteLock();
|
||||
}
|
||||
|
||||
public bool TryEnterPresentation()
|
||||
=> !_lock.IsWriteLockHeld && _lock.TryEnterReadLock(0);
|
||||
|
||||
public void ExitPresentation() => _lock.ExitReadLock();
|
||||
}
|
||||
Reference in New Issue
Block a user