Files
SonicForgeStudio/native_bridge/src/main_fx.cpp
T
admin 244dc8ee9e V45.43: fix Ozone 11 GUI crash on Intel UHD 630 + load diagnostics
Root-cause hypothesis: fx_vst_bridge parked embed window offscreen (-20000,-20000)
during VST3 editor attach; Ozone 11 GL init null-derefs (c0000005 in
iZOzone11Core.dll+18f6b92) on Intel ICD. Reaper hosts window on-screen -> OK.

- RenderFxJob.cpp: env toggle SF_FXGUI_ATTACH_ONSCREEN=1 -> SetWindowPos(100,100)
  before fxGuiLoadAttach, park back offscreen after attach succeeds
- main_fx.cpp: FxCrashDumpHandler prints [CRASH] in <module>+0x<offset> and
  [CRASH] minidump: <path> so logs name the faulting plugin DLL directly
- app.jsx VstGuiEmbed: frameLoadedRef + 90s watchdog -> onDead if bridge alive
  but no frame (no more infinite 'Dang tai plugin VST...' flash); clearer onDead
  toasts with %TEMP%\sf_dumps\fx_bridge_crash_*.dmp hint (track + master)
- rebuild fx_vst_bridge + daw_vst_bridge, NSIS/MSI v=202609021809
2026-09-02 21:08:49 +07:00

185 lines
8.3 KiB
C++

// native_bridge/src/main_fx.cpp
// Entry point of fx_vst_bridge.exe — VST FX host bridge (task: split FX out
// of daw_vst_bridge). Windows only. Modes:
// --render-fx <job.json> --in <input.wav> --out <output.wav> (offline)
// --realtime-fx <job.json> --shm <name> [--parent <pid>] (SHM realtime)
// --scan <dir>
// --open-fx-gui <job.json>
// --fx-gui <job.json>
// SHM realtime loop (--realtime-fx): cloud-FX loop cho VST3 trong master FX
// chain — browser gửi mix qua engine → SHM input ring → bridge xử lý (cùng
// RealtimeFxChain như daw_vst_bridge master chain) → SHM output ring →
// engine → browser. Xem FxRealtimeIPC.h + RealtimeFxLoop.cpp.
#include "RenderFxJob.h"
#ifdef _WIN32
#include <windows.h>
#include <imm.h>
#include <dbghelp.h>
#include <cstdio>
#include <cstdlib>
#else
#include <cstdlib>
#endif
#include <cstring>
#include <iostream>
#include <string>
#ifdef _WIN32
// Minidump on crash -> %TEMP%\sf_dumps (same convention as daw_vst_bridge).
static LONG WINAPI FxCrashDumpHandler(EXCEPTION_POINTERS* ep) {
static LONG once = 0;
if (InterlockedCompareExchange(&once, 1, 0)) return EXCEPTION_CONTINUE_SEARCH;
PEXCEPTION_RECORD er = ep ? ep->ExceptionRecord : nullptr;
std::cerr << "[CRASH] fx_vst_bridge exception code=0x" << std::hex
<< (er ? er->ExceptionCode : 0) << " addr=0x"
<< (er ? (void*)er->ExceptionAddress : nullptr) << std::dec << std::endl;
if (er) {
HMODULE hMod = nullptr;
DWORD_PTR crashAddr = (DWORD_PTR)er->ExceptionAddress;
if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCSTR)er->ExceptionAddress, &hMod) && hMod) {
char modPath[MAX_PATH];
if (GetModuleFileNameA(hMod, modPath, MAX_PATH))
std::cerr << "[CRASH] in " << modPath << "+0x" << std::hex
<< (crashAddr - (DWORD_PTR)hMod) << std::dec << std::endl;
}
}
char dumpDir[MAX_PATH];
if (GetTempPathA(MAX_PATH, dumpDir)) {
std::strncat(dumpDir, "sf_dumps", MAX_PATH - std::strlen(dumpDir) - 1);
CreateDirectoryA(dumpDir, nullptr);
char dumpPath[MAX_PATH];
std::snprintf(dumpPath, sizeof(dumpPath), "%s/fx_bridge_crash_%lu.dmp",
dumpDir, GetCurrentProcessId());
HMODULE hDbg = LoadLibraryA("dbghelp.dll");
if (hDbg) {
typedef BOOL(WINAPI* MiniDumpWriteDumpFn)(
HANDLE, DWORD, HANDLE, MINIDUMP_TYPE,
PMINIDUMP_EXCEPTION_INFORMATION, PMINIDUMP_USER_STREAM_INFORMATION,
PMINIDUMP_CALLBACK_INFORMATION);
auto pMiniDumpWriteDump =
(MiniDumpWriteDumpFn)GetProcAddress(hDbg, "MiniDumpWriteDump");
if (pMiniDumpWriteDump) {
HANDLE hFile = CreateFileA(dumpPath, GENERIC_WRITE, 0, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
nullptr);
if (hFile != INVALID_HANDLE_VALUE) {
MINIDUMP_EXCEPTION_INFORMATION mei = {};
mei.ThreadId = GetCurrentThreadId();
mei.ExceptionPointers = ep;
mei.ClientPointers = TRUE;
pMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
hFile, MiniDumpNormal, &mei, nullptr,
nullptr);
CloseHandle(hFile);
std::cerr << "[CRASH] minidump: " << dumpPath << std::endl;
}
}
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
static LONG WINAPI FxVectoredCrashHandler(PEXCEPTION_POINTERS ep) {
PEXCEPTION_RECORD er = ep ? ep->ExceptionRecord : nullptr;
DWORD code = er ? er->ExceptionCode : 0;
if (code != 0xC0000374 && code != 0xC0000005 && code != 0xC0000409)
return EXCEPTION_CONTINUE_SEARCH;
return FxCrashDumpHandler(ep);
}
// Same daemon init as daw_vst_bridge main(): no console flash, no power
// throttling, per-monitor DPI (plugin renderers must not be OS-scaled),
// no default IME on the main thread (IME recursion fix). Redirected stdio
// (bridge.log pipe / SF_FXGUI_PORT reader) survives FreeConsole.
static void win_daemon_init() {
if (!std::getenv("SF_KEEP_CONSOLE")) FreeConsole();
#ifndef PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION
#define PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION 0x00000004
#endif
#ifndef PROCESS_POWER_THROTTLING_EXECUTION_SPEED
#define PROCESS_POWER_THROTTLING_EXECUTION_SPEED 0x00000001
#endif
PROCESS_POWER_THROTTLING_STATE powerState = {};
powerState.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION;
powerState.ControlMask = PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION |
PROCESS_POWER_THROTTLING_EXECUTION_SPEED;
powerState.StateMask = 0;
SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling,
&powerState, sizeof(powerState));
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
ImmAssociateContextEx(nullptr, nullptr, IACE_DEFAULT);
}
#endif // _WIN32
int main(int argc, char* argv[]) {
#ifdef _WIN32
SetUnhandledExceptionFilter(FxCrashDumpHandler);
AddVectoredExceptionHandler(1, FxVectoredCrashHandler);
// Suppress Windows loader error dialogs (plugin dependency thiếu -> 0x7E
// phải fail âm thầm, không MessageBox chặn user; child kế thừa error mode).
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
win_daemon_init();
#endif
// stderr, not stdout: stdout is reserved for machine-readable output in
// one-shot modes (--scan JSON, --fx-gui "SF_FXGUI_PORT=...").
std::cerr << "[FxBridge] Starting DAW FX Host Bridge..." << std::endl;
std::string renderFxJob, renderFxIn, renderFxOut;
std::string scanDir;
std::string openFxGuiJob;
std::string fxGuiServerJob;
std::string realtimeFxJob, realtimeShm;
uint32_t realtimeParent = 0;
for (int i = 1; i < argc; ++i) {
if (std::strcmp(argv[i], "--render-fx") == 0 && i + 1 < argc)
renderFxJob = argv[i + 1];
else if (std::strcmp(argv[i], "--in") == 0 && i + 1 < argc)
renderFxIn = argv[i + 1];
else if (std::strcmp(argv[i], "--out") == 0 && i + 1 < argc)
renderFxOut = argv[i + 1];
else if (std::strcmp(argv[i], "--scan") == 0 && i + 1 < argc)
scanDir = argv[i + 1];
else if (std::strcmp(argv[i], "--open-fx-gui") == 0 && i + 1 < argc)
openFxGuiJob = argv[i + 1];
else if (std::strcmp(argv[i], "--fx-gui") == 0 && i + 1 < argc)
fxGuiServerJob = argv[i + 1];
else if (std::strcmp(argv[i], "--realtime-fx") == 0 && i + 1 < argc)
realtimeFxJob = argv[i + 1];
else if (std::strcmp(argv[i], "--shm") == 0 && i + 1 < argc)
realtimeShm = argv[i + 1];
else if (std::strcmp(argv[i], "--parent") == 0 && i + 1 < argc)
realtimeParent = (uint32_t)std::strtoul(argv[i + 1], nullptr, 10);
}
if (!realtimeFxJob.empty()) {
if (realtimeShm.empty()) {
std::cerr << "[RealtimeFxLoop] --realtime-fx requires --shm <name>"
<< std::endl;
return 1;
}
return run_realtime_fx_loop(realtimeFxJob, realtimeShm, realtimeParent);
}
if (!renderFxJob.empty()) {
if (renderFxIn.empty() || renderFxOut.empty()) {
std::cerr << "[RenderFxJob] --render-fx requires --in <input.wav>"
" --out <output.wav>" << std::endl;
return 1;
}
return run_render_fx_job(renderFxJob, renderFxIn, renderFxOut);
}
if (!scanDir.empty()) return run_scan_dir(scanDir);
if (!openFxGuiJob.empty()) return run_open_fx_gui(openFxGuiJob);
if (!fxGuiServerJob.empty()) return run_fx_gui_server(fxGuiServerJob);
std::cerr << "fx_vst_bridge: no mode. Usage:\n"
" --render-fx <job.json> --in <input.wav> --out <output.wav>\n"
" --realtime-fx <job.json> --shm <name> [--parent <pid>]\n"
" --scan <dir>\n"
" --open-fx-gui <job.json>\n"
" --fx-gui <job.json>" << std::endl;
return 1;
}