/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <memory>
#include <thread>
#include <opengl/win/gdiimpl.hxx>
#include <sal/log.hxx>
#include <comphelper/windowserrorstring.hxx>
#include <opengl/zone.hxx>
#include <win/wincomp.hxx>
#include <win/saldata.hxx>
#include <win/salframe.h>
#include <win/salinst.h>
#include <epoxy/wgl.h>
#include <ControlCacheKey.hxx>
static std::vector<HGLRC> g_vShareList;
static bool g_bAnyCurrent;
class GLWinWindow : public GLWindow
{
public:
HWND hWnd;
HDC hDC;
HGLRC hRC;
GLWinWindow();
};
GLWinWindow::GLWinWindow()
: hWnd(nullptr)
, hDC(nullptr)
, hRC(nullptr)
{
}
class WinOpenGLContext : public OpenGLContext
{
public:
bool init( HDC hDC, HWND hWnd );
virtual void initWindow() override;
private:
GLWinWindow m_aGLWin;
virtual const GLWindow& getOpenGLWindow() const override { return m_aGLWin; }
virtual GLWindow& getModifiableOpenGLWindow() override { return m_aGLWin; }
virtual bool ImplInit() override;
virtual void makeCurrent() override;
virtual void destroyCurrentContext() override;
virtual bool isCurrent() override;
virtual bool isAnyCurrent() override;
virtual void resetCurrent() override;
virtual void swapBuffers() override;
};
void WinOpenGLContext::swapBuffers()
{
OpenGLZone aZone;
SwapBuffers(m_aGLWin.hDC);
BuffersSwapped();
}
void WinOpenGLContext::resetCurrent()
{
clearCurrent();
OpenGLZone aZone;
wglMakeCurrent(nullptr, nullptr);
g_bAnyCurrent = false;
}
void ensureDispatchTable()
{
thread_local bool bEpoxyDispatchMakeCurrentCalled = false;
if (!bEpoxyDispatchMakeCurrentCalled)
{
epoxy_handle_external_wglMakeCurrent();
bEpoxyDispatchMakeCurrentCalled = true;
}
}
bool WinOpenGLContext::isCurrent()
{
OpenGLZone aZone;
if (!g_bAnyCurrent || !m_aGLWin.hRC)
return false;
ensureDispatchTable();
return wglGetCurrentContext() == m_aGLWin.hRC && wglGetCurrentDC() == m_aGLWin.hDC;
}
bool WinOpenGLContext::isAnyCurrent()
{
return g_bAnyCurrent && wglGetCurrentContext() != nullptr;
}
void WinOpenGLContext::makeCurrent()
{
if (isCurrent())
return;
OpenGLZone aZone;
clearCurrent();
ensureDispatchTable();
if (!wglMakeCurrent(m_aGLWin.hDC, m_aGLWin.hRC))
{
g_bAnyCurrent = false;
DWORD nLastError = GetLastError();
if (nLastError != ERROR_SUCCESS)
SAL_WARN("vcl.opengl", "wglMakeCurrent failed: " << WindowsErrorString(nLastError));
return;
}
g_bAnyCurrent = true;
registerAsCurrent();
}
bool WinOpenGLContext::init(HDC hDC, HWND hWnd)
{
if (isInitialized())
return true;
m_aGLWin.hDC = hDC;
m_aGLWin.hWnd = hWnd;
return ImplInit();
}
void WinOpenGLContext::initWindow()
{
if( !m_pChildWindow )
{
SystemWindowData winData = generateWinData(mpWindow, false);
m_pChildWindow = VclPtr<SystemChildWindow>::Create(mpWindow, 0, &winData, false);
}
if (m_pChildWindow)
{
InitChildWindow(m_pChildWindow.get());
const SystemEnvData* sysData(m_pChildWindow->GetSystemData());
m_aGLWin.hWnd = sysData->hWnd;
}
m_aGLWin.hDC = GetDC(m_aGLWin.hWnd);
}
void WinOpenGLContext::destroyCurrentContext()
{
if (m_aGLWin.hRC)
{
std::vector<HGLRC>::iterator itr = std::remove(g_vShareList.begin(), g_vShareList.end(), m_aGLWin.hRC);
if (itr != g_vShareList.end())
g_vShareList.erase(itr);
if (wglGetCurrentContext() != nullptr)
{
wglMakeCurrent(nullptr, nullptr);
g_bAnyCurrent = false;
}
wglDeleteContext( m_aGLWin.hRC );
ReleaseDC( m_aGLWin.hWnd, m_aGLWin.hDC );
m_aGLWin.hRC = nullptr;
}
}
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
return 0;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
case WM_DESTROY:
return 0;
default:
return DefWindowProcW(hwnd, message, wParam, lParam);
}
}
bool InitTempWindow(HWND& hwnd, int width, int height, const PIXELFORMATDESCRIPTOR& inPfd, GLWinWindow& glWin)
{
OpenGLZone aZone;
PIXELFORMATDESCRIPTOR pfd = inPfd;
int ret;
WNDCLASSW wc;
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = wc.cbWndExtra = 0;
wc.hInstance = nullptr;
wc.hIcon = nullptr;
wc.hCursor = nullptr;
wc.hbrBackground = nullptr;
wc.lpszMenuName = nullptr;
wc.lpszClassName = L"GLRenderer";
RegisterClassW(&wc);
hwnd = CreateWindowW(wc.lpszClassName, nullptr, WS_DISABLED, 0, 0, width, height, nullptr, nullptr, wc.hInstance, nullptr);
glWin.hDC = GetDC(hwnd);
int nPixelFormat = ChoosePixelFormat(glWin.hDC, &pfd);
if (!nPixelFormat)
{
ReleaseDC(hwnd, glWin.hDC);
DestroyWindow(hwnd);
return false;
}
ret = SetPixelFormat(glWin.hDC, nPixelFormat, &pfd);
if(!ret)
{
ReleaseDC(hwnd, glWin.hDC);
DestroyWindow(hwnd);
return false;
}
glWin.hRC = wglCreateContext(glWin.hDC);
if(!(glWin.hRC))
{
ReleaseDC(hwnd, glWin.hDC);
DestroyWindow(hwnd);
return false;
}
ret = wglMakeCurrent(glWin.hDC, glWin.hRC);
if(!ret)
{
wglMakeCurrent(nullptr, nullptr);
g_bAnyCurrent = false;
wglDeleteContext(glWin.hRC);
ReleaseDC(hwnd, glWin.hDC);
DestroyWindow(hwnd);
return false;
}
g_bAnyCurrent = false;
return true;
}
bool WGLisExtensionSupported(const char *extension)
{
OpenGLZone aZone;
const size_t extlen = strlen(extension);
const char *supported = nullptr;
// Try to use wglGetExtensionStringARB on current DC, if possible
PROC wglGetExtString = wglGetProcAddress("wglGetExtensionsStringARB");
if (wglGetExtString)
supported = reinterpret_cast<char*(__stdcall*)(HDC)>(wglGetExtString)(wglGetCurrentDC());
// If that failed, try standard OpenGL extensions string
if (supported == nullptr)
supported = reinterpret_cast<char const *>(glGetString(GL_EXTENSIONS));
// If that failed too, must be no extensions supported
if (supported == nullptr)
return false;
// Begin examination at start of string, increment by 1 on false match
for (const char* p = supported; ; p++)
{
// Advance p up to the next possible match
p = strstr(p, extension);
if (p == nullptr)
return false; // No Match
// Make sure that match is at the start of the string or that
// the previous char is a space, or else we could accidentally
// match "wglFunkywglExtension" with "wglExtension"
// Also, make sure that the following character is space or null
// or else "wglExtensionTwo" might match "wglExtension"
if ((p==supported || p[-1]==' ') && (p[extlen]=='\0' || p[extlen]==' '))
return true; // Match
}
}
bool InitMultisample(const PIXELFORMATDESCRIPTOR& pfd, int& rPixelFormat,
bool bUseDoubleBufferedRendering, bool bRequestVirtualDevice)
{
OpenGLZone aZone;
HWND hWnd = nullptr;
GLWinWindow glWin;
// Create a temp window to check whether support multi-sample, if support, get the format
if (!InitTempWindow(hWnd, 32, 32, pfd, glWin))
{
SAL_WARN("vcl.opengl", "Can't create temp window to test");
return false;
}
// See if the string exists in WGL
if (!WGLisExtensionSupported("WGL_ARB_multisample"))
{
SAL_WARN("vcl.opengl", "Device doesn't support multisample");
wglMakeCurrent(nullptr, nullptr);
g_bAnyCurrent = false;
wglDeleteContext(glWin.hRC);
ReleaseDC(hWnd, glWin.hDC);
DestroyWindow(hWnd);
return false;
}
// Get our pixel format
PFNWGLCHOOSEPIXELFORMATARBPROC fn_wglChoosePixelFormatARB = reinterpret_cast<PFNWGLCHOOSEPIXELFORMATARBPROC>(wglGetProcAddress("wglChoosePixelFormatARB"));
if (!fn_wglChoosePixelFormatARB)
{
wglMakeCurrent(nullptr, nullptr);
g_bAnyCurrent = false;
wglDeleteContext(glWin.hRC);
ReleaseDC(hWnd, glWin.hDC);
DestroyWindow(hWnd);
return false;
}
// Get our current device context
HDC hDC = GetDC(hWnd);
int pixelFormat;
int valid;
UINT numFormats;
float fAttributes[] = {0,0};
// These attributes are the bits we want to test for in our sample.
// Everything is pretty standard, the only one we want to
// really focus on is the WGL_SAMPLE_BUFFERS_ARB and WGL_SAMPLES_ARB.
// These two are going to do the main testing for whether or not
// we support multisampling on this hardware.
int iAttributes[] =
{
WGL_DOUBLE_BUFFER_ARB,GL_TRUE,
WGL_DRAW_TO_WINDOW_ARB,GL_TRUE,
WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
WGL_COLOR_BITS_ARB,24,
WGL_ALPHA_BITS_ARB,8,
WGL_DEPTH_BITS_ARB,24,
WGL_STENCIL_BITS_ARB,0,
WGL_SAMPLE_BUFFERS_ARB,GL_TRUE,
WGL_SAMPLES_ARB,8,
0,0
};
if (!bUseDoubleBufferedRendering)
{
// Use asserts to make sure the iAttributes array is not changed without changing these ugly
// hardcode indexes into it.
assert(iAttributes[0] == WGL_DOUBLE_BUFFER_ARB);
iAttributes[1] = GL_FALSE;
}
if (bRequestVirtualDevice)
{
assert(iAttributes[2] == WGL_DRAW_TO_WINDOW_ARB);
iAttributes[2] = WGL_DRAW_TO_BITMAP_ARB;
}
bool bArbMultisampleSupported = false;
// First we check to see if we can get a pixel format for 8 samples
valid = fn_wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
// If we returned true, and our format count is greater than 1
if (valid && numFormats >= 1)
{
bArbMultisampleSupported = true;
rPixelFormat = pixelFormat;
wglMakeCurrent(nullptr, nullptr);
g_bAnyCurrent = false;
wglDeleteContext(glWin.hRC);
ReleaseDC(hWnd, glWin.hDC);
DestroyWindow(hWnd);
return bArbMultisampleSupported;
}
// Our pixel format with 8 samples failed, test for 2 samples
assert(iAttributes[18] == WGL_SAMPLES_ARB);
iAttributes[19] = 2;
valid = fn_wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
if (valid && numFormats >= 1)
{
bArbMultisampleSupported = true;
rPixelFormat = pixelFormat;
wglMakeCurrent(nullptr, nullptr);
g_bAnyCurrent = false;
wglDeleteContext(glWin.hRC);
ReleaseDC(hWnd, glWin.hDC);
DestroyWindow(hWnd);
return bArbMultisampleSupported;
}
// Return the valid format
wglMakeCurrent(nullptr, nullptr);
g_bAnyCurrent = false;
wglDeleteContext(glWin.hRC);
ReleaseDC(hWnd, glWin.hDC);
DestroyWindow(hWnd);
return bArbMultisampleSupported;
}
namespace
{
bool tryShaders(const OUString& rVertexShader, const OUString& rFragmentShader, const OUString& rGeometryShader = "", const OString& rPreamble = "")
{
GLint nId;
// Somewhat mysteriously, the OpenGLHelper::LoadShaders() API saves a compiled binary of the
// shader only if you give it the digest of the shaders. We have API to calculate the digest
// only of the combination of vertex and fragment (but not geometry) shader. So if we have a
// geometry shader, we should not save the binary.
if (rGeometryShader.isEmpty())
{
nId = OpenGLHelper::LoadShaders(rVertexShader, rFragmentShader, rPreamble, OpenGLHelper::GetDigest( rVertexShader, rFragmentShader, rPreamble));
}
else
{
assert(rPreamble.isEmpty());
nId = OpenGLHelper::LoadShaders(rVertexShader, rFragmentShader, rGeometryShader);
}
if (!nId)
return false;
// We're interested in the error returned by glDeleteProgram().
glGetError();
glDeleteProgram(nId);
return glGetError() == GL_NO_ERROR;
}
bool compiledShaderBinariesWork()
{
static bool bBeenHere = false;
static bool bResult;
if (bBeenHere)
return bResult;
bBeenHere = true;
bResult =
(
#if 0 // Only look at shaders used by vcl for now
// canvas
tryShaders("dummyVertexShader", "linearMultiColorGradientFragmentShader") &&
tryShaders("dummyVertexShader", "linearTwoColorGradientFragmentShader") &&
tryShaders("dummyVertexShader", "radialMultiColorGradientFragmentShader") &&
tryShaders("dummyVertexShader", "radialTwoColorGradientFragmentShader") &&
tryShaders("dummyVertexShader", "rectangularMultiColorGradientFragmentShader") &&
tryShaders("dummyVertexShader", "rectangularTwoColorGradientFragmentShader") &&
// chart2
(GLEW_VERSION_3_3 ?
(tryShaders("shape3DVertexShader", "shape3DFragmentShader") &&
tryShaders("shape3DVertexShaderBatchScroll", "shape3DFragmentShaderBatchScroll") &&
tryShaders("shape3DVertexShaderBatch", "shape3DFragmentShaderBatch") &&
tryShaders("textVertexShaderBatch", "textFragmentShaderBatch")) :
(tryShaders("shape3DVertexShaderV300", "shape3DFragmentShaderV300"))) &&
tryShaders("textVertexShader", "textFragmentShader") &&
tryShaders("screenTextVertexShader", "screenTextFragmentShader") &&
tryShaders("commonVertexShader", "commonFragmentShader") &&
tryShaders("pickingVertexShader", "pickingFragmentShader") &&
tryShaders("backgroundVertexShader", "backgroundFragmentShader") &&
tryShaders("symbolVertexShader", "symbolFragmentShader") &&
tryShaders("symbolVertexShader", "symbolFragmentShader") &&
// slideshow
tryShaders("reflectionVertexShader", "reflectionFragmentShader") &&
tryShaders("basicVertexShader", "basicFragmentShader") &&
tryShaders("vortexVertexShader", "vortexFragmentShader", "vortexGeometryShader") &&
tryShaders("basicVertexShader", "rippleFragmentShader") &&
tryShaders("glitterVertexShader", "glitterFragmentShader") &&
tryShaders("honeycombVertexShader", "honeycombFragmentShader", "honeycombGeometryShader") &&
#endif
// vcl
tryShaders("combinedVertexShader", "combinedFragmentShader") &&
tryShaders("dumbVertexShader", "invert50FragmentShader") &&
tryShaders("combinedTextureVertexShader", "combinedTextureFragmentShader") &&
tryShaders("textureVertexShader", "areaScaleFragmentShader") &&
tryShaders("transformedTextureVertexShader", "maskedTextureFragmentShader") &&
tryShaders("transformedTextureVertexShader", "areaScaleFastFragmentShader") &&
tryShaders("transformedTextureVertexShader", "areaScaleFastFragmentShader", "", "#define MASKED") &&
tryShaders("transformedTextureVertexShader", "areaScaleFragmentShader") &&
tryShaders("transformedTextureVertexShader", "areaScaleFragmentShader", "", "#define MASKED") &&
tryShaders("transformedTextureVertexShader", "textureFragmentShader") &&
tryShaders("combinedTextureVertexShader", "combinedTextureFragmentShader") &&
tryShaders("combinedTextureVertexShader", "combinedTextureFragmentShader", "", "// flush shader\n") &&
tryShaders("textureVertexShader", "linearGradientFragmentShader") &&
tryShaders("textureVertexShader", "radialGradientFragmentShader") &&
tryShaders("textureVertexShader", "areaHashCRC64TFragmentShader") &&
tryShaders("textureVertexShader", "replaceColorFragmentShader") &&
tryShaders("textureVertexShader", "greyscaleFragmentShader") &&
tryShaders("textureVertexShader", "textureFragmentShader") &&
tryShaders("textureVertexShader", "convolutionFragmentShader") &&
tryShaders("textureVertexShader", "areaScaleFastFragmentShader"));
return bResult;
}
} // unnamed namespace
bool WinOpenGLContext::ImplInit()
{
static bool bFirstCall = true;
OpenGLZone aZone;
VCL_GL_INFO("OpenGLContext::ImplInit----start");
// PixelFormat tells Windows how we want things to be
PIXELFORMATDESCRIPTOR PixelFormatFront =
{
sizeof(PIXELFORMATDESCRIPTOR),
1, // Version Number
PFD_SUPPORT_OPENGL,
PFD_TYPE_RGBA, // Request An RGBA Format
BYTE(32), // Select Our 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, // Accumulation Bits Ignored
24, // 24 bit z-buffer
8, // stencil buffer
0, // No Auxiliary Buffer
0, // now ignored
0, // Reserved
0, 0, 0 // Layer Masks Ignored
};
if (mbUseDoubleBufferedRendering)
PixelFormatFront.dwFlags |= PFD_DOUBLEBUFFER;
PixelFormatFront.dwFlags |= PFD_DRAW_TO_WINDOW;
// we must check whether can set the MSAA
int WindowPix = 0;
bool bMultiSampleSupport = false;
if (!mbVCLOnly)
bMultiSampleSupport = InitMultisample(PixelFormatFront, WindowPix, mbUseDoubleBufferedRendering, false);
else
VCL_GL_INFO("Skipping multisample detection for VCL.");
if (bMultiSampleSupport && WindowPix != 0)
{
m_aGLWin.bMultiSampleSupported = true;
}
else
{
WindowPix = ChoosePixelFormat(m_aGLWin.hDC, &PixelFormatFront);
#if OSL_DEBUG_LEVEL > 0
PIXELFORMATDESCRIPTOR pfd;
DescribePixelFormat(m_aGLWin.hDC, WindowPix, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
SAL_WARN("vcl.opengl", "Render Target: Window: " << static_cast<int>((pfd.dwFlags & PFD_DRAW_TO_WINDOW) != 0) << ", Bitmap: " << static_cast<int>((pfd.dwFlags & PFD_DRAW_TO_BITMAP) != 0));
SAL_WARN("vcl.opengl", "Supports OpenGL: " << static_cast<int>((pfd.dwFlags & PFD_SUPPORT_OPENGL) != 0));
#endif
}
if (WindowPix == 0)
{
SAL_WARN("vcl.opengl", "Invalid pixelformat");
return false;
}
if (!SetPixelFormat(m_aGLWin.hDC, WindowPix, &PixelFormatFront))
{
SAL_WARN("vcl.opengl", "SetPixelFormat failed: " << WindowsErrorString(GetLastError()));
return false;
}
HGLRC hTempRC = wglCreateContext(m_aGLWin.hDC);
if (hTempRC == nullptr)
{
SAL_WARN("vcl.opengl", "wglCreateContext failed: "<< WindowsErrorString(GetLastError()));
return false;
}
if (!wglMakeCurrent(m_aGLWin.hDC, hTempRC))
{
g_bAnyCurrent = false;
SAL_WARN("vcl.opengl", "wglMakeCurrent failed: "<< WindowsErrorString(GetLastError()));
return false;
}
g_bAnyCurrent = true;
if (!InitGL())
{
wglMakeCurrent(nullptr, nullptr);
g_bAnyCurrent = false;
wglDeleteContext(hTempRC);
return false;
}
HGLRC hSharedCtx = nullptr;
if (!g_vShareList.empty())
hSharedCtx = g_vShareList.front();
if (!wglCreateContextAttribsARB)
{
wglMakeCurrent(nullptr, nullptr);
g_bAnyCurrent = false;
wglDeleteContext(hTempRC);
return false;
}
// now setup the shared context; this needs a temporary context already
// set up in order to work
int const attribs [] =
{
#ifdef DBG_UTIL
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
#endif
0
};
m_aGLWin.hRC = wglCreateContextAttribsARB(m_aGLWin.hDC, hSharedCtx, attribs);
if (m_aGLWin.hRC == nullptr)
{
SAL_WARN("vcl.opengl", "wglCreateContextAttribsARB failed: "<< WindowsErrorString(GetLastError()));
wglMakeCurrent(nullptr, nullptr);
g_bAnyCurrent = false;
wglDeleteContext(hTempRC);
return false;
}
if (!compiledShaderBinariesWork())
{
wglMakeCurrent(nullptr, nullptr);
g_bAnyCurrent = false;
wglDeleteContext(hTempRC);
return false;
}
wglMakeCurrent(nullptr, nullptr);
g_bAnyCurrent = false;
wglDeleteContext(hTempRC);
if (!wglMakeCurrent(m_aGLWin.hDC, m_aGLWin.hRC))
{
g_bAnyCurrent = false;
SAL_WARN("vcl.opengl", "wglMakeCurrent failed: " << WindowsErrorString(GetLastError()));
return false;
}
g_bAnyCurrent = true;
if (bFirstCall)
{
// Checking texture size
GLint nMaxTextureSize;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &nMaxTextureSize);
if (nMaxTextureSize <= 4096)
SAL_WARN("vcl.opengl", "Max texture size is " << nMaxTextureSize
<< ". This may not be enough for normal operation.");
else
VCL_GL_INFO("Max texture size: " << nMaxTextureSize);
// Trying to make a texture and check its size
for (GLint nWidthHeight = 1023; nWidthHeight < nMaxTextureSize; nWidthHeight += (nWidthHeight + 1))
{
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, 4, nWidthHeight, nWidthHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, nullptr);
CHECK_GL_ERROR();
if (glGetError() == GL_NO_ERROR)
{
GLint nWidth = 0;
GLint nHeight = 0;
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &nWidth);
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &nHeight);
VCL_GL_INFO("Created texture " << nWidthHeight << "," << nWidthHeight << " reports size: " << nWidth << ", " << nHeight);
}
else
{
SAL_WARN("vcl.opengl", "Error when creating a " << nWidthHeight << ", " << nWidthHeight << " test texture.");
}
}
}
InitGLDebugging();
g_vShareList.push_back(m_aGLWin.hRC);
RECT clientRect;
GetClientRect(WindowFromDC(m_aGLWin.hDC), &clientRect);
m_aGLWin.Width = clientRect.right - clientRect.left;
m_aGLWin.Height = clientRect.bottom - clientRect.top;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
registerAsCurrent();
bFirstCall = false;
static OString aVendor(reinterpret_cast<const char*>(glGetString(GL_VENDOR)));
if (aVendor.equalsIgnoreAsciiCase("intel"))
maOpenGLCapabilitySwitch.mbLimitedShaderRegisters = true;
return true;
}
OpenGLContext* WinSalInstance::CreateOpenGLContext()
{
return new WinOpenGLContext;
}
WinOpenGLSalGraphicsImpl::WinOpenGLSalGraphicsImpl(WinSalGraphics& rGraphics,
SalGeometryProvider *mpProvider):
OpenGLSalGraphicsImpl(rGraphics,mpProvider),
mrWinParent(rGraphics)
{
}
void WinOpenGLSalGraphicsImpl::copyBits( const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics )
{
OpenGLSalGraphicsImpl *pImpl = pSrcGraphics ? static_cast< OpenGLSalGraphicsImpl* >(pSrcGraphics->GetImpl()) : static_cast< OpenGLSalGraphicsImpl *>(mrParent.GetImpl());
OpenGLSalGraphicsImpl::DoCopyBits( rPosAry, *pImpl );
}
rtl::Reference<OpenGLContext> WinOpenGLSalGraphicsImpl::CreateWinContext()
{
rtl::Reference<WinOpenGLContext> xContext(new WinOpenGLContext);
xContext->setVCLOnly();
if (!xContext->init(mrWinParent.mhLocalDC, mrWinParent.mhWnd))
{
SAL_WARN("vcl.opengl", "Context could not be created.");
return rtl::Reference<OpenGLContext>();
}
return rtl::Reference<OpenGLContext>(xContext.get());
}
void WinOpenGLSalGraphicsImpl::Init()
{
if (!IsOffscreen() && mpContext.is() && mpContext->isInitialized())
{
const GLWinWindow& rGLWindow = static_cast<const GLWinWindow&>(mpContext->getOpenGLWindow());
if (rGLWindow.hWnd != mrWinParent.mhWnd || rGLWindow.hDC == mrWinParent.mhLocalDC)
{
// This can legitimately happen, SalFrame keeps 2x
// SalGraphics which share the same hWnd and hDC.
// The shape 'Area' dialog does reparenting to trigger this.
SAL_WARN("vcl.opengl", "Unusual: Windows handle / DC changed without DeInit");
DeInit();
}
}
OpenGLSalGraphicsImpl::Init();
}
TheTextureCache::TheTextureCache(): cache(200) {}
ControlCacheType & TheTextureCache::get() {
SalData * data = GetSalData();
if (!data->m_pTextureCache) {
data->m_pTextureCache.reset(new TheTextureCache);
}
return data->m_pTextureCache->cache;
}
bool WinOpenGLSalGraphicsImpl::TryRenderCachedNativeControl(ControlCacheKey const & rControlCacheKey, int nX, int nY)
{
static bool gbCacheEnabled = !getenv("SAL_WITHOUT_WIDGET_CACHE");
if (!gbCacheEnabled)
return false;
auto & gTextureCache = TheTextureCache::get();
ControlCacheType::const_iterator iterator = gTextureCache.find(rControlCacheKey);
if (iterator == gTextureCache.end())
return false;
const std::unique_ptr<TextureCombo>& pCombo = iterator->second;
bool bRet = false;
PreDraw();
bRet = RenderTextureCombo(*pCombo, nX, nY);
PostDraw();
return bRet;
}
bool WinOpenGLSalGraphicsImpl::RenderTextureCombo(TextureCombo const & rCombo, int nX, int nY)
{
OpenGLTexture& rTexture = *rCombo.mpTexture;
SalTwoRect aPosAry(0, 0, rTexture.GetWidth(), rTexture.GetHeight(),
nX, nY, rTexture.GetWidth(), rTexture.GetHeight());
DrawTextureDiff(rTexture, *rCombo.mpMask, aPosAry, false);
return true;
}
bool WinOpenGLSalGraphicsImpl::RenderCompatibleDC(OpenGLCompatibleDC& rWhite, OpenGLCompatibleDC& rBlack,
int nX, int nY, TextureCombo& rCombo)
{
bool bRet = false;
PreDraw();
rCombo.mpTexture.reset(rWhite.getTexture());
rCombo.mpMask.reset(rBlack.getTexture());
bRet = RenderTextureCombo(rCombo, nX, nY);
PostDraw();
return bRet;
}
bool WinOpenGLSalGraphicsImpl::RenderAndCacheNativeControl(OpenGLCompatibleDC& rWhite, OpenGLCompatibleDC& rBlack,
int nX, int nY , ControlCacheKey& aControlCacheKey)
{
std::unique_ptr<TextureCombo> pCombo(new TextureCombo);
bool bResult = RenderCompatibleDC(rWhite, rBlack, nX, nY, *pCombo);
if (!bResult)
return false;
if (!aControlCacheKey.canCacheControl())
return true;
ControlCachePair pair(aControlCacheKey, std::move(pCombo));
TheTextureCache::get().insert(std::move(pair));
return bResult;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V501 There are identical sub-expressions 'tryShaders("combinedTextureVertexShader", "combinedTextureFragmentShader")' to the left and to the right of the '&&' operator.