summaryrefslogtreecommitdiff
path: root/indra/llwindow
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llwindow')
-rw-r--r--indra/llwindow/llwindowmacosx-objc.h2
-rw-r--r--indra/llwindow/llwindowwin32.cpp355
-rw-r--r--indra/llwindow/llwindowwin32.h13
3 files changed, 331 insertions, 39 deletions
diff --git a/indra/llwindow/llwindowmacosx-objc.h b/indra/llwindow/llwindowmacosx-objc.h
index b302a705da..ec9afb1844 100644
--- a/indra/llwindow/llwindowmacosx-objc.h
+++ b/indra/llwindow/llwindowmacosx-objc.h
@@ -78,6 +78,8 @@ void initMainLoop();
void cleanupViewer();
void handleUrl(const char* url);
void dispatchUrl(std::string url);
+void startWatchdog(std::string_view state);
+void stopWatchdog();
/* Defined in llwindowmacosx-objc.mm: */
int createNSApp(int argc, const char **argv);
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index dbdbfe390f..ddd9ce32f6 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -65,6 +65,7 @@
#include <utility> // std::pair
#include <d3d9.h>
+#include <d3d11.h>
#include <dxgi1_4.h>
#include <timeapi.h>
@@ -115,7 +116,15 @@ static std::thread::id sMainThreadId;
LPWSTR gIconResource = IDI_APPLICATION;
LPWSTR gIconSmallResource = IDI_APPLICATION;
-LPDIRECTINPUT8 gDirectInput8;
+
+namespace
+{
+ LPDIRECTINPUT8 gDirectInput8;
+ ID3D11Device* gD3D11Device = nullptr;
+ ID3D11DeviceContext* gD3D11Context = nullptr;
+ LUID gExpectedAdapterLUID;
+ HMODULE gD3D11Library;
+}
LLW32MsgCallback gAsyncMsgCallback = NULL;
@@ -508,6 +517,10 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
//MAINT-516 -- force a load of opengl32.dll just in case windows went sideways
LoadLibrary(L"opengl32.dll");
+ // Request high-performance GPU before creating OpenGL context
+ // This increases probability of discrete GPU being used when
+ // the context is created.
+ requestHighPerformanceGPU();
if (mMaxCores != 0)
{
@@ -523,6 +536,8 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
SetProcessAffinityMask(hProcess, mask);
}
+ setThreadPriorityHigh();
+
#if 0 // this is probably a bad idea, but keep it in your back pocket if you see what looks like
// process deprioritization during profiles
// force high thread priority
@@ -545,41 +560,6 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
}
#endif
-#if 0 // this is also probably a bad idea, but keep it in your back pocket for getting main thread off of background thread cores (see also LLThread::threadRun)
- HANDLE hThread = GetCurrentThread();
-
- SYSTEM_INFO sysInfo;
-
- GetSystemInfo(&sysInfo);
- U32 core_count = sysInfo.dwNumberOfProcessors;
-
- if (max_cores != 0)
- {
- core_count = llmin(core_count, max_cores);
- }
-
- if (hThread)
- {
- int priority = GetThreadPriority(hThread);
-
- if (priority < THREAD_PRIORITY_TIME_CRITICAL)
- {
- if (SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL))
- {
- LL_INFOS() << "Set thread priority to THREAD_PRIORITY_TIME_CRITICAL" << LL_ENDL;
- }
- else
- {
- LL_INFOS() << "Failed to set thread priority: " << std::hex << GetLastError() << LL_ENDL;
- }
-
- // tell main thread to prefer core 0
- SetThreadIdealProcessor(hThread, 0);
- }
- }
-#endif
-
-
mFSAASamples = fsaa_samples;
mIconResource = gIconResource;
mIconSmallResource = gIconSmallResource;
@@ -1009,6 +989,7 @@ void LLWindowWin32::close()
}
mDragDrop->reset();
+ clearHighPerformanceGPURequest();
// Go back to screen mode written in the registry.
@@ -1073,6 +1054,52 @@ bool LLWindowWin32::isValid()
return (mWindowHandle != NULL);
}
+void LLWindowWin32::setThreadPriorityHigh()
+{
+ // Threads start at normal priority. But this is our main window/rendering thread,
+ // even if window handle belongs to another thread. So we can raise its priority
+ // to ensure better responsiveness and less blocking by lack of resources.
+ HANDLE hThread = GetCurrentThread();
+ if (hThread)
+ {
+ int priority = GetThreadPriority(hThread);
+
+ if (priority == THREAD_PRIORITY_ERROR_RETURN)
+ {
+ LL_WARNS_ONCE("Window") << "Failed to get thread priority: " << std::hex << GetLastError() << LL_ENDL;
+ }
+ else if (priority > THREAD_PRIORITY_HIGHEST)
+ {
+ // At the moment nothing should be setting 'critical' priority,
+ // but if that happens for some reason, we don't want to mess with it.
+ LL_WARNS("Window") << "setThreadPriorityHigh ignored, priority was " << (S32)priority << LL_ENDL;
+ }
+ else if (priority != THREAD_PRIORITY_HIGHEST)
+ {
+ if (SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST))
+ {
+ LL_DEBUGS("Window") << "Set thread priority to THREAD_PRIORITY_HIGHEST" << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("Window") << "Failed to set thread priority: " << std::hex << GetLastError() << LL_ENDL;
+ }
+ }
+ }
+}
+
+void LLWindowWin32::setThreadPriorityNormal()
+{
+ HANDLE hThread = GetCurrentThread();
+ if (hThread)
+ {
+ if (!SetThreadPriority(hThread, THREAD_PRIORITY_NORMAL))
+ {
+ LL_WARNS_ONCE("Window") << "Failed to set thread priority: " << std::hex << GetLastError() << LL_ENDL;
+ }
+ }
+}
+
bool LLWindowWin32::getVisible()
{
return (mWindowHandle && IsWindowVisible(mWindowHandle));
@@ -3043,19 +3070,31 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
// means that the window was un-minimized.
if (w_param == SIZE_RESTORED && window_imp->mLastSizeWParam != SIZE_RESTORED)
{
- WINDOW_IMP_POST(window_imp->mCallbacks->handleActivate(window_imp, true));
+ window_imp->post([=]()
+ {
+ window_imp->setThreadPriorityHigh();
+ window_imp->mCallbacks->handleActivate(window_imp, true);
+ });
}
// handle case of window being maximized from fully minimized state
if (w_param == SIZE_MAXIMIZED && window_imp->mLastSizeWParam != SIZE_MAXIMIZED)
{
- WINDOW_IMP_POST(window_imp->mCallbacks->handleActivate(window_imp, true));
+ window_imp->post([=]()
+ {
+ window_imp->setThreadPriorityHigh();
+ window_imp->mCallbacks->handleActivate(window_imp, true);
+ });
}
// Also handle the minimization case
if (w_param == SIZE_MINIMIZED && window_imp->mLastSizeWParam != SIZE_MINIMIZED)
{
- WINDOW_IMP_POST(window_imp->mCallbacks->handleActivate(window_imp, false));
+ window_imp->post([=]()
+ {
+ window_imp->setThreadPriorityNormal();
+ window_imp->mCallbacks->handleActivate(window_imp, false);
+ });
}
// Actually resize all of our views
@@ -4620,6 +4659,238 @@ void LLWindowWin32::setDPIAwareness()
}
}
+void LLWindowWin32::requestHighPerformanceGPU() const
+{
+ // Try to load d3d11.dll and request high performance adapter
+ gD3D11Library = LoadLibraryA("d3d11.dll");
+ if (gD3D11Library)
+ {
+ typedef HRESULT(WINAPI* PFN_D3D11_CREATE_DEVICE)(
+ IDXGIAdapter*, D3D_DRIVER_TYPE, HMODULE, UINT,
+ const D3D_FEATURE_LEVEL*, UINT, UINT, ID3D11Device**,
+ D3D_FEATURE_LEVEL*, ID3D11DeviceContext**);
+
+ PFN_D3D11_CREATE_DEVICE pD3D11CreateDevice =
+ (PFN_D3D11_CREATE_DEVICE)GetProcAddress(gD3D11Library, "D3D11CreateDevice");
+
+ if (pD3D11CreateDevice)
+ {
+ // Try to enumerate adapters and select the best one
+ IDXGIFactory1* pFactory = nullptr;
+ IDXGIAdapter1* pSelectedAdapter = nullptr;
+ std::string selected_descr;
+ HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&pFactory);
+
+ if (SUCCEEDED(hr) && pFactory)
+ {
+ IDXGIAdapter1* pAdapter = nullptr;
+ SIZE_T maxDedicatedMemory = 0;
+ UINT adapterIndex = 0;
+ S32 adapter_count = 0;
+
+ // Enumerate all adapters and find the one with the most dedicated video memory
+ while (pFactory->EnumAdapters1(adapterIndex, &pAdapter) != DXGI_ERROR_NOT_FOUND)
+ {
+ DXGI_ADAPTER_DESC1 desc;
+ pAdapter->GetDesc1(&desc);
+
+ std::wstring description_w(desc.Description);
+ std::string description = ll_convert_wide_to_string(description_w);
+
+
+ // Skip software adapters
+ if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
+ {
+ LL_DEBUGS("Window") << "Adapter " << adapterIndex << ": " << description
+ << ", Dedicated VRAM: " << (desc.DedicatedVideoMemory / 1024 / 1024) << " MB"
+ << ", Vendor: 0x" << std::hex << desc.VendorId << std::dec
+ << ", Flags: " << desc.Flags << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS("Window") << "Adapter " << adapterIndex << ": " << description
+ << ", Dedicated VRAM: " << (desc.DedicatedVideoMemory / 1024 / 1024) << " MB"
+ << ", Vendor: 0x" << std::hex << desc.VendorId << std::dec
+ << ", Flags: " << desc.Flags << LL_ENDL;
+
+ adapter_count++;
+ // Select adapter with most dedicated video memory (typically the discrete GPU)
+ if (desc.DedicatedVideoMemory > maxDedicatedMemory)
+ {
+ if (pSelectedAdapter)
+ {
+ pSelectedAdapter->Release();
+ }
+ pSelectedAdapter = pAdapter;
+ pSelectedAdapter->AddRef();
+ maxDedicatedMemory = desc.DedicatedVideoMemory;
+ gExpectedAdapterLUID = desc.AdapterLuid;
+ selected_descr = description;
+ }
+ }
+
+ pAdapter->Release();
+ adapterIndex++;
+ }
+ pFactory->Release();
+
+ if (adapter_count < 2)
+ {
+ // Only one adapter, no need to request high-performance GPU
+ if (pSelectedAdapter)
+ {
+ pSelectedAdapter->Release();
+ }
+ gExpectedAdapterLUID = { 0, 0 };
+ FreeLibrary(gD3D11Library);
+ gD3D11Library = nullptr;
+ return;
+ }
+
+ LL_INFOS("Window") << "Selected as preferred adapter (highest VRAM): " << selected_descr << LL_ENDL;
+ }
+
+ // Create a temporary device to ensure high-performance GPU is selected
+ // This initialization can help "wake up" the discrete GPU
+ D3D_FEATURE_LEVEL featureLevel;
+ D3D_FEATURE_LEVEL requestedLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1 };
+
+ bool adapterSelected = (pSelectedAdapter != nullptr);
+ if (adapterSelected)
+ {
+ hr = pD3D11CreateDevice(
+ pSelectedAdapter,
+ D3D_DRIVER_TYPE_UNKNOWN,
+ nullptr,
+ 0,
+ requestedLevels,
+ _countof(requestedLevels),
+ D3D11_SDK_VERSION,
+ &gD3D11Device,
+ &featureLevel,
+ &gD3D11Context
+ );
+ pSelectedAdapter->Release();
+
+ if (!SUCCEEDED(hr))
+ {
+ LL_WARNS("Window") << "D3D11 failed to use preffered adapter " << selected_descr << LL_ENDL;
+ gExpectedAdapterLUID = { 0, 0 };
+ adapterSelected = false;
+ }
+ }
+
+ if (!adapterSelected)
+ {
+ // Either failed to select or didn't find an adapter.
+ hr = pD3D11CreateDevice(
+ nullptr,
+ D3D_DRIVER_TYPE_HARDWARE,
+ nullptr,
+ 0,
+ requestedLevels,
+ _countof(requestedLevels),
+ D3D11_SDK_VERSION,
+ &gD3D11Device,
+ &featureLevel,
+ &gD3D11Context
+ );
+ if (!SUCCEEDED(hr))
+ {
+ LL_WARNS("Window") << "D3D11 failed to use hardware adapter" << LL_ENDL;
+ FreeLibrary(gD3D11Library);
+ gD3D11Library = nullptr;
+ // These shouldn't be set, but make sure they are null.
+ gD3D11Device = nullptr;
+ gD3D11Context = nullptr;
+ }
+ }
+ }
+ else
+ {
+ LL_WARNS("Window") << "Failed to get D3D11CreateDevice function from d3d11.dll. High-performance GPU request failed." << LL_ENDL;
+ FreeLibrary(gD3D11Library);
+ gD3D11Library = nullptr;
+ }
+ }
+}
+
+bool LLWindowWin32::detectGPUChange() const
+{
+ if (!gD3D11Device)
+ {
+ // Can't detect without D3D11 device
+ return false;
+ }
+
+ if (gExpectedAdapterLUID.LowPart == 0 && gExpectedAdapterLUID.HighPart == 0)
+ {
+ // No specific adapter was selected, can't detect changes.
+ return false;
+ }
+
+ IDXGIDevice* pDXGIDevice = nullptr;
+ HRESULT hr = gD3D11Device->QueryInterface(__uuidof(IDXGIDevice), (void**)&pDXGIDevice);
+
+ if (SUCCEEDED(hr) && pDXGIDevice)
+ {
+ IDXGIAdapter* pCurrentAdapter = nullptr;
+ hr = pDXGIDevice->GetAdapter(&pCurrentAdapter);
+
+ if (SUCCEEDED(hr) && pCurrentAdapter)
+ {
+ DXGI_ADAPTER_DESC desc;
+ pCurrentAdapter->GetDesc(&desc);
+
+ std::wstring description_w(desc.Description);
+
+ bool changed = false;
+
+ // Check if LUID has changed
+ if (desc.AdapterLuid.LowPart != gExpectedAdapterLUID.LowPart ||
+ desc.AdapterLuid.HighPart != gExpectedAdapterLUID.HighPart)
+ {
+ changed = true;
+ std::string current_gpu_name = ll_convert_wide_to_string(description_w);
+ LL_WARNS("Window") << "GPU change detected! Current adapter: " << current_gpu_name << LL_ENDL;
+ }
+
+ pCurrentAdapter->Release();
+ pDXGIDevice->Release();
+
+ return changed;
+ }
+
+ if (pDXGIDevice)
+ {
+ pDXGIDevice->Release();
+ }
+ }
+
+ return false;
+}
+
+void LLWindowWin32::clearHighPerformanceGPURequest() const
+{
+ detectGPUChange();
+ gExpectedAdapterLUID = { 0, 0 };
+ if (gD3D11Context)
+ {
+ gD3D11Context->Release();
+ gD3D11Context = nullptr;
+ }
+ if (gD3D11Device)
+ {
+ gD3D11Device->Release();
+ gD3D11Device = nullptr;
+ }
+ if (gD3D11Library)
+ {
+ FreeLibrary(gD3D11Library);
+ gD3D11Library = nullptr;
+ }
+}
+
void* LLWindowWin32::getDirectInput8()
{
return &gDirectInput8;
@@ -4648,6 +4919,12 @@ bool LLWindowWin32::getInputDevices(U32 device_type_filter,
void LLWindowWin32::initWatchdog()
{
mWindowThread->initTimeout();
+
+ // Watchdog is effectively a 'login complete event', as the
+ // 'unstable' part is done and from now on we are tracking
+ // performance.
+ // No need to hold D3D11 context/device any more.
+ clearHighPerformanceGPURequest();
}
F32 LLWindowWin32::getSystemUISize()
diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h
index 8159092794..aab2635a34 100644
--- a/indra/llwindow/llwindowwin32.h
+++ b/indra/llwindow/llwindowwin32.h
@@ -148,6 +148,8 @@ protected:
void initCursors();
HCURSOR loadColorCursor(LPCTSTR name);
bool isValid();
+ void setThreadPriorityHigh();
+ void setThreadPriorityNormal();
void moveWindow(const LLCoordScreen& position,const LLCoordScreen& size);
virtual LLSD getNativeKeyData();
@@ -170,6 +172,17 @@ protected:
void handleCompositionMessage(U32 indexes);
bool handleImeRequests(WPARAM request, LPARAM param, LRESULT *result);
+ // Additional function to request and hold a high-performance GPU on Windows 10+
+ //
+ // Laptops can dynamically switch between integrated and discrete GPUs.
+ // The Viewer has gpu-specific optimizations, and this switching can cause problems and crashes.
+ // The login screen requires low performance, which can lead to the OS deciding to switch to the integrated GPU.
+ // To avoid this, we request and hold a high-performance GPU using A D3D11 context until login.
+ // For diagnostics, we also log GPU changes.
+ void requestHighPerformanceGPU() const;
+ bool detectGPUChange() const;
+ void clearHighPerformanceGPURequest() const;
+
protected:
//
// Platform specific methods