您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
295 行
7.9 KiB
295 行
7.9 KiB
//#define VERBOSE
|
|
|
|
using System;
|
|
using Unity.IO.LowLevel.Unsafe;
|
|
using Unity.Collections;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
using Unity.Mathematics;
|
|
using UnityEngine;
|
|
|
|
namespace Unity.DemoTeam.DigitalHuman
|
|
{
|
|
public unsafe struct NativeFrameStream : IDisposable
|
|
{
|
|
public string filename;
|
|
public int frameOffset;
|
|
public int frameCount;
|
|
public int frameSize;
|
|
|
|
public int seekRadius;
|
|
|
|
public int ringCapacityPow2;
|
|
public NativeArray<byte> ringData;
|
|
public NativeArray<ReadHandle> ringDataHnd;
|
|
public NativeArray<int> ringDataTag;
|
|
|
|
public NativeFrameStream(string filename, int frameOffset, int frameCount, int frameSize, int seekRadius, int ringCapacity = -1)
|
|
{
|
|
this.filename = filename;
|
|
this.frameOffset = frameOffset;
|
|
this.frameCount = frameCount;
|
|
this.frameSize = frameSize;
|
|
|
|
this.seekRadius = seekRadius;
|
|
|
|
ringCapacityPow2 = math.ceilpow2(math.max(ringCapacity, 1 + 2 * seekRadius));
|
|
ringData = new NativeArray<byte>(ringCapacityPow2 * frameSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
ringDataHnd = new NativeArray<ReadHandle>(ringCapacityPow2, Allocator.Persistent, NativeArrayOptions.ClearMemory);
|
|
ringDataTag = new NativeArray<int>(ringCapacityPow2, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
|
|
for (int i = 0; i != ringCapacityPow2; i++)
|
|
{
|
|
ringDataTag[i] = -1;
|
|
}
|
|
}
|
|
|
|
[System.Diagnostics.Conditional("UNITY_EDITOR")]
|
|
void ASSERT_BUFFERS()
|
|
{
|
|
Debug.Assert(ringData.IsCreated);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (ringDataHnd.IsCreated)
|
|
{
|
|
// ensure that all pending reads have completed
|
|
for (int i = 0; i != ringCapacityPow2; i++)
|
|
{
|
|
var dataHnd = ringDataHnd[i];
|
|
if (dataHnd.IsValid() && dataHnd.Status == ReadStatus.InProgress)
|
|
dataHnd.JobHandle.Complete();
|
|
}
|
|
|
|
ringDataHnd.Dispose();
|
|
}
|
|
|
|
if (ringDataTag.IsCreated)
|
|
ringDataTag.Dispose();
|
|
|
|
if (ringData.IsCreated)
|
|
ringData.Dispose();
|
|
}
|
|
|
|
void WaitForRingIndex(int ringIndex)
|
|
{
|
|
|
|
}
|
|
|
|
public void SeekFrame(int frameIndex)
|
|
{
|
|
ASSERT_BUFFERS();
|
|
|
|
#if VERBOSE
|
|
Debug.Log("SEEK " + frameIndex);
|
|
#endif
|
|
|
|
// sanitize user input
|
|
frameIndex = Mathf.Clamp(frameIndex, 0, frameCount - 1);
|
|
|
|
// expand range within bounds
|
|
int readIndexLo = Mathf.Max(frameIndex - seekRadius, 0);
|
|
int readIndexHi = Mathf.Min(frameIndex + seekRadius, frameCount - 1);
|
|
|
|
// schedule each read individually
|
|
var ringDataPtr = (byte*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(ringData);
|
|
for (int readIndex = readIndexLo; readIndex <= readIndexHi; readIndex++)
|
|
{
|
|
// map frame to index in ring
|
|
int ringIndex = (readIndex & (ringCapacityPow2 - 1));
|
|
|
|
// if frame is not already in ring
|
|
if (ringDataTag[ringIndex] != readIndex)
|
|
{
|
|
ringDataTag[ringIndex] = readIndex;
|
|
|
|
// wait for pending element operation
|
|
var dataHnd = ringDataHnd[ringIndex];
|
|
if (dataHnd.IsValid() && dataHnd.Status == ReadStatus.InProgress)
|
|
dataHnd.JobHandle.Complete();
|
|
|
|
// schedule the read
|
|
ReadCommand cmd;
|
|
cmd.Buffer = frameSize * ringIndex + ringDataPtr;
|
|
cmd.Offset = frameSize * readIndex + frameOffset;
|
|
cmd.Size = frameSize;
|
|
ringDataHnd[ringIndex] = AsyncReadManager.Read(filename, &cmd, 1);
|
|
|
|
#if VERBOSE
|
|
Debug.Log("schedule " + frameIndex + " -> ringIndex " + ringIndex);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
public void* ReadFrame(int frameIndex)
|
|
{
|
|
ASSERT_BUFFERS();
|
|
|
|
// sanitize user input
|
|
frameIndex = Mathf.Clamp(frameIndex, 0, frameCount - 1);
|
|
|
|
// map frame to index in ring
|
|
int ringIndex = (frameIndex & (ringCapacityPow2 - 1));
|
|
|
|
#if VERBOSE
|
|
Debug.Log("read " + frameIndex + " -> ringIndex " + ringIndex);
|
|
#endif
|
|
|
|
// seek if frame is not already in ring
|
|
if (ringDataTag[ringIndex] != frameIndex)
|
|
{
|
|
SeekFrame(frameIndex);
|
|
}
|
|
|
|
// wait for pending element operation
|
|
var dataHnd = ringDataHnd[ringIndex];
|
|
if (dataHnd.IsValid() && dataHnd.Status == ReadStatus.InProgress)
|
|
dataHnd.JobHandle.Complete();
|
|
|
|
// return the data
|
|
byte* ringDataPtr = (byte*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(ringData);
|
|
return ringDataPtr + ringIndex * frameSize;
|
|
}
|
|
}
|
|
|
|
/*
|
|
public unsafe struct NativeFrameStream2 : IDisposable
|
|
{
|
|
public string filename;
|
|
public int frameCount;
|
|
public int frameSize;
|
|
|
|
public int streamPosition;
|
|
public int streamReadAhead;
|
|
|
|
public int ringCapacity;
|
|
public int ringFrameLo;
|
|
public int ringFrameHi;
|
|
|
|
public NativeArray<byte> ringData;
|
|
public NativeArray<ReadHandle> ringDataHnd;
|
|
|
|
public NativeFrameStream2(string filename, int frameCount, int frameSize, int ringCapacity, int ringReadAhead)
|
|
{
|
|
this.filename = filename;
|
|
this.frameCount = frameCount;
|
|
this.frameSize = frameSize;
|
|
|
|
streamPosition = -1;
|
|
streamReadAhead = ringReadAhead;
|
|
|
|
this.ringCapacity = ringCapacity;
|
|
ringFrameLo = -1;
|
|
ringFrameHi = -1;
|
|
|
|
ringData = new NativeArray<byte>(this.ringCapacity * this.frameSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
ringDataHnd = new NativeArray<ReadHandle>(this.ringCapacity, Allocator.Persistent, NativeArrayOptions.ClearMemory);
|
|
}
|
|
|
|
[System.Diagnostics.Conditional("UNITY_EDITOR")]
|
|
void ASSERT_BUFFERS()
|
|
{
|
|
Debug.Assert(ringData.IsCreated);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
for (int i = 0; i != ringCapacity; i++)
|
|
{
|
|
if (ringDataHnd[i].Status == ReadStatus.InProgress)
|
|
ringDataHnd[i].JobHandle.Complete();
|
|
}
|
|
|
|
ringData.Dispose();
|
|
ringDataHnd.Dispose();
|
|
}
|
|
|
|
public void Seek(int frameIndex)
|
|
{
|
|
ASSERT_BUFFERS();
|
|
|
|
// sanitize user input
|
|
frameIndex = Mathf.Clamp(frameIndex, 0, frameCount - 1);
|
|
|
|
// requested range will be either
|
|
// a) fully contained
|
|
// b) fully disjoint
|
|
// c) extending up
|
|
// d) extending down
|
|
int frameLo = Mathf.Max(frameIndex - streamReadAhead, 0);
|
|
int frameHi = Mathf.Min(frameIndex + streamReadAhead, frameCount - 1);
|
|
|
|
var fullyContained = (frameLo >= ringFrameLo) && (frameHi <= ringFrameHi);
|
|
if (fullyContained)
|
|
{
|
|
return;// already buffered
|
|
}
|
|
else
|
|
{
|
|
var fullyDisjoint = (frameLo > ringFrameHi + 1) || (frameHi < ringFrameLo - 1);
|
|
if (fullyDisjoint)
|
|
{
|
|
ringFrameLo = frameLo;
|
|
ringFrameHi = frameHi;
|
|
}
|
|
else
|
|
{
|
|
var extendingUp = frameHi > ringFrameHi;
|
|
if (extendingUp)
|
|
{
|
|
frameLo = ringFrameHi + 1;
|
|
ringFrameHi = frameHi;
|
|
ringFrameLo = ringFrameHi - Mathf.Min(ringCapacity, ringFrameHi - ringFrameLo);
|
|
}
|
|
else
|
|
{
|
|
frameHi = ringFrameLo - 1;
|
|
ringFrameLo = frameLo;
|
|
ringFrameLo = ringFrameHi - Mathf.Min(ringCapacity, ringFrameHi - ringFrameLo);
|
|
}
|
|
}
|
|
}
|
|
|
|
// request each frame individually
|
|
var ringDataPtr = (byte*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(ringData);
|
|
|
|
for (int streamIndex = frameLo; streamIndex <= frameHi; streamIndex++)
|
|
{
|
|
// wait for element in ring
|
|
int ringIndex = (streamIndex % ringCapacity);
|
|
if (ringDataHnd[ringIndex].Status == ReadStatus.InProgress)
|
|
ringDataHnd[ringIndex].JobHandle.Complete();
|
|
|
|
// schedule the read
|
|
ReadCommand cmd;
|
|
cmd.Buffer = ringDataPtr + frameSize * ringIndex;
|
|
cmd.Offset = frameSize * streamIndex;
|
|
cmd.Size = frameSize;
|
|
ringDataHnd[ringIndex] = AsyncReadManager.Read(filename, &cmd, 1);
|
|
}
|
|
}
|
|
|
|
public void* Read(int frameIndex)
|
|
{
|
|
ASSERT_BUFFERS();
|
|
|
|
// sanitize user input
|
|
frameIndex = Mathf.Clamp(frameIndex, 0, frameCount - 1);
|
|
|
|
// seek if frame is not already in ring
|
|
if (frameIndex < ringFrameLo || frameIndex > ringFrameHi)
|
|
Seek(frameIndex);
|
|
|
|
// wait for element in ring
|
|
int ringIndex = (frameIndex % ringCapacity);
|
|
if (ringDataHnd[ringIndex].Status == ReadStatus.InProgress)
|
|
ringDataHnd[ringIndex].JobHandle.Complete();
|
|
|
|
// return the data
|
|
byte* ringDataPtr = (byte*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(ringData);
|
|
return ringDataPtr + ringIndex;
|
|
}
|
|
}
|
|
//*/
|
|
}
|