// Magica Cloth.
// Copyright (c) MagicaSoft, 2020-2022.
// https://magicasoft.jp
using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
namespace MagicaCloth
{
///
/// かたまり(チャンク)ごとに確保した固定インデックスNativeList
/// 一度確保したインデックスはズレない(ここ重要)
///
///
public class FixedChunkNativeArray : IDisposable where T : struct
{
///
/// ネイティブリスト
///
NativeArray nativeArray0;
NativeArray nativeArray1;
///
/// ネイティブリストの配列数
/// ※ジョブでエラーが出ないように事前に確保しておく
///
int nativeLength;
///
/// 空インデックススタック
///
List emptyChunkList = new List();
///
/// 使用インデックスセット
///
List useChunkList = new List();
int chunkSeed;
//int initLength = 256;
int initLength = 64;
T emptyElement;
int useLength;
//=========================================================================================
public FixedChunkNativeArray()
{
nativeArray0 = new NativeArray(initLength, Allocator.Persistent);
nativeLength = nativeArray0.Length;
useLength = 0;
}
//public FixedChunkNativeArray(int length)
//{
// initLength = length;
// nativeArray0 = new NativeArray(initLength, Allocator.Persistent);
// nativeLength = nativeArray0.Length;
// useLength = 0;
//}
public void Dispose()
{
if (nativeArray0.IsCreated)
{
nativeArray0.Dispose();
}
if (nativeArray1.IsCreated)
{
nativeArray1.Dispose();
}
nativeLength = 0;
useLength = 0;
emptyChunkList.Clear();
useChunkList.Clear();
}
public void SetEmptyElement(T empty)
{
emptyElement = empty;
}
//=========================================================================================
///
/// データチャンクの追加
///
///
///
public ChunkData AddChunk(int length)
{
// 再利用チェック
for (int i = 0; i < emptyChunkList.Count; i++)
{
var cdata = emptyChunkList[i];
if (cdata.dataLength >= length)
{
// このチャンクを再利用する
int remainder = cdata.dataLength - length;
if (remainder > 0)
{
// 分割
var rchunk = new ChunkData()
{
chunkNo = ++chunkSeed,
startIndex = cdata.startIndex + length,
dataLength = remainder,
};
emptyChunkList[i] = rchunk;
}
else
{
emptyChunkList.RemoveAt(i);
}
cdata.dataLength = length;
// 使用リストに追加
useChunkList.Add(cdata);
return cdata;
}
}
// 新規追加
var data = new ChunkData()
{
chunkNo = ++chunkSeed,
startIndex = useLength,
dataLength = length,
};
useChunkList.Add(data);
useLength += length;
if (nativeArray0.Length < useLength)
{
// 拡張
int len = nativeArray0.Length;
while (len < useLength)
len += Mathf.Min(len, 4096);
//len += len;
var nativeArray2 = new NativeArray(len, Allocator.Persistent);
nativeArray2.CopyFromFast(nativeArray0);
nativeArray0.Dispose();
nativeArray0 = nativeArray2;
nativeLength = nativeArray0.Length;
}
return data;
}
///
/// サイズ1のチャンクを追加しデータを設定する
///
///
///
public ChunkData Add(T data)
{
var c = AddChunk(1);
nativeArray0[c.startIndex] = data;
return c;
}
///
/// データチャンクの削除
///
///
public void RemoveChunk(int chunkNo)
{
for (int i = 0; i < useChunkList.Count; i++)
{
if (useChunkList[i].chunkNo == chunkNo)
{
// このチャンクを削除する
var cdata = useChunkList[i];
useChunkList.RemoveAt(i);
// データクリア
nativeArray0.SetValue(cdata.startIndex, cdata.dataLength, emptyElement);
// 前後の未使用チャンクと接続できるなら結合する
for (int j = 0; j < emptyChunkList.Count;)
{
var edata = emptyChunkList[j];
if ((edata.startIndex + edata.dataLength) == cdata.startIndex)
{
// 結合
edata.dataLength += cdata.dataLength;
cdata = edata;
emptyChunkList.RemoveAt(j);
continue;
}
else if (edata.startIndex == (cdata.startIndex + cdata.dataLength))
{
// 結合
cdata.dataLength += edata.dataLength;
emptyChunkList.RemoveAt(j);
continue;
}
j++;
}
// チャンクを空リストに追加
emptyChunkList.Add(cdata);
return;
}
}
}
///
/// データチャンクの削除
///
///
public void RemoveChunk(ChunkData chunk)
{
if (chunk.IsValid())
RemoveChunk(chunk.chunkNo);
}
///
/// 指定データで埋める
///
///
///
public void Fill(ChunkData chunk, T data)
{
//int end = chunk.startIndex + chunk.dataLength;
//for (int i = chunk.startIndex; i < end; i++)
//{
// nativeArray[i] = data;
//}
nativeArray0.SetValue(chunk.startIndex, chunk.dataLength, data);
}
///
/// 確保されているネイティブ配列の要素数を返す
///
public int Length
{
get
{
return nativeLength;
}
}
///
/// 実際に利用されているチャンク数を返す
///
public int ChunkCount
{
get
{
return useChunkList.Count;
}
}
///
/// 実際に使用されている要素数を返す
///
public int Count
{
get
{
int cnt = 0;
foreach (var c in useChunkList)
cnt += c.dataLength;
return cnt;
}
}
public T this[int index]
{
get
{
return nativeArray0[index];
}
set
{
nativeArray0[index] = value;
}
}
//public void Clear()
//{
// if (nativeArray0.IsCreated)
// nativeArray0.Dispose();
// nativeArray0 = new NativeArray(initLength, Allocator.Persistent);
// nativeLength = initLength;
// useLength = 0;
// emptyChunkList.Clear();
// useChunkList.Clear();
//}
//public T[] ToArray()
//{
// return nativeArray.ToArray();
//}
///
/// Jobで利用する場合はこの関数でNativeArrayに変換して受け渡す
///
///
public NativeArray ToJobArray()
{
return nativeArray0;
}
public NativeArray ToJobArray(int bufferIndex)
{
return bufferIndex == 0 ? nativeArray0 : nativeArray1;
}
//public NativeArray BackJobArray()
//{
// return nativeArray1;
//}
public void SwapBuffer()
{
var back = nativeArray1;
nativeArray1 = nativeArray0;
// サイズを合わせる
if (back.IsCreated == false || back.Length != nativeArray0.Length)
{
if (back.IsCreated)
back.Dispose();
//back = new NativeArray(nativeArray0.Length, Allocator.Persistent, NativeArrayOptions.ClearMemory);
back = new NativeArray(nativeArray0.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
back.CopyFromFast(nativeArray0);
//Debug.Log("★サイズ変更!");
}
nativeArray0 = back;
}
//=========================================================================================
public override string ToString()
{
string str = string.Empty;
str += "nativeList length=" + Length + "\n";
str += "use chunk count=" + ChunkCount + "\n";
str += "empty chunk count=" + emptyChunkList.Count + "\n";
str += "<< use chunks >>\n";
foreach (var cdata in useChunkList)
{
str += cdata;
}
str += "<< empty chunks >>\n";
foreach (var cdata in emptyChunkList)
{
str += cdata;
}
return str;
}
}
}