/*-------------------------------------------------------------------------
* drawElements Quality Program Tester Core
* ----------------------------------------
*
* Copyright 2014 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.
*
*//*!
* \file
* \brief WGL Utilities.
*//*--------------------------------------------------------------------*/
#include "tcuWGL.hpp"
#include "tcuWin32Window.hpp"
#include "deDynamicLibrary.hpp"
#include "deMemory.h"
#include "deStringUtil.hpp"
#include "tcuFormatUtil.hpp"
#include "gluRenderConfig.hpp"
#include <WinGDI.h>
// WGL_ARB_pixel_format
#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_DRAW_TO_BITMAP_ARB 0x2002
#define WGL_ACCELERATION_ARB 0x2003
#define WGL_NEED_PALETTE_ARB 0x2004
#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005
#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006
#define WGL_SWAP_METHOD_ARB 0x2007
#define WGL_NUMBER_OVERLAYS_ARB 0x2008
#define WGL_NUMBER_UNDERLAYS_ARB 0x2009
#define WGL_TRANSPARENT_ARB 0x200A
#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037
#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
#define WGL_SHARE_DEPTH_ARB 0x200C
#define WGL_SHARE_STENCIL_ARB 0x200D
#define WGL_SHARE_ACCUM_ARB 0x200E
#define WGL_SUPPORT_GDI_ARB 0x200F
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_STEREO_ARB 0x2012
#define WGL_PIXEL_TYPE_ARB 0x2013
#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_RED_BITS_ARB 0x2015
#define WGL_RED_SHIFT_ARB 0x2016
#define WGL_GREEN_BITS_ARB 0x2017
#define WGL_GREEN_SHIFT_ARB 0x2018
#define WGL_BLUE_BITS_ARB 0x2019
#define WGL_BLUE_SHIFT_ARB 0x201A
#define WGL_ALPHA_BITS_ARB 0x201B
#define WGL_ALPHA_SHIFT_ARB 0x201C
#define WGL_ACCUM_BITS_ARB 0x201D
#define WGL_ACCUM_RED_BITS_ARB 0x201E
#define WGL_ACCUM_GREEN_BITS_ARB 0x201F
#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
#define WGL_DEPTH_BITS_ARB 0x2022
#define WGL_STENCIL_BITS_ARB 0x2023
#define WGL_AUX_BUFFERS_ARB 0x2024
#define WGL_NO_ACCELERATION_ARB 0x2025
#define WGL_GENERIC_ACCELERATION_ARB 0x2026
#define WGL_FULL_ACCELERATION_ARB 0x2027
#define WGL_TYPE_RGBA_ARB 0x202B
#define WGL_TYPE_COLORINDEX_ARB 0x202C
// WGL_ARB_color_buffer_float
#define WGL_TYPE_RGBA_FLOAT_ARB 0x21A0
// WGL_EXT_pixel_type_packed_float
#define WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT 0x20A8
// WGL_ARB_multisample
#define WGL_SAMPLE_BUFFERS_ARB 0x2041
#define WGL_SAMPLES_ARB 0x2042
// WGL_ARB_create_context
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
#define WGL_CONTEXT_FLAGS_ARB 0x2094
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004
// WGL_ARB_create_context_robustness
#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x0004
#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261
#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252
DE_BEGIN_EXTERN_C
// WGL core
typedef HGLRC (WINAPI* wglCreateContextFunc) (HDC hdc);
typedef BOOL (WINAPI* wglDeleteContextFunc) (HGLRC hglrc);
typedef BOOL (WINAPI* wglMakeCurrentFunc) (HDC hdc, HGLRC hglrc);
typedef PROC (WINAPI* wglGetProcAddressFunc) (LPCSTR lpszProc);
typedef BOOL (WINAPI* wglSwapLayerBuffersFunc) (HDC dhc, UINT fuPlanes);
// WGL_ARB_pixel_format
typedef BOOL (WINAPI* wglGetPixelFormatAttribivARBFunc) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues);
typedef BOOL (WINAPI* wglGetPixelFormatAttribfvARBFunc) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, FLOAT *pfValues);
typedef BOOL (WINAPI* wglChoosePixelFormatARBFunc) (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
// WGL_ARB_create_context
typedef HGLRC (WINAPI* wglCreateContextAttribsARBFunc) (HDC hdc, HGLRC hshareContext, const int* attribList);
DE_END_EXTERN_C
namespace tcu
{
namespace wgl
{
// Functions
struct Functions
{
// Core
wglCreateContextFunc createContext;
wglDeleteContextFunc deleteContext;
wglMakeCurrentFunc makeCurrent;
wglGetProcAddressFunc getProcAddress;
wglSwapLayerBuffersFunc swapLayerBuffers;
// WGL_ARB_pixel_format
wglGetPixelFormatAttribivARBFunc getPixelFormatAttribivARB;
wglGetPixelFormatAttribfvARBFunc getPixelFormatAttribfvARB;
wglChoosePixelFormatARBFunc choosePixelFormatARB;
// WGL_ARB_create_context
wglCreateContextAttribsARBFunc createContextAttribsARB;
Functions (void)
: createContext (DE_NULL)
, deleteContext (DE_NULL)
, makeCurrent (DE_NULL)
, getProcAddress (DE_NULL)
, swapLayerBuffers (DE_NULL)
, getPixelFormatAttribivARB (DE_NULL)
, getPixelFormatAttribfvARB (DE_NULL)
, choosePixelFormatARB (DE_NULL)
, createContextAttribsARB (DE_NULL)
{
}
};
// Library
class Library
{
public:
Library (HINSTANCE instance);
~Library (void);
const Functions& getFunctions (void) const { return m_functions; }
const de::DynamicLibrary& getGLLibrary (void) const { return m_library; }
private:
de::DynamicLibrary m_library;
Functions m_functions;
};
Library::Library (HINSTANCE instance)
: m_library("opengl32.dll")
{
// Temporary 1x1 window for creating context
win32::Window tmpWindow(instance, 1, 1);
// Load WGL core.
m_functions.createContext = (wglCreateContextFunc) m_library.getFunction("wglCreateContext");
m_functions.deleteContext = (wglDeleteContextFunc) m_library.getFunction("wglDeleteContext");
m_functions.getProcAddress = (wglGetProcAddressFunc) m_library.getFunction("wglGetProcAddress");
m_functions.makeCurrent = (wglMakeCurrentFunc) m_library.getFunction("wglMakeCurrent");
m_functions.swapLayerBuffers = (wglSwapLayerBuffersFunc) m_library.getFunction("wglSwapLayerBuffers");
if (!m_functions.createContext ||
!m_functions.deleteContext ||
!m_functions.getProcAddress ||
!m_functions.makeCurrent ||
!m_functions.swapLayerBuffers)
throw ResourceError("Failed to load core WGL functions");
{
PIXELFORMATDESCRIPTOR pixelFormatDesc;
deMemset(&pixelFormatDesc, 0, sizeof(pixelFormatDesc));
pixelFormatDesc.nSize = sizeof(pixelFormatDesc);
pixelFormatDesc.nVersion = 1;
pixelFormatDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pixelFormatDesc.iPixelType = PFD_TYPE_RGBA;
pixelFormatDesc.iLayerType = PFD_MAIN_PLANE;
int pixelFormat = ChoosePixelFormat(tmpWindow.getDeviceContext(), &pixelFormatDesc);
if (!SetPixelFormat(tmpWindow.getDeviceContext(), pixelFormat, &pixelFormatDesc))
throw ResourceError("Failed to set pixel format for temporary context creation");
}
// Create temporary context for loading extension functions.
HGLRC tmpCtx = m_functions.createContext(tmpWindow.getDeviceContext());
if (!tmpCtx || !m_functions.makeCurrent(tmpWindow.getDeviceContext(), tmpCtx))
{
if (tmpCtx)
m_functions.deleteContext(tmpCtx);
throw ResourceError("Failed to create temporary WGL context");
}
// WGL_ARB_pixel_format
m_functions.getPixelFormatAttribivARB = (wglGetPixelFormatAttribivARBFunc)m_functions.getProcAddress("wglGetPixelFormatAttribivARB");
m_functions.getPixelFormatAttribfvARB = (wglGetPixelFormatAttribfvARBFunc)m_functions.getProcAddress("wglGetPixelFormatAttribfvARB");
m_functions.choosePixelFormatARB = (wglChoosePixelFormatARBFunc)m_functions.getProcAddress("wglChoosePixelFormatARB");
// WGL_ARB_create_context
m_functions.createContextAttribsARB = (wglCreateContextAttribsARBFunc)m_functions.getProcAddress("wglCreateContextAttribsARB");
m_functions.makeCurrent(tmpWindow.getDeviceContext(), NULL);
m_functions.deleteContext(tmpCtx);
if (!m_functions.getPixelFormatAttribivARB ||
!m_functions.getPixelFormatAttribfvARB ||
!m_functions.choosePixelFormatARB ||
!m_functions.createContextAttribsARB)
throw ResourceError("Failed to load WGL extension functions");
}
Library::~Library (void)
{
}
// Core
Core::Core (HINSTANCE instance)
: m_library(new Library(instance))
{
}
Core::~Core (void)
{
delete m_library;
}
std::vector<int> Core::getPixelFormats (HDC deviceCtx) const
{
const Functions& wgl = m_library->getFunctions();
int attribs[] = { WGL_NUMBER_PIXEL_FORMATS_ARB };
int values[DE_LENGTH_OF_ARRAY(attribs)];
if (!wgl.getPixelFormatAttribivARB(deviceCtx, 0, 0, DE_LENGTH_OF_ARRAY(attribs), &attribs[0], &values[0]))
throw ResourceError("Failed to query number of WGL pixel formats");
// \todo [2013-04-14 pyry] Do we need to filter values at all?
std::vector<int> pixelFormats(values[0]);
for (int i = 0; i < values[0]; i++)
pixelFormats[i] = i+1;
return pixelFormats;
}
static PixelFormatInfo::Acceleration translateAcceleration (int accel)
{
switch (accel)
{
case WGL_NO_ACCELERATION_ARB: return PixelFormatInfo::ACCELERATION_NONE;
case WGL_GENERIC_ACCELERATION_ARB: return PixelFormatInfo::ACCELERATION_GENERIC;
case WGL_FULL_ACCELERATION_ARB: return PixelFormatInfo::ACCELERATION_FULL;
default: return PixelFormatInfo::ACCELERATION_UNKNOWN;
}
}
static PixelFormatInfo::PixelType translatePixelType (int type)
{
switch (type)
{
case WGL_TYPE_RGBA_ARB: return PixelFormatInfo::PIXELTYPE_RGBA;
case WGL_TYPE_RGBA_FLOAT_ARB: return PixelFormatInfo::PIXELTYPE_RGBA_FLOAT;
case WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT: return PixelFormatInfo::PIXELTYPE_RGBA_UNSIGNED_FLOAT;
case WGL_TYPE_COLORINDEX_ARB: return PixelFormatInfo::PIXELTYPE_COLOR_INDEX;
default: return PixelFormatInfo::PIXELTYPE_UNKNOWN;
}
}
PixelFormatInfo Core::getPixelFormatInfo (HDC deviceCtx, int pixelFormat) const
{
const Functions& wgl = m_library->getFunctions();
int attribs [14];
int values [DE_LENGTH_OF_ARRAY(attribs)];
attribs[0] = WGL_DRAW_TO_WINDOW_ARB;
attribs[1] = WGL_DRAW_TO_BITMAP_ARB;
attribs[2] = WGL_ACCELERATION_ARB;
attribs[3] = WGL_SUPPORT_OPENGL_ARB;
attribs[4] = WGL_DOUBLE_BUFFER_ARB;
attribs[5] = WGL_PIXEL_TYPE_ARB;
attribs[6] = WGL_RED_BITS_ARB;
attribs[7] = WGL_GREEN_BITS_ARB;
attribs[8] = WGL_BLUE_BITS_ARB;
attribs[9] = WGL_ALPHA_BITS_ARB;
attribs[10] = WGL_DEPTH_BITS_ARB;
attribs[11] = WGL_STENCIL_BITS_ARB;
attribs[12] = WGL_SAMPLE_BUFFERS_ARB;
attribs[13] = WGL_SAMPLES_ARB;
deMemset(&values[0], 0, sizeof(values));
if (!wgl.getPixelFormatAttribivARB(deviceCtx, pixelFormat, 0, DE_LENGTH_OF_ARRAY(attribs), &attribs[0], &values[0]))
throw ResourceError("Pixel format query failed");
// Translate values.
PixelFormatInfo info;
info.pixelFormat = pixelFormat;
info.surfaceTypes |= (values[0] ? PixelFormatInfo::SURFACE_WINDOW : 0);
info.surfaceTypes |= (values[1] ? PixelFormatInfo::SURFACE_PIXMAP : 0);
info.acceleration = translateAcceleration(values[2]);
info.supportOpenGL = values[3] != 0;
info.doubleBuffer = values[4] != 0;
info.pixelType = translatePixelType(values[5]);
info.redBits = values[6];
info.greenBits = values[7];
info.blueBits = values[8];
info.alphaBits = values[9];
info.depthBits = values[10];
info.stencilBits = values[11];
info.sampleBuffers = values[12];
info.samples = values[13];
return info;
}
// Context
Context::Context (const Core* core, HDC deviceCtx, glu::ContextType ctxType, int pixelFormat)
: m_core (core)
, m_deviceCtx (deviceCtx)
, m_context (0)
{
const Functions& wgl = core->getLibrary()->getFunctions();
// Map context type & flags.
int profileBit = 0;
int flags = 0;
switch (ctxType.getProfile())
{
case glu::PROFILE_CORE:
profileBit = WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
break;
case glu::PROFILE_ES:
profileBit = WGL_CONTEXT_ES_PROFILE_BIT_EXT;
break;
case glu::PROFILE_COMPATIBILITY:
profileBit = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
break;
default:
throw NotSupportedError("Unsupported context type for WGL");
}
if ((ctxType.getFlags() & glu::CONTEXT_FORWARD_COMPATIBLE) != 0)
flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
if ((ctxType.getFlags() & glu::CONTEXT_DEBUG) != 0)
flags |= WGL_CONTEXT_DEBUG_BIT_ARB;
if ((ctxType.getFlags() & glu::CONTEXT_ROBUST) != 0)
flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB;
const int attribList[] =
{
WGL_CONTEXT_MAJOR_VERSION_ARB, ctxType.getMajorVersion(),
WGL_CONTEXT_MINOR_VERSION_ARB, ctxType.getMinorVersion(),
WGL_CONTEXT_PROFILE_MASK_ARB, profileBit,
WGL_CONTEXT_FLAGS_ARB, flags,
0
};
// Set pixel format
{
PIXELFORMATDESCRIPTOR pixelFormatDesc;
deMemset(&pixelFormatDesc, 0, sizeof(pixelFormatDesc));
if (!DescribePixelFormat(deviceCtx, pixelFormat, sizeof(pixelFormatDesc), &pixelFormatDesc))
throw ResourceError("DescribePixelFormat() failed");
if (!SetPixelFormat(deviceCtx, pixelFormat, &pixelFormatDesc))
throw ResourceError("Failed to set pixel format");
}
// Create context
m_context = wgl.createContextAttribsARB(deviceCtx, NULL, attribList);
if (!m_context)
throw ResourceError("Failed to create WGL context");
if (!wgl.makeCurrent(deviceCtx, m_context))
{
wgl.deleteContext(m_context);
throw ResourceError("wglMakeCurrent() failed");
}
}
Context::~Context (void)
{
const Functions& wgl = m_core->getLibrary()->getFunctions();
wgl.makeCurrent(m_deviceCtx, NULL);
wgl.deleteContext(m_context);
}
FunctionPtr Context::getGLFunction (const char* name) const
{
FunctionPtr ptr = DE_NULL;
// Try first with wglGeProcAddress()
ptr = (FunctionPtr)m_core->getLibrary()->getFunctions().getProcAddress(name);
// Fall-back to dynlib
if (!ptr)
ptr = (FunctionPtr)m_core->getLibrary()->getGLLibrary().getFunction(name);
return ptr;
}
void Context::swapBuffers (void) const
{
const Functions& wgl = m_core->getLibrary()->getFunctions();
if (!wgl.swapLayerBuffers(m_deviceCtx, WGL_SWAP_MAIN_PLANE))
throw ResourceError("wglSwapBuffers() failed");
}
int choosePixelFormat (const Core& wgl, HDC deviceCtx, const glu::RenderConfig& config)
{
std::vector<int> pixelFormats = wgl.getPixelFormats(deviceCtx);
for (std::vector<int>::const_iterator fmtIter = pixelFormats.begin(); fmtIter != pixelFormats.end(); ++fmtIter)
{
PixelFormatInfo info = wgl.getPixelFormatInfo(deviceCtx, *fmtIter);
// Base rules: Must be OpenGL-compatible, RGBA, double-buffered, and window-renderable
if (!info.supportOpenGL ||
!(info.pixelType == wgl::PixelFormatInfo::PIXELTYPE_RGBA) ||
!info.doubleBuffer ||
!(info.surfaceTypes & wgl::PixelFormatInfo::SURFACE_WINDOW))
continue;
if (config.redBits != glu::RenderConfig::DONT_CARE &&
config.redBits != info.redBits)
continue;
if (config.greenBits != glu::RenderConfig::DONT_CARE &&
config.greenBits != info.greenBits)
continue;
if (config.blueBits != glu::RenderConfig::DONT_CARE &&
config.blueBits != info.blueBits)
continue;
if (config.alphaBits != glu::RenderConfig::DONT_CARE &&
config.alphaBits != info.alphaBits)
continue;
if (config.depthBits != glu::RenderConfig::DONT_CARE &&
config.depthBits != info.depthBits)
continue;
if (config.stencilBits != glu::RenderConfig::DONT_CARE &&
config.stencilBits != info.stencilBits)
continue;
if (config.numSamples != glu::RenderConfig::DONT_CARE &&
config.numSamples != info.samples)
continue;
// Passed all tests - select this.
return info.pixelFormat;
}
return -1;
}
} // wgl
} // tcu