/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "EglOsApi.h" #include "aemu/base/synchronization/Lock.h" #include "aemu/base/SharedLibrary.h" #include "CoreProfileConfigs.h" #include "host-common/logging.h" #include "GLcommon/GLLibrary.h" #include #include #include #include #include #include #include #include #include #include #include #include #define IS_TRUE(a) \ do { if (!(a)) return NULL; } while (0) #define EXIT_IF_FALSE(a) \ do { if (!(a)) return; } while (0) #define DEBUG 0 #if DEBUG #define D(...) fprintf(stderr, __VA_ARGS__) #else #define D(...) ((void)0) #endif #define WGL_ERR(...) do { \ fprintf(stderr, __VA_ARGS__); \ GL_LOG(__VA_ARGS__); \ } while(0) \ // TODO: Replace with latency tracker. #define PROFILE_SLOW(tag) namespace { using android::base::SharedLibrary; typedef GlLibrary::GlFunctionPointer GlFunctionPointer; // Returns true if an extension is include in a given extension list. // |extension| is an GL extension name. // |extensionList| is a space-separated list of supported extension. // Returns true if the extension is supported, false otherwise. bool supportsExtension(const char* extension, const char* extensionList) { size_t extensionLen = ::strlen(extension); const char* list = extensionList; for (;;) { const char* p = const_cast(::strstr(list, extension)); if (!p) { return false; } // Check that the extension appears as a single word in the list // i.e. that it is wrapped by either spaces or the start/end of // the list. if ((p == extensionList || p[-1] == ' ') && (p[extensionLen] == '\0' || p[extensionLen] == ' ')) { return true; } // otherwise, skip over the current position to find something else. p += extensionLen; } } ///// ///// W G L D I S P A T C H T A B L E S ///// // A technical note to explain what is going here, trust me, it's important. // // I. Library-dependent symbol resolution: // --------------------------------------- // // The code here can deal with two kinds of OpenGL Windows libraries: the // system-provided opengl32.dll, or alternate software renderers like Mesa // (e.g. mesa_opengl32.dll). // // When using the system library, pixel-format related functions, like // SetPixelFormat(), are provided by gdi32.dll, and _not_ opengl32.dll // (even though they are documented as part of the Windows GL API). // // These functions must _not_ be used when using alternative renderers. // Instead, these provide _undocumented_ alternatives with the 'wgl' prefix, // as in wglSetPixelFormat(), wglDescribePixelFormat(), etc... which // implement the same calling conventions. // // For more details about this, see 5.190 of the Windows OpenGL FAQ at: // https://www.opengl.org/archives/resources/faq/technical/mswindows.htm // // Another good source of information on this topic: // http://stackoverflow.com/questions/20645706/why-are-functions-duplicated-between-opengl32-dll-and-gdi32-dll // // In practice, it means that the code here should resolve 'naked' symbols // (e.g. 'GetPixelFormat()) when using the system library, and 'prefixed' ones // (e.g. 'wglGetPixelFormat()) otherwise. // // List of WGL functions of interest to probe with GetProcAddress() #define LIST_WGL_FUNCTIONS(X) \ X(HGLRC, wglCreateContext, (HDC hdc)) \ X(BOOL, wglDeleteContext, (HGLRC hglrc)) \ X(BOOL, wglMakeCurrent, (HDC hdc, HGLRC hglrc)) \ X(BOOL, wglShareLists, (HGLRC hglrc1, HGLRC hglrc2)) \ X(HGLRC, wglGetCurrentContext, (void)) \ X(HDC, wglGetCurrentDC, (void)) \ X(GlFunctionPointer, wglGetProcAddress, (const char* functionName)) \ // List of WGL functions exported by GDI32 that must be used when using // the system's opengl32.dll only, i.e. not with a software renderer like // Mesa. For more information, see 5.190 at: // And also: #define LIST_GDI32_FUNCTIONS(X) \ X(int, ChoosePixelFormat, (HDC hdc, const PIXELFORMATDESCRIPTOR* ppfd)) \ X(BOOL, SetPixelFormat, (HDC hdc, int iPixelFormat, const PIXELFORMATDESCRIPTOR* pfd)) \ X(int, GetPixelFormat, (HDC hdc)) \ X(int, DescribePixelFormat, (HDC hdc, int iPixelFormat, UINT nbytes, LPPIXELFORMATDESCRIPTOR ppfd)) \ X(BOOL, SwapBuffers, (HDC hdc)) \ // Declare a structure containing pointers to all functions listed above, // and a way to initialize them. struct WglBaseDispatch { // declare all function pointers, followed by a dummy member used // to terminate the constructor's initialization list properly. #define DECLARE_WGL_POINTER(return_type, function_name, signature) \ return_type (GL_APIENTRY* function_name) signature; LIST_WGL_FUNCTIONS(DECLARE_WGL_POINTER) LIST_GDI32_FUNCTIONS(DECLARE_WGL_POINTER) SharedLibrary* mLib = nullptr; bool mIsSystemLib = false; // Default Constructor WglBaseDispatch() : #define INIT_WGL_POINTER(return_type, function_name, signature) \ function_name(), LIST_WGL_FUNCTIONS(INIT_WGL_POINTER) LIST_GDI32_FUNCTIONS(INIT_WGL_POINTER) mIsSystemLib(false) {} // Copy constructor WglBaseDispatch(const WglBaseDispatch& other) : #define COPY_WGL_POINTER(return_type, function_name, signature) \ function_name(other.function_name), LIST_WGL_FUNCTIONS(COPY_WGL_POINTER) LIST_GDI32_FUNCTIONS(COPY_WGL_POINTER) mLib(other.mLib), mIsSystemLib(other.mIsSystemLib) {} // Initialize the dispatch table from shared library |glLib|, which // must point to either the system or non-system opengl32.dll // implementation. If |systemLib| is true, this considers the library // to be the system one, and will try to find the GDI32 functions // like GetPixelFormat() directly. If |systemLib| is false, this will // try to load the wglXXX variants (e.g. for Mesa). See technical note // above for details. // Returns true on success, false otherwise (i.e. if one of the // required symbols could not be loaded). bool init(SharedLibrary* glLib, bool systemLib) { bool result = true; mLib = glLib; mIsSystemLib = systemLib; #define LOAD_WGL_POINTER(return_type, function_name, signature) \ this->function_name = reinterpret_cast< \ return_type (GL_APIENTRY*) signature>( \ glLib->findSymbol(#function_name)); \ if (!this->function_name) { \ WGL_ERR("%s: Could not find %s in GL library\n", __FUNCTION__, \ #function_name); \ result = false; \ } #define LOAD_WGL_GDI32_POINTER(return_type, function_name, signature) \ this->function_name = reinterpret_cast< \ return_type (GL_APIENTRY*) signature>( \ GetProcAddress(gdi32, #function_name)); \ if (!this->function_name) { \ WGL_ERR("%s: Could not find %s in GDI32 library\n", __FUNCTION__, \ #function_name); \ result = false; \ } #define LOAD_WGL_INNER_POINTER(return_type, function_name, signature) \ this->function_name = reinterpret_cast< \ return_type (GL_APIENTRY*) signature>( \ glLib->findSymbol("wgl" #function_name)); \ if (!this->function_name) { \ WGL_ERR("%s: Could not find %s in GL library\n", __FUNCTION__, \ "wgl" #function_name); \ result = false; \ } LIST_WGL_FUNCTIONS(LOAD_WGL_POINTER) if (systemLib) { HMODULE gdi32 = GetModuleHandleA("gdi32.dll"); LIST_GDI32_FUNCTIONS(LOAD_WGL_GDI32_POINTER) } else { LIST_GDI32_FUNCTIONS(LOAD_WGL_INNER_POINTER) } return result; } // Find a function, using wglGetProcAddress() first, and if that doesn't // work, SharedLibrary::findSymbol(). // |functionName| is the function name. // |glLib| is the GL library to probe with findSymbol() is needed. GlFunctionPointer findFunction(const char* functionName) const { GlFunctionPointer result = this->wglGetProcAddress(functionName); if (!result && mLib) { result = mLib->findSymbol(functionName); } return result; } }; // Used internally by createDummyWindow(). LRESULT CALLBACK dummyWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { return DefWindowProcA(hwnd, uMsg, wParam, lParam); } // Create a new dummy window, and return its handle. // Note that the window is 1x1 pixels and not visible, but // it can be used to create a device context and associated // OpenGL rendering context. Return NULL on failure. HWND createDummyWindow() { WNDCLASSA wcx; // wcx.cbSize = sizeof(wcx); // size of structure wcx.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; // redraw if size changes wcx.lpfnWndProc = dummyWndProc; // points to window procedure wcx.cbClsExtra = 0; // no extra class memory wcx.cbWndExtra = sizeof(void*); // save extra window memory, to store VasWindow instance wcx.hInstance = NULL; // handle to instance wcx.hIcon = NULL; // predefined app. icon wcx.hCursor = NULL; wcx.hbrBackground = NULL; // no background brush wcx.lpszMenuName = NULL; // name of menu resource wcx.lpszClassName = "DummyWin"; // name of window class RegisterClassA(&wcx); HWND hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, "DummyWin", "Dummy", WS_POPUP, 0, 0, 1, 1, NULL, NULL, 0,0); return hwnd; } // List of functions defined by the WGL_ARB_extensions_string extension. #define LIST_extensions_string_FUNCTIONS(X) \ X(const char*, wglGetExtensionsString, (HDC hdc)) // List of functions defined by the WGL_ARB_pixel_format extension. #define LIST_pixel_format_FUNCTIONS(X) \ X(BOOL, wglGetPixelFormatAttribiv, (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int* piAttributes, int* piValues)) \ X(BOOL, wglGetPixelFormatAttribfv, (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int* piAttributes, FLOAT* pfValues)) \ X(BOOL, wglChoosePixelFormat, (HDC, const int* piAttribList, const FLOAT* pfAttribList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats)) \ // List of functions defined by the WGL_ARB_make_current_read extension. #define LIST_make_current_read_FUNCTIONS(X) \ X(BOOL, wglMakeContextCurrent, (HDC hDrawDC, HDC hReadDC, HGLRC hglrc)) \ X(HDC, wglGetCurrentReadDC, (void)) \ // List of functions defined by the WGL_ARB_pbuffer extension. #define LIST_pbuffer_FUNCTIONS(X) \ X(HPBUFFERARB, wglCreatePbuffer, (HDC hdc, int iPixelFormat, int iWidth, int iHeight, const int* piAttribList)) \ X(HDC, wglGetPbufferDC, (HPBUFFERARB hPbuffer)) \ X(int, wglReleasePbufferDC, (HPBUFFERARB hPbuffer, HDC hdc)) \ X(BOOL, wglDestroyPbuffer, (HPBUFFERARB hPbuffer)) \ // List of functions defined by WGL_ARB_create_context #define LIST_create_context_FUNCTIONS(X) \ X(HGLRC, wglCreateContextAttribs, (HDC hDC, HGLRC hshareContext, const int *attribList)) \ // List of functions define by WGL_EXT_swap_control #define LIST_swap_control_FUNCTIONS(X) \ X(void, wglSwapInterval, (int)) \ X(int, wglGetSwapInterval, (void)) \ #define LIST_WGL_EXTENSIONS_FUNCTIONS(X) \ LIST_pixel_format_FUNCTIONS(X) \ LIST_make_current_read_FUNCTIONS(X) \ LIST_pbuffer_FUNCTIONS(X) \ LIST_create_context_FUNCTIONS(X) \ LIST_swap_control_FUNCTIONS(X) \ // A structure used to hold pointers to WGL extension functions. struct WglExtensionsDispatch : public WglBaseDispatch { public: LIST_WGL_EXTENSIONS_FUNCTIONS(DECLARE_WGL_POINTER) int dummy; // Default constructor explicit WglExtensionsDispatch(const WglBaseDispatch& baseDispatch) : WglBaseDispatch(baseDispatch), LIST_WGL_EXTENSIONS_FUNCTIONS(INIT_WGL_POINTER) dummy(0) {} // Initialization bool init(HDC hdc) { // Base initialization happens first. bool result = WglBaseDispatch::init(mLib, mIsSystemLib); if (!result) { return false; } // Find the list of extensions. typedef const char* (GL_APIENTRY* GetExtensionsStringFunc)(HDC hdc); const char* extensionList = nullptr; GetExtensionsStringFunc wglGetExtensionsString = reinterpret_cast( this->findFunction("wglGetExtensionsStringARB")); bool foundArb = wglGetExtensionsString != nullptr; if (wglGetExtensionsString) { extensionList = wglGetExtensionsString(hdc); } bool extensionListArbNull = extensionList == nullptr; bool extensionListArbEmpty = !extensionListArbNull && !strcmp(extensionList, ""); bool foundExt = false; bool extensionListExtNull = false; bool extensionListExtEmpty = false; // wglGetExtensionsStringARB failed, try wglGetExtensionsStringEXT. if (!extensionList || !strcmp(extensionList, "")) { wglGetExtensionsString = reinterpret_cast( this->findFunction("wglGetExtensionsStringEXT")); foundExt = wglGetExtensionsString != nullptr; if (wglGetExtensionsString) { extensionList = wglGetExtensionsString(hdc); } } extensionListExtNull = extensionList == nullptr; extensionListExtEmpty = !extensionListExtNull && !strcmp(extensionList, ""); // Both failed, suicide. if (!extensionList || !strcmp(extensionList, "")) { bool isRemoteSession = GetSystemMetrics(SM_REMOTESESSION); WGL_ERR( "%s: Could not find wglGetExtensionsString! " "arbFound %d listarbNull/empty %d %d " "extFound %d extNull/empty %d %d remote %d\n", __FUNCTION__, foundArb, extensionListArbNull, extensionListArbEmpty, foundExt, extensionListExtNull, extensionListExtEmpty, isRemoteSession); return false; } // Load each extension individually. #define LOAD_WGL_EXTENSION_FUNCTION(return_type, function_name, signature) \ this->function_name = reinterpret_cast< \ return_type (GL_APIENTRY*) signature>( \ this->findFunction(#function_name "ARB")); \ if (!this->function_name) { \ this->function_name = reinterpret_cast< \ return_type (GL_APIENTRY*) signature>( \ this->findFunction(#function_name "EXT")); \ } \ if (!this->function_name) { \ WGL_ERR("ERROR: %s: Missing extension function %s\n", __FUNCTION__, \ #function_name); \ result = false; \ } #define LOAD_WGL_EXTENSION(extension) \ if (supportsExtension("WGL_ARB_" #extension, extensionList) || \ supportsExtension("WGL_EXT_" #extension, extensionList)) { \ LIST_##extension##_FUNCTIONS(LOAD_WGL_EXTENSION_FUNCTION) \ } else { \ WGL_ERR("WARNING: %s: Missing WGL extension %s\n", __FUNCTION__, #extension); \ } LOAD_WGL_EXTENSION(pixel_format) LOAD_WGL_EXTENSION(make_current_read) LOAD_WGL_EXTENSION(pbuffer) LOAD_WGL_EXTENSION(create_context) LOAD_WGL_EXTENSION(swap_control) // Done. return result; } private: WglExtensionsDispatch(); // no default constructor. }; const WglExtensionsDispatch* initExtensionsDispatch( const WglBaseDispatch* dispatch) { HWND hwnd = createDummyWindow(); HDC hdc = GetDC(hwnd); if (!hwnd || !hdc){ int err = GetLastError(); WGL_ERR("error while getting DC: 0x%x\n", err); return NULL; } PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd 1, // version number PFD_DRAW_TO_WINDOW | // support window PFD_SUPPORT_OPENGL | // support OpenGL PFD_DOUBLEBUFFER, // double buffered PFD_TYPE_RGBA, // RGBA type 32, // 32-bit color depth 0, 0, 0, 0, 0, 0, // color bits ignored 0, // no alpha buffer 0, // shift bit ignored 0, // no accumulation buffer 0, 0, 0, 0, // accum bits ignored 24, // 24-bit z-buffer 0, // no stencil buffer 0, // no auxiliary buffer PFD_MAIN_PLANE, // main layer 0, // reserved 0, 0, 0 // layer masks ignored }; int iPixelFormat = dispatch->ChoosePixelFormat(hdc, &pfd); if (iPixelFormat <= 0) { int err = GetLastError(); WGL_ERR("error while choosing pixel format 0x%x\n", err); return NULL; } if (!dispatch->SetPixelFormat(hdc, iPixelFormat, &pfd)) { int err = GetLastError(); WGL_ERR("error while setting pixel format 0x%x\n", err); return NULL; } int err; HGLRC ctx = dispatch->wglCreateContext(hdc); if (!ctx) { err = GetLastError(); WGL_ERR("error while creating dummy context: 0x%x\n", err); } if (!dispatch->wglMakeCurrent(hdc, ctx)) { err = GetLastError(); WGL_ERR("error while making dummy context current: 0x%x\n", err); } WglExtensionsDispatch* result = new WglExtensionsDispatch(*dispatch); result->init(hdc); dispatch->wglMakeCurrent(hdc, NULL); dispatch->wglDeleteContext(ctx); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return result; } class WinPixelFormat : public EglOS::PixelFormat { public: WinPixelFormat(const PIXELFORMATDESCRIPTOR* desc, int configId) : mDesc(*desc), mConfigId(configId) {} EglOS::PixelFormat* clone() { return new WinPixelFormat(&mDesc, mConfigId); } const PIXELFORMATDESCRIPTOR* desc() const { return &mDesc; } const int configId() const { return mConfigId; } static const WinPixelFormat* from(const EglOS::PixelFormat* f) { return static_cast(f); } private: WinPixelFormat(const WinPixelFormat& other) = delete; PIXELFORMATDESCRIPTOR mDesc = {}; int mConfigId = 0; }; class WinSurface : public EglOS::Surface { public: explicit WinSurface(HWND wnd, const WglExtensionsDispatch* dispatch) : Surface(WINDOW), m_hwnd(wnd), m_hdc(GetDC(wnd)), m_dispatch(dispatch) {} explicit WinSurface(HPBUFFERARB pb, const EglOS::PixelFormat* pixelFormat, const WglExtensionsDispatch* dispatch) : Surface(PBUFFER), m_pb(pb), m_pixelFormat(pixelFormat), m_dispatch(dispatch) { refreshDC(); } bool releaseDC() { if (m_dcReleased) return true; bool res = false; if (m_dispatch->wglReleasePbufferDC) { res = m_dispatch->wglReleasePbufferDC(m_pb, m_hdc); m_hdc = 0; m_dcReleased = true; } return res; } bool refreshDC() { if (!m_dcReleased) releaseDC(); if (m_dispatch->wglGetPbufferDC) { m_hdc = m_dispatch->wglGetPbufferDC(m_pb); } if (!m_hdc) return false; m_dcReleased = false; return true; } ~WinSurface() { if (type() == WINDOW) { ReleaseDC(m_hwnd, m_hdc); } } void onMakeCurrent() { if (m_everMadeCurrent) return; if (m_dispatch->wglSwapInterval) { m_dispatch->wglSwapInterval(0); } m_everMadeCurrent = true; } HWND getHwnd() const { return m_hwnd; } HDC getDC() const { return m_hdc; } HPBUFFERARB getPbuffer() const { return m_pb; } const EglOS::PixelFormat* getPixelFormat() const { return m_pixelFormat; } static WinSurface* from(EglOS::Surface* s) { return static_cast(s); } private: bool m_everMadeCurrent = false; bool m_dcReleased = true; HWND m_hwnd = nullptr; HPBUFFERARB m_pb = nullptr; HDC m_hdc = nullptr; const EglOS::PixelFormat* m_pixelFormat = nullptr; const WglExtensionsDispatch* m_dispatch = nullptr; }; static android::base::StaticLock sGlobalLock; class WinContext : public EglOS::Context { public: explicit WinContext(const WglExtensionsDispatch* dispatch, HGLRC ctx) : mDispatch(dispatch), mCtx(ctx) {} virtual ~WinContext() { android::base::AutoLock lock(sGlobalLock); if (!mDispatch->wglDeleteContext(mCtx)) { WGL_ERR("error deleting WGL context! error 0x%x\n", (unsigned)GetLastError()); } } HGLRC context() const { return mCtx; } static HGLRC from(const EglOS::Context* c) { if (!c) return nullptr; return static_cast(c)->context(); } private: const WglExtensionsDispatch* mDispatch = nullptr; HGLRC mCtx = nullptr; }; // A helper class used to deal with a vexing limitation of the WGL API. // The documentation for SetPixelFormat() states the following: // // -- If hdc references a window, calling the SetPixelFormat function also // -- changes the pixel format of the window. Setting the pixel format of a // -- window more than once [...] is not allowed. An application can only set // -- the pixel format of a window one time. Once a window's pixel format is // -- set, it cannot be changed. // -- // -- You should select a pixel format in the device context before calling // -- the wglCreateContext function. The wglCreateContext function creates a // -- rendering context for drawing on the device in the selected pixel format // -- of the device context. // // In other words, creating a GL context requires having a unique window and // device context for the corresponding EGLConfig. // // This code deals with this by implementing the followin scheme: // // - For each unique PixelFormat ID (a.k.a. EGLConfig number), provide a way // to create a new hidden 1x1 window, and corresponding HDC. // // - Implement a simple thread-local mapping from PixelFormat IDs to // (HWND, HDC) pairs that are created on demand. // // WinGlobals is the class used to implement this scheme. Usage is the // following: // // - Create a single global instance, passing a WglBaseDispatch pointer // which is required to call its SetPixelFormat() method. // // - Call getDefaultDummyDC() to retrieve a thread-local device context that // can be used to query / probe the list of available pixel formats for the // host window, but not perform rendering. // // - Call getDummyDC() to retrieve a thread-local device context that can be // used to create WGL context objects to render into a specific pixel // format. // // - These devic contexts are thread-local, i.e. they are automatically // reclaimed on thread exit. This also means that the caller should not // call either ReleaseDC() or DeleteDC() on them. // class WinGlobals { public: // Constructor. |dispatch| will be used to call the right version of // ::SetPixelFormat() depending on GPU emulation configuration. See // technical notes above for details. explicit WinGlobals(const WglBaseDispatch* dispatch) : mDispatch(dispatch) {} // Return a thread-local device context that can be used to list // available pixel formats for the host window. The caller cannot use // this context for drawing though. The context is owned // by this instance and will be automatically reclaimed on thread exit. HDC getDefaultDummyDC() { return getInternalDC(nullptr); } HDC getDefaultNontrivialDC() { return mNontrivialDC; } void setNontrivialDC(const WinPixelFormat* format) { mNontrivialDC = getInternalDC(format); } // Return a thread-local device context associated with a specific // pixel format. The result is owned by this instance, and is automatically // reclaimed on thread exit. Don't try to call ReleaseDC() on it. HDC getDummyDC(const WinPixelFormat* format) { return getInternalDC(format); } private: // ConfigDC holds a (HWND,HDC) pair to be associated with a given // pixel format ID. This serves as the value type for ConfigMap // declared below. Implemented as a movable type without copy-operations. class ConfigDC { public: // Constructor. This creates a new dummy 1x1 invisible window and // an associated device context. If |format| is not nullptr, then // calls |dispatch->SetPixelFormat()| on the resulting context to // set the window's pixel format. ConfigDC(const WinPixelFormat* format, const WglBaseDispatch* dispatch) { mWindow = createDummyWindow(); if (mWindow) { mDeviceContext = GetDC(mWindow); if (format) { dispatch->SetPixelFormat(mDeviceContext, format->configId(), format->desc()); } } } // Destructor. ~ConfigDC() { if (mWindow) { ReleaseDC(mWindow, mDeviceContext); DestroyWindow(mWindow); mWindow = nullptr; } } // Supports moves - this disables auto-generated copy-constructors. ConfigDC(ConfigDC&& other) : mWindow(other.mWindow), mDeviceContext(other.mDeviceContext) { other.mWindow = nullptr; } ConfigDC& operator=(ConfigDC&& other) { mWindow = other.mWindow; mDeviceContext = other.mDeviceContext; other.mWindow = nullptr; return *this; } // Return device context for this instance. HDC dc() const { return mDeviceContext; } private: HWND mWindow = nullptr; HDC mDeviceContext = nullptr; }; // Convenience type alias for mapping pixel format IDs, a.k.a. EGLConfig // IDs, to ConfigDC instances. using ConfigMap = std::unordered_map; // Called when a thread terminates to delete the thread-local map // that associates pixel format IDs with ConfigDC instances. static void onThreadTermination(void* opaque) { auto map = reinterpret_cast(opaque); delete map; } // Helper function used by getDefaultDummyDC() and getDummyDC(). // // If |format| is nullptr, return a thread-local device context that can // be used to list all available pixel formats for the host window. // // If |format| is not nullptr, then return a thread-local device context // associated with the corresponding pixel format. // // Both cases will lazily create a 1x1 invisible dummy window which // the device is connected to. All objects are automatically destroyed // on thread exit. Return nullptr on failure. HDC getInternalDC(const WinPixelFormat* format) { int formatId = format ? format->configId() : 0; static thread_local ConfigMap configMap; auto it = configMap.find(formatId); if (it != configMap.end()) { return it->second.dc(); } ConfigDC newValue(format, mDispatch); HDC result = newValue.dc(); configMap.emplace(formatId, std::move(newValue)); return result; } const WglBaseDispatch* mDispatch = nullptr; HDC mNontrivialDC = nullptr; }; bool initPixelFormat(HDC dc, const WglExtensionsDispatch* dispatch) { if (dispatch->wglChoosePixelFormat) { unsigned int numpf; int iPixelFormat; int i0 = 0; float f0 = 0.0f; return dispatch->wglChoosePixelFormat( dc, &i0, &f0, 1, &iPixelFormat, &numpf); } else { PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd 1, // version number PFD_DRAW_TO_WINDOW | // support window PFD_SUPPORT_OPENGL | // support OpenGL PFD_DOUBLEBUFFER, // double buffered PFD_TYPE_RGBA, // RGBA type 32, // 32-bit color depth 0, 0, 0, 0, 0, 0, // color bits ignored 0, // no alpha buffer 0, // shift bit ignored 0, // no accumulation buffer 0, 0, 0, 0, // accum bits ignored 24, // 24-bit z-buffer 0, // no stencil buffer 0, // no auxiliary buffer PFD_MAIN_PLANE, // main layer 0, // reserved 0, 0, 0 // layer masks ignored }; return dispatch->ChoosePixelFormat(dc, &pfd); } } void pixelFormatToConfig(WinGlobals* globals, const WglExtensionsDispatch* dispatch, int renderableType, const PIXELFORMATDESCRIPTOR* frmt, int index, EglOS::AddConfigCallback* addConfigFunc, void* addConfigOpaque) { EglOS::ConfigInfo info; memset(&info, 0, sizeof(info)); if (frmt->iPixelType != PFD_TYPE_RGBA) { D("%s: Not an RGBA type!\n", __FUNCTION__); return; // other formats are not supported yet } if (!(frmt->dwFlags & PFD_SUPPORT_OPENGL)) { D("%s: No OpenGL support\n", __FUNCTION__); return; } // NOTE: Software renderers don't always support double-buffering. if (dispatch->mIsSystemLib && !(frmt->dwFlags & PFD_DOUBLEBUFFER)) { D("%s: No double-buffer support\n", __FUNCTION__); return; } if ((frmt->dwFlags & (PFD_GENERIC_FORMAT | PFD_NEED_PALETTE)) != 0) { //discard generic pixel formats as well as pallete pixel formats D("%s: Generic format or needs palette\n", __FUNCTION__); return; } if (!dispatch->wglGetPixelFormatAttribiv) { D("%s: Missing wglGetPixelFormatAttribiv\n", __FUNCTION__); return; } GLint window = 0, pbuffer = 0; HDC dpy = globals->getDefaultDummyDC(); if (dispatch->mIsSystemLib) { int windowAttrib = WGL_DRAW_TO_WINDOW_ARB; EXIT_IF_FALSE(dispatch->wglGetPixelFormatAttribiv( dpy, index, 0, 1, &windowAttrib, &window)); } int pbufferAttrib = WGL_DRAW_TO_PBUFFER_ARB; EXIT_IF_FALSE(dispatch->wglGetPixelFormatAttribiv( dpy, index, 0, 1, &pbufferAttrib, &pbuffer)); info.surface_type = 0; if (window) { info.surface_type |= EGL_WINDOW_BIT; } if (pbuffer) { info.surface_type |= EGL_PBUFFER_BIT; } if (!info.surface_type) { D("%s: Missing surface type\n", __FUNCTION__); return; } //default values info.native_visual_id = 0; info.native_visual_type = EGL_NONE; info.caveat = EGL_NONE; info.native_renderable = EGL_FALSE; info.renderable_type = renderableType; info.max_pbuffer_width = PBUFFER_MAX_WIDTH; info.max_pbuffer_height = PBUFFER_MAX_HEIGHT; info.max_pbuffer_size = PBUFFER_MAX_PIXELS; info.samples_per_pixel = 0; info.frame_buffer_level = 0; GLint transparent; int transparentAttrib = WGL_TRANSPARENT_ARB; EXIT_IF_FALSE(dispatch->wglGetPixelFormatAttribiv( dpy, index, 0, 1, &transparentAttrib, &transparent)); if (transparent) { info.transparent_type = EGL_TRANSPARENT_RGB; int transparentRedAttrib = WGL_TRANSPARENT_RED_VALUE_ARB; EXIT_IF_FALSE(dispatch->wglGetPixelFormatAttribiv( dpy, index, 0, 1, &transparentRedAttrib, &info.trans_red_val)); int transparentGreenAttrib = WGL_TRANSPARENT_GREEN_VALUE_ARB; EXIT_IF_FALSE(dispatch->wglGetPixelFormatAttribiv( dpy, index, 0, 1, &transparentGreenAttrib, &info.trans_green_val)); int transparentBlueAttrib = WGL_TRANSPARENT_RED_VALUE_ARB; EXIT_IF_FALSE(dispatch->wglGetPixelFormatAttribiv( dpy,index, 0, 1, &transparentBlueAttrib, &info.trans_blue_val)); } else { info.transparent_type = EGL_NONE; } info.red_size = frmt->cRedBits; info.green_size = frmt->cGreenBits; info.blue_size = frmt->cBlueBits; info.alpha_size = frmt->cAlphaBits; info.depth_size = frmt->cDepthBits; info.stencil_size = frmt->cStencilBits; info.frmt = new WinPixelFormat(frmt, index); if (!globals->getDefaultNontrivialDC() && info.red_size >= 8) { globals->setNontrivialDC(WinPixelFormat::from(info.frmt)); } (*addConfigFunc)(addConfigOpaque, &info); } class WglDisplay : public EglOS::Display { public: WglDisplay(const WglExtensionsDispatch* dispatch, WinGlobals* globals) : mDispatch(dispatch), mGlobals(globals) {} virtual EglOS::GlesVersion getMaxGlesVersion() { if (!mCoreProfileSupported) { return EglOS::GlesVersion::ES2; } return EglOS::calcMaxESVersionFromCoreVersion( mCoreMajorVersion, mCoreMinorVersion); } virtual void queryConfigs(int renderableType, EglOS::AddConfigCallback addConfigFunc, void* addConfigOpaque) { HDC dpy = mGlobals->getDefaultDummyDC(); // wglChoosePixelFormat() needs to be called at least once, // i.e. it seems that the driver needs to initialize itself. // Do it here during initialization. initPixelFormat(dpy, mDispatch); // Quering number of formats PIXELFORMATDESCRIPTOR pfd; int maxFormat = mDispatch->DescribePixelFormat( dpy, 1, sizeof(PIXELFORMATDESCRIPTOR), &pfd); if (0 == maxFormat) { WGL_ERR("No pixel formats found from wglDescribePixelFormat! " "error: 0x%x\n", static_cast(GetLastError())); } // Inserting rest of formats. Try to map each one to an EGL Config. for (int configId = 1; configId <= maxFormat; configId++) { mDispatch->DescribePixelFormat( dpy, configId, sizeof(PIXELFORMATDESCRIPTOR), &pfd); pixelFormatToConfig( mGlobals, mDispatch, renderableType, &pfd, configId, addConfigFunc, addConfigOpaque); } queryCoreProfileSupport(); if (mDispatch->wglSwapInterval) { mDispatch->wglSwapInterval(0); // use guest SF / HWC instead } } virtual bool isValidNativeWin(EglOS::Surface* win) { if (!win) { return false; } else { return isValidNativeWin(WinSurface::from(win)->getHwnd()); } } virtual bool isValidNativeWin(EGLNativeWindowType win) { return IsWindow(win); } virtual bool checkWindowPixelFormatMatch( EGLNativeWindowType win, const EglOS::PixelFormat* pixelFormat, unsigned int* width, unsigned int* height) { RECT r; if (!GetClientRect(win, &r)) { return false; } *width = r.right - r.left; *height = r.bottom - r.top; HDC dc = GetDC(win); const WinPixelFormat* format = WinPixelFormat::from(pixelFormat); bool ret = mDispatch->SetPixelFormat(dc, format->configId(), format->desc()); ReleaseDC(win, dc); return ret; } virtual std::shared_ptr createContext( EGLint profileMask, const EglOS::PixelFormat* pixelFormat, EglOS::Context* sharedContext) { android::base::AutoLock lock(sGlobalLock); const WinPixelFormat* format = WinPixelFormat::from(pixelFormat); HDC dpy = mGlobals->getDummyDC(format); if (!dpy) { return nullptr; } bool useCoreProfile = mCoreProfileSupported && (profileMask & EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR); HGLRC ctx; if (useCoreProfile) { ctx = mDispatch->wglCreateContextAttribs( dpy, WinContext::from(sharedContext), mCoreProfileCtxAttribs); } else { ctx = mDispatch->wglCreateContext(dpy); if (ctx && sharedContext) { if (!mDispatch->wglShareLists(WinContext::from(sharedContext), ctx)) { mDispatch->wglDeleteContext(ctx); return NULL; } } } return std::make_shared(mDispatch, ctx); } virtual EglOS::Surface* createPbufferSurface( const EglOS::PixelFormat* pixelFormat, const EglOS::PbufferInfo* info) { android::base::AutoLock lock(sGlobalLock); (void)info; bool needPrime = false; auto& freeElts = mFreePbufs[pixelFormat]; if (freeElts.empty()) { needPrime = true; } if (needPrime) { // Policy here is based on the behavior of Android UI when opening // and closing lots of apps, and to avoid frequent pauses to create // pbuffers. We start by priming with 8 pbuffers, and then double // the current set of live pbuffers each time. This will then // require only a few primings (up to ~32 pbuffers) to make it // unnecessary to create or delete a pbuffer ever again after that. // Creating many pbuffers in a batch is faster than interrupting // the process at various times to create them one at a time. PROFILE_SLOW("createPbufferSurface (slow path)"); int toCreate = std::max((int)mLivePbufs[pixelFormat].size(), kPbufPrimingCount); for (int i = 0; i < toCreate; i++) { freeElts.push_back(createPbufferSurfaceImpl(pixelFormat)); } } PROFILE_SLOW("createPbufferSurface (fast path)"); EglOS::Surface* surf = freeElts.back(); WinSurface* winSurface = WinSurface::from(surf); if (!winSurface->refreshDC()) { // TODO: Figure out why some Intel GPUs can hang flakily if // we just destroy the pbuffer here. mDeletePbufs.push_back(surf); surf = createPbufferSurfaceImpl(pixelFormat); } freeElts.pop_back(); mLivePbufs[pixelFormat].insert(surf); return surf; } virtual bool releasePbuffer(EglOS::Surface* pb) { android::base::AutoLock lock(sGlobalLock); if (!pb) return false; WinSurface* winpb = WinSurface::from(pb); if (!winpb->releaseDC()) return false; const EglOS::PixelFormat* pixelFormat = winpb->getPixelFormat(); auto& frees = mFreePbufs[pixelFormat]; frees.push_back(pb); mLivePbufs[pixelFormat].erase(pb); for (auto surf : mDeletePbufs) { WinSurface* winSurface = WinSurface::from(surf); mDispatch->wglDestroyPbuffer(winSurface->getPbuffer()); delete winSurface; } mDeletePbufs.clear(); return true; } virtual bool makeCurrent(EglOS::Surface* read, EglOS::Surface* draw, EglOS::Context* context) { android::base::AutoLock lock(sGlobalLock); WinSurface* readWinSurface = WinSurface::from(read); WinSurface* drawWinSurface = WinSurface::from(draw); HDC hdcRead = read ? readWinSurface->getDC() : NULL; HDC hdcDraw = draw ? drawWinSurface->getDC() : NULL; HGLRC hdcContext = context ? WinContext::from(context) : 0; const WglExtensionsDispatch* dispatch = mDispatch; bool ret = false; if (hdcRead == hdcDraw){ // The following loop is a work-around for a problem when // occasionally the rendering is incorrect after hibernating and // waking up windows. // wglMakeCurrent will sometimes fail for a short period of time // in such situation. // // For a stricter test, in addition to checking the return value, we // might also want to check its error code. On my computer in such // situation GetLastError() returns 0 (which is documented as // success code). This is not a documented behaviour and is // unreliable. But in case one needs to use it, here is the code: // // while (!(isSuccess = dispatch->wglMakeCurrent(hdcDraw, hdcContext)) // && GetLastError()==0) Sleep(16); // int count = 100; while (!dispatch->wglMakeCurrent(hdcDraw, hdcContext) && --count > 0 && !GetLastError()) { Sleep(16); } if (count <= 0) { D("Error: wglMakeCurrent() failed, error %d\n", (int)GetLastError()); return false; } ret = true; } else if (!dispatch->wglMakeContextCurrent) { return false; } else { ret = dispatch->wglMakeContextCurrent( hdcDraw, hdcRead, hdcContext); } if (ret) { if (readWinSurface) readWinSurface->onMakeCurrent(); if (drawWinSurface) drawWinSurface->onMakeCurrent(); } return ret; } virtual void swapBuffers(EglOS::Surface* srfc) { android::base::AutoLock lock(sGlobalLock); if (srfc && !mDispatch->SwapBuffers(WinSurface::from(srfc)->getDC())) { GetLastError(); } } private: // Returns the highest level of OpenGL core profile support in // this WGL implementation. void queryCoreProfileSupport() { mCoreProfileSupported = false; if (!mDispatch->wglCreateContextAttribs) { WGL_ERR("OpenGL Core Profile not supported.\n"); // Not supported, don't even try. return; } // Ascending index order of context attribs : // decreasing GL major/minor version HGLRC testContext = nullptr; HDC dpy = mGlobals->getDefaultNontrivialDC(); for (int i = 0; i < getNumCoreProfileCtxAttribs(); i++) { const int* attribs = getCoreProfileCtxAttribs(i); testContext = mDispatch->wglCreateContextAttribs( dpy, nullptr /* no shared context */, attribs); if (testContext) { mCoreProfileSupported = true; mCoreProfileCtxAttribs = attribs; getCoreProfileCtxAttribsVersion( attribs, &mCoreMajorVersion, &mCoreMinorVersion); mDispatch->wglDeleteContext(testContext); return; } } } EglOS::Surface* createPbufferSurfaceImpl(const EglOS::PixelFormat* pixelFormat) { // we never care about width or height, since we just use // opengl fbos anyway. const WinPixelFormat* format = WinPixelFormat::from(pixelFormat); HDC dpy = mGlobals->getDummyDC(format); const WglExtensionsDispatch* dispatch = mDispatch; if (!dispatch->wglCreatePbuffer) { return NULL; } HPBUFFERARB pb = dispatch->wglCreatePbuffer(dpy, format->configId(), 1, 1, nullptr); if (!pb) { GetLastError(); return NULL; } return new WinSurface(pb, pixelFormat, dispatch); } bool mCoreProfileSupported = false; int mCoreMajorVersion = 4; int mCoreMinorVersion = 5; const int* mCoreProfileCtxAttribs = nullptr; const WglExtensionsDispatch* mDispatch = nullptr; WinGlobals* mGlobals = nullptr; std::unordered_map > mFreePbufs; std::unordered_map > mLivePbufs; std::vector mDeletePbufs; static constexpr int kPbufPrimingCount = 8; }; constexpr int WglDisplay::kPbufPrimingCount; class WglLibrary : public GlLibrary { public: WglLibrary(const WglBaseDispatch* dispatch) : mDispatch(dispatch) {} virtual GlFunctionPointer findSymbol(const char* name) { return mDispatch->findFunction(name); } private: const WglBaseDispatch* mDispatch = nullptr; }; class WinEngine : public EglOS::Engine { public: WinEngine(); ~WinEngine() { delete mDispatch; } virtual EglOS::Display* getDefaultDisplay() { return new WglDisplay(mDispatch, &mGlobals); } virtual GlLibrary* getGlLibrary() { return &mGlLib; } virtual void* eglGetProcAddress(const char*) { return 0; } virtual EglOS::Surface* createWindowSurface(EglOS::PixelFormat* cfg, EGLNativeWindowType wnd) { (void)cfg; return new WinSurface(wnd, mDispatch); } private: SharedLibrary* mLib = nullptr; const WglExtensionsDispatch* mDispatch = nullptr; WglBaseDispatch mBaseDispatch = {}; WglLibrary mGlLib; WinGlobals mGlobals; }; WinEngine::WinEngine() : mGlLib(&mBaseDispatch), mGlobals(&mBaseDispatch) { const char* kLibName = "opengl32.dll"; bool isSystemLib = true; const char* env = ::getenv("ANDROID_GL_LIB"); if (env && !strcmp(env, "mesa")) { kLibName = "mesa_opengl32.dll"; isSystemLib = false; } char error[256]; GL_LOG("%s: Trying to load %s\n", __FUNCTION__, kLibName); mLib = SharedLibrary::open(kLibName, error, sizeof(error)); if (!mLib) { WGL_ERR("ERROR: %s: Could not open %s: %s\n", __FUNCTION__, kLibName, error); exit(1); } GL_LOG("%s: Library loaded at %p\n", __FUNCTION__, mLib); mBaseDispatch.init(mLib, isSystemLib); mDispatch = initExtensionsDispatch(&mBaseDispatch); GL_LOG("%s: Dispatch initialized\n", __FUNCTION__); } static WinEngine* sHostEngine() { static WinEngine* e = new WinEngine; return e; } } // namespace // static EglOS::Engine* EglOS::Engine::getHostInstance() { return sHostEngine(); }