SHA256
Merge branch 'main' into video_bg_sync
This commit is contained in:
5 files changed
+118
-44
No files matched your search
@@ -26,6 +26,9 @@ namespace ADOFAIRenderer.Core
|
|||||||
|
|
||||||
public const int MaxQueuedFrames = 64;
|
public const int MaxQueuedFrames = 64;
|
||||||
public const int MaxRenderRetry = 64;
|
public const int MaxRenderRetry = 64;
|
||||||
|
|
||||||
|
// Statistics
|
||||||
|
public long framesSent;
|
||||||
|
|
||||||
// Core Render Configuration and Path
|
// Core Render Configuration and Path
|
||||||
public int countdown;
|
public int countdown;
|
||||||
@@ -332,7 +335,7 @@ namespace ADOFAIRenderer.Core
|
|||||||
var targetMethod = typeof(scnEditor).GetMethod("TryApplicationQuit",
|
var targetMethod = typeof(scnEditor).GetMethod("TryApplicationQuit",
|
||||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
|
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())
|
if (ADOBase.levelPath.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
scnEditor.instance.TryQuitToMenu();
|
scnEditor.instance.TryQuitToMenu();
|
||||||
@@ -382,6 +385,7 @@ namespace ADOFAIRenderer.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
frameCount = 0;
|
frameCount = 0;
|
||||||
|
framesSent = 0;
|
||||||
timeBase = Time.timeAsDouble;
|
timeBase = Time.timeAsDouble;
|
||||||
unscaledTimeBase = Time.unscaledTimeAsDouble;
|
unscaledTimeBase = Time.unscaledTimeAsDouble;
|
||||||
dspTimeBase = AudioSettings.dspTime;
|
dspTimeBase = AudioSettings.dspTime;
|
||||||
@@ -507,12 +511,15 @@ namespace ADOFAIRenderer.Core
|
|||||||
if (frame.ManagedBuffer.Length > 0)
|
if (frame.ManagedBuffer.Length > 0)
|
||||||
{
|
{
|
||||||
videoFFmpeg.SendDataFromBuffer(frame.ManagedBuffer, frame.Length);
|
videoFFmpeg.SendDataFromBuffer(frame.ManagedBuffer, frame.Length);
|
||||||
|
Interlocked.Increment(ref framesSent);
|
||||||
|
|
||||||
ArrayPool<byte>.Shared.Return(frame.ManagedBuffer);
|
ArrayPool<byte>.Shared.Return(frame.ManagedBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (frame.NativePointer != IntPtr.Zero && frame.Length > 0)
|
else if (frame.NativePointer != IntPtr.Zero && frame.Length > 0)
|
||||||
{
|
{
|
||||||
videoFFmpeg.SendDataFromNativeBuffer(frame.NativePointer, frame.Length);
|
videoFFmpeg.SendDataFromNativeBuffer(frame.NativePointer, frame.Length);
|
||||||
|
Interlocked.Increment(ref framesSent);
|
||||||
|
|
||||||
if (frame.ReadbackState != null)
|
if (frame.ReadbackState != null)
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ using MelonLoader.Utils;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.EventSystems;
|
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")]
|
[assembly: MelonGame("7th Beat Games", "A Dance of Fire and Ice")]
|
||||||
|
|
||||||
namespace ADOFAIRenderer
|
namespace ADOFAIRenderer
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using ADOFAIRenderer.Core;
|
using ADOFAIRenderer.Core;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -94,7 +95,7 @@ namespace ADOFAIRenderer.UI
|
|||||||
panelRect.anchorMax = new Vector2(1, 1);
|
panelRect.anchorMax = new Vector2(1, 1);
|
||||||
panelRect.pivot = new Vector2(1, 1);
|
panelRect.pivot = new Vector2(1, 1);
|
||||||
panelRect.anchoredPosition = new Vector2(-30, -30);
|
panelRect.anchoredPosition = new Vector2(-30, -30);
|
||||||
panelRect.sizeDelta = new Vector2(340, 250);
|
panelRect.sizeDelta = new Vector2(360, 220);
|
||||||
|
|
||||||
// 텍스트 생성
|
// 텍스트 생성
|
||||||
var textObj = new GameObject("MonitorText");
|
var textObj = new GameObject("MonitorText");
|
||||||
@@ -187,8 +188,7 @@ namespace ADOFAIRenderer.UI
|
|||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
sb.AppendLine("<b><color=#00ccff>ADOFAI Renderer Realtime Monitor</color></b>");
|
sb.AppendLine("<b><color=#00ccff>ADOFAI Renderer Realtime Monitor</color></b>");
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
sb.AppendLine($"• Rendered Progress: <b>{(RenderManager.Instance ? RenderManager.Instance.frameCount : 0)}</b> frames");
|
|
||||||
|
|
||||||
var queueCount = 0;
|
var queueCount = 0;
|
||||||
if (RenderManager.Instance)
|
if (RenderManager.Instance)
|
||||||
{
|
{
|
||||||
@@ -197,7 +197,10 @@ namespace ADOFAIRenderer.UI
|
|||||||
queueCount = RenderManager.Instance.ImageQueue.Count;
|
queueCount = RenderManager.Instance.ImageQueue.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.AppendLine($"• Queued Buffers: <b>{queueCount}</b> / {RenderManager.MaxQueuedFrames} frames");
|
|
||||||
|
sb.AppendLine($"• Queue: <b>{queueCount}</b> / {RenderManager.MaxQueuedFrames} frames");
|
||||||
|
sb.AppendLine($"• Rendered Frames: <b>{(RenderManager.Instance ? RenderManager.Instance.frameCount : 0)}</b>");
|
||||||
|
sb.AppendLine($"• Frames Sent: <b>{(RenderManager.Instance ? Interlocked.Read(ref RenderManager.Instance.framesSent) : 0)}</b>");
|
||||||
|
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
sb.AppendLine($"• Throttling Controls: {(RenderManager.Instance ? RenderManager.Instance.throttleCount : 0)} times");
|
sb.AppendLine($"• Throttling Controls: {(RenderManager.Instance ? RenderManager.Instance.throttleCount : 0)} times");
|
||||||
|
|||||||
+94
-19
@@ -22,7 +22,12 @@ namespace ADOFAIRenderer.UI
|
|||||||
private TextMeshProUGUI _statusText = null!;
|
private TextMeshProUGUI _statusText = null!;
|
||||||
|
|
||||||
private bool _isDownloading;
|
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 System.Threading.CancellationTokenSource? _cts;
|
||||||
private readonly System.Collections.Concurrent.ConcurrentQueue<Action> _mainThreadQueue = new();
|
private readonly System.Collections.Concurrent.ConcurrentQueue<Action> _mainThreadQueue = new();
|
||||||
@@ -31,9 +36,15 @@ namespace ADOFAIRenderer.UI
|
|||||||
{
|
{
|
||||||
var userLibs = MelonEnvironment.UserLibsDirectory;
|
var userLibs = MelonEnvironment.UserLibsDirectory;
|
||||||
if (!Directory.Exists(userLibs)) return false;
|
if (!Directory.Exists(userLibs)) return false;
|
||||||
|
|
||||||
|
string searchPattern = Application.platform switch
|
||||||
|
{
|
||||||
|
RuntimePlatform.WindowsPlayer => "avcodec-*.dll",
|
||||||
|
RuntimePlatform.OSXPlayer => "libavcodec.*.dylib",
|
||||||
|
_ => "libavcodec.so*"
|
||||||
|
};
|
||||||
|
|
||||||
// Check for avcodec-*.dll
|
var files = Directory.GetFiles(userLibs, searchPattern);
|
||||||
var files = Directory.GetFiles(userLibs, "avcodec-*.dll");
|
|
||||||
return files.Length > 0;
|
return files.Length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,7 +413,13 @@ namespace ADOFAIRenderer.UI
|
|||||||
Directory.CreateDirectory(userLibsDir);
|
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<float>(percent =>
|
var progressReporter = new Progress<float>(percent =>
|
||||||
{
|
{
|
||||||
@@ -410,7 +427,7 @@ namespace ADOFAIRenderer.UI
|
|||||||
UpdateStatus($"다운로드 중... ({(percent * 100f):F1}%)", new Color(0f, 0.8f, 1f));
|
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));
|
UpdateStatus("압축 해제 및 DLL 파일 복사 중...", new Color(0.9f, 0.9f, 0.2f));
|
||||||
@@ -532,26 +549,84 @@ namespace ADOFAIRenderer.UI
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Task.Run(() =>
|
if (Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.WindowsPlayer)
|
||||||
{
|
{
|
||||||
using var archive = ZipFile.OpenRead(zipPath);
|
await Task.Run(() =>
|
||||||
foreach (var entry in archive.Entries)
|
|
||||||
{
|
{
|
||||||
// dll 파일만 userlibs 디렉토리 경로에 바로 복사
|
var targetExtension = Application.platform == RuntimePlatform.OSXPlayer ? ".dylib" : ".dll";
|
||||||
if (entry.FullName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
|
using var archive = ZipFile.OpenRead(zipPath);
|
||||||
|
foreach (var entry in archive.Entries)
|
||||||
{
|
{
|
||||||
var targetPath = Path.Combine(outputDirectory, entry.Name);
|
if (entry.FullName.EndsWith(targetExtension, StringComparison.OrdinalIgnoreCase))
|
||||||
|
|
||||||
var dir = Path.GetDirectoryName(targetPath);
|
|
||||||
if (dir != null && !Directory.Exists(dir))
|
|
||||||
{
|
{
|
||||||
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
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
+8
-19
@@ -207,27 +207,16 @@ namespace ADOFAIRenderer.Util
|
|||||||
var data = request.GetData<byte>();
|
var data = request.GetData<byte>();
|
||||||
// 비동기 요청 버퍼 데이터를 지속성(NativeBuffer) 메모리로 복사
|
// 비동기 요청 버퍼 데이터를 지속성(NativeBuffer) 메모리로 복사
|
||||||
NativeArray<byte>.Copy(data, state.NativeBuffer, data.Length);
|
NativeArray<byte>.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();
|
NativePointer = ptr,
|
||||||
var capFrame = new RenderManager.CapturedFrame
|
Length = state.NativeBuffer.Length,
|
||||||
{
|
ReadbackState = state
|
||||||
NativePointer = ptr,
|
};
|
||||||
Length = state.NativeBuffer.Length,
|
PushToQueue(frame, capFrame);
|
||||||
ReadbackState = state
|
|
||||||
};
|
|
||||||
PushToQueue(frame, capFrame);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user