fix(streaming): preserve portal destination ownership
Detach old-world spatial ownership atomically, prioritize destination retirement dependencies, and reveal the viewport at the retail transition edge. Give private paperdoll views independent mesh ownership and retain dormant ACE entities so portal revisits preserve server objects without extending active GPU lifetimes.
This commit is contained in:
parent
2c848d4167
commit
823936ec31
57 changed files with 2551 additions and 324 deletions
|
|
@ -32,9 +32,12 @@ internal sealed unsafe class PrivateEntityViewportRenderer :
|
|||
private readonly WbDrawDispatcher _dispatcher;
|
||||
private readonly SceneLightingUboBinding _lightUbo;
|
||||
private readonly FixedEntityTextureOwnerLease _textureOwnerLease;
|
||||
private readonly IWbMeshAdapter _meshAdapter;
|
||||
private readonly IPrivateEntityViewportCamera _camera;
|
||||
private readonly HashSet<uint> _animatedIds;
|
||||
private readonly string _diagnosticName;
|
||||
private readonly List<SyntheticEntityMeshReferenceOwner>
|
||||
_retiringMeshReferences = [];
|
||||
|
||||
private uint _fbo;
|
||||
private uint _colorTex;
|
||||
|
|
@ -42,12 +45,14 @@ internal sealed unsafe class PrivateEntityViewportRenderer :
|
|||
private int _fbW;
|
||||
private int _fbH;
|
||||
private WorldEntity? _entity;
|
||||
private SyntheticEntityMeshReferenceOwner? _meshReferences;
|
||||
|
||||
public PrivateEntityViewportRenderer(
|
||||
GL gl,
|
||||
WbDrawDispatcher dispatcher,
|
||||
SceneLightingUboBinding lightUbo,
|
||||
IEntityTextureLifetime textureLifetime,
|
||||
IWbMeshAdapter meshAdapter,
|
||||
uint renderId,
|
||||
IPrivateEntityViewportCamera camera,
|
||||
string diagnosticName)
|
||||
|
|
@ -57,6 +62,8 @@ internal sealed unsafe class PrivateEntityViewportRenderer :
|
|||
_gl = gl ?? throw new ArgumentNullException(nameof(gl));
|
||||
_dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
|
||||
_lightUbo = lightUbo ?? throw new ArgumentNullException(nameof(lightUbo));
|
||||
_meshAdapter = meshAdapter
|
||||
?? throw new ArgumentNullException(nameof(meshAdapter));
|
||||
_camera = camera ?? throw new ArgumentNullException(nameof(camera));
|
||||
_diagnosticName = string.IsNullOrWhiteSpace(diagnosticName)
|
||||
? "creature viewport"
|
||||
|
|
@ -69,10 +76,63 @@ internal sealed unsafe class PrivateEntityViewportRenderer :
|
|||
|
||||
public void SetEntity(WorldEntity? entity)
|
||||
{
|
||||
ReleaseRetiringMeshReferences();
|
||||
|
||||
if (ReferenceEquals(_entity, entity))
|
||||
return;
|
||||
_textureOwnerLease.Replace(entity is not null);
|
||||
|
||||
SyntheticEntityMeshReferenceOwner? replacement = null;
|
||||
if (entity is not null)
|
||||
{
|
||||
replacement = new SyntheticEntityMeshReferenceOwner(
|
||||
_meshAdapter,
|
||||
CollectMeshIds(entity));
|
||||
replacement.Acquire();
|
||||
}
|
||||
|
||||
SyntheticEntityMeshReferenceOwner? previous = _meshReferences;
|
||||
try
|
||||
{
|
||||
_textureOwnerLease.Replace(entity is not null);
|
||||
}
|
||||
catch (Exception textureFailure)
|
||||
{
|
||||
if (replacement is null)
|
||||
throw;
|
||||
|
||||
try
|
||||
{
|
||||
replacement.Dispose();
|
||||
}
|
||||
catch (Exception rollbackFailure)
|
||||
{
|
||||
throw new AggregateException(
|
||||
$"The {_diagnosticName} texture-owner replacement failed "
|
||||
+ "and the replacement mesh-owner rollback did not converge.",
|
||||
textureFailure,
|
||||
rollbackFailure);
|
||||
}
|
||||
|
||||
System.Runtime.ExceptionServices.ExceptionDispatchInfo
|
||||
.Capture(textureFailure)
|
||||
.Throw();
|
||||
}
|
||||
|
||||
_meshReferences = replacement;
|
||||
_entity = entity;
|
||||
|
||||
if (previous is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
previous.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
_retiringMeshReferences.Add(previous);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public uint Render(int width, int height)
|
||||
|
|
@ -246,7 +306,78 @@ internal sealed unsafe class PrivateEntityViewportRenderer :
|
|||
public void Dispose()
|
||||
{
|
||||
_entity = null;
|
||||
_textureOwnerLease.Dispose();
|
||||
DeleteFramebuffer();
|
||||
if (_meshReferences is { } current)
|
||||
{
|
||||
_meshReferences = null;
|
||||
_retiringMeshReferences.Add(current);
|
||||
}
|
||||
|
||||
List<Exception>? failures = null;
|
||||
try
|
||||
{
|
||||
_textureOwnerLease.Dispose();
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
(failures ??= []).Add(error);
|
||||
}
|
||||
try
|
||||
{
|
||||
ReleaseRetiringMeshReferences();
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
(failures ??= []).Add(error);
|
||||
}
|
||||
try
|
||||
{
|
||||
DeleteFramebuffer();
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
(failures ??= []).Add(error);
|
||||
}
|
||||
|
||||
if (failures is not null)
|
||||
{
|
||||
throw new AggregateException(
|
||||
$"The {_diagnosticName} resources did not fully release.",
|
||||
failures);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<ulong> CollectMeshIds(WorldEntity entity)
|
||||
{
|
||||
for (int i = 0; i < entity.MeshRefs.Count; i++)
|
||||
yield return entity.MeshRefs[i].GfxObjId;
|
||||
for (int i = 0; i < entity.PartOverrides.Count; i++)
|
||||
yield return entity.PartOverrides[i].GfxObjId;
|
||||
}
|
||||
|
||||
private void ReleaseRetiringMeshReferences()
|
||||
{
|
||||
List<Exception>? failures = null;
|
||||
for (int i = _retiringMeshReferences.Count - 1; i >= 0; i--)
|
||||
{
|
||||
SyntheticEntityMeshReferenceOwner owner =
|
||||
_retiringMeshReferences[i];
|
||||
try
|
||||
{
|
||||
owner.Dispose();
|
||||
if (owner.IsDisposed)
|
||||
_retiringMeshReferences.RemoveAt(i);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
(failures ??= []).Add(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (failures is not null)
|
||||
{
|
||||
throw new AggregateException(
|
||||
$"One or more {_diagnosticName} mesh owners remain pending.",
|
||||
failures);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue