blob: 86e94243d5738e451f03bfa3d5a83361c761021f [file] [log] [blame]
/*
* 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();
}