summaryrefslogtreecommitdiff
path: root/indra/llwindow
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llwindow')
-rw-r--r--indra/llwindow/CMakeLists.txt2
-rw-r--r--indra/llwindow/llopenglview-objc.mm19
-rw-r--r--indra/llwindow/llwindow.cpp7
-rw-r--r--indra/llwindow/llwindow.h10
-rw-r--r--indra/llwindow/llwindowheadless.h135
-rw-r--r--indra/llwindow/llwindowmacosx.cpp64
-rw-r--r--indra/llwindow/llwindowmacosx.h3
-rw-r--r--indra/llwindow/llwindowwin32.cpp588
-rw-r--r--indra/llwindow/llwindowwin32.h8
9 files changed, 590 insertions, 246 deletions
diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt
index ca08e38f77..7b1430c67c 100644
--- a/indra/llwindow/CMakeLists.txt
+++ b/indra/llwindow/CMakeLists.txt
@@ -127,6 +127,8 @@ if (WINDOWS)
list(APPEND llwindow_LINK_LIBRARIES
comdlg32 # Common Dialogs for ChooseColor
ole32
+ dxgi
+ d3d9
)
endif (WINDOWS)
diff --git a/indra/llwindow/llopenglview-objc.mm b/indra/llwindow/llopenglview-objc.mm
index 7936245744..586e00b5e4 100644
--- a/indra/llwindow/llopenglview-objc.mm
+++ b/indra/llwindow/llopenglview-objc.mm
@@ -141,7 +141,12 @@ attributedStringInfo getSegments(NSAttributedString *str)
CGLError the_err = CGLQueryRendererInfo (CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay), &info, &num_renderers);
if(0 == the_err)
{
- CGLDescribeRenderer (info, 0, kCGLRPTextureMemoryMegabytes, &vram_megabytes);
+ // The name, uses, and other platform definitions of gGLManager.mVRAM suggest that this is supposed to be total vram in MB,
+ // rather than, say, just the texture memory. The two exceptions are:
+ // 1. LLAppViewer::getViewerInfo() puts the value in a field labeled "TEXTURE_MEMORY"
+ // 2. For years, this present function used kCGLRPTextureMemoryMegabytes
+ // Now we use kCGLRPVideoMemoryMegabytes to bring it in line with everything else (except thatone label).
+ CGLDescribeRenderer (info, 0, kCGLRPVideoMemoryMegabytes, &vram_megabytes);
CGLDestroyRendererInfo (info);
}
else
@@ -256,6 +261,7 @@ attributedStringInfo getSegments(NSAttributedString *str)
NSOpenGLPFADepthSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFAColorSize, 24,
+ NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core,
0
};
@@ -544,7 +550,16 @@ attributedStringInfo getSegments(NSAttributedString *str)
if (mModifiers & mask)
{
eventData.mKeyEvent = NativeKeyEventData::KEYDOWN;
- callKeyDown(&eventData, [theEvent keyCode], 0, [[theEvent characters] characterAtIndex:0]);
+
+ wchar_t c = 0;
+ if([theEvent type] == NSEventTypeKeyDown)
+ {
+ // characters property is only valid when the event is of type KeyDown or KeyUp
+ // https://developer.apple.com/documentation/appkit/nsevent/1534183-characters?language=objc
+ c = [[theEvent characters] characterAtIndex:0];
+ }
+
+ callKeyDown(&eventData, [theEvent keyCode], 0, c);
}
else
{
diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp
index c5725677b4..9e281dfc99 100644
--- a/indra/llwindow/llwindow.cpp
+++ b/indra/llwindow/llwindow.cpp
@@ -407,7 +407,10 @@ LLWindow* LLWindowManager::createWindow(
BOOL enable_vsync,
BOOL use_gl,
BOOL ignore_pixel_depth,
- U32 fsaa_samples)
+ U32 fsaa_samples,
+ U32 max_cores,
+ U32 max_vram,
+ F32 max_gl_version)
{
LLWindow* new_window;
@@ -424,7 +427,7 @@ LLWindow* LLWindowManager::createWindow(
#elif LL_WINDOWS
new_window = new LLWindowWin32(callbacks,
title, name, x, y, width, height, flags,
- fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth, fsaa_samples);
+ fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth, fsaa_samples, max_cores, max_vram, max_gl_version);
#elif LL_DARWIN
new_window = new LLWindowMacOSX(callbacks,
title, name, x, y, width, height, flags,
diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h
index 45049e1539..279e269f43 100644
--- a/indra/llwindow/llwindow.h
+++ b/indra/llwindow/llwindow.h
@@ -163,7 +163,10 @@ public:
virtual F32 getNativeAspectRatio() = 0;
virtual F32 getPixelAspectRatio() = 0;
virtual void setNativeAspectRatio(F32 aspect) = 0;
-
+
+ // query VRAM usage
+ virtual U32 getAvailableVRAMMegabytes() = 0;
+
virtual void beforeDialog() {}; // prepare to put up an OS dialog (if special measures are required, such as in fullscreen mode)
virtual void afterDialog() {}; // undo whatever was done in beforeDialog()
@@ -309,7 +312,10 @@ public:
BOOL enable_vsync = FALSE,
BOOL use_gl = TRUE,
BOOL ignore_pixel_depth = FALSE,
- U32 fsaa_samples = 0);
+ U32 fsaa_samples = 0,
+ U32 max_cores = 0,
+ U32 max_vram = 0,
+ F32 max_gl_version = 4.6f);
static BOOL destroyWindow(LLWindow* window);
static BOOL isWindowValid(LLWindow *window);
};
diff --git a/indra/llwindow/llwindowheadless.h b/indra/llwindow/llwindowheadless.h
index 410da79623..2f2c0de5bd 100644
--- a/indra/llwindow/llwindowheadless.h
+++ b/indra/llwindow/llwindowheadless.h
@@ -32,72 +32,79 @@
class LLWindowHeadless : public LLWindow
{
public:
- /*virtual*/ void show() {};
- /*virtual*/ void hide() {};
- /*virtual*/ void close() {};
- /*virtual*/ BOOL getVisible() {return FALSE;};
- /*virtual*/ BOOL getMinimized() {return FALSE;};
- /*virtual*/ BOOL getMaximized() {return FALSE;};
- /*virtual*/ BOOL maximize() {return FALSE;};
- /*virtual*/ void minimize() {};
- /*virtual*/ void restore() {};
- /*virtual*/ BOOL getFullscreen() {return FALSE;};
- /*virtual*/ BOOL getPosition(LLCoordScreen *position) {return FALSE;};
- /*virtual*/ BOOL getSize(LLCoordScreen *size) {return FALSE;};
- /*virtual*/ BOOL getSize(LLCoordWindow *size) {return FALSE;};
- /*virtual*/ BOOL setPosition(LLCoordScreen position) {return FALSE;};
- /*virtual*/ BOOL setSizeImpl(LLCoordScreen size) {return FALSE;};
- /*virtual*/ BOOL setSizeImpl(LLCoordWindow size) {return FALSE;};
- /*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL enable_vsync, const LLCoordScreen * const posp = NULL) {return FALSE;};
- void* createSharedContext() { return nullptr; }
- void makeContextCurrent(void*) {}
- void destroySharedContext(void*) {}
- /*virtual*/ void toggleVSync(bool enable_vsync) { }
- /*virtual*/ BOOL setCursorPosition(LLCoordWindow position) {return FALSE;};
- /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position) {return FALSE;};
+ /*virtual*/ void show() override {}
+ /*virtual*/ void hide() override {}
+ /*virtual*/ void close() override {}
+ /*virtual*/ BOOL getVisible() override {return FALSE;}
+ /*virtual*/ BOOL getMinimized() override {return FALSE;}
+ /*virtual*/ BOOL getMaximized() override {return FALSE;}
+ /*virtual*/ BOOL maximize() override {return FALSE;}
+ /*virtual*/ void minimize() override {}
+ /*virtual*/ void restore() override {}
+ // TODO: LLWindow::getFullscreen() is (intentionally?) NOT virtual.
+ // Apparently the coder of LLWindowHeadless didn't realize that. Is it a
+ // mistake to shadow the base-class method with an LLWindowHeadless
+ // override when called on the subclass, yet call the base-class method
+ // when indirecting through a polymorphic pointer or reference?
+ BOOL getFullscreen() {return FALSE;}
+ /*virtual*/ BOOL getPosition(LLCoordScreen *position) override {return FALSE;}
+ /*virtual*/ BOOL getSize(LLCoordScreen *size) override {return FALSE;}
+ /*virtual*/ BOOL getSize(LLCoordWindow *size) override {return FALSE;}
+ /*virtual*/ BOOL setPosition(LLCoordScreen position) override {return FALSE;}
+ /*virtual*/ BOOL setSizeImpl(LLCoordScreen size) override {return FALSE;}
+ /*virtual*/ BOOL setSizeImpl(LLCoordWindow size) override {return FALSE;}
+ /*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL enable_vsync, const LLCoordScreen * const posp = NULL) override {return FALSE;}
+ void* createSharedContext() override { return nullptr; }
+ void makeContextCurrent(void*) override {}
+ void destroySharedContext(void*) override {}
+ /*virtual*/ void toggleVSync(bool enable_vsync) override { }
+ /*virtual*/ BOOL setCursorPosition(LLCoordWindow position) override {return FALSE;}
+ /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position) override {return FALSE;}
#if LL_WINDOWS
- /*virtual*/ BOOL getCursorDelta(LLCoordCommon* delta) { return FALSE; }
+ /*virtual*/ BOOL getCursorDelta(LLCoordCommon* delta) override { return FALSE; }
#endif
- /*virtual*/ void showCursor() {};
- /*virtual*/ void hideCursor() {};
- /*virtual*/ void showCursorFromMouseMove() {};
- /*virtual*/ void hideCursorUntilMouseMove() {};
- /*virtual*/ BOOL isCursorHidden() {return FALSE;};
- /*virtual*/ void updateCursor() {};
- //virtual ECursorType getCursor() { return mCurrentCursor; };
- /*virtual*/ void captureMouse() {};
- /*virtual*/ void releaseMouse() {};
- /*virtual*/ void setMouseClipping( BOOL b ) {};
- /*virtual*/ BOOL isClipboardTextAvailable() {return FALSE; };
- /*virtual*/ BOOL pasteTextFromClipboard(LLWString &dst) {return FALSE; };
- /*virtual*/ BOOL copyTextToClipboard(const LLWString &src) {return FALSE; };
- /*virtual*/ void flashIcon(F32 seconds) {};
- /*virtual*/ F32 getGamma() {return 1.0f; };
- /*virtual*/ BOOL setGamma(const F32 gamma) {return FALSE; }; // Set the gamma
- /*virtual*/ void setFSAASamples(const U32 fsaa_samples) { }
- /*virtual*/ U32 getFSAASamples() { return 0; }
- /*virtual*/ BOOL restoreGamma() {return FALSE; }; // Restore original gamma table (before updating gamma)
- //virtual ESwapMethod getSwapMethod() { return mSwapMethod; }
- /*virtual*/ void gatherInput() {};
- /*virtual*/ void delayInputProcessing() {};
- /*virtual*/ void swapBuffers();
+ /*virtual*/ void showCursor() override {}
+ /*virtual*/ void hideCursor() override {}
+ /*virtual*/ void showCursorFromMouseMove() override {}
+ /*virtual*/ void hideCursorUntilMouseMove() override {}
+ /*virtual*/ BOOL isCursorHidden() override {return FALSE;}
+ /*virtual*/ void updateCursor() override {}
+ //virtual ECursorType getCursor() override { return mCurrentCursor; }
+ /*virtual*/ void captureMouse() override {}
+ /*virtual*/ void releaseMouse() override {}
+ /*virtual*/ void setMouseClipping( BOOL b ) override {}
+ /*virtual*/ BOOL isClipboardTextAvailable() override {return FALSE; }
+ /*virtual*/ BOOL pasteTextFromClipboard(LLWString &dst) override {return FALSE; }
+ /*virtual*/ BOOL copyTextToClipboard(const LLWString &src) override {return FALSE; }
+ /*virtual*/ void flashIcon(F32 seconds) override {}
+ /*virtual*/ F32 getGamma() override {return 1.0f; }
+ /*virtual*/ BOOL setGamma(const F32 gamma) override {return FALSE; } // Set the gamma
+ /*virtual*/ void setFSAASamples(const U32 fsaa_samples) override { }
+ /*virtual*/ U32 getFSAASamples() override { return 0; }
+ /*virtual*/ BOOL restoreGamma() override {return FALSE; } // Restore original gamma table (before updating gamma)
+ //virtual ESwapMethod getSwapMethod() override { return mSwapMethod; }
+ /*virtual*/ void gatherInput() override {}
+ /*virtual*/ void delayInputProcessing() override {}
+ /*virtual*/ void swapBuffers() override;
// handy coordinate space conversion routines
- /*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordWindow *to) { return FALSE; };
- /*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordScreen *to) { return FALSE; };
- /*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordGL *to) { return FALSE; };
- /*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordWindow *to) { return FALSE; };
- /*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordGL *to) { return FALSE; };
- /*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordScreen *to) { return FALSE; };
+ /*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordWindow *to) override { return FALSE; }
+ /*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordScreen *to) override { return FALSE; }
+ /*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordGL *to) override { return FALSE; }
+ /*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordWindow *to) override { return FALSE; }
+ /*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordGL *to) override { return FALSE; }
+ /*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordScreen *to) override { return FALSE; }
- /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) { return NULL; };
- /*virtual*/ F32 getNativeAspectRatio() { return 1.0f; };
- /*virtual*/ F32 getPixelAspectRatio() { return 1.0f; };
- /*virtual*/ void setNativeAspectRatio(F32 ratio) {}
+ /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override { return NULL; }
+ /*virtual*/ F32 getNativeAspectRatio() override { return 1.0f; }
+ /*virtual*/ F32 getPixelAspectRatio() override { return 1.0f; }
+ /*virtual*/ void setNativeAspectRatio(F32 ratio) override {}
- /*virtual*/ void *getPlatformWindow() { return 0; };
- /*virtual*/ void bringToFront() {};
+ U32 getAvailableVRAMMegabytes() override { return 4096; }
+
+ /*virtual*/ void *getPlatformWindow() override { return 0; }
+ /*virtual*/ void bringToFront() override {}
LLWindowHeadless(LLWindowCallbacks* callbacks,
const std::string& title, const std::string& name,
@@ -113,12 +120,12 @@ private:
class LLSplashScreenHeadless : public LLSplashScreen
{
public:
- LLSplashScreenHeadless() {};
- virtual ~LLSplashScreenHeadless() {};
+ LLSplashScreenHeadless() {}
+ virtual ~LLSplashScreenHeadless() {}
- /*virtual*/ void showImpl() {};
- /*virtual*/ void updateImpl(const std::string& mesg) {};
- /*virtual*/ void hideImpl() {};
+ /*virtual*/ void showImpl() override {}
+ /*virtual*/ void updateImpl(const std::string& mesg) override {}
+ /*virtual*/ void hideImpl() override {}
};
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index aa87a6803f..66b9a5d060 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -193,7 +193,7 @@ LLWindowMacOSX::LLWindowMacOSX(LLWindowCallbacks* callbacks,
return;
}
- //start with arrow cursor
+ //start with arrow cursor
initCursors();
setCursor( UI_CURSOR_ARROW );
@@ -647,6 +647,34 @@ BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits
mGLView = createOpenGLView(mWindow, mFSAASamples, enable_vsync);
mContext = getCGLContextObj(mGLView);
gGLManager.mVRAM = getVramSize(mGLView);
+
+ if(!mPixelFormat)
+ {
+ CGLPixelFormatAttribute attribs[] =
+ {
+ kCGLPFANoRecovery,
+ kCGLPFADoubleBuffer,
+ kCGLPFAClosestPolicy,
+ kCGLPFAAccelerated,
+ kCGLPFAMultisample,
+ kCGLPFASampleBuffers, static_cast<CGLPixelFormatAttribute>((mFSAASamples > 0 ? 1 : 0)),
+ kCGLPFASamples, static_cast<CGLPixelFormatAttribute>(mFSAASamples),
+ kCGLPFAStencilSize, static_cast<CGLPixelFormatAttribute>(8),
+ kCGLPFADepthSize, static_cast<CGLPixelFormatAttribute>(24),
+ kCGLPFAAlphaSize, static_cast<CGLPixelFormatAttribute>(8),
+ kCGLPFAColorSize, static_cast<CGLPixelFormatAttribute>(24),
+ kCGLPFAOpenGLProfile, static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_GL4_Core),
+ static_cast<CGLPixelFormatAttribute>(0)
+ };
+
+ GLint numPixelFormats;
+ CGLChoosePixelFormat (attribs, &mPixelFormat, &numPixelFormats);
+
+ if(mPixelFormat == NULL) {
+ CGLChoosePixelFormat (attribs, &mPixelFormat, &numPixelFormats);
+ }
+ }
+
}
// This sets up our view to recieve text from our non-inline text input window.
@@ -1235,6 +1263,16 @@ F32 LLWindowMacOSX::getPixelAspectRatio()
return 1.f;
}
+U32 LLWindowMacOSX::getAvailableVRAMMegabytes() {
+ // MTL (and MoltenVK) has some additional gpu data, such as recommendedMaxWorkingSetSize and currentAllocatedSize.
+ // But these are not available for OpenGL and/or our current mimimum OS version.
+ // So we will estimate.
+ static const U32 mb = 1024*1024;
+ // We're asked for total available gpu memory, but we only have allocation info on texture usage. So estimate by doubling that.
+ static const U32 total_factor = 2; // estimated total/textures
+ return gGLManager.mVRAM - (LLImageGL::getTextureBytesAllocated() * total_factor/mb);
+}
+
//static SInt32 oldWindowLevel;
// MBW -- XXX -- There's got to be a better way than this. Find it, please...
@@ -2417,6 +2455,11 @@ void* LLWindowMacOSX::createSharedContext()
{
sharedContext* sc = new sharedContext();
CGLCreateContext(mPixelFormat, mContext, &(sc->mContext));
+
+ if (sUseMultGL)
+ {
+ CGLEnable(mContext, kCGLCEMPEngine);
+ }
return (void *)sc;
}
@@ -2424,6 +2467,25 @@ void* LLWindowMacOSX::createSharedContext()
void LLWindowMacOSX::makeContextCurrent(void* context)
{
CGLSetCurrentContext(((sharedContext*)context)->mContext);
+
+ //enable multi-threaded OpenGL
+ if (sUseMultGL)
+ {
+ CGLError cgl_err;
+ CGLContextObj ctx = CGLGetCurrentContext();
+
+ cgl_err = CGLEnable( ctx, kCGLCEMPEngine);
+
+ if (cgl_err != kCGLNoError )
+ {
+ LL_INFOS("GLInit") << "Multi-threaded OpenGL not available." << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS("GLInit") << "Multi-threaded OpenGL enabled." << LL_ENDL;
+ }
+ }
+
}
void LLWindowMacOSX::destroySharedContext(void* context)
diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h
index 19136aa3de..d577d90c1c 100644
--- a/indra/llwindow/llwindowmacosx.h
+++ b/indra/llwindow/llwindowmacosx.h
@@ -100,6 +100,9 @@ public:
F32 getPixelAspectRatio() override;
void setNativeAspectRatio(F32 ratio) override { mOverrideAspectRatio = ratio; }
+ // query VRAM usage
+ /*virtual*/ U32 getAvailableVRAMMegabytes() override;
+
void beforeDialog() override;
void afterDialog() override;
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index 694f010ef3..d66d4658dc 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)
@@ -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;
+ }
}
@@ -4589,13 +4584,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();
}
/**
@@ -4644,17 +4643,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)
@@ -4687,6 +4902,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");
@@ -4695,6 +4917,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)
diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h
index b0d5c557b8..79bc4ad797 100644
--- a/indra/llwindow/llwindowwin32.h
+++ b/indra/llwindow/llwindowwin32.h
@@ -108,7 +108,9 @@ public:
/*virtual*/ F32 getPixelAspectRatio();
/*virtual*/ void setNativeAspectRatio(F32 ratio) { mOverrideAspectRatio = ratio; }
- /*virtual*/ BOOL dialogColorPicker(F32 *r, F32 *g, F32 *b );
+ U32 getAvailableVRAMMegabytes() override;
+
+ /*virtual*/ BOOL dialogColorPicker(F32 *r, F32 *g, F32 *b );
/*virtual*/ void *getPlatformWindow();
/*virtual*/ void bringToFront();
@@ -140,7 +142,7 @@ protected:
LLWindowWin32(LLWindowCallbacks* callbacks,
const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags,
BOOL fullscreen, BOOL clearBg, BOOL enable_vsync, BOOL use_gl,
- BOOL ignore_pixel_depth, U32 fsaa_samples);
+ BOOL ignore_pixel_depth, U32 fsaa_samples, U32 max_cores, U32 max_vram, F32 max_gl_version);
~LLWindowWin32();
void initCursors();
@@ -211,6 +213,8 @@ protected:
F32 mCurrentGamma;
U32 mFSAASamples;
+ U32 mMaxCores; // for debugging only -- maximum number of CPU cores to use, or 0 for no limit
+ F32 mMaxGLVersion; // maximum OpenGL version to attempt to use (clamps to 3.2 - 4.6)
WORD mPrevGammaRamp[3][256];
WORD mCurrentGammaRamp[3][256];
BOOL mCustomGammaSet;