Optimize Frame Readback and Encoding Pipeline

This commit is contained in:
copecone committed 2026-10-07 15:48:09 +09:00
1 parent 892f1ca7b9
commit 86c8a87a0f
3 files changed
+58 -111

No files matched your search

+15 -51
View File
@@ -974,29 +974,22 @@ namespace ADOFAIRenderer.Util.FFmpeg
/// Encodes a frame from IntPtr (native pointer).
/// Can be used from external native plugins or custom capture sources.
/// </summary>
public void SendDataFromNativeBuffer(IntPtr pointer, int length)
public void SendDataFromNativeBuffer(IntPtr pointer, int length, bool isColorConverted)
{
lock (_lock)
{
if (_disposed || !_isInitialized) return;
var preConvertedSize = _settings.Width * _settings.Height * 3 / 2;
if (!_loggedConversionMode)
{
_loggedConversionMode = true;
Melon<RenderMod>.Logger.Msg(length == preConvertedSize
? "[Encoder] High-speed GPU color conversion enabled (NV12/YUV420P Direct Write, bypassing sws_scale)"
Melon<RenderMod>.Logger.Msg(isColorConverted
? "[Encoder] GPU color conversion enabled (NV12/YUV420P Direct Write, bypassing sws_scale)"
: "[Encoder] CPU color conversion fallback enabled (RGBA Input, using sws_scale)");
}
if (length == preConvertedSize)
{
WritePreConvertedFrame((byte*)pointer.ToPointer());
}
else
{
WriteFrameFromBuffer((byte*)pointer.ToPointer());
}
if (isColorConverted) WritePreConvertedFrame((byte*)pointer.ToPointer());
else WriteFrameFromBuffer((byte*)pointer.ToPointer());
}
}
@@ -1004,31 +997,23 @@ namespace ADOFAIRenderer.Util.FFmpeg
/// Encodes a frame from a managed byte[].
/// Pins the array using fixed keyword to prevent GC from moving it before passing the pointer.
/// </summary>
public void SendDataFromBuffer(byte[] buffer, int length)
public void SendDataFromBuffer(byte[] buffer, int length, bool isColorConverted)
{
lock (_lock)
{
if (_disposed || !_isInitialized) return;
fixed (byte* pBuffer = buffer)
{
var preConvertedSize = _settings.Width * _settings.Height * 3 / 2;
if (!_loggedConversionMode)
{
_loggedConversionMode = true;
Melon<RenderMod>.Logger.Msg(length == preConvertedSize
Melon<RenderMod>.Logger.Msg(isColorConverted
? "[Encoder] High-speed GPU color conversion enabled (NV12/YUV420P Direct Write, bypassing sws_scale)"
: "[Encoder] CPU color conversion fallback enabled (RGBA Input, using sws_scale)");
}
if (length == preConvertedSize)
{
WritePreConvertedFrame(pBuffer);
}
else
{
WriteFrameFromBuffer(pBuffer);
}
if (isColorConverted) WritePreConvertedFrame(pBuffer);
else WriteFrameFromBuffer(pBuffer);
}
}
}
@@ -1145,33 +1130,7 @@ namespace ADOFAIRenderer.Util.FFmpeg
throw new NotSupportedException($"This pixel format ({_dstPixFmt}) does not support direct copying of pre-converted buffers.");
}
// QSV acceleration handling or encoder send
if (_isQsvEncoder)
{
ffmpeg.av_frame_unref(_pHwFrame);
try
{
var ret = ffmpeg.av_hwframe_get_buffer(_pCodecContext->hw_frames_ctx, _pHwFrame, 0);
if (ret < 0)
throw new Exception($"Failed to allocate QSV hardware frame buffer — {GetFFmpegErrorString(ret)} (Code: {ret})");
ret = ffmpeg.av_hwframe_transfer_data(_pHwFrame, _pDstFrame, 0);
if (ret < 0)
throw new Exception($"Failed to upload QSV frame — {GetFFmpegErrorString(ret)} (Code: {ret})");
_pHwFrame->pts = _frameIndex++;
SendFrameAndWritePackets(_pHwFrame);
}
finally
{
ffmpeg.av_frame_unref(_pHwFrame);
}
}
else
{
_pDstFrame->pts = _frameIndex++;
SendFrameAndWritePackets(_pDstFrame);
}
SubmitDstFrame();
}
/// <summary>
@@ -1209,6 +1168,11 @@ namespace ADOFAIRenderer.Util.FFmpeg
_pDstFrame->data, _pDstFrame->linesize
);
SubmitDstFrame();
}
private void SubmitDstFrame()
{
if (_isQsvEncoder)
{
// ── QSV hw_frames_ctx mode: System memory → GPU surface upload ──