From f1aa8a0582db04f4ad194533157823ed041f57d3e0e08746ed56b5d1855b4759 Mon Sep 17 00:00:00 2001 From: copecone Date: Sat, 20 Jun 2026 07:00:45 +0900 Subject: [PATCH 1/6] Refactor --- Core/RenderManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/RenderManager.cs b/Core/RenderManager.cs index d4a68b8..2a2f4a0 100644 --- a/Core/RenderManager.cs +++ b/Core/RenderManager.cs @@ -332,7 +332,7 @@ namespace ADOFAIRenderer.Core var targetMethod = typeof(scnEditor).GetMethod("TryApplicationQuit", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); - if (targetMethod == null || scnEditor.instance == null) return; + if (targetMethod == null || !scnEditor.instance) return; if (ADOBase.levelPath.IsNullOrEmpty()) { scnEditor.instance.TryQuitToMenu(); From bb16bdad0558be4854ee5263880767d36683c12c88c8ae98cee291a2f63394cd Mon Sep 17 00:00:00 2001 From: copecone Date: Sat, 20 Jun 2026 07:39:49 +0900 Subject: [PATCH 2/6] Update Debug UI --- Core/RenderManager.cs | 7 +++++++ UI/DebugMonitorUI.cs | 11 +++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Core/RenderManager.cs b/Core/RenderManager.cs index 2a2f4a0..b6c7d7c 100644 --- a/Core/RenderManager.cs +++ b/Core/RenderManager.cs @@ -26,6 +26,9 @@ namespace ADOFAIRenderer.Core public const int MaxQueuedFrames = 64; public const int MaxRenderRetry = 64; + + // Statistics + public long framesSent; // Core Render Configuration and Path public int countdown; @@ -382,6 +385,7 @@ namespace ADOFAIRenderer.Core } frameCount = 0; + framesSent = 0; timeBase = Time.timeAsDouble; unscaledTimeBase = Time.unscaledTimeAsDouble; dspTimeBase = AudioSettings.dspTime; @@ -507,12 +511,15 @@ namespace ADOFAIRenderer.Core if (frame.ManagedBuffer.Length > 0) { videoFFmpeg.SendDataFromBuffer(frame.ManagedBuffer, frame.Length); + Interlocked.Increment(ref framesSent); + ArrayPool.Shared.Return(frame.ManagedBuffer); } } else if (frame.NativePointer != IntPtr.Zero && frame.Length > 0) { videoFFmpeg.SendDataFromNativeBuffer(frame.NativePointer, frame.Length); + Interlocked.Increment(ref framesSent); if (frame.ReadbackState != null) { diff --git a/UI/DebugMonitorUI.cs b/UI/DebugMonitorUI.cs index 20849cf..ec01f41 100644 --- a/UI/DebugMonitorUI.cs +++ b/UI/DebugMonitorUI.cs @@ -1,5 +1,6 @@ using System; using System.Text; +using System.Threading; using ADOFAIRenderer.Core; using TMPro; using UnityEngine; @@ -94,7 +95,7 @@ namespace ADOFAIRenderer.UI panelRect.anchorMax = new Vector2(1, 1); panelRect.pivot = new Vector2(1, 1); panelRect.anchoredPosition = new Vector2(-30, -30); - panelRect.sizeDelta = new Vector2(340, 250); + panelRect.sizeDelta = new Vector2(360, 220); // 텍스트 생성 var textObj = new GameObject("MonitorText"); @@ -187,8 +188,7 @@ namespace ADOFAIRenderer.UI var sb = new StringBuilder(); sb.AppendLine("ADOFAI Renderer Realtime Monitor"); sb.AppendLine(); - sb.AppendLine($"• Rendered Progress: {(RenderManager.Instance ? RenderManager.Instance.frameCount : 0)} frames"); - + var queueCount = 0; if (RenderManager.Instance) { @@ -197,7 +197,10 @@ namespace ADOFAIRenderer.UI queueCount = RenderManager.Instance.ImageQueue.Count; } } - sb.AppendLine($"• Queued Buffers: {queueCount} / {RenderManager.MaxQueuedFrames} frames"); + + sb.AppendLine($"• Queue: {queueCount} / {RenderManager.MaxQueuedFrames} frames"); + sb.AppendLine($"• Rendered Frames: {(RenderManager.Instance ? RenderManager.Instance.frameCount : 0)}"); + sb.AppendLine($"• Frames Sent: {(RenderManager.Instance ? Interlocked.Read(ref RenderManager.Instance.framesSent) : 0)}"); sb.AppendLine(); sb.AppendLine($"• Throttling Controls: {(RenderManager.Instance ? RenderManager.Instance.throttleCount : 0)} times"); From 5ab21a4dac989819b19009bf3185857c1b594a374784b38ae6eca6d4ea5e5bea Mon Sep 17 00:00:00 2001 From: copecone Date: Sat, 20 Jun 2026 07:40:47 +0900 Subject: [PATCH 3/6] Fix Duplicate Code --- Util/FrameReader.cs | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/Util/FrameReader.cs b/Util/FrameReader.cs index 9ffbb10..3e1db0b 100644 --- a/Util/FrameReader.cs +++ b/Util/FrameReader.cs @@ -207,27 +207,16 @@ namespace ADOFAIRenderer.Util var data = request.GetData(); // 비동기 요청 버퍼 데이터를 지속성(NativeBuffer) 메모리로 복사 NativeArray.Copy(data, state.NativeBuffer, data.Length); - - var ptr = (IntPtr)state.NativeBuffer.GetUnsafeReadOnlyPtr(); - var capFrame = new RenderManager.CapturedFrame - { - NativePointer = ptr, - Length = state.NativeBuffer.Length, - ReadbackState = state - }; - PushToQueue(frame, capFrame); } - else + + var ptr = (IntPtr)state.NativeBuffer.GetUnsafeReadOnlyPtr(); + var capFrame = new RenderManager.CapturedFrame { - var ptr = (IntPtr)state.NativeBuffer.GetUnsafeReadOnlyPtr(); - var capFrame = new RenderManager.CapturedFrame - { - NativePointer = ptr, - Length = state.NativeBuffer.Length, - ReadbackState = state - }; - PushToQueue(frame, capFrame); - } + NativePointer = ptr, + Length = state.NativeBuffer.Length, + ReadbackState = state + }; + PushToQueue(frame, capFrame); } } } From b717f4febc9242bc8b01a475a3f964b57000508723e116ddffe65b81425072fc Mon Sep 17 00:00:00 2001 From: copecone Date: Sat, 20 Jun 2026 07:44:41 +0900 Subject: [PATCH 4/6] Upgrade Version --- RenderMod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RenderMod.cs b/RenderMod.cs index 5c3128d..2501695 100644 --- a/RenderMod.cs +++ b/RenderMod.cs @@ -7,7 +7,7 @@ using MelonLoader.Utils; using UnityEngine; using UnityEngine.EventSystems; -[assembly: MelonInfo(typeof(RenderMod), "ADOFAI Renderer", "0.2.1-BETA", "ADOFAI Editor Forum")] +[assembly: MelonInfo(typeof(RenderMod), "ADOFAI Renderer", "0.2.2-BETA", "ADOFAI Editor Forum")] [assembly: MelonGame("7th Beat Games", "A Dance of Fire and Ice")] namespace ADOFAIRenderer From ab588f2ead9814e00a947b418d5ee9c4db4c898811f2d442f6544c35729bb4d0 Mon Sep 17 00:00:00 2001 From: copecone Date: Sat, 20 Jun 2026 08:16:19 +0900 Subject: [PATCH 5/6] Update `FFmpegDownloadUI` to Support Cross-Platform Downloads --- UI/FFmpegDownloadUI.cs | 113 ++++++++++++++++++++++++++++++++++------- 1 file changed, 94 insertions(+), 19 deletions(-) diff --git a/UI/FFmpegDownloadUI.cs b/UI/FFmpegDownloadUI.cs index b0f06c0..03dfb8f 100644 --- a/UI/FFmpegDownloadUI.cs +++ b/UI/FFmpegDownloadUI.cs @@ -22,7 +22,12 @@ namespace ADOFAIRenderer.UI private TextMeshProUGUI _statusText = null!; private bool _isDownloading; - private readonly string _downloadUrl = "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl-shared.zip"; + private string DownloadUrl => Application.platform switch + { + RuntimePlatform.WindowsPlayer => "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl-shared.zip", + RuntimePlatform.OSXPlayer => "https://github.com/ColorsWind/FFmpeg-macOS/releases/download/n5.0.1-patch3/FFmpeg-shared-n5.0.1-OSX-universal.zip", + _ => "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-linux64-gpl-shared.tar.xz" + }; private System.Threading.CancellationTokenSource? _cts; private readonly System.Collections.Concurrent.ConcurrentQueue _mainThreadQueue = new(); @@ -32,8 +37,14 @@ namespace ADOFAIRenderer.UI var userLibs = MelonEnvironment.UserLibsDirectory; if (!Directory.Exists(userLibs)) return false; - // Check for avcodec-*.dll - var files = Directory.GetFiles(userLibs, "avcodec-*.dll"); + string searchPattern = Application.platform switch + { + RuntimePlatform.WindowsPlayer => "avcodec-*.dll", + RuntimePlatform.OSXPlayer => "libavcodec.*.dylib", + _ => "libavcodec.so*" + }; + + var files = Directory.GetFiles(userLibs, searchPattern); return files.Length > 0; } @@ -402,7 +413,13 @@ namespace ADOFAIRenderer.UI Directory.CreateDirectory(userLibsDir); } - tempZipPath = Path.Combine(userLibsDir, "ffmpeg_temp.zip"); + string extension = Application.platform switch + { + RuntimePlatform.WindowsPlayer => ".zip", + RuntimePlatform.OSXPlayer => ".zip", + _ => ".tar.xz" + }; + tempZipPath = Path.Combine(userLibsDir, "ffmpeg_temp" + extension); var progressReporter = new Progress(percent => { @@ -410,7 +427,7 @@ namespace ADOFAIRenderer.UI UpdateStatus($"다운로드 중... ({(percent * 100f):F1}%)", new Color(0f, 0.8f, 1f)); }); - await DownloadFileAsync(_downloadUrl, tempZipPath, progressReporter, _cts.Token); + await DownloadFileAsync(DownloadUrl, tempZipPath, progressReporter, _cts.Token); // 다운로드 완료 후 압축 해제 UpdateStatus("압축 해제 및 DLL 파일 복사 중...", new Color(0.9f, 0.9f, 0.2f)); @@ -532,26 +549,84 @@ namespace ADOFAIRenderer.UI { try { - await Task.Run(() => + if (Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.WindowsPlayer) { - using var archive = ZipFile.OpenRead(zipPath); - foreach (var entry in archive.Entries) + await Task.Run(() => { - // dll 파일만 userlibs 디렉토리 경로에 바로 복사 - if (entry.FullName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) + var targetExtension = Application.platform == RuntimePlatform.OSXPlayer ? ".dylib" : ".dll"; + using var archive = ZipFile.OpenRead(zipPath); + foreach (var entry in archive.Entries) { - var targetPath = Path.Combine(outputDirectory, entry.Name); - - var dir = Path.GetDirectoryName(targetPath); - if (dir != null && !Directory.Exists(dir)) + if (entry.FullName.EndsWith(targetExtension, StringComparison.OrdinalIgnoreCase)) { - Directory.CreateDirectory(dir); + var targetPath = Path.Combine(outputDirectory, entry.Name); + + var dir = Path.GetDirectoryName(targetPath); + if (dir != null && !Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + + entry.ExtractToFile(targetPath, overwrite: true); } - - entry.ExtractToFile(targetPath, overwrite: true); } - } - }); + }); + } + else + { + // 리눅스 (또는 기타 플랫폼) - tar.xz 해제 + await Task.Run(() => + { + var tempExtractDir = Path.Combine(outputDirectory, "ffmpeg_temp_extracted"); + if (Directory.Exists(tempExtractDir)) + { + Directory.Delete(tempExtractDir, true); + } + Directory.CreateDirectory(tempExtractDir); + + var archiveUnixPath = zipPath.Replace('\\', '/'); + var tempExtractUnixDir = tempExtractDir.Replace('\\', '/'); + + var startInfo = new System.Diagnostics.ProcessStartInfo + { + FileName = "tar", + Arguments = $"-xf \"{archiveUnixPath}\" -C \"{tempExtractUnixDir}\"", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + }; + + using (var process = System.Diagnostics.Process.Start(startInfo)) + { + process!.WaitForExit(); + if (process.ExitCode != 0) + { + var error = process.StandardError.ReadToEnd(); + throw new Exception($"tar 압축 해제 실패 (ExitCode: {process.ExitCode}): {error}"); + } + } + + // 압축이 풀린 파일 중에서 .so 파일들을 찾아서 outputDirectory로 이동 + var soFiles = Directory.GetFiles(tempExtractDir, "*.so*", SearchOption.AllDirectories); + foreach (var soFile in soFiles) + { + var fileName = Path.GetFileName(soFile); + var targetPath = Path.Combine(outputDirectory, fileName); + File.Copy(soFile, targetPath, true); + } + + // 임시 디렉토리 정리 + try + { + Directory.Delete(tempExtractDir, true); + } + catch + { + // ignored + } + }); + } } finally { From 1d3653b725eb772e5026ef01722a41f49cebe208ddebe82d881f9eae43d52094 Mon Sep 17 00:00:00 2001 From: copecone Date: Sat, 20 Jun 2026 13:44:00 +0900 Subject: [PATCH 6/6] Refactor --- UI/FFmpegDownloadUI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/FFmpegDownloadUI.cs b/UI/FFmpegDownloadUI.cs index 03dfb8f..1b71ec3 100644 --- a/UI/FFmpegDownloadUI.cs +++ b/UI/FFmpegDownloadUI.cs @@ -36,7 +36,7 @@ namespace ADOFAIRenderer.UI { var userLibs = MelonEnvironment.UserLibsDirectory; if (!Directory.Exists(userLibs)) return false; - + string searchPattern = Application.platform switch { RuntimePlatform.WindowsPlayer => "avcodec-*.dll",