Move Memory Copy Handling from Main Thread to Render Thread

This commit is contained in:
copecone committed 2026-05-29 12:03:03 +09:00
1 parent 459b2f3b0e
commit 036216c006
2 files changed
+68 -25

No files matched your search

+50 -11
View File
@@ -79,8 +79,16 @@ namespace ADOFAIRenderer.Core
}
}
internal struct CapturedFrame
{
public IntPtr NativePointer;
public int Length;
public byte[]? ManagedBuffer; // Non-null if it's a managed buffer (e.g. black frame)
public ReadbackRequestState? ReadbackState; // Associated state to recycle
}
// Thread and Queue State
public SortedList<long, byte[]> ImageQueue = new SortedList<long, byte[]>();
internal SortedList<long, CapturedFrame> ImageQueue = new SortedList<long, CapturedFrame>();
public Thread? RenderThread;
public long passCount;
public bool endRequested;
@@ -202,7 +210,11 @@ namespace ADOFAIRenderer.Core
lock (ImageQueue)
{
ImageQueue[videoFrameIndex] = blackFrame;
ImageQueue[videoFrameIndex] = new CapturedFrame
{
ManagedBuffer = blackFrame,
Length = byteSize
};
}
}
@@ -416,7 +428,8 @@ namespace ADOFAIRenderer.Core
while (true)
{
byte[]? buffer = null;
CapturedFrame frame = default;
bool hasFrame = false;
lock (ImageQueue)
{
if (ImageQueue.Count > 0)
@@ -430,20 +443,42 @@ namespace ADOFAIRenderer.Core
if (firstKey == passCount)
{
buffer = ImageQueue[firstKey];
frame = ImageQueue[firstKey];
ImageQueue.RemoveAt(0);
passCount++;
hasFrame = true;
}
}
}
if (buffer != null)
if (hasFrame)
{
if (buffer.Length > 0)
if (frame.ManagedBuffer != null)
{
var bufferSize = Configuration.RenderWidth * Configuration.RenderHeight * 4;
videoFFmpeg.SendDataFromBuffer(buffer, bufferSize);
if (frame.ManagedBuffer.Length > 0)
{
videoFFmpeg.SendDataFromBuffer(frame.ManagedBuffer, frame.Length);
ArrayPool<byte>.Shared.Return(frame.ManagedBuffer);
}
}
else if (frame.NativePointer != IntPtr.Zero && frame.Length > 0)
{
var buffer = FFmpegProcess.RentRawBytes(frame.Length);
unsafe
{
fixed (byte* destPtr = buffer)
{
Buffer.MemoryCopy((void*)frame.NativePointer, destPtr, buffer.Length, frame.Length);
}
}
videoFFmpeg.SendDataFromBuffer(buffer, frame.Length);
ArrayPool<byte>.Shared.Return(buffer);
if (frame.ReadbackState != null)
{
_frameReader?.RecycleState(frame.ReadbackState);
}
}
continue;
}
@@ -514,11 +549,15 @@ namespace ADOFAIRenderer.Core
// 1. 메모리 누수 방지: 큐에 렌트된 임시 프레임 버퍼 풀 반환 및 클리어
lock (ImageQueue)
{
foreach (var kvp in ImageQueue)
foreach (var kvp in ImageQueue.Values)
{
if (kvp.Value != null && kvp.Value.Length > 0)
if (kvp.ManagedBuffer != null && kvp.ManagedBuffer.Length > 0)
{
ArrayPool<byte>.Shared.Return(kvp.Value);
ArrayPool<byte>.Shared.Return(kvp.ManagedBuffer);
}
if (kvp.ReadbackState != null)
{
_frameReader?.RecycleState(kvp.ReadbackState);
}
}
ImageQueue.Clear();
+18 -14
View File
@@ -91,28 +91,32 @@ namespace ADOFAIRenderer.Util
var byteSize = state.NativeBuffer.Length;
var blackFrame = ArrayPool<byte>.Shared.Rent(byteSize);
Array.Clear(blackFrame, 0, byteSize);
PushToQueue(frame, blackFrame);
var capFrame = new RenderManager.CapturedFrame
{
ManagedBuffer = blackFrame,
Length = byteSize
};
PushToQueue(frame, capFrame);
RecycleState(state);
}
else
{
unsafe
{
var ptr = state.NativeBuffer.GetUnsafeReadOnlyPtr();
var rawBytes = FFmpegProcess.RentRawBytes(state.NativeBuffer.Length);
fixed (byte* destPtr = rawBytes)
var ptr = (IntPtr)state.NativeBuffer.GetUnsafeReadOnlyPtr();
var capFrame = new RenderManager.CapturedFrame
{
Buffer.MemoryCopy(ptr, destPtr, rawBytes.Length, state.NativeBuffer.Length);
}
PushToQueue(frame, rawBytes);
NativePointer = ptr,
Length = state.NativeBuffer.Length,
ReadbackState = state
};
PushToQueue(frame, capFrame);
}
}
RecycleState(state);
}
private void RecycleState(ReadbackRequestState state)
internal void RecycleState(ReadbackRequestState state)
{
lock (_poolLock)
{
@@ -127,12 +131,12 @@ namespace ADOFAIRenderer.Util
}
}
private static void PushToQueue(long frame, byte[] rawBytes)
private static void PushToQueue(long frame, RenderManager.CapturedFrame capturedFrame)
{
if (!RenderManager.Instance) return;
lock (RenderManager.Instance.ImageQueue)
{
RenderManager.Instance.ImageQueue[frame] = rawBytes;
RenderManager.Instance.ImageQueue[frame] = capturedFrame;
}
}