| /* |
| * 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 "emugl/common/lazy_instance.h" |
| #include "emugl/common/shared_library.h" |
| #include "emugl/common/thread_store.h" |
| #include "GLcommon/GLLibrary.h" |
| |
| #include "OpenglCodecCommon/ErrorLog.h" |
| |
| #include <windows.h> |
| #include <wingdi.h> |
| |
| #include <GLES/glplatform.h> |
| #include <GL/gl.h> |
| #include <GL/wglext.h> |
| |
| #include <unordered_map> |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #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 |
| |
| namespace { |
| |
| using emugl::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<const char*>(::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) { \ |
| 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) { \ |
| 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) { \ |
| 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 = GetModuleHandle("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 DefWindowProc(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() { |
| WNDCLASSEX 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 |
| wcx.hIconSm = (HICON) NULL; // small class icon |
| |
| RegisterClassEx(&wcx); |
| |
| HWND hwnd = CreateWindowEx(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_WGL_ARB_extensions_string_FUNCTIONS(X) \ |
| X(const char*, wglGetExtensionsStringARB, (HDC hdc)) |
| |
| // List of functions defined by the WGL_ARB_pixel_format extension. |
| #define LIST_WGL_ARB_pixel_format_FUNCTIONS(X) \ |
| X(BOOL, wglGetPixelFormatAttribivARB, (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int* piAttributes, int* piValues)) \ |
| X(BOOL, wglGetPixelFormatAttribfvARB, (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int* piAttributes, FLOAT* pfValues)) \ |
| X(BOOL, wglChoosePixelFormatARB, (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_WGL_ARB_make_current_read_FUNCTIONS(X) \ |
| X(BOOL, wglMakeContextCurrentARB, (HDC hDrawDC, HDC hReadDC, HGLRC hglrc)) \ |
| X(HDC, wglGetCurrentReadDCARB, (void)) \ |
| |
| // List of functions defined by the WGL_ARB_pbuffer extension. |
| #define LIST_WGL_ARB_pbuffer_FUNCTIONS(X) \ |
| X(HPBUFFERARB, wglCreatePbufferARB, (HDC hdc, int iPixelFormat, int iWidth, int iHeight, const int* piAttribList)) \ |
| X(HDC, wglGetPbufferDCARB, (HPBUFFERARB hPbuffer)) \ |
| X(int, wglReleasePbufferDCARB, (HPBUFFERARB hPbuffer, HDC hdc)) \ |
| X(BOOL, wglDestroyPbufferARB, (HPBUFFERARB hPbuffer)) \ |
| |
| #define LIST_WGL_EXTENSIONS_FUNCTIONS(X) \ |
| LIST_WGL_ARB_pixel_format_FUNCTIONS(X) \ |
| LIST_WGL_ARB_make_current_read_FUNCTIONS(X) \ |
| LIST_WGL_ARB_pbuffer_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); |
| |
| GetExtensionsStringFunc wglGetExtensionsStringARB = |
| reinterpret_cast<GetExtensionsStringFunc>( |
| this->findFunction("wglGetExtensionsStringARB")); |
| if (!wglGetExtensionsStringARB) { |
| ERR("%s: Could not find wglGetExtensionsStringARB!\n", |
| __FUNCTION__); |
| return false; |
| } |
| const char* extensionList = wglGetExtensionsStringARB(hdc); |
| if (!extensionList) { |
| extensionList = ""; |
| } |
| |
| // 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)); \ |
| if (!this->function_name) { \ |
| ERR("ERROR: %s: Missing extension function %s\n", __FUNCTION__, \ |
| #function_name); \ |
| result = false; \ |
| } |
| |
| #define LOAD_WGL_EXTENSION(extension) \ |
| if (!supportsExtension(#extension, extensionList)) { \ |
| ERR("WARNING: %s: Missing WGL extension %s\n", __FUNCTION__, #extension); \ |
| } else { \ |
| LIST_##extension##_FUNCTIONS(LOAD_WGL_EXTENSION_FUNCTION) \ |
| } |
| |
| LOAD_WGL_EXTENSION(WGL_ARB_pixel_format) |
| LOAD_WGL_EXTENSION(WGL_ARB_make_current_read) |
| LOAD_WGL_EXTENSION(WGL_ARB_pbuffer) |
| |
| // Done. |
| return result; |
| } |
| |
| private: |
| WglExtensionsDispatch(); // no default constructor. |
| }; |
| |
| const WglExtensionsDispatch* initExtensionsDispatch( |
| const WglBaseDispatch* dispatch) { |
| HWND hwnd = createDummyWindow(); |
| HDC hdc = GetDC(hwnd); |
| if (!hwnd || !hdc){ |
| fprintf(stderr,"error while getting DC\n"); |
| 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(); |
| fprintf(stderr,"error while choosing pixel format 0x%x\n", err); |
| return NULL; |
| } |
| if (!dispatch->SetPixelFormat(hdc, iPixelFormat, &pfd)) { |
| int err = GetLastError(); |
| fprintf(stderr,"error while setting pixel format 0x%x\n", err); |
| return NULL; |
| } |
| |
| int err; |
| HGLRC ctx = dispatch->wglCreateContext(hdc); |
| if (!ctx) { |
| err = GetLastError(); |
| fprintf(stderr,"error while creating dummy context %d\n", err); |
| } |
| if (!dispatch->wglMakeCurrent(hdc, ctx)) { |
| err = GetLastError(); |
| fprintf(stderr,"error while making dummy context current %d\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<const WinPixelFormat*>(f); |
| } |
| |
| private: |
| WinPixelFormat(const WinPixelFormat& other) = delete; |
| |
| PIXELFORMATDESCRIPTOR mDesc = {}; |
| int mConfigId = 0; |
| }; |
| |
| class WinSurface : public EglOS::Surface { |
| public: |
| explicit WinSurface(HWND wnd) : |
| Surface(WINDOW), |
| m_hwnd(wnd), |
| m_hdc(GetDC(wnd)) {} |
| |
| explicit WinSurface(HPBUFFERARB pb, const WglExtensionsDispatch* dispatch) : |
| Surface(PBUFFER), |
| m_pb(pb) { |
| if (dispatch->wglGetPbufferDCARB) { |
| m_hdc = dispatch->wglGetPbufferDCARB(pb); |
| } |
| } |
| |
| ~WinSurface() { |
| if (type() == WINDOW) { |
| ReleaseDC(m_hwnd, m_hdc); |
| } |
| } |
| |
| HWND getHwnd() const { return m_hwnd; } |
| HDC getDC() const { return m_hdc; } |
| HPBUFFERARB getPbuffer() const { return m_pb; } |
| |
| static WinSurface* from(EglOS::Surface* s) { |
| return static_cast<WinSurface*>(s); |
| } |
| |
| private: |
| HWND m_hwnd = nullptr; |
| HPBUFFERARB m_pb = nullptr; |
| HDC m_hdc = nullptr; |
| }; |
| |
| class WinContext : public EglOS::Context { |
| public: |
| explicit WinContext(HGLRC ctx) : mCtx(ctx) {} |
| |
| HGLRC context() const { return mCtx; } |
| |
| static HGLRC from(const EglOS::Context* c) { |
| return static_cast<const WinContext*>(c)->context(); |
| } |
| |
| private: |
| 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), mTls(onThreadTermination) {} |
| |
| // 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); |
| } |
| |
| // 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<int, ConfigDC>; |
| |
| // 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<ConfigMap*>(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; |
| auto map = reinterpret_cast<ConfigMap*>(mTls.get()); |
| if (!map) { |
| map = new ConfigMap(); |
| mTls.set(map); |
| } |
| |
| auto it = map->find(formatId); |
| if (it != map->end()) { |
| return it->second.dc(); |
| } |
| |
| ConfigDC newValue(format, mDispatch); |
| HDC result = newValue.dc(); |
| map->emplace(formatId, std::move(newValue)); |
| return result; |
| } |
| |
| const WglBaseDispatch* mDispatch = nullptr; |
| emugl::ThreadStore mTls; |
| }; |
| |
| bool initPixelFormat(HDC dc, const WglExtensionsDispatch* dispatch) { |
| if (dispatch->wglChoosePixelFormatARB) { |
| unsigned int numpf; |
| int iPixelFormat; |
| int i0 = 0; |
| float f0 = 0.0f; |
| return dispatch->wglChoosePixelFormatARB( |
| dc, &i0, &f0, 1, &iPixelFormat, &numpf); |
| } else { |
| PIXELFORMATDESCRIPTOR pfd; |
| 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->wglGetPixelFormatAttribivARB) { |
| D("%s: Missing wglGetPixelFormatAttribivARB\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->wglGetPixelFormatAttribivARB( |
| dpy, index, 0, 1, &windowAttrib, &window)); |
| } |
| int pbufferAttrib = WGL_DRAW_TO_PBUFFER_ARB; |
| EXIT_IF_FALSE(dispatch->wglGetPixelFormatAttribivARB( |
| 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->wglGetPixelFormatAttribivARB( |
| 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->wglGetPixelFormatAttribivARB( |
| dpy, index, 0, 1, &transparentRedAttrib, &info.trans_red_val)); |
| int transparentGreenAttrib = WGL_TRANSPARENT_GREEN_VALUE_ARB; |
| EXIT_IF_FALSE(dispatch->wglGetPixelFormatAttribivARB( |
| dpy, index, 0, 1, &transparentGreenAttrib, |
| &info.trans_green_val)); |
| int transparentBlueAttrib = WGL_TRANSPARENT_RED_VALUE_ARB; |
| EXIT_IF_FALSE(dispatch->wglGetPixelFormatAttribivARB( |
| 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.config_id = (EGLint) index; |
| info.frmt = new WinPixelFormat(frmt, index); |
| |
| (*addConfigFunc)(addConfigOpaque, &info); |
| } |
| |
| class WglDisplay : public EglOS::Display { |
| public: |
| WglDisplay(const WglExtensionsDispatch* dispatch, |
| WinGlobals* globals) |
| : mDispatch(dispatch), mGlobals(globals) {} |
| |
| 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); |
| |
| // 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); |
| } |
| } |
| |
| 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 EglOS::Context* createContext( |
| const EglOS::PixelFormat* pixelFormat, |
| EglOS::Context* sharedContext) { |
| const WinPixelFormat* format = WinPixelFormat::from(pixelFormat); |
| HDC dpy = mGlobals->getDummyDC(format); |
| if (!dpy) { |
| return nullptr; |
| } |
| |
| HGLRC ctx = mDispatch->wglCreateContext(dpy); |
| if (ctx && sharedContext) { |
| if (!mDispatch->wglShareLists(WinContext::from(sharedContext), ctx)) { |
| mDispatch->wglDeleteContext(ctx); |
| return NULL; |
| } |
| } |
| return new WinContext(ctx); |
| } |
| |
| virtual bool destroyContext(EglOS::Context* context) { |
| if (!context) { |
| return false; |
| } |
| if (!mDispatch->wglDeleteContext(WinContext::from(context))) { |
| GetLastError(); |
| return false; |
| } |
| delete context; |
| return true; |
| } |
| |
| virtual EglOS::Surface* createPbufferSurface( |
| const EglOS::PixelFormat* pixelFormat, |
| const EglOS::PbufferInfo* info) { |
| const WinPixelFormat* format = WinPixelFormat::from(pixelFormat); |
| HDC dpy = mGlobals->getDummyDC(format); |
| |
| int wglTexFormat = WGL_NO_TEXTURE_ARB; |
| int wglTexTarget = |
| (info->target == EGL_TEXTURE_2D) ? WGL_TEXTURE_2D_ARB |
| : WGL_NO_TEXTURE_ARB; |
| switch (info->format) { |
| case EGL_TEXTURE_RGB: |
| wglTexFormat = WGL_TEXTURE_RGB_ARB; |
| break; |
| case EGL_TEXTURE_RGBA: |
| wglTexFormat = WGL_TEXTURE_RGBA_ARB; |
| break; |
| } |
| |
| const int pbAttribs[] = { |
| WGL_TEXTURE_TARGET_ARB, wglTexTarget, |
| WGL_TEXTURE_FORMAT_ARB, wglTexFormat, |
| 0 |
| }; |
| |
| const WglExtensionsDispatch* dispatch = mDispatch; |
| if (!dispatch->wglCreatePbufferARB) { |
| return NULL; |
| } |
| HPBUFFERARB pb = dispatch->wglCreatePbufferARB( |
| dpy, format->configId(), info->width, info->height, pbAttribs); |
| if (!pb) { |
| GetLastError(); |
| return NULL; |
| } |
| return new WinSurface(pb, dispatch); |
| } |
| |
| virtual bool releasePbuffer(EglOS::Surface* pb) { |
| if (!pb) { |
| return false; |
| } |
| const WglExtensionsDispatch* dispatch = mDispatch; |
| if (!dispatch->wglReleasePbufferDCARB || |
| !dispatch->wglDestroyPbufferARB) { |
| return false; |
| } |
| WinSurface* winpb = WinSurface::from(pb); |
| if (!dispatch->wglReleasePbufferDCARB( |
| winpb->getPbuffer(), winpb->getDC()) || |
| !dispatch->wglDestroyPbufferARB(winpb->getPbuffer())) { |
| GetLastError(); |
| return false; |
| } |
| return true; |
| } |
| |
| virtual bool makeCurrent(EglOS::Surface* read, |
| EglOS::Surface* draw, |
| EglOS::Context* context) { |
| HDC hdcRead = read ? WinSurface::from(read)->getDC() : NULL; |
| HDC hdcDraw = draw ? WinSurface::from(draw)->getDC() : NULL; |
| HGLRC hdcContext = context ? WinContext::from(context) : 0; |
| |
| const WglExtensionsDispatch* dispatch = mDispatch; |
| |
| 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) { |
| Sleep(16); |
| } |
| if (count <= 0) { |
| D("Error: wglMakeCurrent() failed, error %d\n", (int)GetLastError()); |
| return false; |
| } |
| return true; |
| } else if (!dispatch->wglMakeContextCurrentARB) { |
| return false; |
| } |
| bool retVal = dispatch->wglMakeContextCurrentARB( |
| hdcDraw, hdcRead, hdcContext); |
| return retVal; |
| } |
| |
| virtual void swapBuffers(EglOS::Surface* srfc) { |
| if (srfc && !mDispatch->SwapBuffers(WinSurface::from(srfc)->getDC())) { |
| GetLastError(); |
| } |
| } |
| |
| private: |
| const WglExtensionsDispatch* mDispatch = nullptr; |
| WinGlobals* mGlobals = nullptr; |
| }; |
| |
| 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; |
| delete mLib; |
| } |
| |
| virtual EglOS::Display* getDefaultDisplay() { |
| return new WglDisplay(mDispatch, &mGlobals); |
| } |
| |
| virtual GlLibrary* getGlLibrary() { |
| return &mGlLib; |
| } |
| |
| virtual EglOS::Surface* createWindowSurface(EGLNativeWindowType wnd) { |
| return new WinSurface(wnd); |
| } |
| |
| 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]; |
| D("%s: Trying to load %s\n", __FUNCTION__, kLibName); |
| mLib = SharedLibrary::open(kLibName, error, sizeof(error)); |
| if (!mLib) { |
| ERR("ERROR: %s: Could not open %s: %s\n", __FUNCTION__, |
| kLibName, error); |
| exit(1); |
| } |
| |
| D("%s: Library loaded at %p\n", __FUNCTION__, mLib); |
| mBaseDispatch.init(mLib, isSystemLib); |
| mDispatch = initExtensionsDispatch(&mBaseDispatch); |
| D("%s: Dispatch initialized\n", __FUNCTION__); |
| } |
| |
| emugl::LazyInstance<WinEngine> sHostEngine = LAZY_INSTANCE_INIT; |
| |
| } // namespace |
| |
| // static |
| EglOS::Engine* EglOS::Engine::getHostInstance() { |
| return sHostEngine.ptr(); |
| } |