X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/4aa59c3df6c324a6e6f9d540eac25e88b9c28acd..ea412ac4eb9f19e3c28dfa32aa9099a81ac7c74c:/src/msw/glcanvas.cpp diff --git a/src/msw/glcanvas.cpp b/src/msw/glcanvas.cpp index cefcac96a9..15ad41545b 100644 --- a/src/msw/glcanvas.cpp +++ b/src/msw/glcanvas.cpp @@ -29,7 +29,6 @@ #include "wx/intl.h" #include "wx/log.h" #include "wx/app.h" - #include "wx/module.h" #endif #include "wx/msw/private.h" @@ -46,6 +45,38 @@ LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, #define WXUNUSED_WITHOUT_GL_EXT_vertex_array(name) WXUNUSED(name) #endif +// ---------------------------------------------------------------------------- +// define possibly missing WGL constants +// ---------------------------------------------------------------------------- + +#ifndef WGL_ARB_pixel_format +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NUMBER_OVERLAYS_ARB 0x2008 +#define WGL_NUMBER_UNDERLAYS_ARB 0x2009 +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_STEREO_ARB 0x2012 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_ALPHA_BITS_ARB 0x201B +#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_FULL_ACCELERATION_ARB 0x2027 +#endif + +#ifndef WGL_ARB_multisample +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 +#endif + // ---------------------------------------------------------------------------- // libraries // ---------------------------------------------------------------------------- @@ -72,110 +103,6 @@ LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, # pragma comment( lib, "glu32" ) #endif -// ---------------------------------------------------------------------------- -// constants -// ---------------------------------------------------------------------------- - -static const wxChar *wxGLCanvasClassName = wxT("wxGLCanvasClass"); -static const wxChar *wxGLCanvasClassNameNoRedraw = wxT("wxGLCanvasClassNR"); - -// ============================================================================ -// implementation -// ============================================================================ - -// ---------------------------------------------------------------------------- -// wxGLModule is responsible for unregistering wxGLCanvasClass Windows class -// ---------------------------------------------------------------------------- - -class wxGLModule : public wxModule -{ -public: - bool OnInit() { return true; } - void OnExit() { UnregisterClasses(); } - - // register the GL classes if not done yet, return true if ok, false if - // registration failed - static bool RegisterClasses(); - - // unregister the classes, done automatically on program termination - static void UnregisterClasses(); - -private: - // wxGLCanvas is only used from the main thread so this is MT-ok - static bool ms_registeredGLClasses; - - DECLARE_DYNAMIC_CLASS(wxGLModule) -}; - -IMPLEMENT_DYNAMIC_CLASS(wxGLModule, wxModule) - -bool wxGLModule::ms_registeredGLClasses = false; - -/* static */ -bool wxGLModule::RegisterClasses() -{ - if ( ms_registeredGLClasses ) - return true; - - // We have to register a special window class because we need the CS_OWNDC - // style for GLCanvas: some OpenGL drivers are buggy and don't work with - // windows without this style - WNDCLASS wndclass; - - // the fields which are common to all classes - wndclass.lpfnWndProc = (WNDPROC)wxWndProc; - wndclass.cbClsExtra = 0; - wndclass.cbWndExtra = sizeof( DWORD ); // VZ: what is this DWORD used for? - wndclass.hInstance = wxhInstance; - wndclass.hIcon = (HICON) NULL; - wndclass.hCursor = ::LoadCursor((HINSTANCE)NULL, IDC_ARROW); - wndclass.lpszMenuName = NULL; - - // Register the GLCanvas class name - wndclass.hbrBackground = (HBRUSH)NULL; - wndclass.lpszClassName = wxGLCanvasClassName; - wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC; - - if ( !::RegisterClass(&wndclass) ) - { - wxLogLastError(wxT("RegisterClass(wxGLCanvasClass)")); - return false; - } - - // Register the GLCanvas class name for windows which don't do full repaint - // on resize - wndclass.lpszClassName = wxGLCanvasClassNameNoRedraw; - wndclass.style &= ~(CS_HREDRAW | CS_VREDRAW); - - if ( !::RegisterClass(&wndclass) ) - { - wxLogLastError(wxT("RegisterClass(wxGLCanvasClassNameNoRedraw)")); - - ::UnregisterClass(wxGLCanvasClassName, wxhInstance); - - return false; - } - - ms_registeredGLClasses = true; - - return true; -} - -/* static */ -void wxGLModule::UnregisterClasses() -{ - // we need to unregister the classes in case we're in a DLL which is - // unloaded and then loaded again because if we don't, the registration is - // going to fail in wxGLCanvas::Create() the next time we're loaded - if ( ms_registeredGLClasses ) - { - ::UnregisterClass(wxGLCanvasClassName, wxhInstance); - ::UnregisterClass(wxGLCanvasClassNameNoRedraw, wxhInstance); - - ms_registeredGLClasses = false; - } -} - // ---------------------------------------------------------------------------- // wxGLContext // ---------------------------------------------------------------------------- @@ -190,7 +117,9 @@ wxGLContext::wxGLContext(wxGLCanvas *win, const wxGLContext* other) if ( other ) { if ( !wglShareLists(other->m_glContext, m_glContext) ) - wxLogLastError(_T("wglShareLists")); + { + wxLogLastError(wxT("wglShareLists")); + } } } @@ -204,7 +133,7 @@ bool wxGLContext::SetCurrent(const wxGLCanvas& win) const { if ( !wglMakeCurrent(win.GetHDC(), m_glContext) ) { - wxLogLastError(_T("wglMakeCurrent")); + wxLogLastError(wxT("wglMakeCurrent")); return false; } return true; @@ -225,6 +154,8 @@ END_EVENT_TABLE() // wxGLCanvas construction // ---------------------------------------------------------------------------- +static int ChoosePixelFormatARB(HDC hdc, const int *attribList); + void wxGLCanvas::Init() { #if WXWIN_COMPATIBILITY_2_8 @@ -254,24 +185,15 @@ wxGLCanvas::~wxGLCanvas() // Replaces wxWindow::Create functionality, since we need to use a different // window class -bool wxGLCanvas::Create(wxWindow *parent, - wxWindowID id, - const wxPoint& pos, - const wxSize& size, - long style, - const wxString& name, - const int *attribList, - const wxPalette& palette) +bool wxGLCanvas::CreateWindow(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) { wxCHECK_MSG( parent, false, wxT("can't create wxWindow without parent") ); - if ( !wxGLModule::RegisterClasses() ) - { - wxLogError(_("Failed to register OpenGL window class.")); - - return false; - } - if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) ) return false; @@ -287,16 +209,80 @@ bool wxGLCanvas::Create(wxWindow *parent, DWORD msflags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; msflags |= MSWGetStyle(style, &exStyle); - if ( !MSWCreate(wxGLCanvasClassName, NULL, pos, size, msflags, exStyle) ) + if ( !MSWCreate(wxApp::GetRegisteredClassName(wxT("wxGLCanvas"), -1, CS_OWNDC), + NULL, pos, size, msflags, exStyle) ) return false; m_hDC = ::GetDC(GetHwnd()); if ( !m_hDC ) return false; - if ( !DoSetup(attribList) ) + return true; +} + +bool wxGLCanvas::Create(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name, + const int *attribList, + const wxPalette& palette) +{ + // Create the window first: we will either use it as is or use it to query + // for multisampling support and recreate it later with another pixel format + if ( !CreateWindow(parent, id, pos, size, style, name) ) return false; + PIXELFORMATDESCRIPTOR pfd; + const int setupVal = DoSetup(pfd, attribList); + if ( setupVal == 0 ) // PixelFormat error + return false; + + if ( setupVal == -1 ) // FSAA requested + { + // now that we have a valid OpenGL window, query it for FSAA support + int pixelFormat; + { + wxGLContext ctx(this); + ctx.SetCurrent(*this); + pixelFormat = ::ChoosePixelFormatARB(m_hDC, attribList); + } + + if ( pixelFormat > 0 ) + { + // from http://msdn.microsoft.com/en-us/library/ms537559(VS.85).aspx: + // + // Setting the pixel format of a window more than once can + // lead to significant complications for the Window Manager + // and for multithread applications, so it 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. + // + // so we need to delete the old window and create the new one + + // destroy Window + ::ReleaseDC(GetHwnd(), m_hDC); + m_hDC = 0; + + parent->RemoveChild(this); + const HWND hwnd = GetHwnd(); + DissociateHandle(); // will do SetHWND(0); + ::DestroyWindow(hwnd); + + // now recreate with FSAA pixelFormat + if ( !CreateWindow(parent, id, pos, size, style, name) ) + return false; + + if ( !::SetPixelFormat(m_hDC, pixelFormat, &pfd) ) + { + wxLogLastError(wxT("SetPixelFormat")); + return false; + } + } + } + #if wxUSE_PALETTE if ( !SetupPalette(palette) ) return false; @@ -315,27 +301,226 @@ bool wxGLCanvas::SwapBuffers() { if ( !::SwapBuffers(m_hDC) ) { - wxLogLastError(_T("SwapBuffers")); + wxLogLastError(wxT("SwapBuffers")); return false; } return true; } + +// ---------------------------------------------------------------------------- +// multi sample support +// ---------------------------------------------------------------------------- + +// this macro defines a variable of type "name_t" called "name" and initializes +// it with the pointer to WGL function "name" (which may be NULL) +#define wxDEFINE_WGL_FUNC(name) \ + name##_t name = (name##_t)wglGetProcAddress(#name) + +/* static */ +bool wxGLCanvasBase::IsExtensionSupported(const char *extension) +{ + static const char *s_extensionsList = (char *)wxUIntPtr(-1); + if ( s_extensionsList == (char *)wxUIntPtr(-1) ) + { + typedef const char * (WINAPI *wglGetExtensionsStringARB_t)(HDC hdc); + + wxDEFINE_WGL_FUNC(wglGetExtensionsStringARB); + if ( wglGetExtensionsStringARB ) + { + s_extensionsList = wglGetExtensionsStringARB(wglGetCurrentDC()); + } + else + { + typedef const char * (WINAPI * wglGetExtensionsStringEXT_t)(); + + wxDEFINE_WGL_FUNC(wglGetExtensionsStringEXT); + if ( wglGetExtensionsStringEXT ) + { + s_extensionsList = wglGetExtensionsStringEXT(); + } + else + { + s_extensionsList = NULL; + } + } + } + + return s_extensionsList && IsExtensionInList(s_extensionsList, extension); +} + +// this is a wrapper around wglChoosePixelFormatARB(): returns the pixel format +// index matching the given attributes on success or 0 on failure +static int ChoosePixelFormatARB(HDC hdc, const int *attribList) +{ + if ( !wxGLCanvas::IsExtensionSupported("WGL_ARB_multisample") ) + return 0; + + typedef BOOL (WINAPI * wglChoosePixelFormatARB_t) + (HDC hdc, + const int *piAttribIList, + const FLOAT *pfAttribFList, + UINT nMaxFormats, + int *piFormats, + UINT *nNumFormats + ); + + wxDEFINE_WGL_FUNC(wglChoosePixelFormatARB); + if ( !wglChoosePixelFormatARB ) + return 0; // should not occur if extension is supported + + int iAttributes[128]; + int dst = 0; // index in iAttributes array + + #define ADD_ATTR(attr, value) \ + iAttributes[dst++] = attr; iAttributes[dst++] = value + + ADD_ATTR( WGL_DRAW_TO_WINDOW_ARB, GL_TRUE ); + ADD_ATTR( WGL_SUPPORT_OPENGL_ARB, GL_TRUE ); + ADD_ATTR( WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB ); + + if ( !attribList ) + { + ADD_ATTR( WGL_COLOR_BITS_ARB, 24 ); + ADD_ATTR( WGL_ALPHA_BITS_ARB, 8 ); + ADD_ATTR( WGL_DEPTH_BITS_ARB, 16 ); + ADD_ATTR( WGL_STENCIL_BITS_ARB, 0 ); + ADD_ATTR( WGL_DOUBLE_BUFFER_ARB, GL_TRUE ); + ADD_ATTR( WGL_SAMPLE_BUFFERS_ARB, GL_TRUE ); + ADD_ATTR( WGL_SAMPLES_ARB, 4 ); + } + else // have custom attributes + { + #define ADD_ATTR_VALUE(attr) ADD_ATTR(attr, attribList[src++]) + + int src = 0; + while ( attribList[src] ) + { + switch ( attribList[src++] ) + { + case WX_GL_RGBA: + ADD_ATTR( WGL_COLOR_BITS_ARB, 24 ); + ADD_ATTR( WGL_ALPHA_BITS_ARB, 8 ); + break; + + case WX_GL_BUFFER_SIZE: + ADD_ATTR_VALUE( WGL_COLOR_BITS_ARB); + break; + + case WX_GL_LEVEL: + if ( attribList[src] > 0 ) + { + ADD_ATTR( WGL_NUMBER_OVERLAYS_ARB, 1 ); + } + else if ( attribList[src] <0 ) + { + ADD_ATTR( WGL_NUMBER_UNDERLAYS_ARB, 1 ); + } + //else: ignore it + + src++; // skip the value in any case + break; + + case WX_GL_DOUBLEBUFFER: + ADD_ATTR( WGL_DOUBLE_BUFFER_ARB, GL_TRUE ); + break; + + case WX_GL_STEREO: + ADD_ATTR( WGL_STEREO_ARB, GL_TRUE ); + break; + + case WX_GL_AUX_BUFFERS: + ADD_ATTR_VALUE( WGL_AUX_BUFFERS_ARB ); + break; + + case WX_GL_MIN_RED: + ADD_ATTR_VALUE( WGL_RED_BITS_ARB ); + break; + + case WX_GL_MIN_GREEN: + ADD_ATTR_VALUE( WGL_GREEN_BITS_ARB ); + break; + + case WX_GL_MIN_BLUE: + ADD_ATTR_VALUE( WGL_BLUE_BITS_ARB ); + break; + + case WX_GL_MIN_ALPHA: + ADD_ATTR_VALUE( WGL_ALPHA_BITS_ARB ); + break; + + case WX_GL_DEPTH_SIZE: + ADD_ATTR_VALUE( WGL_DEPTH_BITS_ARB ); + break; + + case WX_GL_STENCIL_SIZE: + ADD_ATTR_VALUE( WGL_STENCIL_BITS_ARB ); + break; + + case WX_GL_MIN_ACCUM_RED: + ADD_ATTR_VALUE( WGL_ACCUM_RED_BITS_ARB ); + break; + + case WX_GL_MIN_ACCUM_GREEN: + ADD_ATTR_VALUE( WGL_ACCUM_GREEN_BITS_ARB ); + break; + + case WX_GL_MIN_ACCUM_BLUE: + ADD_ATTR_VALUE( WGL_ACCUM_BLUE_BITS_ARB ); + break; + + case WX_GL_MIN_ACCUM_ALPHA: + ADD_ATTR_VALUE( WGL_ACCUM_ALPHA_BITS_ARB ); + break; + + case WX_GL_SAMPLE_BUFFERS: + ADD_ATTR_VALUE( WGL_SAMPLE_BUFFERS_ARB ); + break; + + case WX_GL_SAMPLES: + ADD_ATTR_VALUE( WGL_SAMPLES_ARB ); + break; + } + } + + #undef ADD_ATTR_VALUE + } + + #undef ADD_ATTR + + iAttributes[dst++] = 0; + + int pf; + UINT numFormats = 0; + if ( !wglChoosePixelFormatARB(hdc, iAttributes, NULL, 1, &pf, &numFormats) ) + { + wxLogLastError(wxT("wglChoosePixelFormatARB")); + return 0; + } + + return pf; +} + // ---------------------------------------------------------------------------- // pixel format stuff // ---------------------------------------------------------------------------- -static void +// returns true if pfd was adjusted accordingly to attributes provided, false +// if there is an error with attributes or -1 if the attributes indicate +// features not supported by ChoosePixelFormat() at all (currently only multi +// sampling) +static int AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR& pfd, const int *attribList) { if ( !attribList ) - return; + return 1; // remove default attributes pfd.dwFlags &= ~PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_COLORINDEX; + bool requestFSAA = false; for ( int arg = 0; attribList[arg]; ) { switch ( attribList[arg++] ) @@ -411,8 +596,17 @@ AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR& pfd, const int *attribList) case WX_GL_MIN_ACCUM_ALPHA: pfd.cAccumBits += (pfd.cAccumAlphaBits = attribList[arg++]); break; + + case WX_GL_SAMPLE_BUFFERS: + case WX_GL_SAMPLES: + // There is no support for multisample when using PIXELFORMATDESCRIPTOR + requestFSAA = true; // Remember that multi sample is requested. + arg++; // will call ChoosePixelFormatARB() later + break; } } + + return requestFSAA ? -1 : 1; } /* static */ @@ -448,9 +642,27 @@ wxGLCanvas::ChooseMatchingPixelFormat(HDC hdc, else *ppfd = pfd; - AdjustPFDForAttributes(*ppfd, attribList); + // adjust the PFD using the provided attributes and also check if we can + // use PIXELFORMATDESCRIPTOR at all: if multisampling is requested, we + // can't as it's not supported by ChoosePixelFormat() + switch ( AdjustPFDForAttributes(*ppfd, attribList) ) + { + case 1: + return ::ChoosePixelFormat(hdc, ppfd); + + default: + wxFAIL_MSG( "unexpected AdjustPFDForAttributes() return value" ); + // fall through - return ::ChoosePixelFormat(hdc, ppfd); + case 0: + // error in attributes + return 0; + + case -1: + // requestFSAA == true, will continue as normal + // in order to query later for a FSAA pixelformat + return -1; + } } /* static */ @@ -461,23 +673,27 @@ bool wxGLCanvasBase::IsDisplaySupported(const int *attribList) return wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList) > 0; } -bool wxGLCanvas::DoSetup(const int *attribList) +int wxGLCanvas::DoSetup(PIXELFORMATDESCRIPTOR &pfd, const int *attribList) { - PIXELFORMATDESCRIPTOR pfd; - const int pixelFormat = ChooseMatchingPixelFormat(m_hDC, attribList, &pfd); + int pixelFormat = ChooseMatchingPixelFormat(m_hDC, attribList, &pfd); + + const bool requestFSAA = pixelFormat == -1; + if ( requestFSAA ) + pixelFormat = ::ChoosePixelFormat(m_hDC, &pfd); + if ( !pixelFormat ) { - wxLogLastError(_T("ChoosePixelFormat")); - return false; + wxLogLastError(wxT("ChoosePixelFormat")); + return 0; } if ( !::SetPixelFormat(m_hDC, pixelFormat, &pfd) ) { - wxLogLastError(_T("SetPixelFormat")); - return false; + wxLogLastError(wxT("SetPixelFormat")); + return 0; } - return true; + return requestFSAA ? -1 : 1; } // ---------------------------------------------------------------------------- @@ -491,14 +707,14 @@ bool wxGLCanvas::SetupPalette(const wxPalette& palette) const int pixelFormat = ::GetPixelFormat(m_hDC); if ( !pixelFormat ) { - wxLogLastError(_T("GetPixelFormat")); + wxLogLastError(wxT("GetPixelFormat")); return false; } PIXELFORMATDESCRIPTOR pfd; if ( !::DescribePixelFormat(m_hDC, pixelFormat, sizeof(pfd), &pfd) ) { - wxLogLastError(_T("DescribePixelFormat")); + wxLogLastError(wxT("DescribePixelFormat")); return false; } @@ -516,13 +732,13 @@ bool wxGLCanvas::SetupPalette(const wxPalette& palette) if ( !::SelectPalette(m_hDC, GetHpaletteOf(m_palette), FALSE) ) { - wxLogLastError(_T("SelectPalette")); + wxLogLastError(wxT("SelectPalette")); return false; } if ( ::RealizePalette(m_hDC) == GDI_ERROR ) { - wxLogLastError(_T("RealizePalette")); + wxLogLastError(wxT("RealizePalette")); return false; } @@ -545,13 +761,12 @@ wxPalette wxGLCanvas::CreateDefaultPalette() pPal->palNumEntries = (WORD)paletteSize; /* build a simple RGB color palette */ - { int redMask = (1 << pfd.cRedBits) - 1; int greenMask = (1 << pfd.cGreenBits) - 1; int blueMask = (1 << pfd.cBlueBits) - 1; - int i; - for (i=0; ipalPalEntry[i].peRed = (BYTE)((((i >> pfd.cRedShift) & redMask) * 255) / redMask); pPal->palPalEntry[i].peGreen = @@ -560,7 +775,6 @@ wxPalette wxGLCanvas::CreateDefaultPalette() (BYTE)((((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask); pPal->palPalEntry[i].peFlags = 0; } - } HPALETTE hPalette = CreatePalette(pPal); free(pPal);