summaryrefslogtreecommitdiff
path: root/indra/llwindow/llwindowwin32.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llwindow/llwindowwin32.cpp')
-rw-r--r--indra/llwindow/llwindowwin32.cpp590
1 files changed, 416 insertions, 174 deletions
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index 2e560ddb0a..057d7a700e 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -47,6 +47,7 @@
#include "llglslshader.h"
#include "llthreadsafequeue.h"
#include "stringize.h"
+#include "llframetimer.h"
// System includes
#include <commdlg.h>
@@ -61,6 +62,10 @@
#include <sstream>
#include <utility> // std::pair
+#include <d3d9.h>
+#include <dxgi1_4.h>
+#include <timeapi.h>
+
// Require DirectInput version 8
#define DIRECTINPUT_VERSION 0x0800
@@ -347,19 +352,33 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool
void run() override;
+ void glReady()
+ {
+ mGLReady = true;
+ }
+
+ // initialzie DXGI adapter (for querying available VRAM)
+ void initDX();
+
+ // initialize D3D (if DXGI cannot be used)
+ void initD3D();
+
+ // call periodically to update available VRAM
+ void updateVRAMUsage();
+
+ U32 getAvailableVRAMMegabytes()
+ {
+ return mAvailableVRAM;
+ }
+
/// called by main thread to post work to this window thread
template <typename CALLABLE>
void post(CALLABLE&& func)
{
- try
- {
- getQueue().post(std::forward<CALLABLE>(func));
- }
- catch (const LLThreadSafeQueueInterrupt&)
- {
- // Shutdown timing is tricky. The main thread can end up trying
- // to post a cursor position after having closed the WorkQueue.
- }
+ // Ignore bool return. Shutdown timing is tricky: the main thread can
+ // end up trying to post a cursor position after having closed the
+ // WorkQueue.
+ getQueue().post(std::forward<CALLABLE>(func));
}
/**
@@ -395,6 +414,18 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool
void gatherInput();
HWND mWindowHandle = NULL;
HDC mhDC = 0;
+
+ // *HACK: Attempt to prevent startup crashes by deferring memory accounting
+ // until after some graphics setup. See SL-20177. -Cosmic,2023-09-18
+ bool mGLReady = false;
+ // best guess at available video memory in MB
+ std::atomic<U32> mAvailableVRAM;
+
+ U32 mMaxVRAM = 0; // maximum amount of vram to allow in the "budget", or 0 for no maximum (see updateVRAMUsage)
+
+ IDXGIAdapter3* mDXGIAdapter = nullptr;
+ LPDIRECT3D9 mD3D = nullptr;
+ LPDIRECT3DDEVICE9 mD3DDevice = nullptr;
};
@@ -404,13 +435,93 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
BOOL fullscreen, BOOL clearBg,
BOOL enable_vsync, BOOL use_gl,
BOOL ignore_pixel_depth,
- U32 fsaa_samples)
- : LLWindow(callbacks, fullscreen, flags)
+ U32 fsaa_samples,
+ U32 max_cores,
+ U32 max_vram,
+ F32 max_gl_version)
+ :
+ LLWindow(callbacks, fullscreen, flags),
+ mMaxGLVersion(max_gl_version),
+ mMaxCores(max_cores)
{
sMainThreadId = LLThread::currentID();
mWindowThread = new LLWindowWin32Thread();
+ mWindowThread->mMaxVRAM = max_vram;
+
//MAINT-516 -- force a load of opengl32.dll just in case windows went sideways
LoadLibrary(L"opengl32.dll");
+
+
+ if (mMaxCores != 0)
+ {
+ HANDLE hProcess = GetCurrentProcess();
+ mMaxCores = llmin(mMaxCores, (U32) 64);
+ DWORD_PTR mask = 0;
+
+ for (int i = 0; i < mMaxCores; ++i)
+ {
+ mask |= ((DWORD_PTR) 1) << i;
+ }
+
+ SetProcessAffinityMask(hProcess, mask);
+ }
+
+#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
+ HANDLE hProcess = GetCurrentProcess();
+
+ if (hProcess)
+ {
+ int priority = GetPriorityClass(hProcess);
+ if (priority < REALTIME_PRIORITY_CLASS)
+ {
+ if (SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS))
+ {
+ LL_INFOS() << "Set process priority to REALTIME_PRIORITY_CLASS" << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS() << "Failed to set process priority: " << std::hex << GetLastError() << LL_ENDL;
+ }
+ }
+ }
+#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;
@@ -943,16 +1054,19 @@ BOOL LLWindowWin32::maximize()
BOOL success = FALSE;
if (!mWindowHandle) return success;
- WINDOWPLACEMENT placement;
- placement.length = sizeof(WINDOWPLACEMENT);
-
- success = GetWindowPlacement(mWindowHandle, &placement);
- if (!success) return success;
+ mWindowThread->post([=]
+ {
+ WINDOWPLACEMENT placement;
+ placement.length = sizeof(WINDOWPLACEMENT);
- placement.showCmd = SW_MAXIMIZE;
+ if (GetWindowPlacement(mWindowHandle, &placement))
+ {
+ placement.showCmd = SW_MAXIMIZE;
+ SetWindowPlacement(mWindowHandle, &placement);
+ }
+ });
- success = SetWindowPlacement(mWindowHandle, &placement);
- return success;
+ return TRUE;
}
BOOL LLWindowWin32::getFullscreen()
@@ -1293,22 +1407,6 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen& size, BO
LL_INFOS("Window") << "pfd.dwDamageMask: " << pfd.dwDamageMask << LL_ENDL ;
LL_INFOS("Window") << "--- end pixel format dump ---" << LL_ENDL ;
- if (pfd.cColorBits < 32)
- {
- OSMessageBox(mCallbacks->translateString("MBTrueColorWindow"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- close();
- return FALSE;
- }
-
- if (pfd.cAlphaBits < 8)
- {
- OSMessageBox(mCallbacks->translateString("MBAlpha"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- close();
- return FALSE;
- }
-
if (!SetPixelFormat(mhDC, pixel_format, &pfd))
{
OSMessageBox(mCallbacks->translateString("MBPixelFmtSetErr"),
@@ -1348,8 +1446,8 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen& size, BO
attrib_list[cur_attrib++] = WGL_DEPTH_BITS_ARB;
attrib_list[cur_attrib++] = 24;
- attrib_list[cur_attrib++] = WGL_STENCIL_BITS_ARB;
- attrib_list[cur_attrib++] = 8;
+ //attrib_list[cur_attrib++] = WGL_STENCIL_BITS_ARB; //stencil buffer is deprecated (performance penalty)
+ //attrib_list[cur_attrib++] = 8;
attrib_list[cur_attrib++] = WGL_DRAW_TO_WINDOW_ARB;
attrib_list[cur_attrib++] = GL_TRUE;
@@ -1367,7 +1465,7 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen& size, BO
attrib_list[cur_attrib++] = 24;
attrib_list[cur_attrib++] = WGL_ALPHA_BITS_ARB;
- attrib_list[cur_attrib++] = 8;
+ attrib_list[cur_attrib++] = 0;
U32 end_attrib = 0;
if (mFSAASamples > 0)
@@ -1590,21 +1688,6 @@ const S32 max_format = (S32)num_formats - 1;
<< " Depth Bits " << S32(pfd.cDepthBits)
<< LL_ENDL;
- // make sure we have 32 bits per pixel
- if (pfd.cColorBits < 32 || GetDeviceCaps(mhDC, BITSPIXEL) < 32)
- {
- OSMessageBox(mCallbacks->translateString("MBTrueColorWindow"), mCallbacks->translateString("MBError"), OSMB_OK);
- close();
- return FALSE;
- }
-
- if (pfd.cAlphaBits < 8)
- {
- OSMessageBox(mCallbacks->translateString("MBAlpha"), mCallbacks->translateString("MBError"), OSMB_OK);
- close();
- return FALSE;
- }
-
mhRC = 0;
if (wglCreateContextAttribsARB)
{ //attempt to create a specific versioned context
@@ -1622,8 +1705,6 @@ const S32 max_format = (S32)num_formats - 1;
return FALSE;
}
- LL_PROFILER_GPU_CONTEXT
-
if (!gGLManager.initGL())
{
OSMessageBox(mCallbacks->translateString("MBVideoDrvErr"), mCallbacks->translateString("MBError"), OSMB_OK);
@@ -1647,6 +1728,13 @@ const S32 max_format = (S32)num_formats - 1;
// ok to post quit messages now
mPostQuit = TRUE;
+ // *HACK: Attempt to prevent startup crashes by deferring memory accounting
+ // until after some graphics setup. See SL-20177. -Cosmic,2023-09-18
+ mWindowThread->post([=]()
+ {
+ mWindowThread->glReady();
+ });
+
if (auto_show)
{
show();
@@ -1655,6 +1743,8 @@ const S32 max_format = (S32)num_formats - 1;
swapBuffers();
}
+ LL_PROFILER_GPU_CONTEXT;
+
return TRUE;
}
@@ -1767,10 +1857,15 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw
void* LLWindowWin32::createSharedContext()
{
+ mMaxGLVersion = llclamp(mMaxGLVersion, 3.f, 4.6f);
+
+ S32 version_major = llfloor(mMaxGLVersion);
+ S32 version_minor = llround((mMaxGLVersion-version_major)*10);
+
S32 attribs[] =
{
- WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
- WGL_CONTEXT_MINOR_VERSION_ARB, 6,
+ WGL_CONTEXT_MAJOR_VERSION_ARB, version_major,
+ WGL_CONTEXT_MINOR_VERSION_ARB, version_minor,
WGL_CONTEXT_PROFILE_MASK_ARB, LLRender::sGLCoreProfile ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
WGL_CONTEXT_FLAGS_ARB, gDebugGL ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
0
@@ -1819,6 +1914,7 @@ void* LLWindowWin32::createSharedContext()
void LLWindowWin32::makeContextCurrent(void* contextPtr)
{
wglMakeCurrent(mhDC, (HGLRC) contextPtr);
+ LL_PROFILER_GPU_CONTEXT;
}
void LLWindowWin32::destroySharedContext(void* contextPtr)
@@ -1833,7 +1929,7 @@ void LLWindowWin32::toggleVSync(bool enable_vsync)
LL_INFOS("Window") << "Disabling vertical sync" << LL_ENDL;
wglSwapIntervalEXT(0);
}
- else
+ else if (wglSwapIntervalEXT)
{
LL_INFOS("Window") << "Enabling vertical sync" << LL_ENDL;
wglSwapIntervalEXT(1);
@@ -2195,13 +2291,8 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
ASSERT_WINDOW_THREAD();
LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32;
- LL_DEBUGS("Window") << "mainWindowProc(" << std::hex << h_wnd
- << ", " << u_msg
- << ", " << w_param << ")" << std::dec << LL_ENDL;
-
if (u_msg == WM_POST_FUNCTION_)
{
- LL_DEBUGS("Window") << "WM_POST_FUNCTION_" << LL_ENDL;
// from LLWindowWin32Thread::Post()
// Cast l_param back to the pointer to the heap FuncType
// allocated by Post(). Capture in unique_ptr so we'll delete
@@ -2222,8 +2313,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
LLWindowWin32* window_imp = (LLWindowWin32*)GetWindowLongPtr(h_wnd, GWLP_USERDATA);
- bool debug_window_proc = false; // gDebugWindowProc || debugLoggingEnabled("Window");
-
if (NULL != window_imp)
{
// Juggle to make sure we can get negative positions for when
@@ -2250,11 +2339,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_DEVICECHANGE:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_DEVICECHANGE");
- if (debug_window_proc)
- {
- LL_INFOS("Window") << " WM_DEVICECHANGE: wParam=" << w_param
- << "; lParam=" << l_param << LL_ENDL;
- }
if (w_param == DBT_DEVNODES_CHANGED || w_param == DBT_DEVICEARRIVAL)
{
WINDOW_IMP_POST(window_imp->mCallbacks->handleDeviceChange(window_imp));
@@ -2316,16 +2400,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
{
// This message should be sent whenever the app gains or loses focus.
BOOL activating = (BOOL)w_param;
- BOOL minimized = window_imp->getMinimized();
-
- if (debug_window_proc)
- {
- LL_INFOS("Window") << "WINDOWPROC ActivateApp "
- << " activating " << S32(activating)
- << " minimized " << S32(minimized)
- << " fullscreen " << S32(window_imp->mFullscreen)
- << LL_ENDL;
- }
if (window_imp->mFullscreen)
{
@@ -2360,23 +2434,10 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
// Can be one of WA_ACTIVE, WA_CLICKACTIVE, or WA_INACTIVE
BOOL activating = (LOWORD(w_param) != WA_INACTIVE);
- BOOL minimized = BOOL(HIWORD(w_param));
-
if (!activating && LLWinImm::isAvailable() && window_imp->mPreeditor)
{
window_imp->interruptLanguageTextInput();
}
-
- // JC - I'm not sure why, but if we don't report that we handled the
- // WM_ACTIVATE message, the WM_ACTIVATEAPP messages don't work
- // properly when we run fullscreen.
- if (debug_window_proc)
- {
- LL_INFOS("Window") << "WINDOWPROC Activate "
- << " activating " << S32(activating)
- << " minimized " << S32(minimized)
- << LL_ENDL;
- }
});
break;
@@ -2454,16 +2515,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
window_imp->mRawWParam = w_param;
window_imp->mRawLParam = l_param;
- {
- if (debug_window_proc)
- {
- LL_INFOS("Window") << "Debug WindowProc WM_KEYDOWN "
- << " key " << S32(w_param)
- << LL_ENDL;
- }
-
- gKeyboard->handleKeyDown(w_param, mask);
- }
+ gKeyboard->handleKeyDown(w_param, mask);
});
if (eat_keystroke) return 0; // skip DefWindowProc() handling if we're consuming the keypress
break;
@@ -2483,14 +2535,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
window_imp->mRawLParam = l_param;
{
- LL_RECORD_BLOCK_TIME(FTM_KEYHANDLER);
-
- if (debug_window_proc)
- {
- LL_INFOS("Window") << "Debug WindowProc WM_KEYUP "
- << " key " << S32(w_param)
- << LL_ENDL;
- }
+ LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_KEYUP");
gKeyboard->handleKeyUp(w_param, mask);
}
});
@@ -2500,10 +2545,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_IME_SETCONTEXT:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_SETCONTEXT");
- if (debug_window_proc)
- {
- LL_INFOS("Window") << "WM_IME_SETCONTEXT" << LL_ENDL;
- }
if (LLWinImm::isAvailable() && window_imp->mPreeditor)
{
l_param &= ~ISC_SHOWUICOMPOSITIONWINDOW;
@@ -2514,10 +2555,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_IME_STARTCOMPOSITION:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_STARTCOMPOSITION");
- if (debug_window_proc)
- {
- LL_INFOS() << "WM_IME_STARTCOMPOSITION" << LL_ENDL;
- }
if (LLWinImm::isAvailable() && window_imp->mPreeditor)
{
WINDOW_IMP_POST(window_imp->handleStartCompositionMessage());
@@ -2528,10 +2565,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_IME_ENDCOMPOSITION:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_ENDCOMPOSITION");
- if (debug_window_proc)
- {
- LL_INFOS() << "WM_IME_ENDCOMPOSITION" << LL_ENDL;
- }
if (LLWinImm::isAvailable() && window_imp->mPreeditor)
{
return 0;
@@ -2541,10 +2574,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_IME_COMPOSITION:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_COMPOSITION");
- if (debug_window_proc)
- {
- LL_INFOS() << "WM_IME_COMPOSITION" << LL_ENDL;
- }
if (LLWinImm::isAvailable() && window_imp->mPreeditor)
{
WINDOW_IMP_POST(window_imp->handleCompositionMessage(l_param));
@@ -2555,10 +2584,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_IME_REQUEST:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_REQUEST");
- if (debug_window_proc)
- {
- LL_INFOS() << "WM_IME_REQUEST" << LL_ENDL;
- }
if (LLWinImm::isAvailable() && window_imp->mPreeditor)
{
LRESULT result;
@@ -2587,12 +2612,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
// it is worth trying. The good old WM_CHAR works just fine even for supplementary
// characters. We just need to take care of surrogate pairs sent as two WM_CHAR's
// by ourselves. It is not that tough. -- Alissa Sabre @ SL
- if (debug_window_proc)
- {
- LL_INFOS("Window") << "Debug WindowProc WM_CHAR "
- << " key " << S32(w_param)
- << LL_ENDL;
- }
+
// Even if LLWindowCallbacks::handleUnicodeChar(llwchar, BOOL) returned FALSE,
// we *did* processed the event, so I believe we should not pass it to DefWindowProc...
window_imp->handleUnicodeUTF16((U16)w_param, gKeyboard->currentMask(FALSE));
@@ -2916,23 +2936,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SIZE");
window_imp->updateWindowRect();
- S32 width = S32(LOWORD(l_param));
- S32 height = S32(HIWORD(l_param));
-
-
- if (debug_window_proc)
- {
- BOOL maximized = (w_param == SIZE_MAXIMIZED);
- BOOL restored = (w_param == SIZE_RESTORED);
- BOOL minimized = (w_param == SIZE_MINIMIZED);
-
- LL_INFOS("Window") << "WINDOWPROC Size "
- << width << "x" << height
- << " max " << S32(maximized)
- << " min " << S32(minimized)
- << " rest " << S32(restored)
- << LL_ENDL;
- }
// There's an odd behavior with WM_SIZE that I would call a bug. If
// the window is maximized, and you call MoveWindow() with a size smaller
@@ -2998,10 +3001,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_SETFOCUS:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SETFOCUS");
- if (debug_window_proc)
- {
- LL_INFOS("Window") << "WINDOWPROC SetFocus" << LL_ENDL;
- }
WINDOW_IMP_POST(window_imp->mCallbacks->handleFocus(window_imp));
return 0;
}
@@ -3009,10 +3008,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_KILLFOCUS:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_KILLFOCUS");
- if (debug_window_proc)
- {
- LL_INFOS("Window") << "WINDOWPROC KillFocus" << LL_ENDL;
- }
WINDOW_IMP_POST(window_imp->mCallbacks->handleFocusLost(window_imp));
return 0;
}
@@ -3133,10 +3128,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
default:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - default");
- if (debug_window_proc)
- {
- LL_INFOS("Window") << "Unhandled windows message code: 0x" << std::hex << U32(u_msg) << LL_ENDL;
- }
+ LL_DEBUGS("Window") << "Unhandled windows message code: 0x" << std::hex << U32(u_msg) << LL_ENDL;
}
break;
}
@@ -3564,7 +3556,7 @@ BOOL LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 re
// Don't change anything if we don't have to
if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
{
- if (dev_mode.dmPelsWidth == width &&
+ if (dev_mode.dmPelsWidth == width &&
dev_mode.dmPelsHeight == height &&
dev_mode.dmBitsPerPel == bits &&
dev_mode.dmDisplayFrequency == refresh )
@@ -3630,12 +3622,15 @@ BOOL LLWindowWin32::resetDisplayResolution()
void LLWindowWin32::swapBuffers()
{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32;
- ASSERT_MAIN_THREAD();
- glFlush(); //superstitious flush for maybe frame stall removal?
- SwapBuffers(mhDC);
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32;
+ SwapBuffers(mhDC);
+ }
- LL_PROFILER_GPU_COLLECT
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("GPU Collect");
+ LL_PROFILER_GPU_COLLECT;
+ }
}
@@ -4586,13 +4581,17 @@ std::vector<std::string> LLWindowWin32::getDynamicFallbackFontList()
return std::vector<std::string>();
}
+U32 LLWindowWin32::getAvailableVRAMMegabytes()
+{
+ return mWindowThread ? mWindowThread->getAvailableVRAMMegabytes() : 0;
+}
#endif // LL_WINDOWS
inline LLWindowWin32::LLWindowWin32Thread::LLWindowWin32Thread()
- : ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE)
+ : LL::ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE)
{
- ThreadPool::start();
+ LL::ThreadPool::start();
}
/**
@@ -4641,17 +4640,233 @@ private:
std::string mPrev;
};
+// Print hardware debug info about available graphics adapters in ordinal order
+void debugEnumerateGraphicsAdapters()
+{
+ LL_INFOS("Window") << "Enumerating graphics adapters..." << LL_ENDL;
+
+ IDXGIFactory1* factory;
+ HRESULT res = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&factory);
+ if (FAILED(res) || !factory)
+ {
+ LL_WARNS() << "CreateDXGIFactory1 failed: 0x" << std::hex << res << LL_ENDL;
+ }
+ else
+ {
+ UINT graphics_adapter_index = 0;
+ IDXGIAdapter3* dxgi_adapter;
+ while (true)
+ {
+ res = factory->EnumAdapters(graphics_adapter_index, reinterpret_cast<IDXGIAdapter**>(&dxgi_adapter));
+ if (FAILED(res))
+ {
+ if (graphics_adapter_index == 0)
+ {
+ LL_WARNS() << "EnumAdapters failed: 0x" << std::hex << res << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS("Window") << "Done enumerating graphics adapters" << LL_ENDL;
+ }
+ }
+ else
+ {
+ DXGI_ADAPTER_DESC desc;
+ dxgi_adapter->GetDesc(&desc);
+ std::wstring description_w((wchar_t*)desc.Description);
+ std::string description(description_w.begin(), description_w.end());
+ LL_INFOS("Window") << "Graphics adapter index: " << graphics_adapter_index << ", "
+ << "Description: " << description << ", "
+ << "DeviceId: " << desc.DeviceId << ", "
+ << "SubSysId: " << desc.SubSysId << ", "
+ << "AdapterLuid: " << desc.AdapterLuid.HighPart << "_" << desc.AdapterLuid.LowPart << ", "
+ << "DedicatedVideoMemory: " << desc.DedicatedVideoMemory / 1024 / 1024 << ", "
+ << "DedicatedSystemMemory: " << desc.DedicatedSystemMemory / 1024 / 1024 << ", "
+ << "SharedSystemMemory: " << desc.SharedSystemMemory / 1024 / 1024 << LL_ENDL;
+ }
+
+ if (dxgi_adapter)
+ {
+ dxgi_adapter->Release();
+ dxgi_adapter = NULL;
+ }
+ else
+ {
+ break;
+ }
+
+ graphics_adapter_index++;
+ }
+ }
+
+ if (factory)
+ {
+ factory->Release();
+ }
+}
+
+void LLWindowWin32::LLWindowWin32Thread::initDX()
+{
+ if (!mGLReady) { return; }
+
+ if (mDXGIAdapter == NULL)
+ {
+ debugEnumerateGraphicsAdapters();
+
+ IDXGIFactory4* pFactory = nullptr;
+
+ HRESULT res = CreateDXGIFactory1(__uuidof(IDXGIFactory4), (void**)&pFactory);
+
+ if (FAILED(res))
+ {
+ LL_WARNS() << "CreateDXGIFactory1 failed: 0x" << std::hex << res << LL_ENDL;
+ }
+ else
+ {
+ res = pFactory->EnumAdapters(0, reinterpret_cast<IDXGIAdapter**>(&mDXGIAdapter));
+ if (FAILED(res))
+ {
+ LL_WARNS() << "EnumAdapters failed: 0x" << std::hex << res << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS() << "EnumAdapters success" << LL_ENDL;
+ }
+ }
+
+ if (pFactory)
+ {
+ pFactory->Release();
+ }
+ }
+}
+
+void LLWindowWin32::LLWindowWin32Thread::initD3D()
+{
+ if (!mGLReady) { return; }
+
+ if (mDXGIAdapter == NULL && mD3DDevice == NULL && mWindowHandle != 0)
+ {
+ mD3D = Direct3DCreate9(D3D_SDK_VERSION);
+
+ D3DPRESENT_PARAMETERS d3dpp;
+
+ ZeroMemory(&d3dpp, sizeof(d3dpp));
+ d3dpp.Windowed = TRUE;
+ d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
+
+ HRESULT res = mD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, mWindowHandle, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &mD3DDevice);
+
+ if (FAILED(res))
+ {
+ LL_WARNS() << "(fallback) CreateDevice failed: 0x" << std::hex << res << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS() << "(fallback) CreateDevice success" << LL_ENDL;
+ }
+ }
+}
+
+void LLWindowWin32::LLWindowWin32Thread::updateVRAMUsage()
+{
+ LL_PROFILE_ZONE_SCOPED;
+ if (!mGLReady) { return; }
+
+ if (mDXGIAdapter != nullptr)
+ {
+ // NOTE: what lies below is hand wavy math based on compatibility testing and observation against a variety of hardware
+ // It doesn't make sense, but please don't refactor it to make sense. -- davep
+
+ DXGI_QUERY_VIDEO_MEMORY_INFO info;
+ mDXGIAdapter->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &info);
+#if 0 // debug 0 budget and 0 CU
+ info.Budget = 0;
+ info.CurrentUsage = 0;
+#endif
+
+ U32 budget_mb = info.Budget / 1024 / 1024;
+ gGLManager.mVRAM = llmax(gGLManager.mVRAM, (S32) budget_mb);
+
+ U32 afr_mb = info.AvailableForReservation / 1024 / 1024;
+ // correct for systems that misreport budget
+ if (budget_mb == 0)
+ {
+ // fall back to available for reservation clamped between 512MB and 2GB
+ budget_mb = llclamp(afr_mb, (U32) 512, (U32) 2048);
+ }
+
+ if ( mMaxVRAM != 0)
+ {
+ budget_mb = llmin(budget_mb, mMaxVRAM);
+ }
+
+ U32 cu_mb = info.CurrentUsage / 1024 / 1024;
+
+ // get an estimated usage based on texture bytes allocated
+ U32 eu_mb = LLImageGL::getTextureBytesAllocated() * 2 / 1024 / 1024;
+
+ if (cu_mb == 0)
+ { // current usage is sometimes unreliable on Intel GPUs, fall back to estimated usage
+ cu_mb = llmax((U32)1, eu_mb);
+ }
+ U32 target_mb = budget_mb;
+
+ if (target_mb > 4096) // if 4GB are installed, try to leave 2GB free
+ {
+ target_mb -= 2048;
+ }
+ else // if less than 4GB are installed, try not to use more than half of it
+ {
+ target_mb /= 2;
+ }
+
+ mAvailableVRAM = cu_mb < target_mb ? target_mb - cu_mb : 0;
+
+#if 0
+
+ F32 eu_error = (F32)((S32)eu_mb - (S32)cu_mb) / (F32)cu_mb;
+ LL_INFOS("Window") << "\nLocal\nAFR: " << info.AvailableForReservation / 1024 / 1024
+ << "\nBudget: " << info.Budget / 1024 / 1024
+ << "\nCR: " << info.CurrentReservation / 1024 / 1024
+ << "\nCU: " << info.CurrentUsage / 1024 / 1024
+ << "\nEU: " << eu_mb << llformat(" (%.2f)", eu_error)
+ << "\nTU: " << target_mb
+ << "\nAM: " << mAvailableVRAM << LL_ENDL;
+#endif
+ }
+ else if (mD3DDevice != NULL)
+ { // fallback to D3D9
+ mAvailableVRAM = mD3DDevice->GetAvailableTextureMem() / 1024 / 1024;
+ }
+}
+
void LLWindowWin32::LLWindowWin32Thread::run()
{
sWindowThreadId = std::this_thread::get_id();
LogChange logger("Window");
+ //as good a place as any to up the MM timer resolution (see ms_sleep)
+ //attempt to set timer resolution to 1ms
+ TIMECAPS tc;
+ if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) == TIMERR_NOERROR)
+ {
+ timeBeginPeriod(llclamp((U32) 1, tc.wPeriodMin, tc.wPeriodMax));
+ }
+
while (! getQueue().done())
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32;
+ // lazily call initD3D inside this loop to catch when mGLReady has been set to true
+ initDX();
+
if (mWindowHandle != 0)
{
+ // lazily call initD3D inside this loop to catch when mWindowHandle has been set, and mGLReady has been set to true
+ // *TODO: Shutdown if this fails when mWindowHandle exists
+ initD3D();
+
MSG msg;
BOOL status;
if (mhDC == 0)
@@ -4684,6 +4899,13 @@ void LLWindowWin32::LLWindowWin32Thread::run()
getQueue().runPending();
}
+ // update available vram once every 3 seconds
+ static LLFrameTimer vramTimer;
+ if (vramTimer.getElapsedTimeF32() > 3.f)
+ {
+ updateVRAMUsage();
+ vramTimer.reset();
+ }
#if 0
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - Sleep");
@@ -4692,6 +4914,26 @@ void LLWindowWin32::LLWindowWin32Thread::run()
}
#endif
}
+
+ //clean up DXGI/D3D resources
+ if (mDXGIAdapter)
+ {
+ mDXGIAdapter->Release();
+ mDXGIAdapter = nullptr;
+ }
+
+ if (mD3DDevice)
+ {
+ mD3DDevice->Release();
+ mD3DDevice = nullptr;
+ }
+
+ if (mD3D)
+ {
+ mD3D->Release();
+ mD3D = nullptr;
+ }
+
}
void LLWindowWin32::post(const std::function<void()>& func)