diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /vcl/opengl/win | |
parent | Initial commit. (diff) | |
download | libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/opengl/win')
-rw-r--r-- | vcl/opengl/win/WinDeviceInfo.cxx | 494 | ||||
-rw-r--r-- | vcl/opengl/win/gdiimpl.cxx | 898 | ||||
-rw-r--r-- | vcl/opengl/win/winlayout.cxx | 60 |
3 files changed, 1452 insertions, 0 deletions
diff --git a/vcl/opengl/win/WinDeviceInfo.cxx b/vcl/opengl/win/WinDeviceInfo.cxx new file mode 100644 index 000000000..301c8e74d --- /dev/null +++ b/vcl/opengl/win/WinDeviceInfo.cxx @@ -0,0 +1,494 @@ +/* -*- 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 <opengl/win/WinDeviceInfo.hxx> + +#include <driverblocklist.hxx> +#include <config_folders.h> + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <objbase.h> +#include <setupapi.h> +#include <algorithm> +#include <cstdint> +#include <memory> + +#include <osl/file.hxx> +#include <rtl/bootstrap.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <tools/stream.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +#include <desktop/crashreport.hxx> + +namespace { + +bool GetKeyValue(const WCHAR* keyLocation, const WCHAR* keyName, OUString& destString, int type) +{ + HKEY key; + DWORD dwcbData; + DWORD dValue; + DWORD resultType; + LONG result; + bool retval = true; + + result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyLocation, 0, KEY_QUERY_VALUE, &key); + if (result != ERROR_SUCCESS) + { + return false; + } + + switch (type) + { + case REG_DWORD: + { + // We only use this for vram size + dwcbData = sizeof(dValue); + result = RegQueryValueExW(key, keyName, nullptr, &resultType, + reinterpret_cast<LPBYTE>(&dValue), &dwcbData); + if (result == ERROR_SUCCESS && resultType == REG_DWORD) + { + dValue = dValue / 1024 / 1024; + destString += OUString::number(int32_t(dValue)); + } + else + { + retval = false; + } + break; + } + case REG_MULTI_SZ: + { + // A chain of null-separated strings; we convert the nulls to spaces + WCHAR wCharValue[1024]; + dwcbData = sizeof(wCharValue); + + result = RegQueryValueExW(key, keyName, nullptr, &resultType, + reinterpret_cast<LPBYTE>(wCharValue), &dwcbData); + if (result == ERROR_SUCCESS && resultType == REG_MULTI_SZ) + { + // This bit here could probably be cleaner. + bool isValid = false; + + DWORD strLen = dwcbData/sizeof(wCharValue[0]); + for (DWORD i = 0; i < strLen; i++) + { + if (wCharValue[i] == '\0') + { + if (i < strLen - 1 && wCharValue[i + 1] == '\0') + { + isValid = true; + break; + } + else + { + wCharValue[i] = ' '; + } + } + } + + // ensure wCharValue is null terminated + wCharValue[strLen-1] = '\0'; + + if (isValid) + destString = OUString(o3tl::toU(wCharValue)); + + } + else + { + retval = false; + } + + break; + } + } + RegCloseKey(key); + + return retval; +} + +// The device ID is a string like PCI\VEN_15AD&DEV_0405&SUBSYS_040515AD +// this function is used to extract the id's out of it +uint32_t ParseIDFromDeviceID(const OUString &key, const char *prefix, int length) +{ + OUString id = key.toAsciiUpperCase(); + OUString aPrefix = OUString::fromUtf8(prefix); + int32_t start = id.indexOf(aPrefix); + if (start != -1) + { + id = id.copy(start + aPrefix.getLength(), length); + } + return id.toUInt32(16); +} + +/* Other interesting places for info: + * IDXGIAdapter::GetDesc() + * IDirectDraw7::GetAvailableVidMem() + * e->GetAvailableTextureMem() + * */ + +template<typename T> void appendIntegerWithPadding(OUString& rString, T value, sal_uInt32 nChars) +{ + rString += "0x"; + OUString aValue = OUString::number(value, 16); + sal_Int32 nLength = aValue.getLength(); + sal_uInt32 nPadLength = nChars - nLength; + assert(nPadLength >= 0); + OUStringBuffer aBuffer; + for (sal_uInt32 i = 0; i < nPadLength; ++i) + { + aBuffer.append("0"); + } + rString += aBuffer.makeStringAndClear() + aValue; +} + +#define DEVICE_KEY_PREFIX L"\\Registry\\Machine\\" +} + +WinOpenGLDeviceInfo::WinOpenGLDeviceInfo(): + mbHasDualGPU(false), + mbRDP(false) +{ + GetData(); +} + +WinOpenGLDeviceInfo::~WinOpenGLDeviceInfo() +{ +} + +static OUString getBlacklistFile() +{ + OUString url("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER); + rtl::Bootstrap::expandMacros(url); + + return url + "/opengl/opengl_blacklist_windows.xml"; +} + +bool WinOpenGLDeviceInfo::FindBlocklistedDeviceInList() +{ + return DriverBlocklist::IsDeviceBlocked( getBlacklistFile(), DriverBlocklist::VersionType::OpenGL, + maDriverVersion, maAdapterVendorID, maAdapterDeviceID); +} + +namespace { + +OUString getCacheFolder() +{ + OUString url("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/cache/"); + rtl::Bootstrap::expandMacros(url); + + osl::Directory::create(url); + + return url; +} + +void writeToLog(SvStream& rStrm, const char* pKey, const OUString & rVal) +{ + rStrm.WriteCharPtr(pKey); + rStrm.WriteCharPtr(": "); + rStrm.WriteOString(OUStringToOString(rVal, RTL_TEXTENCODING_UTF8)); + rStrm.WriteChar('\n'); +} + +} + +bool WinOpenGLDeviceInfo::isDeviceBlocked() +{ + CrashReporter::addKeyValue("OpenGLVendor", maAdapterVendorID, CrashReporter::AddItem); + CrashReporter::addKeyValue("OpenGLDevice", maAdapterDeviceID, CrashReporter::AddItem); + CrashReporter::addKeyValue("OpenGLDriver", maDriverVersion, CrashReporter::Write); + + SAL_INFO("vcl.opengl", maDriverVersion); + SAL_INFO("vcl.opengl", maDriverDate); + SAL_INFO("vcl.opengl", maDeviceID); + SAL_INFO("vcl.opengl", maAdapterVendorID); + SAL_INFO("vcl.opengl", maAdapterDeviceID); + SAL_INFO("vcl.opengl", maAdapterSubsysID); + SAL_INFO("vcl.opengl", maDeviceKey); + SAL_INFO("vcl.opengl", maDeviceString); + + OUString aCacheFolder = getCacheFolder(); + + OUString aCacheFile(aCacheFolder + "/opengl_device.log"); + SvFileStream aOpenGLLogFile(aCacheFile, StreamMode::WRITE|StreamMode::TRUNC); + + writeToLog(aOpenGLLogFile, "DriverVersion", maDriverVersion); + writeToLog(aOpenGLLogFile, "DriverDate", maDriverDate); + writeToLog(aOpenGLLogFile, "DeviceID", maDeviceID); + writeToLog(aOpenGLLogFile, "AdapterVendorID", maAdapterVendorID); + writeToLog(aOpenGLLogFile, "AdapterDeviceID", maAdapterDeviceID); + writeToLog(aOpenGLLogFile, "AdapterSubsysID", maAdapterSubsysID); + writeToLog(aOpenGLLogFile, "DeviceKey", maDeviceKey); + writeToLog(aOpenGLLogFile, "DeviceString", maDeviceString); + + // Check if the device is blocked from the downloaded blocklist. If not, check + // the static list after that. This order is used so that we can later escape + // out of static blocks (i.e. if we were wrong or something was patched, we + // can back out our static block without doing a release). + if (mbRDP) + { + SAL_WARN("vcl.opengl", "all OpenGL blocked for RDP sessions"); + return true; + } + + return FindBlocklistedDeviceInList(); +} + +void WinOpenGLDeviceInfo::GetData() +{ + DISPLAY_DEVICEW displayDevice; + displayDevice.cb = sizeof(displayDevice); + + int deviceIndex = 0; + + while (EnumDisplayDevicesW(nullptr, deviceIndex, &displayDevice, 0)) + { + if (displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) + { + break; + } + deviceIndex++; + } + + // make sure the string is null terminated + // (using the term "null" here to mean a zero UTF-16 unit) + if (wcsnlen(displayDevice.DeviceKey, SAL_N_ELEMENTS(displayDevice.DeviceKey)) + == SAL_N_ELEMENTS(displayDevice.DeviceKey)) + { + // we did not find a null + SAL_WARN("vcl.opengl", "string not null terminated"); + return; + } + + /* DeviceKey is "reserved" according to MSDN so we'll be careful with it */ + /* check that DeviceKey begins with DEVICE_KEY_PREFIX */ + /* some systems have a DeviceKey starting with \REGISTRY\Machine\ so we need to compare case insensitively */ + if (_wcsnicmp(displayDevice.DeviceKey, DEVICE_KEY_PREFIX, SAL_N_ELEMENTS(DEVICE_KEY_PREFIX)-1) != 0) + { + SAL_WARN("vcl.opengl", "incorrect DeviceKey"); + return; + } + + // chop off DEVICE_KEY_PREFIX + maDeviceKey = o3tl::toU(displayDevice.DeviceKey) + SAL_N_ELEMENTS(DEVICE_KEY_PREFIX)-1; + + maDeviceID = o3tl::toU(displayDevice.DeviceID); + maDeviceString = o3tl::toU(displayDevice.DeviceString); + + if (maDeviceID.isEmpty() && + (maDeviceString == "RDPDD Chained DD" || + (maDeviceString == "RDPUDD Chained DD"))) + { + // we need to block RDP as it does not provide OpenGL 2.1+ + mbRDP = true; + SAL_WARN("vcl.opengl", "RDP => blocked"); + return; + } + + /* create a device information set composed of the current display device */ + HDEVINFO devinfo = SetupDiGetClassDevsW(nullptr, o3tl::toW(maDeviceID.getStr()), nullptr, + DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES); + + if (devinfo != INVALID_HANDLE_VALUE) + { + HKEY key; + LONG result; + WCHAR value[255]; + DWORD dwcbData; + SP_DEVINFO_DATA devinfoData; + DWORD memberIndex = 0; + + devinfoData.cbSize = sizeof(devinfoData); + /* enumerate device information elements in the device information set */ + while (SetupDiEnumDeviceInfo(devinfo, memberIndex++, &devinfoData)) + { + /* get a string that identifies the device's driver key */ + if (SetupDiGetDeviceRegistryPropertyW(devinfo, + &devinfoData, + SPDRP_DRIVER, + nullptr, + reinterpret_cast<PBYTE>(value), + sizeof(value), + nullptr)) + { + OUString driverKey(OUStringLiteral("System\\CurrentControlSet\\Control\\Class\\") + o3tl::toU(value)); + result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, o3tl::toW(driverKey.getStr()), 0, KEY_QUERY_VALUE, &key); + if (result == ERROR_SUCCESS) + { + /* we've found the driver we're looking for */ + dwcbData = sizeof(value); + result = RegQueryValueExW(key, L"DriverVersion", nullptr, nullptr, + reinterpret_cast<LPBYTE>(value), &dwcbData); + if (result == ERROR_SUCCESS) + { + maDriverVersion = OUString(o3tl::toU(value)); + } + else + { + // If the entry wasn't found, assume the worst (0.0.0.0). + maDriverVersion = OUString("0.0.0.0"); + } + dwcbData = sizeof(value); + result = RegQueryValueExW(key, L"DriverDate", nullptr, nullptr, + reinterpret_cast<LPBYTE>(value), &dwcbData); + if (result == ERROR_SUCCESS) + { + maDriverDate = o3tl::toU(value); + } + else + { + // Again, assume the worst + maDriverDate = OUString("01-01-1970"); + } + RegCloseKey(key); + break; + } + } + } + + SetupDiDestroyDeviceInfoList(devinfo); + } + else + { + SAL_WARN("vcl.opengl", "invalid handle value"); + } + + appendIntegerWithPadding(maAdapterVendorID, ParseIDFromDeviceID(maDeviceID, "VEN_", 4), 4); + appendIntegerWithPadding(maAdapterDeviceID, ParseIDFromDeviceID(maDeviceID, "&DEV_", 4), 4); + appendIntegerWithPadding(maAdapterSubsysID, ParseIDFromDeviceID(maDeviceID, "&SUBSYS_", 8), 8); + + // We now check for second display adapter. + + // Device interface class for display adapters. + CLSID GUID_DISPLAY_DEVICE_ARRIVAL; + HRESULT hresult = CLSIDFromString(L"{1CA05180-A699-450A-9A0C-DE4FBE3DDD89}", + &GUID_DISPLAY_DEVICE_ARRIVAL); + if (hresult == NOERROR) + { + devinfo = SetupDiGetClassDevsW(&GUID_DISPLAY_DEVICE_ARRIVAL, + nullptr, nullptr, + DIGCF_PRESENT | DIGCF_INTERFACEDEVICE); + + if (devinfo != INVALID_HANDLE_VALUE) + { + HKEY key; + LONG result; + WCHAR value[255]; + DWORD dwcbData; + SP_DEVINFO_DATA devinfoData; + DWORD memberIndex = 0; + devinfoData.cbSize = sizeof(devinfoData); + + OUString aAdapterDriver2; + OUString aDeviceID2; + OUString aDriverVersion2; + OUString aDriverDate2; + uint32_t adapterVendorID2; + uint32_t adapterDeviceID2; + + /* enumerate device information elements in the device information set */ + while (SetupDiEnumDeviceInfo(devinfo, memberIndex++, &devinfoData)) + { + /* get a string that identifies the device's driver key */ + if (SetupDiGetDeviceRegistryPropertyW(devinfo, + &devinfoData, + SPDRP_DRIVER, + nullptr, + reinterpret_cast<PBYTE>(value), + sizeof(value), + nullptr)) + { + OUString driverKey2(OUStringLiteral("System\\CurrentControlSet\\Control\\Class\\") + o3tl::toU(value)); + result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, o3tl::toW(driverKey2.getStr()), 0, KEY_QUERY_VALUE, &key); + if (result == ERROR_SUCCESS) + { + dwcbData = sizeof(value); + result = RegQueryValueExW(key, L"MatchingDeviceId", nullptr, + nullptr, reinterpret_cast<LPBYTE>(value), &dwcbData); + if (result != ERROR_SUCCESS) + { + continue; + } + aDeviceID2 = o3tl::toU(value); + OUString aAdapterVendorID2String; + OUString aAdapterDeviceID2String; + adapterVendorID2 = ParseIDFromDeviceID(aDeviceID2, "VEN_", 4); + appendIntegerWithPadding(aAdapterVendorID2String, adapterVendorID2, 4); + adapterDeviceID2 = ParseIDFromDeviceID(aDeviceID2, "&DEV_", 4); + appendIntegerWithPadding(aAdapterDeviceID2String, adapterDeviceID2, 4); + if (maAdapterVendorID == aAdapterVendorID2String && + maAdapterDeviceID == aAdapterDeviceID2String) + { + RegCloseKey(key); + continue; + } + + // If this device is missing driver information, it is unlikely to + // be a real display adapter. + if (!GetKeyValue(o3tl::toW(driverKey2.getStr()), L"InstalledDisplayDrivers", + aAdapterDriver2, REG_MULTI_SZ)) + { + RegCloseKey(key); + continue; + } + dwcbData = sizeof(value); + result = RegQueryValueExW(key, L"DriverVersion", nullptr, nullptr, + reinterpret_cast<LPBYTE>(value), &dwcbData); + if (result != ERROR_SUCCESS) + { + RegCloseKey(key); + continue; + } + aDriverVersion2 = o3tl::toU(value); + dwcbData = sizeof(value); + result = RegQueryValueExW(key, L"DriverDate", nullptr, nullptr, + reinterpret_cast<LPBYTE>(value), &dwcbData); + if (result != ERROR_SUCCESS) + { + RegCloseKey(key); + continue; + } + aDriverDate2 = o3tl::toU(value); + dwcbData = sizeof(value); + result = RegQueryValueExW(key, L"Device Description", nullptr, + nullptr, reinterpret_cast<LPBYTE>(value), &dwcbData); + if (result != ERROR_SUCCESS) + { + dwcbData = sizeof(value); + result = RegQueryValueExW(key, L"DriverDesc", nullptr, nullptr, + reinterpret_cast<LPBYTE>(value), &dwcbData); + } + RegCloseKey(key); + if (result == ERROR_SUCCESS) + { + mbHasDualGPU = true; + maDeviceString2 = o3tl::toU(value); + maDeviceID2 = aDeviceID2; + maDeviceKey2 = driverKey2; + maDriverVersion2 = aDriverVersion2; + maDriverDate2 = aDriverDate2; + appendIntegerWithPadding(maAdapterVendorID2, adapterVendorID2, 4); + appendIntegerWithPadding(maAdapterDeviceID2, adapterDeviceID2, 4); + appendIntegerWithPadding(maAdapterSubsysID2, ParseIDFromDeviceID(maDeviceID2, "&SUBSYS_", 8), 8); + break; + } + } + } + } + + SetupDiDestroyDeviceInfoList(devinfo); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/opengl/win/gdiimpl.cxx b/vcl/opengl/win/gdiimpl.cxx new file mode 100644 index 000000000..eabfe8a09 --- /dev/null +++ b/vcl/opengl/win/gdiimpl.cxx @@ -0,0 +1,898 @@ +/* -*- 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 <vcl/opengl/OpenGLHelper.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; + +namespace { + +class GLWinWindow : public GLWindow +{ +public: + HWND hWnd; + HDC hDC; + HGLRC hRC; + GLWinWindow(); +}; + +} + +GLWinWindow::GLWinWindow() + : hWnd(nullptr) + , hDC(nullptr) + , hRC(nullptr) +{ +} + +namespace { + +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; +} + +static 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); + } +} + +static 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; +} + +static 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 + } +} + +static 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("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 + }; + + 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, /*bUseDoubleBufferedRendering*/true, 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(); +} + +OpenGLControlsCache::OpenGLControlsCache(): cache(200) {} + +OpenGLControlCacheType & OpenGLControlsCache::get() { + SalData * data = GetSalData(); + if (!data->m_pOpenGLControlsCache) { + data->m_pOpenGLControlsCache.reset(new OpenGLControlsCache); + } + return data->m_pOpenGLControlsCache->cache; +} + +OpenGLCompatibleDC::OpenGLCompatibleDC(SalGraphics &rGraphics, int x, int y, int width, int height) +: CompatibleDC( rGraphics, x, y, width, height, false ) +{ +} + +OpenGLTexture* OpenGLCompatibleDC::getOpenGLTexture() const +{ + if (!mpImpl) + return nullptr; + + // turn what's in the mpData into a texture + return new OpenGLTexture(maRects.mnSrcWidth, maRects.mnSrcHeight, GL_BGRA, GL_UNSIGNED_BYTE, mpData); +} + +std::unique_ptr<CompatibleDC::Texture> OpenGLCompatibleDC::getAsMaskTexture() const +{ + auto ret = std::make_unique<OpenGLCompatibleDC::Texture>(); + ret->texture = OpenGLTexture(maRects.mnSrcWidth, maRects.mnSrcHeight, GL_BGRA, GL_UNSIGNED_BYTE, mpData); + return ret; +} + +bool OpenGLCompatibleDC::copyToTexture(CompatibleDC::Texture& aTexture) const +{ + if (!mpImpl) + return false; + + assert(dynamic_cast<OpenGLCompatibleDC::Texture*>(&aTexture)); + return static_cast<OpenGLCompatibleDC::Texture&>(aTexture).texture.CopyData( + maRects.mnSrcWidth, maRects.mnSrcHeight, GL_BGRA, GL_UNSIGNED_BYTE, reinterpret_cast<sal_uInt8*>(mpData)); +} + +bool WinOpenGLSalGraphicsImpl::TryRenderCachedNativeControl(ControlCacheKey const & rControlCacheKey, int nX, int nY) +{ + static bool gbCacheEnabled = !getenv("SAL_WITHOUT_WIDGET_CACHE"); + + if (!gbCacheEnabled) + return false; + + auto & gTextureCache = OpenGLControlsCache::get(); + OpenGLControlCacheType::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.getOpenGLTexture()); + rCombo.mpMask.reset(rBlack.getOpenGLTexture()); + + bRet = RenderTextureCombo(rCombo, nX, nY); + + PostDraw(); + return bRet; +} + +bool WinOpenGLSalGraphicsImpl::RenderAndCacheNativeControl(CompatibleDC& rWhite, CompatibleDC& rBlack, + int nX, int nY , ControlCacheKey& aControlCacheKey) +{ + assert(dynamic_cast<OpenGLCompatibleDC*>(&rWhite)); + assert(dynamic_cast<OpenGLCompatibleDC*>(&rBlack)); + + std::unique_ptr<TextureCombo> pCombo(new TextureCombo); + + bool bResult = RenderCompatibleDC(static_cast<OpenGLCompatibleDC&>(rWhite), + static_cast<OpenGLCompatibleDC&>(rBlack), nX, nY, *pCombo); + if (!bResult) + return false; + + if (!aControlCacheKey.canCacheControl()) + return true; + + OpenGLControlCachePair pair(aControlCacheKey, std::move(pCombo)); + OpenGLControlsCache::get().insert(std::move(pair)); + + return bResult; +} + +void WinOpenGLSalGraphicsImpl::PreDrawText() +{ + PreDraw(); +} + +void WinOpenGLSalGraphicsImpl::PostDrawText() +{ + PostDraw(); +} + +void WinOpenGLSalGraphicsImpl::DeferredTextDraw(const CompatibleDC::Texture* pTexture, Color aMaskColor, const SalTwoRect& rPosAry) +{ + assert(dynamic_cast<const OpenGLCompatibleDC::Texture*>(pTexture)); + mpRenderList->addDrawTextureWithMaskColor( + static_cast<const OpenGLCompatibleDC::Texture*>(pTexture)->texture, aMaskColor, rPosAry); + PostBatchDraw(); +} + +void WinOpenGLSalGraphicsImpl::DrawTextMask( CompatibleDC::Texture* pTexture, Color nMaskColor, const SalTwoRect& rPosAry ) +{ + assert(dynamic_cast<OpenGLCompatibleDC::Texture*>(pTexture)); + DrawMask( static_cast<OpenGLCompatibleDC::Texture*>(pTexture)->texture, nMaskColor, rPosAry ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/opengl/win/winlayout.cxx b/vcl/opengl/win/winlayout.cxx new file mode 100644 index 000000000..59bf12c25 --- /dev/null +++ b/vcl/opengl/win/winlayout.cxx @@ -0,0 +1,60 @@ +/* -*- 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 <opengl/win/winlayout.hxx> + +#include <opengl/win/gdiimpl.hxx> + +bool OpenGLGlobalWinGlyphCache::AllocateTexture(WinGlyphDrawElement& rElement, CompatibleDC* dc) +{ + assert(rElement.maTexture.get() == nullptr); + assert(dynamic_cast<OpenGLCompatibleDC*>(dc)); + OpenGLCompatibleDC* odc = static_cast<OpenGLCompatibleDC*>(dc); + OpenGLCompatibleDC::Texture* texture = new OpenGLCompatibleDC::Texture; + rElement.maTexture.reset(texture); + texture->texture = maPackedTextureAtlas.Reserve(dc->getBitmapWidth(), dc->getBitmapHeight()); + if (!texture->texture) + return false; + if (!odc->copyToTexture(*rElement.maTexture)) + return false; + return true; +} + +void OpenGLGlobalWinGlyphCache::Prune() +{ + std::vector<GLuint> aTextureIDs = maPackedTextureAtlas.ReduceTextureNumber(8); + if (!aTextureIDs.empty()) + { + for (auto& pWinGlyphCache : maWinGlyphCaches) + static_cast<OpenGLWinGlyphCache*>(pWinGlyphCache)->RemoveTextures(aTextureIDs); + } +} + +void OpenGLWinGlyphCache::RemoveTextures(std::vector<GLuint>& rTextureIDs) +{ + auto it = maWinTextureCache.begin(); + + while (it != maWinTextureCache.end()) + { + assert(dynamic_cast<OpenGLCompatibleDC::Texture*>(it->second.maTexture.get())); + GLuint nTextureID + = static_cast<OpenGLCompatibleDC::Texture*>(it->second.maTexture.get())->texture.Id(); + + if (std::find(rTextureIDs.begin(), rTextureIDs.end(), nTextureID) != rTextureIDs.end()) + { + it = maWinTextureCache.erase(it); + } + else + { + ++it; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |