diff --git a/src/app/nebula_controller.cpp b/src/app/nebula_controller.cpp index fa2d99f..da7c373 100644 --- a/src/app/nebula_controller.cpp +++ b/src/app/nebula_controller.cpp @@ -40,6 +40,12 @@ CefWindowInfo ChildWindowInfo(HWND parent, const RECT& rect) { return info; } +CefBrowserSettings BrowserSettings() { + CefBrowserSettings settings; + settings.webgl = STATE_ENABLED; + return settings; +} + int ParseTabId(const std::string& value) { int tab_id = 0; const auto result = std::from_chars(value.data(), value.data() + value.size(), tab_id); @@ -388,7 +394,7 @@ void NebulaController::CreateChromeBrowser() { } const auto layout = window_->CurrentLayout(); - CefBrowserSettings browser_settings; + CefBrowserSettings browser_settings = BrowserSettings(); chrome_client_ = new nebula::cef::NebulaBrowserClient(nebula::cef::BrowserRole::Chrome, this); CefWindowInfo window_info = ChildWindowInfo(window_->hwnd(), layout.chrome); CefBrowserHost::CreateBrowser( @@ -403,7 +409,7 @@ void NebulaController::CreateContentBrowser() { const auto* tab = tabs_.ActiveTab(); const std::string url = tab && !tab->url.empty() ? tab->url : nebula::ui::GetHomeUrl(); const auto layout = window_->CurrentLayout(); - CefBrowserSettings browser_settings; + CefBrowserSettings browser_settings = BrowserSettings(); content_client_ = new nebula::cef::NebulaBrowserClient(nebula::cef::BrowserRole::Content, this); CefWindowInfo window_info = ChildWindowInfo(window_->hwnd(), layout.content); CefBrowserHost::CreateBrowser( @@ -431,7 +437,7 @@ void NebulaController::CreateMenuPopupBrowser() { } const auto layout = window_->CurrentLayout(); - CefBrowserSettings browser_settings; + CefBrowserSettings browser_settings = BrowserSettings(); menu_popup_client_ = new nebula::cef::NebulaBrowserClient(nebula::cef::BrowserRole::MenuPopup, this); CefWindowInfo window_info = ChildWindowInfo(window_->hwnd(), MenuPopupRect(window_->hwnd(), layout)); CefBrowserHost::CreateBrowser( diff --git a/src/app/run.cpp b/src/app/run.cpp index 07af574..31df932 100644 --- a/src/app/run.cpp +++ b/src/app/run.cpp @@ -29,6 +29,19 @@ int RunNebula(HINSTANCE instance, int show_command) { CefSettings settings; settings.no_sandbox = true; + // A persistent profile is required for the GPU shader cache and several + // hardware acceleration features. Without these Chromium silently falls + // back to software rendering, which causes choppy video and disables + // WebGL/WebGL2 in the GPU diagnostics page. + const std::wstring user_data_dir = nebula::ui::GetUserDataDirectory().wstring(); + const std::wstring cache_dir = nebula::ui::GetCacheDirectory().wstring(); + if (!user_data_dir.empty()) { + CefString(&settings.root_cache_path).FromWString(user_data_dir); + } + if (!cache_dir.empty()) { + CefString(&settings.cache_path).FromWString(cache_dir); + } + if (!CefInitialize(main_args, settings, app, nullptr)) { return CefGetExitCode(); } diff --git a/src/cef/nebula_app.cpp b/src/cef/nebula_app.cpp index 1cc2c43..975d294 100644 --- a/src/cef/nebula_app.cpp +++ b/src/cef/nebula_app.cpp @@ -58,6 +58,26 @@ void NebulaApp::OnBeforeCommandLineProcessing(const CefString& process_type, // The bundled UI is loaded from file:// and uses ES modules. command_line->AppendSwitch("allow-file-access-from-files"); + + // CefSettings.no_sandbox disables the browser-level sandbox, but Chromium + // still attempts to bring up a separate GPU sandbox inside the GPU process. + // Without the host-side sandbox plumbing this fails with STATUS_BREAKPOINT + // (-2147483645) immediately on startup, which is exactly what the GPU + // diagnostics page was showing - the GPU process crashed three times and + // Chromium then fell back to software rendering. Disabling the GPU sandbox + // matches the rest of our no_sandbox configuration and lets the GPU + // process initialize. + command_line->AppendSwitch("no-sandbox"); + command_line->AppendSwitch("disable-gpu-sandbox"); + command_line->AppendSwitch("in-process-gpu"); + + // Avoid Chromium's conservative GPU blocklist, but let Chromium choose the + // safest graphics backend for this machine. Forcing raster/zero-copy paths + // can prevent WebGL shared contexts from initializing on some drivers. + command_line->AppendSwitch("ignore-gpu-blocklist"); + command_line->AppendSwitch("enable-accelerated-video-decode"); + command_line->AppendSwitchWithValue("use-gl", "angle"); + command_line->AppendSwitchWithValue("use-angle", "d3d11"); } void NebulaApp::OnContextCreated(CefRefPtr browser, diff --git a/src/ui/paths.cpp b/src/ui/paths.cpp index 8ef4b23..f9b9102 100644 --- a/src/ui/paths.cpp +++ b/src/ui/paths.cpp @@ -136,6 +136,44 @@ std::filesystem::path GetExecutableDirectory() { return std::filesystem::path(exe_path).parent_path(); } +std::filesystem::path GetUserDataDirectory() { + std::filesystem::path root; + + wchar_t buffer[MAX_PATH] = {}; + // Prefer %LOCALAPPDATA% so the profile follows Chromium conventions and + // survives executable relocation. + const DWORD length = + GetEnvironmentVariableW(L"LOCALAPPDATA", buffer, MAX_PATH); + if (length > 0 && length < MAX_PATH) { + root = std::filesystem::path(buffer); + } else { + // Fall back to a directory next to the executable so a portable + // install still gets a writable profile. + root = GetExecutableDirectory(); + } + + if (root.empty()) { + return {}; + } + + std::filesystem::path user_data = root / L"Nebula" / L"User Data"; + std::error_code ec; + std::filesystem::create_directories(user_data, ec); + return user_data; +} + +std::filesystem::path GetCacheDirectory() { + auto user_data = GetUserDataDirectory(); + if (user_data.empty()) { + return {}; + } + + std::filesystem::path cache = user_data / L"Cache"; + std::error_code ec; + std::filesystem::create_directories(cache, ec); + return cache; +} + std::filesystem::path GetUiPagePath(const std::wstring& page_name) { const auto exe_dir = GetExecutableDirectory(); if (exe_dir.empty()) { diff --git a/src/ui/paths.h b/src/ui/paths.h index caebd74..16e0cfa 100644 --- a/src/ui/paths.h +++ b/src/ui/paths.h @@ -6,6 +6,8 @@ namespace nebula::ui { std::filesystem::path GetExecutableDirectory(); +std::filesystem::path GetUserDataDirectory(); +std::filesystem::path GetCacheDirectory(); std::filesystem::path GetUiPagePath(const std::wstring& page_name); std::string FilePathToUrl(std::filesystem::path path); std::string GetChromeUrl();