Normalize Comment Terminology

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
copeconeandClaude Sonnet 5.5 committed 2026-10-15 18:29:22 +09:00
1 parent 0fc01a84de
commit 961f78a332
25 files changed
+70 -70

No files matched your search

+3 -3
View File
@@ -180,7 +180,7 @@ public class RenderSession
/// Sets the custom condition function that determines when the render session should end.
/// </summary>
/// <remarks>
/// The condition is polled on the main thread once per frame from the start of the countdown until the render ends.
/// The condition is polled on the main Unity thread once per frame from the start of the countdown until the render ends.
/// When it first returns <c>true</c>, the render continues for <see cref="RenderConfig.DelayAfterEnd"/> seconds of
/// render time and then finishes successfully; it is not required to stay <c>true</c>. The default condition is
/// that the level is cleared (<c>scrController.instance.state == States.Won</c>). Calling this method again
@@ -231,7 +231,7 @@ public class RenderSession
/// Adds a callback action to be invoked when the render session completes or fails.
/// </summary>
/// <remarks>
/// Called once per render on the main thread: on success after the output file has been finished, on cancel or
/// Called once per render on the main Unity thread: on success after the output file has been finished, on cancel or
/// failure after the unfinished file has been deleted, and before the game returns to the level editor.
/// It is not called when the render stops because the game quits. Multiple calls combine the callbacks (invoked in
/// the order added). The combined callback replaces the default one, which shows the desktop notification.
@@ -249,7 +249,7 @@ public class RenderSession
/// Adds a callback action to be invoked on the last update of each frame in the render session.
/// </summary>
/// <remarks>
/// Called on the main thread every Unity frame after all other <c>LateUpdate</c> calls, from the moment the game scene
/// Called on the main Unity thread every Unity frame after all other <c>LateUpdate</c> calls, from the moment the game scene
/// of the render has loaded until the render ends (including the countdown, before capture starts).
/// Multiple calls combine the callbacks. An exception thrown by a callback is logged.
/// </remarks>
+1 -1
View File
@@ -39,7 +39,7 @@ public class RenderPreset
public static class PresetManager
{
/// <summary>
/// Built-in presets (H.264 CBR, 48 kHz stereo AAC at 256 kbps, mkv): 1080p, 1440p and 2160p at 60 fps video.
/// Built-in presets (H.264 CBR, 48 kHz stereo AAC at 256 kbps, mkv): 1080p, 1440p and 2160p at 60 FPS video.
/// "(4x)" presets render 240 game frames per second (4 per video frame); "(1x)" renders 60.
/// Video bitrates are in kbps.
/// </summary>
+14 -14
View File
@@ -92,7 +92,7 @@ internal static class RenderContext
}
};
/// <summary>
/// Called once on the main thread when the render finishes (success) or is cancelled or fails.
/// Called once on the main Unity thread when the render finishes (success) or is cancelled or fails.
/// A session callback replaces the default one, so no notification is shown when a session sets OnComplete.
/// Exceptions are caught and logged (RenderManager.TriggerOnComplete).
/// </summary>
@@ -101,7 +101,7 @@ internal static class RenderContext
private static readonly Action OnLastUpdateDefault = () => { };
/// <summary>
/// Called on the main thread every frame after all other LateUpdate calls, through PostRenderHook (attached when the
/// Called on the main Unity thread every frame after all other LateUpdate calls, through PostRenderHook (attached when the
/// game scene of the render has loaded) while the stage is Load or later.
/// Exceptions are caught and logged (RenderManager.TriggerOnLastUpdate).
/// </summary>
@@ -154,7 +154,7 @@ internal class RenderManager : MonoBehaviour
public static bool IsCapturingActive => Instance && Instance.stage >= RenderStage.Render;
/// <summary>
/// Maximum number of frames in ImageQueue. The main thread waits (throttles) while the queue holds more frames.
/// Maximum number of frames in ImageQueue. The main Unity thread waits (throttles) while the queue holds more frames.
/// </summary>
public const int MaxQueuedFrames = 64;
@@ -179,7 +179,7 @@ internal class RenderManager : MonoBehaviour
/// <summary>
/// Number of video frames sent to the encoder in this render. Incremented by the render thread (Interlocked),
/// read with Interlocked.Read by the main thread (result, debug monitor). Reset in SetupPlay.
/// read with Interlocked.Read by the main Unity thread (result, debug monitor). Reset in SetupPlay.
/// </summary>
public long framesSent;
@@ -276,14 +276,14 @@ internal class RenderManager : MonoBehaviour
/// <summary>
/// Configuration of the current render: a copy made by RenderSession.Start, so values adjusted during the render
/// (the audio sample rate in SetupPlay) do not change the caller's object. Read by the main thread, the render
/// (the audio sample rate in SetupPlay) do not change the caller's object. Read by the main Unity thread, the render
/// thread (audio settings) and the patches. Null until the first render starts.
/// </summary>
public RenderConfig Configuration = null!;
// Render Sessions State (OOP Instance Fields)
/// <summary>Current stage of the render. Written on the main thread; read by the render thread in TryDequeueNextFrame.</summary>
/// <summary>Current stage of the render. Written on the main Unity thread; read by the render thread in TryDequeueNextFrame.</summary>
public RenderStage stage = RenderStage.None;
/// <summary>Time.timeAsDouble when capture started. The time patch returns timeBase + frameCount / RenderFPS.</summary>
@@ -303,7 +303,7 @@ internal class RenderManager : MonoBehaviour
/// <summary>
/// Offline mixer of the song and sound effects of this render; null when audio is not rendered or no render runs.
/// Created in SetupPlay. Commands are added on the main thread (OnPostFrame, audio patches), mixing runs on the
/// Created in SetupPlay. Commands are added on the main Unity thread (OnPostFrame, audio patches), mixing runs on the
/// render thread (WriteAudioForFrame); the mixer synchronizes the two internally.
/// </summary>
public OfflineAudioMixer? AudioMixer { get; private set; }
@@ -392,10 +392,10 @@ internal class RenderManager : MonoBehaviour
public long lastVideoFrameIndex = -1;
/// <summary>
/// Video frame index of the last capture request (-1 before the first). Written on the main thread, read by the
/// Video frame index of the last capture request (-1 before the first). Written on the main Unity thread, read by the
/// render thread to know when every requested frame has been sent after endRequested.
/// </summary>
// long cannot be volatile: access via Volatile.Read/Write (written on main thread, read on render thread)
// long cannot be volatile: access via Volatile.Read/Write (written on main Unity thread, read on render thread)
public long lastRequestedVideoFrameIndex = -1;
/// <summary>
@@ -404,10 +404,10 @@ internal class RenderManager : MonoBehaviour
/// </summary>
public int readbackFailureCount;
/// <summary>Number of frames on which the main thread had to wait for the render thread (throttle loop entries).</summary>
/// <summary>Number of frames on which the main Unity thread had to wait for the render thread (throttle loop entries).</summary>
public int throttleCount;
/// <summary>True while the main thread is inside the throttle wait of OnPostFrame.</summary>
/// <summary>True while the main Unity thread is inside the throttle wait of OnPostFrame.</summary>
public bool isThrottling;
/// <summary>
@@ -438,7 +438,7 @@ internal class RenderManager : MonoBehaviour
/// <summary>
/// Captured frames waiting for the render thread, keyed by video frame index. Readbacks may complete in any order;
/// the render thread takes them in index order (passCount). Every access locks the list itself.
/// Frames are added by readback completions (main thread) and removed by the render thread, or released by the
/// Frames are added by readback completions (main Unity thread) and removed by the render thread, or released by the
/// cleanup in StopFFmpeg / RestoreRenderEnvironment.
/// </summary>
internal SortedList<long, CapturedFrame> ImageQueue = new();
@@ -788,7 +788,7 @@ internal class RenderManager : MonoBehaviour
if (!texture) return;
// [Backpressure Throttling]
// The main thread waits while ImageQueue holds more than QueueFrameLimit frames. Only the render thread
// The main Unity thread waits while ImageQueue holds more than QueueFrameLimit frames. Only the render thread
// removes frames, and it signals FrameDequeuedEvent for each one. The wait has a timeout so that the loop
// re-checks the render thread state and pumps pending readback completions (see ThrottleWaitMilliseconds).
isThrottling = false;
@@ -1249,7 +1249,7 @@ internal class RenderManager : MonoBehaviour
/// <summary>
/// Mixes the audio of the frame that was just sent (index passCount - 1) and writes it to the encoder.
/// The sample count per frame comes from SyncCalculator (sample rate / effective video FPS, with the remainder
/// carried to later frames). Mixing waits until the main thread has committed the audio commands up to that time.
/// carried to later frames). Mixing waits until the main Unity thread has committed the audio commands up to that time.
/// Does nothing when audio rendering is off. Render thread.
/// </summary>
/// <exception cref="OperationCanceledException">The mixer was cancelled (render aborted) while waiting.</exception>
+1 -1
View File
@@ -18,7 +18,7 @@ namespace ADOFAIRenderer.Localization;
/// </para>
/// <para>
/// Lookup order in <see cref="Get(string)"/>: active locale, then English, then the key itself.
/// Intended for the Unity main thread; the dictionaries are filled once during initialization and only read afterwards.
/// Intended for the main Unity thread; the dictionaries are filled once during initialization and only read afterwards.
/// </para>
/// </summary>
internal static class LocalizationManager
+3 -3
View File
@@ -30,7 +30,7 @@ public static class AudioManagerFindOrLoadAudioClipExternalPatch
}
/// <summary>
/// AudioManager._Play (private; AudioManager.Play forwards to it): registers each scheduled effect sound (hit sounds, PlaySound
/// AudioManager._Play (private; AudioManager.Play forwards to it): registers each scheduled sound effect (hit sounds, PlaySound
/// events, hold sounds without an end time) as an effect voice of the offline mixer and maps the returned AudioSource to it.
/// Active while capturing (RenderManager.IsCapturingActive) with RenderAudio on. The original always runs: it creates the
/// AudioSource the game keeps using (its PlayScheduled call is blocked during rendering by another patch), and the game sets
@@ -46,7 +46,7 @@ public static class AudioManagerPlayPatch
{
if (RenderManager.IsCapturingActive && RenderManager.Instance.Configuration.AudioConfig.RenderAudio)
{
// Cache the clip on the main thread
// Cache the clip on the main Unity thread
RenderManager.Instance.AudioMixer?.CacheClip(snd);
}
return true; // Let the original method execute to prevent NullReferenceException
@@ -104,7 +104,7 @@ public static class PlayWithEndTimePatch
var mixer = RenderManager.Instance.AudioMixer;
if (mixer != null)
{
// Cache the clip on the main thread
// Cache the clip on the main Unity thread
mixer.CacheClip(snd);
// Convert absolute DSP times to relative times in seconds from render start
+2 -2
View File
@@ -18,7 +18,7 @@ namespace ADOFAIRenderer.Patch.Engine;
/// (game and mod scripts).
/// </para>
/// <para>
/// All prefixes run on the thread that calls the getter (normally the Unity main thread) and read <c>RenderManager</c> state
/// All prefixes run on the thread that calls the getter (normally the main Unity thread) and read <c>RenderManager</c> state
/// without locks.
/// </para>
/// </summary>
@@ -264,7 +264,7 @@ public static class RenderEnvironmentPatches
/// that loads an external audio file of the level into <c>audioLib</c>): from stage Load with
/// <c>AdvancedConfig.ProcessAudio</c> on, forces <c>stream</c> to false. The game passes <c>stream</c> to
/// <c>DownloadHandlerAudioClip.streamAudio</c> for ogg/wav/aif/aiff files, so these clips are loaded as non-streaming
/// clips; the mp3 path uses <c>mp3Streaming</c> and ignores <c>stream</c>.
/// clips; the MP3 path uses <c>mp3Streaming</c> and ignores <c>stream</c>.
/// Reference: https://docs.unity3d.com/ScriptReference/Networking.DownloadHandlerAudioClip-streamAudio.html
/// </summary>
[HarmonyPatch(typeof(AudioManager), "FindOrLoadAudioClipExternal", typeof(string), typeof(bool), typeof(float), typeof(bool))]
+1 -1
View File
@@ -11,7 +11,7 @@ namespace ADOFAIRenderer.Patch.Game;
/// <c>Persistence.language</c> (it returns immediately when already initialized); and <c>RDString.ChangeLanguage(SystemLanguage)</c>,
/// which stores the language in <c>Persistence.language</c>, clears the initialized flag and calls Setup again.
/// </para>
/// <para>Always active (independent of the render stage). Runs on the Unity main thread.</para>
/// <para>Always active (independent of the render stage). Runs on the main Unity thread.</para>
/// </summary>
[HarmonyPatch(typeof(RDString))]
internal static class LanguageSyncPatch
+1 -1
View File
@@ -14,7 +14,7 @@ namespace ADOFAIRenderer.Patch.Game;
/// </para>
/// <para>
/// Always active except when the mod features are disabled (<see cref="RenderMod.IsFeatureDisabled"/>, set from the
/// driver warning dialog). Runs on the Unity main thread each time the editor scene starts.
/// driver warning dialog). Runs on the main Unity thread each time the editor scene starts.
/// </para>
/// </summary>
[HarmonyPatch(typeof(scnEditor))]
+1 -1
View File
@@ -8,7 +8,7 @@ namespace ADOFAIRenderer.Patch.Game;
/// already off.
/// <para>
/// Patched member: <c>scrCamera.SetCustomFrameRate(bool enable, float frame)</c> (level effect that updates the screen at a
/// lower frame rate). Enabling assigns a new RenderTexture to the camera quad material and refreshes it at <c>frame</c> fps.
/// lower frame rate). Enabling assigns a new RenderTexture to the camera quad material and refreshes it at <c>frame</c> FPS.
/// Disabling calls <c>Release()</c> on the quad material's current <c>mainTexture</c> and assigns <c>camRT</c> back. When
/// the effect was never enabled, <c>mainTexture</c> is <c>camRT</c> itself, so the original releases the live camera
/// render texture.
+3 -3
View File
@@ -21,7 +21,7 @@ internal class DialogButton
public string Text;
/// <summary>
/// Action run on the Unity main thread when the button is clicked. The dialog is destroyed after the action
/// Action run on the main Unity thread when the button is clicked. The dialog is destroyed after the action
/// returns, also when the action throws (the exception is logged).
/// </summary>
public Action OnClick;
@@ -49,7 +49,7 @@ internal class DialogButton
/// canvas: a translucent full-screen backdrop that blocks input to everything below, and an O5Kit window on top of it.
/// While the dialog exists, every enabled UI Toolkit root (<see cref="UIDocument"/>) is disabled so that it cannot
/// receive input; the roots are enabled again in <see cref="OnDestroy"/>. Clicking any button runs its action and then
/// destroys the dialog. All members run on the Unity main thread.
/// destroys the dialog. All members run on the main Unity thread.
/// </para>
/// </summary>
internal class CommonWarningDialogUI : MonoBehaviour
@@ -97,7 +97,7 @@ internal class CommonWarningDialogUI : MonoBehaviour
}
/// <summary>
/// Creates and shows a new dialog. Must be called on the Unity main thread.
/// Creates and shows a new dialog. Must be called on the main Unity thread.
/// </summary>
/// <param name="title">Window title text.</param>
/// <param name="description">Body text, centered and word-wrapped; TextMeshPro rich text tags are allowed.</param>
+2 -2
View File
@@ -14,7 +14,7 @@ namespace ADOFAIRenderer.UI;
/// <para>
/// Created by <c>RenderManager.StartFFmpegThread</c> on a GameObject kept across scene loads, and reused for later
/// renders. The overlay is drawn on the game screen only; it is not part of the encoded video frames.
/// <see cref="Update"/> rebuilds the text every frame on the Unity main thread.
/// <see cref="Update"/> rebuilds the text every frame on the main Unity thread.
/// </para>
/// </summary>
internal class DebugMonitorUI : MonoBehaviour
@@ -194,7 +194,7 @@ internal class DebugMonitorUI : MonoBehaviour
// Queue - frames waiting in ImageQueue for the render thread / queue frame limit
// Rendered Frames - RenderManager.frameCount (frames produced by the game)
// Frames Sent - RenderManager.framesSent (frames handed to the encoder; written by the render thread)
// Throttling - RenderManager.throttleCount (times the main thread started waiting because ImageQueue
// Throttling - RenderManager.throttleCount (times the main Unity thread started waiting because ImageQueue
// held more frames than its limit)
// Readback failures- RenderManager.readbackFailureCount (AsyncGPUReadback requests that completed with an error)
// Mono heap - GC.GetTotalMemory(false), managed heap size in MiB without forcing a collection
+1 -1
View File
@@ -14,7 +14,7 @@ namespace ADOFAIRenderer.UI;
/// (measured in real time with a <see cref="Stopwatch"/>, independent of the patched game time) calls
/// <c>RenderManager.CancelRender</c>. Releasing Escape earlier hides the panel and resets the countdown.
/// The component destroys its GameObject as soon as <c>RenderManager.IsActive</c> is false (stage below OnScene).
/// Runs on the Unity main thread.
/// Runs on the main Unity thread.
/// </para>
/// </summary>
internal class EscapeHandlerUI : MonoBehaviour
+8 -8
View File
@@ -25,9 +25,9 @@ namespace ADOFAIRenderer.UI;
/// <c>RenderMod.OnInitializeMelon</c>).
/// </para>
/// <para>
/// Threads: UI construction and all Unity object access happen on the Unity main thread. The download and extraction
/// Threads: UI construction and all Unity object access happen on the main Unity thread. The download and extraction
/// run on thread-pool threads (<see cref="Task.Run(Func{Task})"/>); they post UI updates to
/// <see cref="_mainThreadQueue"/>, which <see cref="Update"/> drains on the main thread.
/// <see cref="_mainThreadQueue"/>, which <see cref="Update"/> drains on the main Unity thread.
/// </para>
/// <para>
/// Flow: buttons -> (Download) progress bar + status text -> download to a temporary archive in UserLibs -> extract
@@ -70,7 +70,7 @@ internal class FFmpegDownloadUI : MonoBehaviour
/// <summary>Cancellation for the running download; created per download, cancelled by Cancel, disposed in OnDestroy.</summary>
private System.Threading.CancellationTokenSource? _cts;
/// <summary>Actions posted from background threads, run in order on the main thread by <see cref="Update"/>.</summary>
/// <summary>Actions posted from background threads, run in order on the main Unity thread by <see cref="Update"/>.</summary>
private readonly System.Collections.Concurrent.ConcurrentQueue<Action> _mainThreadQueue = new();
/// <summary>
@@ -376,7 +376,7 @@ internal class FFmpegDownloadUI : MonoBehaviour
/// <summary>Runs every action posted by the background download task, in posting order.</summary>
private void Update()
{
// Execute queued actions on main thread
// Execute queued actions on main Unity thread
while (_mainThreadQueue.TryDequeue(out var action))
{
action?.Invoke();
@@ -384,7 +384,7 @@ internal class FFmpegDownloadUI : MonoBehaviour
}
/// <summary>
/// Posts an action to run on the main thread during the next <see cref="Update"/>. Safe to call from any thread.
/// Posts an action to run on the main Unity thread during the next <see cref="Update"/>. Safe to call from any thread.
/// Actions posted after the dialog is destroyed are never run.
/// </summary>
private void ExecuteOnMainThread(Action action)
@@ -458,7 +458,7 @@ internal class FFmpegDownloadUI : MonoBehaviour
// Keep the "complete" message visible for 1000 ms before closing
await Task.Delay(1000);
// Destroy UI on main thread. FirstUpdateHook then sees Instance == null and checks the libraries again.
// Destroy UI on main Unity thread. FirstUpdateHook then sees Instance == null and checks the libraries again.
ExecuteOnMainThread(() =>
{
Destroy(gameObject);
@@ -513,7 +513,7 @@ internal class FFmpegDownloadUI : MonoBehaviour
Destroy(gameObject);
}
/// <summary>Posts a status text and color change to the main thread. Callable from any thread.</summary>
/// <summary>Posts a status text and color change to the main Unity thread. Callable from any thread.</summary>
private void UpdateStatus(string message, Color color)
{
ExecuteOnMainThread(() =>
@@ -526,7 +526,7 @@ internal class FFmpegDownloadUI : MonoBehaviour
});
}
/// <summary>Posts a progress bar update to the main thread. Callable from any thread.</summary>
/// <summary>Posts a progress bar update to the main Unity thread. Callable from any thread.</summary>
/// <param name="percent">Filled fraction 0..1 (not 0..100); becomes the fill's <c>anchorMax.x</c>.</param>
/// <param name="text">Text shown on the bar.</param>
private void UpdateProgress(float percent, string text)
+2 -2
View File
@@ -12,7 +12,7 @@ namespace ADOFAIRenderer.UI;
/// The text is set when the binding is made (<see cref="Bind(TextMeshProUGUI, string)"/>), when the component is enabled,
/// and on every <see cref="LocalizationManager.OnLanguageChanged"/> event. A key and a dynamic provider are mutually
/// exclusive: setting one clears the other. With neither set, <see cref="Refresh"/> leaves the text unchanged.
/// Runs on the Unity main thread.
/// Runs on the main Unity thread.
/// </para>
/// </summary>
internal class LocalizedText : MonoBehaviour
@@ -44,7 +44,7 @@ internal class LocalizedText : MonoBehaviour
/// The provider is called on every refresh (enable and language change), so it can combine several keys or
/// runtime values, e.g. a label that depends on UI state.
/// </summary>
/// <param name="provider">Function returning the text to display; called on the Unity main thread.</param>
/// <param name="provider">Function returning the text to display; called on the main Unity thread.</param>
public void SetDynamic(Func<string> provider)
{
_dynamicProvider = provider;
+2 -2
View File
@@ -24,7 +24,7 @@ namespace ADOFAIRenderer.UI.Window;
/// <para>
/// Opened from the level editor through <c>RenderManager</c> (after the unsaved-changes check) with
/// <see cref="CreateWindow"/>. The window is built once and then hidden/shown; its state lives in static fields for the
/// whole game session. All members run on the Unity main thread.
/// whole game session. All members run on the main Unity thread.
/// </para>
/// <para>
/// Data flow:
@@ -658,7 +658,7 @@ internal static class RenderConfigWindow
var (globalLeft, globalRight) = CreateTwoColumnPanel(globalContent);
// Browse: opens the OS folder picker (FolderDialogUtil) at the current folder; a selected folder is written to
// the input and saved to the global settings immediately. FolderDialogUtil runs the callback through the
// caller's SynchronizationContext, i.e. on the Unity main thread; cancel or failure passes null (ignored here).
// caller's SynchronizationContext, i.e. on the main Unity thread; cancel or failure passes null (ignored here).
CreateButton(
ctx, globalLeft,
"ui.render_config.browse",
+1 -1
View File
@@ -8,7 +8,7 @@ namespace ADOFAIRenderer.Util.Audio;
/// The frame rate is converted to an exact fraction Numerator / Denominator (<see cref="RationalUtil.Double2MaxPrecisionInt"/>).
/// Frame k receives floor((k + 1) * sampleRate * Denominator / Numerator) - floor(k * sampleRate * Denominator / Numerator) samples:
/// the remainder of each division is carried into the next frame, so the sum over any number of frames equals the exact
/// sample count of that duration rounded down (for example 44100 Hz at 60 fps gives 735 every frame; at 144 fps three frames of 306
/// sample count of that duration rounded down (for example 44100 Hz at 60 FPS gives 735 every frame; at 144 FPS three frames of 306
/// are followed by one of 307, 306.25 on average).
/// Used by the render thread only (RenderManager.WriteAudioForFrame, once per frame sent to the encoder).
/// </summary>
+1 -1
View File
@@ -175,7 +175,7 @@ internal sealed class AudioClipSource
/// <summary>
/// Most decode streams (reader + resampler) a windowed source opens. A load continues the stream positioned at the chunk start,
/// otherwise opens another stream up to this count, otherwise restarts the least recently used stream.
/// Each stream holds one open reader (file handle and decoder state, measured 0.3 ~ 2.7 MiB for ogg/flac/mp3 with the clip harness).
/// Each stream holds one open reader (file handle and decoder state, measured 0.3 ~ 2.7 MiB for ogg/flac/MP3 with the clip harness).
/// </summary>
public const int MaxDecodeStreams = 4;
+1 -1
View File
@@ -12,7 +12,7 @@ namespace ADOFAIRenderer.Util.Audio.Clip;
/// - Decoder thread (<see cref="Start"/>): the only thread that decodes, resamples, loads and frees chunks.
/// Each step loads one chunk, the one with the lowest priority value over all sources
/// (a chunk a consumer waits for, then cursor windows by slot, then resident chunks in order).
/// - Consumers (render thread, main thread for the spectrum): <see cref="Acquire"/> waits for the decoder thread and never loads.
/// - Consumers (render thread, main Unity thread for the spectrum): <see cref="Acquire"/> waits for the decoder thread and never loads.
/// - Main thread: <see cref="GetOrAdd"/> creates readers (for Unity clips this copies the clip data).
///
/// Memory has no limit; current and peak usage are tracked and logged by <see cref="LogMemoryUsage"/>.
+1 -1
View File
@@ -7,7 +7,7 @@ namespace ADOFAIRenderer.Util.Audio.Clip;
/// <summary>
/// Copies a Unity <see cref="AudioClip"/> into memory for clips without a file on disk
/// (built-in hit sounds from Resources, official/DLC level audio).
/// <see cref="AudioClip.GetData(float[], int)"/> runs on the main thread only, so the whole clip is copied once on the main thread
/// <see cref="AudioClip.GetData(float[], int)"/> runs on the main Unity thread only, so the whole clip is copied once on the main Unity thread
/// and the decoder thread reads the copy through a <see cref="MemoryPCMReader"/>.
/// Streaming clips (<see cref="AudioClipLoadType.Streaming"/>) are rejected because GetData does not read them.
/// Reference: https://docs.unity3d.com/ScriptReference/AudioClip.GetData.html
+2 -2
View File
@@ -6,7 +6,7 @@ using ADOFAIRenderer.Util.Audio.Clip;
namespace ADOFAIRenderer.Util.Audio.Mixer;
/// <summary>
/// One playing sound of a <see cref="VoiceMixer"/> (the song, or one effect sound), created by <see cref="VoiceMixer.Play"/>.
/// One playing sound of a <see cref="VoiceMixer"/> (the song, or one sound effect), created by <see cref="VoiceMixer.Play"/>.
///
/// Main thread: the Set methods queue a change that takes effect at a given time (seconds relative to the render start,
/// the time at which the game made the change). Changes of one voice must be queued in non-decreasing time order.
@@ -43,7 +43,7 @@ internal sealed class Voice
Stop
}
/// <summary>One timed change of a voice, queued by the main thread and applied by the render thread.</summary>
/// <summary>One timed change of a voice, queued by the main Unity thread and applied by the render thread.</summary>
internal readonly struct Change
{
public readonly ChangeKind Kind;
+3 -3
View File
@@ -35,7 +35,7 @@ internal sealed class VoiceMixer : IDisposable
Change
}
/// <summary>Command queued by the main thread and taken by the render thread at the start of <see cref="Mix"/>.</summary>
/// <summary>Command queued by the main Unity thread and taken by the render thread at the start of <see cref="Mix"/>.</summary>
private readonly struct Command
{
public readonly CommandKind Kind;
@@ -55,7 +55,7 @@ internal sealed class VoiceMixer : IDisposable
private readonly AudioClipStore _store;
private readonly CancellationToken _cancellationToken;
// Double-buffered command lists: the main thread appends to _commands under _commandLock; the render thread swaps the
// Double-buffered command lists: the main Unity thread appends to _commands under _commandLock; the render thread swaps the
// two lists under the lock and processes _takenCommands outside it, then clears it for the next swap.
private readonly object _commandLock = new();
private List<Command> _commands = new();
@@ -126,7 +126,7 @@ internal sealed class VoiceMixer : IDisposable
/// <summary>
/// Queues a change of <paramref name="voice"/> that takes effect at sample <see cref="ToSample"/>(<paramref name="time"/>).
/// Called by the Voice Set methods on the main thread.
/// Called by the Voice Set methods on the main Unity thread.
/// </summary>
internal void Enqueue(Voice voice, Voice.ChangeKind kind, double time, double value)
{
+10 -10
View File
@@ -11,7 +11,7 @@ using UnityEngine;
namespace ADOFAIRenderer.Util.Audio;
/// <summary>
/// Audio of one render: the song and effect sounds as <see cref="Voice"/>s of a <see cref="VoiceMixer"/>, clips from an
/// Audio of one render: the song and sound effects as <see cref="Voice"/>s of a <see cref="VoiceMixer"/>, clips from an
/// <see cref="AudioClipStore"/>, followed by the filter chain (RMS compressor → look-ahead limiter → hard clip → -1 dB gain).
///
/// Time: seconds relative to the render start (RenderManager.dspTimeBase); output sample n is at n / sample rate.
@@ -21,15 +21,15 @@ namespace ADOFAIRenderer.Util.Audio;
/// Threads:
/// - Main thread: <see cref="InitializeBGM"/>, <see cref="CacheClip"/>, <see cref="RegisterSoundEvent"/>, <see cref="UpdateBGM"/>,
/// source/voice mapping, and <see cref="Commit"/> after each frame (all commands with an earlier time have been queued).
/// - Render thread: <see cref="GenerateAudioForFrame"/> waits until the main thread has committed the end of the samples it mixes,
/// so every command takes effect at its own sample. <see cref="Complete"/> ends the waiting when the main thread stops.
/// - Render thread: <see cref="GenerateAudioForFrame"/> waits until the main Unity thread has committed the end of the samples it mixes,
/// so every command takes effect at its own sample. <see cref="Complete"/> ends the waiting when the main Unity thread stops.
/// </summary>
internal sealed class OfflineAudioMixer : IDisposable
{
/// <summary>Linear gain applied to every clip (song and effects), before the filter chain (0.5 ≈ -6 dB headroom).</summary>
private const double ClipVolume = 0.5;
/// <summary>Additional gain of effect sounds (registered with <see cref="RegisterSoundEvent"/>).</summary>
/// <summary>Additional gain of sound effects (registered with <see cref="RegisterSoundEvent"/>).</summary>
private const double EffectVolume = 2.0 / 3.0;
/// <summary>
@@ -62,7 +62,7 @@ internal sealed class OfflineAudioMixer : IDisposable
// Main thread.
/// <summary>
/// Mapping of game AudioSources to the state of the effect sound last started on them. Weak keys: a destroyed source
/// Mapping of game AudioSources to the state of the sound effect last started on them. Weak keys: a destroyed source
/// does not keep its state alive. Replaced by a new table to drop every mapping at once.
/// </summary>
private ConditionalWeakTable<AudioSource, SourceState> _sourceStates = new();
@@ -240,7 +240,7 @@ internal sealed class OfflineAudioMixer : IDisposable
}
/// <summary>
/// Starts an effect sound at <paramref name="startTime"/>. An <paramref name="endTime"/> of 0 or more makes it loop until then.
/// Starts a sound effect at <paramref name="startTime"/>. An <paramref name="endTime"/> of 0 or more makes it loop until then.
/// <paramref name="source"/> (when given) is mapped to the returned state, which reports and applies later changes through the AudioSource.
/// </summary>
/// <param name="source">Game AudioSource playing the sound, or null to map it later with <see cref="MapSource"/>.</param>
@@ -283,7 +283,7 @@ internal sealed class OfflineAudioMixer : IDisposable
_sourceStates.Add(source, state);
}
/// <summary>State of the effect sound last started for <paramref name="source"/>. The mapping stays after the sound ends or stops.</summary>
/// <summary>State of the sound effect last started for <paramref name="source"/>. The mapping stays after the sound ends or stops.</summary>
public bool TryGetSourceState(AudioSource? source, out SourceState state)
{
state = null!;
@@ -291,7 +291,7 @@ internal sealed class OfflineAudioMixer : IDisposable
}
/// <summary>
/// Stops every effect sound at <paramref name="now"/>, including ones scheduled to start later
/// Stops every sound effect at <paramref name="now"/>, including ones scheduled to start later
/// (AudioManager.StopAllSounds destroys every AudioSource in liveSources, which holds the sources of
/// AudioManager._Play and scrConductor.PlayWithEndTime). The song voice keeps playing.
/// </summary>
@@ -342,7 +342,7 @@ internal sealed class OfflineAudioMixer : IDisposable
/// <summary>
/// Writes the next <paramref name="samplesNeeded"/> output frames (interleaved float, continuing from the previous call).
/// Blocks until the main thread has committed the voice samples it needs (<see cref="Commit"/>, <see cref="Complete"/>).
/// Blocks until the main Unity thread has committed the voice samples it needs (<see cref="Commit"/>, <see cref="Complete"/>).
/// </summary>
/// <param name="mixedBuffer">Destination; must hold at least samplesNeeded * channels floats. Values beyond that are untouched.</param>
/// <param name="samplesNeeded">Frames (samples per channel) to write.</param>
@@ -377,7 +377,7 @@ internal sealed class OfflineAudioMixer : IDisposable
}
}
/// <summary>Mixes voices at samples [first, first + frames) into the reused double buffer, after the main thread committed them.</summary>
/// <summary>Mixes voices at samples [first, first + frames) into the reused double buffer, after the main Unity thread committed them.</summary>
private Span<double> MixBlock(long first, int frames)
{
WaitForCommit(first + frames);
+2 -2
View File
@@ -34,7 +34,7 @@ namespace ADOFAIRenderer.Util.FFmpeg
///
/// ── Threading ────────────────────────────────────────────────────────
/// Every public method takes <c>_lock</c>, so calls from different threads are serialized.
/// In the renderer, the main thread constructs and starts the encoder, and the render thread
/// In the renderer, the main Unity thread constructs and starts the encoder, and the render thread
/// (RenderManager.RenderThread) sends the video frames and audio samples, calls End() and Dispose().
/// Dispose() may also be called from another thread to release an encoder whose render was stopped.
/// After End() or Dispose(), the Write*/Send* methods return without doing anything.
@@ -916,7 +916,7 @@ namespace ADOFAIRenderer.Util.FFmpeg
// Stream time base: unit of the timestamps stored in the container.
// Set to the codec time base (1 / framerate) so that every frame interval is a whole number of ticks.
// When it is left unset, the muxer uses 1/90000, in which the interval of rates such as
// 59.94 fps (1501.5 ticks), 23.976 fps (3753.75 ticks) or 165 fps (545.45 ticks) is not an integer,
// 59.94 FPS (1501.5 ticks), 23.976 FPS (3753.75 ticks) or 165 FPS (545.45 ticks) is not an integer,
// and the frame durations alternate between two values.
// The muxer may still adjust the value in avformat_write_header (e.g. mp4 scales it to at least 10000);
// packet timestamps are rescaled to the final value in SendFrameAndWritePackets.
+2 -2
View File
@@ -26,7 +26,7 @@ namespace ADOFAIRenderer.Util
/// (RenderManager.SendVideoFrame, then <see cref="RecycleState"/>) or the frame was dropped.
/// </para>
/// <para>
/// Threads: requests and readback completions run on the main thread; <see cref="RecycleState"/> is also called by
/// Threads: requests and readback completions run on the main Unity thread; <see cref="RecycleState"/> is also called by
/// the render thread. The pool and the disposed flag are guarded by _poolLock. One instance per render, created in
/// RenderManager.SetupPlay with the pixel format of the opened encoder.
/// </para>
@@ -290,7 +290,7 @@ namespace ADOFAIRenderer.Util
/// <summary>
/// Returns a state to the pool after its frame was sent or dropped, or disposes it when the reader is disposed.
/// Called by the render thread (after sending) and by the main thread (abort, errors, queue cleanup).
/// Called by the render thread (after sending) and by the main Unity thread (abort, errors, queue cleanup).
/// </summary>
internal void RecycleState(ReadbackRequestState state)
{
+2 -2
View File
@@ -22,7 +22,7 @@ namespace ADOFAIRenderer.Util.OS;
/// cancelled or failed), <c>kdialog --getexistingdirectory</c>.
/// </para>
/// <para>
/// The dialog runs off the Unity main thread so the game keeps rendering frames while it is open. Only one dialog can be open
/// The dialog runs off the main Unity thread so the game keeps rendering frames while it is open. Only one dialog can be open
/// at a time. Failures are reported to the callback as null (same as cancel); exceptions are logged as warnings, except a
/// zenity start failure, which silently moves on to kdialog.
/// </para>
@@ -43,7 +43,7 @@ internal static class FolderDialogUtil
/// "FolderDialog-Win"; macOS/Linux: thread pool task) and <paramref name="onSelected"/> receives the result.
/// </para>
/// <para>
/// Callback thread: when the caller has a <see cref="SynchronizationContext"/> (the Unity main thread has one), the callback
/// Callback thread: when the caller has a <see cref="SynchronizationContext"/> (the main Unity thread has one), the callback
/// is posted to it and runs on the caller's thread; otherwise it runs on the dialog thread. Exceptions thrown by the
/// callback are caught and logged.
/// </para>