mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2026-05-17 18:24:49 +08:00
R26_dev8 - First public commit
This commit is contained in:
@@ -0,0 +1,376 @@
|
||||
using SharpDX.Direct3D11;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Device = SharpDX.Direct3D11.Device;
|
||||
using Buffer = SharpDX.Direct3D11.Buffer;
|
||||
using CodeWalker.World;
|
||||
using System.Collections.Concurrent;
|
||||
using CodeWalker.GameFiles;
|
||||
using System.Threading;
|
||||
using CodeWalker.Properties;
|
||||
|
||||
namespace CodeWalker.Rendering
|
||||
{
|
||||
public class RenderableCache
|
||||
{
|
||||
public DateTime LastUpdate = DateTime.Now;
|
||||
public DateTime LastUnload = DateTime.Now;
|
||||
public double CacheTime = Settings.Default.GPUCacheTime;// 10.0; //seconds to keep something that's not used
|
||||
public double UnloadTime = Settings.Default.GPUCacheFlushTime;// 0.1; //seconds between running unload cycles
|
||||
public int MaxItemsPerLoop = 1; //to keep things flowing
|
||||
public bool ItemsStillPending = false; //whether or not we need another content thread loop
|
||||
|
||||
public long TotalGraphicsMemoryUse
|
||||
{
|
||||
get
|
||||
{
|
||||
//return (GeometryCacheUse + TextureCacheUse + BoundCompCacheUse + InstanceCacheUse);
|
||||
return (renderables.CacheUse + textures.CacheUse + boundcomps.CacheUse + instbatches.CacheUse);
|
||||
}
|
||||
}
|
||||
public int LoadedRenderableCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return renderables.CurrentLoadedCount;// loadedRenderables.Count;
|
||||
}
|
||||
}
|
||||
public int LoadedTextureCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return textures.CurrentLoadedCount;// loadedTextures.Count;
|
||||
}
|
||||
}
|
||||
public int MemCachedRenderableCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return renderables.CurrentCacheCount;// cacheRenderables.Count;
|
||||
}
|
||||
}
|
||||
public int MemCachedTextureCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return textures.CurrentCacheCount;// cacheTextures.Count;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private RenderableCacheLookup<DrawableBase, Renderable> renderables = new RenderableCacheLookup<DrawableBase, Renderable>(Settings.Default.GPUGeometryCacheSize, Settings.Default.GPUCacheTime);
|
||||
private RenderableCacheLookup<Texture, RenderableTexture> textures = new RenderableCacheLookup<Texture, RenderableTexture>(Settings.Default.GPUTextureCacheSize, Settings.Default.GPUCacheTime);
|
||||
private RenderableCacheLookup<BoundComposite, RenderableBoundComposite> boundcomps = new RenderableCacheLookup<BoundComposite, RenderableBoundComposite>(Settings.Default.GPUBoundCompCacheSize, Settings.Default.GPUCacheTime);
|
||||
private RenderableCacheLookup<YmapGrassInstanceBatch, RenderableInstanceBatch> instbatches = new RenderableCacheLookup<YmapGrassInstanceBatch, RenderableInstanceBatch>(67108864, Settings.Default.GPUCacheTime); //64MB - todo: make this a setting
|
||||
private RenderableCacheLookup<YmapDistantLODLights, RenderableDistantLODLights> distlodlights = new RenderableCacheLookup<YmapDistantLODLights, RenderableDistantLODLights>(33554432, Settings.Default.GPUCacheTime); //32MB - todo: make this a setting
|
||||
private RenderableCacheLookup<BasePathData, RenderablePathBatch> pathbatches = new RenderableCacheLookup<BasePathData, RenderablePathBatch>(536870912 /*33554432*/, Settings.Default.GPUCacheTime); // 512MB /*32MB*/ - todo: make this a setting
|
||||
private RenderableCacheLookup<WaterQuad, RenderableWaterQuad> waterquads = new RenderableCacheLookup<WaterQuad, RenderableWaterQuad>(4194304, Settings.Default.GPUCacheTime); //4MB - todo: make this a setting
|
||||
|
||||
|
||||
private object updateSyncRoot = new object();
|
||||
|
||||
private Device currentDevice;
|
||||
|
||||
|
||||
public void OnDeviceCreated(Device device)
|
||||
{
|
||||
currentDevice = device;
|
||||
}
|
||||
public void OnDeviceDestroyed()
|
||||
{
|
||||
currentDevice = null;
|
||||
|
||||
renderables.Clear();
|
||||
textures.Clear();
|
||||
boundcomps.Clear();
|
||||
instbatches.Clear();
|
||||
distlodlights.Clear();
|
||||
pathbatches.Clear();
|
||||
waterquads.Clear();
|
||||
}
|
||||
|
||||
public void ContentThreadProc()
|
||||
{
|
||||
if (currentDevice == null) return; //can't do anything with no device
|
||||
|
||||
Monitor.Enter(updateSyncRoot);
|
||||
|
||||
|
||||
//load the queued items if possible
|
||||
int renderablecount = renderables.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int texturecount = textures.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int boundcompcount = boundcomps.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int instbatchcount = instbatches.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int distlodlightcount = distlodlights.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int pathbatchcount = pathbatches.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int waterquadcount = waterquads.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
|
||||
|
||||
ItemsStillPending = (renderablecount >= MaxItemsPerLoop) ||
|
||||
(texturecount >= MaxItemsPerLoop) ||
|
||||
(boundcompcount >= MaxItemsPerLoop) ||
|
||||
(instbatchcount >= MaxItemsPerLoop) ||
|
||||
(distlodlightcount >= MaxItemsPerLoop) ||
|
||||
(pathbatchcount >= MaxItemsPerLoop) ||
|
||||
(waterquadcount >= MaxItemsPerLoop);
|
||||
|
||||
|
||||
//todo: change this to unload only when necessary (ie when something is loaded)
|
||||
var now = DateTime.Now;
|
||||
var deltat = (now - LastUpdate).TotalSeconds;
|
||||
var unloadt = (now - LastUnload).TotalSeconds;
|
||||
if ((unloadt > UnloadTime) && (deltat < 0.25)) //don't try the unload on every loop... or when really busy
|
||||
{
|
||||
|
||||
//unload items that haven't been used in longer than the cache period.
|
||||
renderables.UnloadProc();
|
||||
textures.UnloadProc();
|
||||
boundcomps.UnloadProc();
|
||||
instbatches.UnloadProc();
|
||||
distlodlights.UnloadProc();
|
||||
pathbatches.UnloadProc();
|
||||
waterquads.UnloadProc();
|
||||
|
||||
LastUnload = DateTime.Now;
|
||||
}
|
||||
|
||||
|
||||
LastUpdate = DateTime.Now;
|
||||
|
||||
Monitor.Exit(updateSyncRoot);
|
||||
}
|
||||
|
||||
public void RenderThreadSync()
|
||||
{
|
||||
renderables.RenderThreadSync();
|
||||
textures.RenderThreadSync();
|
||||
boundcomps.RenderThreadSync();
|
||||
instbatches.RenderThreadSync();
|
||||
distlodlights.RenderThreadSync();
|
||||
pathbatches.RenderThreadSync();
|
||||
waterquads.RenderThreadSync();
|
||||
}
|
||||
|
||||
public Renderable GetRenderable(DrawableBase drawable)
|
||||
{
|
||||
return renderables.Get(drawable);
|
||||
}
|
||||
public RenderableTexture GetRenderableTexture(Texture texture)
|
||||
{
|
||||
return textures.Get(texture);
|
||||
}
|
||||
public RenderableBoundComposite GetRenderableBoundComp(BoundComposite boundcomp)
|
||||
{
|
||||
return boundcomps.Get(boundcomp);
|
||||
}
|
||||
public RenderableInstanceBatch GetRenderableInstanceBatch(YmapGrassInstanceBatch batch)
|
||||
{
|
||||
return instbatches.Get(batch);
|
||||
}
|
||||
public RenderableDistantLODLights GetRenderableDistantLODLights(YmapDistantLODLights lights)
|
||||
{
|
||||
return distlodlights.Get(lights);
|
||||
}
|
||||
public RenderablePathBatch GetRenderablePathBatch(BasePathData pathdata)
|
||||
{
|
||||
return pathbatches.Get(pathdata);
|
||||
}
|
||||
public RenderableWaterQuad GetRenderableWaterQuad(WaterQuad quad)
|
||||
{
|
||||
return waterquads.Get(quad);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void Invalidate(BasePathData path)
|
||||
{
|
||||
lock (updateSyncRoot)
|
||||
{
|
||||
pathbatches.Invalidate(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public abstract class RenderableCacheItem<TKey>
|
||||
{
|
||||
public TKey Key;
|
||||
public volatile bool IsLoaded = false;
|
||||
public volatile bool LoadQueued = false;
|
||||
public long LastUseTime = 0;
|
||||
//public DateTime LastUseTime { get; set; }
|
||||
public long DataSize { get; set; }
|
||||
|
||||
public abstract void Init(TKey key);
|
||||
public abstract void Load(Device device);
|
||||
public abstract void Unload();
|
||||
}
|
||||
|
||||
public class RenderableCacheLookup<TKey, TVal> where TVal: RenderableCacheItem<TKey>, new()
|
||||
{
|
||||
private ConcurrentStack<TVal> itemsToLoad = new ConcurrentStack<TVal>();
|
||||
private ConcurrentStack<TVal> itemsToUnload = new ConcurrentStack<TVal>();
|
||||
private LinkedList<TVal> loadeditems = new LinkedList<TVal>();
|
||||
private Dictionary<TKey, TVal> cacheitems = new Dictionary<TKey, TVal>();
|
||||
public long CacheLimit;
|
||||
public long CacheUse = 0;
|
||||
public double CacheTime;
|
||||
public int LoadedCount = 0;//temporary, per loop
|
||||
|
||||
public RenderableCacheLookup(long limit, double time)
|
||||
{
|
||||
CacheLimit = limit;
|
||||
CacheTime = time;
|
||||
}
|
||||
|
||||
public int CurrentLoadedCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return loadeditems.Count;
|
||||
}
|
||||
}
|
||||
public int CurrentCacheCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return cacheitems.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
TVal item;
|
||||
while (itemsToLoad.TryPop(out item))
|
||||
{ }
|
||||
foreach (TVal rnd in loadeditems)
|
||||
{
|
||||
rnd.Unload();
|
||||
}
|
||||
loadeditems.Clear();
|
||||
cacheitems.Clear();
|
||||
while (itemsToUnload.TryPop(out item))
|
||||
{ }
|
||||
}
|
||||
|
||||
|
||||
public int LoadProc(Device device, int maxitemsperloop)
|
||||
{
|
||||
TVal item;
|
||||
LoadedCount = 0;
|
||||
while (itemsToLoad.TryPop(out item))
|
||||
{
|
||||
if (item.IsLoaded) continue; //don't load it again...
|
||||
LoadedCount++;
|
||||
long gcachefree = CacheLimit - CacheUse;
|
||||
if (gcachefree > item.DataSize)
|
||||
{
|
||||
try
|
||||
{
|
||||
item.Load(device);
|
||||
loadeditems.AddLast(item);
|
||||
Interlocked.Add(ref CacheUse, item.DataSize);
|
||||
}
|
||||
catch //(Exception ex)
|
||||
{
|
||||
//todo: error handling...
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
item.LoadQueued = false; //can try load it again later..
|
||||
}
|
||||
if (LoadedCount >= maxitemsperloop) break;
|
||||
}
|
||||
return LoadedCount;
|
||||
}
|
||||
|
||||
public void UnloadProc()
|
||||
{
|
||||
//unload items that haven't been used in longer than the cache period.
|
||||
var now = DateTime.Now;
|
||||
var rnode = loadeditems.First;
|
||||
while (rnode != null)
|
||||
{
|
||||
var lu = DateTime.FromBinary(Interlocked.Read(ref rnode.Value.LastUseTime));
|
||||
if ((now - lu).TotalSeconds > CacheTime)
|
||||
{
|
||||
var nextnode = rnode.Next;
|
||||
itemsToUnload.Push(rnode.Value);
|
||||
loadeditems.Remove(rnode);
|
||||
rnode = nextnode;
|
||||
}
|
||||
else
|
||||
{
|
||||
rnode = rnode.Next;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void RenderThreadSync()
|
||||
{
|
||||
TVal item;
|
||||
while (itemsToUnload.TryPop(out item))
|
||||
{
|
||||
if ((item.Key != null) && (cacheitems.ContainsKey(item.Key)))
|
||||
{
|
||||
cacheitems.Remove(item.Key);
|
||||
}
|
||||
item.Unload();
|
||||
item.LoadQueued = false;
|
||||
Interlocked.Add(ref CacheUse, -item.DataSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public TVal Get(TKey key)
|
||||
{
|
||||
if (key == null) return null;
|
||||
TVal item = null;
|
||||
if (!cacheitems.TryGetValue(key, out item))
|
||||
{
|
||||
item = new TVal();
|
||||
item.Init(key);
|
||||
cacheitems.Add(key, item);
|
||||
}
|
||||
Interlocked.Exchange(ref item.LastUseTime, DateTime.Now.ToBinary());
|
||||
if ((!item.IsLoaded) && (!item.LoadQueued))// ||
|
||||
{
|
||||
item.LoadQueued = true;
|
||||
itemsToLoad.Push(item);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
public void Invalidate(TKey key)
|
||||
{
|
||||
if (key == null) return;
|
||||
TVal item = null;
|
||||
if (!cacheitems.TryGetValue(key, out item)) return;
|
||||
if (item == null) return;
|
||||
|
||||
if ((item.Key != null) && (cacheitems.ContainsKey(item.Key)))
|
||||
{
|
||||
|
||||
cacheitems.Remove(item.Key);
|
||||
|
||||
item.Unload();
|
||||
item.LoadQueued = false;
|
||||
Interlocked.Add(ref CacheUse, -item.DataSize);
|
||||
Interlocked.Exchange(ref item.LastUseTime, DateTime.Now.AddHours(-1).ToBinary());
|
||||
|
||||
loadeditems.Remove(item);//slow...
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user