11e9fae67SLuboš Luňák /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 21e9fae67SLuboš Luňák /* 31e9fae67SLuboš Luňák * This file is part of the LibreOffice project. 41e9fae67SLuboš Luňák * 51e9fae67SLuboš Luňák * This Source Code Form is subject to the terms of the Mozilla Public 61e9fae67SLuboš Luňák * License, v. 2.0. If a copy of the MPL was not distributed with this 71e9fae67SLuboš Luňák * file, You can obtain one at http://mozilla.org/MPL/2.0/. 81e9fae67SLuboš Luňák */ 91e9fae67SLuboš Luňák 1035e471bbSStephan Bergmann #include <sal/config.h> 1135e471bbSStephan Bergmann 12*3c8af232SMike Kaganski #include <config_features.h> 13*3c8af232SMike Kaganski 1435e471bbSStephan Bergmann #include <string_view> 1535e471bbSStephan Bergmann 161e9fae67SLuboš Luňák #include <vcl/skia/SkiaHelper.hxx> 171e9fae67SLuboš Luňák 185c4f872aSLuboš Luňák #if !HAVE_FEATURE_SKIA 19ea5eb463SLuboš Luňák 20ea5eb463SLuboš Luňák namespace SkiaHelper 21ea5eb463SLuboš Luňák { 22ea5eb463SLuboš Luňák bool isVCLSkiaEnabled() { return false; } 233c63cb12SPatrick Luby bool isAlphaMaskBlendingEnabled() { return false; } 24ea5eb463SLuboš Luňák 25ea5eb463SLuboš Luňák } // namespace 265c4f872aSLuboš Luňák 275c4f872aSLuboš Luňák #else 285c4f872aSLuboš Luňák 292b702f74SLuboš Luňák #include <rtl/bootstrap.hxx> 302b702f74SLuboš Luňák #include <vcl/svapp.hxx> 312b702f74SLuboš Luňák #include <desktop/crashreport.hxx> 322b702f74SLuboš Luňák #include <officecfg/Office/Common.hxx> 332b702f74SLuboš Luňák #include <watchdog.hxx> 342b702f74SLuboš Luňák #include <skia/zone.hxx> 352b702f74SLuboš Luňák #include <sal/log.hxx> 362b702f74SLuboš Luňák #include <driverblocklist.hxx> 37ea5eb463SLuboš Luňák #include <skia/utils.hxx> 382b702f74SLuboš Luňák #include <config_folders.h> 39*3c8af232SMike Kaganski #include <config_skia.h> 40e3f5e49aSLuboš Luňák #include <osl/file.hxx> 41e3f5e49aSLuboš Luňák #include <tools/stream.hxx> 42fc0bff85SLuboš Luňák #include <list> 432c86b79eSLuboš Luňák #include <o3tl/lru_map.hxx> 44ea5eb463SLuboš Luňák 459f5e5ea0SLuboš Luňák #include <SkBitmap.h> 465d4cc08cSLuboš Luňák #include <SkCanvas.h> 479f5e5ea0SLuboš Luňák #include <SkEncodedImageFormat.h> 485d4cc08cSLuboš Luňák #include <SkPaint.h> 498fede4e7SLuboš Luňák #include <SkSurface.h> 50a3020001SLuboš Luňák #include <SkGraphics.h> 51eaf4f6d3SLuboš Luňák #include <GrDirectContext.h> 52110fa313SLuboš Luňák #include <SkRuntimeEffect.h> 53f8c15850SNoel Grandin #include <SkStream.h> 54f8c15850SNoel Grandin #include <SkTileMode.h> 552eeb6822SLuboš Luňák #include <skia_compiler.hxx> 567a38f181SLuboš Luňák #include <skia_opts.hxx> 57f8c15850SNoel Grandin #if defined(MACOSX) 58f8c15850SNoel Grandin #include <premac.h> 59f8c15850SNoel Grandin #endif 60d4afd3aeSLuboš Luňák #include <tools/sk_app/VulkanWindowContext.h> 61d4afd3aeSLuboš Luňák #include <tools/sk_app/MetalWindowContext.h> 62f8c15850SNoel Grandin #if defined(MACOSX) 63f8c15850SNoel Grandin #include <postmac.h> 64f8c15850SNoel Grandin #endif 65f8c15850SNoel Grandin #include <src/core/SkOpts.h> 66f8c15850SNoel Grandin #include <src/core/SkChecksum.h> 67f8c15850SNoel Grandin #include <include/encode/SkPngEncoder.h> 68f8c15850SNoel Grandin #include <ganesh/SkSurfaceGanesh.h> 69f8c15850SNoel Grandin #if defined _MSC_VER 70f8c15850SNoel Grandin #pragma warning(disable : 4100) // "unreferenced formal parameter" 71f8c15850SNoel Grandin #pragma warning(disable : 4324) // "structure was padded due to alignment specifier" 72f8c15850SNoel Grandin #endif 73f8c15850SNoel Grandin #if defined __clang__ 74f8c15850SNoel Grandin #pragma clang diagnostic push 75f8c15850SNoel Grandin #pragma clang diagnostic ignored "-Wunused-parameter" 76f8c15850SNoel Grandin #endif 77f8c15850SNoel Grandin #if defined __GNUC__ && !defined __clang__ 78f8c15850SNoel Grandin #pragma GCC diagnostic push 796ed4c4a1SJustin Luth #pragma GCC diagnostic ignored "-Wunused-parameter" 80f8c15850SNoel Grandin #endif 81f8c15850SNoel Grandin #include <src/image/SkImage_Base.h> 82f8c15850SNoel Grandin #if defined __GNUC__ && !defined __clang__ 83f8c15850SNoel Grandin #pragma GCC diagnostic pop 84f8c15850SNoel Grandin #endif 85f8c15850SNoel Grandin #if defined __clang__ 86f8c15850SNoel Grandin #pragma clang diagnostic pop 87f8c15850SNoel Grandin #endif 88f8c15850SNoel Grandin 898fede4e7SLuboš Luňák #include <fstream> 908fede4e7SLuboš Luňák 91fe3fa169SPatrick Luby #ifdef SK_METAL 92fe3fa169SPatrick Luby #ifdef MACOSX 93fe3fa169SPatrick Luby #include <quartz/cgutils.h> 94fe3fa169SPatrick Luby #endif 95fe3fa169SPatrick Luby #endif 96fe3fa169SPatrick Luby 97ea5eb463SLuboš Luňák namespace SkiaHelper 98ea5eb463SLuboš Luňák { 99e3f5e49aSLuboš Luňák static OUString getCacheFolder() 100e3f5e49aSLuboš Luňák { 101e3f5e49aSLuboš Luňák OUString url("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER 102e3f5e49aSLuboš Luňák "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/cache/"); 103e3f5e49aSLuboš Luňák rtl::Bootstrap::expandMacros(url); 104e3f5e49aSLuboš Luňák osl::Directory::create(url); 105e3f5e49aSLuboš Luňák return url; 106e3f5e49aSLuboš Luňák } 107e3f5e49aSLuboš Luňák 108e3f5e49aSLuboš Luňák static void writeToLog(SvStream& stream, const char* key, const char* value) 109e3f5e49aSLuboš Luňák { 1105fe96b6dSMike Kaganski stream.WriteOString(key); 1115fe96b6dSMike Kaganski stream.WriteOString(": "); 1125fe96b6dSMike Kaganski stream.WriteOString(value); 113e3f5e49aSLuboš Luňák stream.WriteChar('\n'); 114e3f5e49aSLuboš Luňák } 115e3f5e49aSLuboš Luňák 1164356790bSHeiko Tietze OUString readLog() 1174356790bSHeiko Tietze { 1184356790bSHeiko Tietze SvFileStream logFile(getCacheFolder() + "/skia.log", StreamMode::READ); 1194356790bSHeiko Tietze 1204356790bSHeiko Tietze OUString sResult; 1214356790bSHeiko Tietze OString sLine; 1224356790bSHeiko Tietze while (logFile.ReadLine(sLine)) 1234356790bSHeiko Tietze sResult += OStringToOUString(sLine, RTL_TEXTENCODING_UTF8) + "\n"; 1244356790bSHeiko Tietze 1254356790bSHeiko Tietze return sResult; 1264356790bSHeiko Tietze } 1274356790bSHeiko Tietze 128d4afd3aeSLuboš Luňák uint32_t vendorId = 0; 129d4afd3aeSLuboš Luňák 130d4afd3aeSLuboš Luňák #ifdef SK_VULKAN 13135e471bbSStephan Bergmann static void writeToLog(SvStream& stream, const char* key, std::u16string_view value) 132e3f5e49aSLuboš Luňák { 133e3f5e49aSLuboš Luňák writeToLog(stream, key, OUStringToOString(value, RTL_TEXTENCODING_UTF8).getStr()); 134e3f5e49aSLuboš Luňák } 135e3f5e49aSLuboš Luňák 136d4afd3aeSLuboš Luňák static OUString getDenylistFile() 137d4afd3aeSLuboš Luňák { 138d4afd3aeSLuboš Luňák OUString url("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER); 139d4afd3aeSLuboš Luňák rtl::Bootstrap::expandMacros(url); 140d4afd3aeSLuboš Luňák 141d4afd3aeSLuboš Luňák return url + "/skia/skia_denylist_vulkan.xml"; 142d4afd3aeSLuboš Luňák } 143d4afd3aeSLuboš Luňák 144d4afd3aeSLuboš Luňák static uint32_t driverVersion = 0; 145d4afd3aeSLuboš Luňák 146d4afd3aeSLuboš Luňák static OUString versionAsString(uint32_t version) 147d4afd3aeSLuboš Luňák { 148d4afd3aeSLuboš Luňák return OUString::number(version >> 22) + "." + OUString::number((version >> 12) & 0x3ff) + "." 149d4afd3aeSLuboš Luňák + OUString::number(version & 0xfff); 150d4afd3aeSLuboš Luňák } 151d4afd3aeSLuboš Luňák 152d4afd3aeSLuboš Luňák static std::string_view vendorAsString(uint32_t vendor) 153d4afd3aeSLuboš Luňák { 154d4afd3aeSLuboš Luňák return DriverBlocklist::GetVendorNameFromId(vendor); 155d4afd3aeSLuboš Luňák } 156d4afd3aeSLuboš Luňák 157e3f5e49aSLuboš Luňák // Note that this function also logs system information about Vulkan. 158493ae7a6SThorsten Behrens static bool isVulkanDenylisted(const VkPhysicalDeviceProperties& props) 15900dfb826SLuboš Luňák { 1603081998aSLuboš Luňák static const char* const types[] 1613081998aSLuboš Luňák = { "other", "integrated", "discrete", "virtual", "cpu", "??" }; // VkPhysicalDeviceType 162e3f5e49aSLuboš Luňák driverVersion = props.driverVersion; 1631127a8deSLuboš Luňák vendorId = props.vendorID; 1641127a8deSLuboš Luňák OUString vendorIdStr = "0x" + OUString::number(props.vendorID, 16); 1651127a8deSLuboš Luňák OUString deviceIdStr = "0x" + OUString::number(props.deviceID, 16); 166e3f5e49aSLuboš Luňák OUString driverVersionString = versionAsString(driverVersion); 167e3f5e49aSLuboš Luňák OUString apiVersion = versionAsString(props.apiVersion); 168e3f5e49aSLuboš Luňák const char* deviceType = types[std::min<unsigned>(props.deviceType, SAL_N_ELEMENTS(types) - 1)]; 169e3f5e49aSLuboš Luňák 170e3f5e49aSLuboš Luňák CrashReporter::addKeyValue("VulkanVendor", vendorIdStr, CrashReporter::AddItem); 171e3f5e49aSLuboš Luňák CrashReporter::addKeyValue("VulkanDevice", deviceIdStr, CrashReporter::AddItem); 172e3f5e49aSLuboš Luňák CrashReporter::addKeyValue("VulkanAPI", apiVersion, CrashReporter::AddItem); 173e3f5e49aSLuboš Luňák CrashReporter::addKeyValue("VulkanDriver", driverVersionString, CrashReporter::AddItem); 174e3f5e49aSLuboš Luňák CrashReporter::addKeyValue("VulkanDeviceType", OUString::createFromAscii(deviceType), 175e3f5e49aSLuboš Luňák CrashReporter::AddItem); 176e3f5e49aSLuboš Luňák CrashReporter::addKeyValue("VulkanDeviceName", OUString::createFromAscii(props.deviceName), 177e3f5e49aSLuboš Luňák CrashReporter::Write); 178e3f5e49aSLuboš Luňák 1792eeb6822SLuboš Luňák SvFileStream logFile(getCacheFolder() + "/skia.log", StreamMode::WRITE | StreamMode::TRUNC); 180e3f5e49aSLuboš Luňák writeToLog(logFile, "RenderMethod", "vulkan"); 181e3f5e49aSLuboš Luňák writeToLog(logFile, "Vendor", vendorIdStr); 182e3f5e49aSLuboš Luňák writeToLog(logFile, "Device", deviceIdStr); 183e3f5e49aSLuboš Luňák writeToLog(logFile, "API", apiVersion); 184e3f5e49aSLuboš Luňák writeToLog(logFile, "Driver", driverVersionString); 185e3f5e49aSLuboš Luňák writeToLog(logFile, "DeviceType", deviceType); 186e3f5e49aSLuboš Luňák writeToLog(logFile, "DeviceName", props.deviceName); 187e3f5e49aSLuboš Luňák 1883081998aSLuboš Luňák SAL_INFO("vcl.skia", 189e3f5e49aSLuboš Luňák "Vulkan API version: " << apiVersion << ", driver version: " << driverVersionString 190e3f5e49aSLuboš Luňák << ", vendor: " << vendorIdStr << " (" 191e3f5e49aSLuboš Luňák << vendorAsString(vendorId) << "), device: " << deviceIdStr 192e3f5e49aSLuboš Luňák << ", type: " << deviceType << ", name: " << props.deviceName); 193bf62a890SLuboš Luňák bool denylisted 194bf62a890SLuboš Luňák = DriverBlocklist::IsDeviceBlocked(getDenylistFile(), DriverBlocklist::VersionType::Vulkan, 195bf62a890SLuboš Luňák driverVersionString, vendorIdStr, deviceIdStr); 196493ae7a6SThorsten Behrens writeToLog(logFile, "Denylisted", denylisted ? "yes" : "no"); 197493ae7a6SThorsten Behrens return denylisted; 198e3f5e49aSLuboš Luňák } 199d4afd3aeSLuboš Luňák #endif 200d4afd3aeSLuboš Luňák 201d4afd3aeSLuboš Luňák #ifdef SK_METAL 202d4afd3aeSLuboš Luňák static void writeSkiaMetalInfo() 203d4afd3aeSLuboš Luňák { 204d4afd3aeSLuboš Luňák SvFileStream logFile(getCacheFolder() + "/skia.log", StreamMode::WRITE | StreamMode::TRUNC); 205d4afd3aeSLuboš Luňák writeToLog(logFile, "RenderMethod", "metal"); 206d4afd3aeSLuboš Luňák } 207d4afd3aeSLuboš Luňák #endif 208e3f5e49aSLuboš Luňák 209e3f5e49aSLuboš Luňák static void writeSkiaRasterInfo() 210e3f5e49aSLuboš Luňák { 2112eeb6822SLuboš Luňák SvFileStream logFile(getCacheFolder() + "/skia.log", StreamMode::WRITE | StreamMode::TRUNC); 212e3f5e49aSLuboš Luňák writeToLog(logFile, "RenderMethod", "raster"); 213e3f5e49aSLuboš Luňák // Log compiler, Skia works best when compiled with Clang. 2142eeb6822SLuboš Luňák writeToLog(logFile, "Compiler", skia_compiler_name()); 2153081998aSLuboš Luňák } 21600dfb826SLuboš Luňák 217bbf7dc39SLuboš Luňák #if defined(SK_VULKAN) || defined(SK_METAL) 218d4afd3aeSLuboš Luňák static std::unique_ptr<sk_app::WindowContext> getTemporaryWindowContext(); 219d4afd3aeSLuboš Luňák #endif 220b275cce0SLuboš Luňák 221493ae7a6SThorsten Behrens static void checkDeviceDenylisted(bool blockDisable = false) 2223081998aSLuboš Luňák { 2233081998aSLuboš Luňák static bool done = false; 224f45ff1a7SNoel Grandin if (done) 225f45ff1a7SNoel Grandin return; 226f45ff1a7SNoel Grandin 227f45ff1a7SNoel Grandin SkiaZone zone; 2283081998aSLuboš Luňák 229bbf7dc39SLuboš Luňák bool useRaster = false; 230f45ff1a7SNoel Grandin switch (renderMethodToUse()) 231f45ff1a7SNoel Grandin { 232f45ff1a7SNoel Grandin case RenderVulkan: 2333081998aSLuboš Luňák { 234d4afd3aeSLuboš Luňák #ifdef SK_VULKAN 235eaf4f6d3SLuboš Luňák // First try if a GrDirectContext already exists. 236d4afd3aeSLuboš Luňák std::unique_ptr<sk_app::WindowContext> temporaryWindowContext; 237d4afd3aeSLuboš Luňák GrDirectContext* grDirectContext 238eaf4f6d3SLuboš Luňák = sk_app::VulkanWindowContext::getSharedGrDirectContext(); 239d4afd3aeSLuboš Luňák if (!grDirectContext) 2403081998aSLuboš Luňák { 241f45ff1a7SNoel Grandin // This function is called from isVclSkiaEnabled(), which 242f45ff1a7SNoel Grandin // may be called when deciding which X11 visual to use, 243f45ff1a7SNoel Grandin // and that visual is normally needed when creating 244eaf4f6d3SLuboš Luňák // Skia's VulkanWindowContext, which is needed for the GrDirectContext. 245d4afd3aeSLuboš Luňák // Avoid the loop by creating a temporary WindowContext 246f45ff1a7SNoel Grandin // that will use the default X11 visual (that shouldn't matter 247f45ff1a7SNoel Grandin // for just finding out information about Vulkan) and destroying 248f45ff1a7SNoel Grandin // the temporary context will clean up again. 249d4afd3aeSLuboš Luňák temporaryWindowContext = getTemporaryWindowContext(); 250d4afd3aeSLuboš Luňák grDirectContext = sk_app::VulkanWindowContext::getSharedGrDirectContext(); 2513081998aSLuboš Luňák } 252f45ff1a7SNoel Grandin bool denylisted = true; // assume the worst 253d4afd3aeSLuboš Luňák if (grDirectContext) // Vulkan was initialized properly 254f45ff1a7SNoel Grandin { 255f45ff1a7SNoel Grandin denylisted 256f45ff1a7SNoel Grandin = isVulkanDenylisted(sk_app::VulkanWindowContext::getPhysDeviceProperties()); 257f45ff1a7SNoel Grandin SAL_INFO("vcl.skia", "Vulkan denylisted: " << denylisted); 258f45ff1a7SNoel Grandin } 259f45ff1a7SNoel Grandin else 260f45ff1a7SNoel Grandin SAL_INFO("vcl.skia", "Vulkan could not be initialized"); 261f45ff1a7SNoel Grandin if (denylisted && !blockDisable) 262f45ff1a7SNoel Grandin { 263f45ff1a7SNoel Grandin disableRenderMethod(RenderVulkan); 264bbf7dc39SLuboš Luňák useRaster = true; 265f45ff1a7SNoel Grandin } 266d4afd3aeSLuboš Luňák #else 267d4afd3aeSLuboš Luňák SAL_WARN("vcl.skia", "Vulkan support not built in"); 268d4afd3aeSLuboš Luňák (void)blockDisable; 269bbf7dc39SLuboš Luňák useRaster = true; 270d4afd3aeSLuboš Luňák #endif 271bbf7dc39SLuboš Luňák break; 2723081998aSLuboš Luňák } 273d4afd3aeSLuboš Luňák case RenderMetal: 274bbf7dc39SLuboš Luňák { 275d4afd3aeSLuboš Luňák #ifdef SK_METAL 276bbf7dc39SLuboš Luňák // First try if a GrDirectContext already exists. 277bbf7dc39SLuboš Luňák std::unique_ptr<sk_app::WindowContext> temporaryWindowContext; 278bbf7dc39SLuboš Luňák GrDirectContext* grDirectContext = sk_app::getMetalSharedGrDirectContext(); 279bbf7dc39SLuboš Luňák if (!grDirectContext) 280bbf7dc39SLuboš Luňák { 281bbf7dc39SLuboš Luňák // Create a temporary window context just to get the GrDirectContext, 282bbf7dc39SLuboš Luňák // as an initial test of Metal functionality. 283bbf7dc39SLuboš Luňák temporaryWindowContext = getTemporaryWindowContext(); 284bbf7dc39SLuboš Luňák grDirectContext = sk_app::getMetalSharedGrDirectContext(); 285bbf7dc39SLuboš Luňák } 286bbf7dc39SLuboš Luňák if (grDirectContext) // Metal was initialized properly 287bbf7dc39SLuboš Luňák { 288fe3fa169SPatrick Luby #ifdef MACOSX 289fe3fa169SPatrick Luby if (!blockDisable && !DefaultMTLDeviceIsSupported()) 290fe3fa169SPatrick Luby { 291fe3fa169SPatrick Luby SAL_INFO("vcl.skia", "Metal default device not supported"); 292fe3fa169SPatrick Luby disableRenderMethod(RenderMetal); 293fe3fa169SPatrick Luby useRaster = true; 294fe3fa169SPatrick Luby } 295fe3fa169SPatrick Luby else 296fe3fa169SPatrick Luby #endif 297fe3fa169SPatrick Luby { 298fe3fa169SPatrick Luby SAL_INFO("vcl.skia", "Using Skia Metal mode"); 299fe3fa169SPatrick Luby writeSkiaMetalInfo(); 300fe3fa169SPatrick Luby } 301bbf7dc39SLuboš Luňák } 302bbf7dc39SLuboš Luňák else 303bbf7dc39SLuboš Luňák { 304bbf7dc39SLuboš Luňák SAL_INFO("vcl.skia", "Metal could not be initialized"); 305bbf7dc39SLuboš Luňák disableRenderMethod(RenderMetal); 306bbf7dc39SLuboš Luňák useRaster = true; 307bbf7dc39SLuboš Luňák } 308d4afd3aeSLuboš Luňák #else 309d4afd3aeSLuboš Luňák SAL_WARN("vcl.skia", "Metal support not built in"); 310bbf7dc39SLuboš Luňák useRaster = true; 311d4afd3aeSLuboš Luňák #endif 312bbf7dc39SLuboš Luňák break; 313bbf7dc39SLuboš Luňák } 314f45ff1a7SNoel Grandin case RenderRaster: 315bbf7dc39SLuboš Luňák useRaster = true; 316d4afd3aeSLuboš Luňák break; 3173081998aSLuboš Luňák } 318bbf7dc39SLuboš Luňák if (useRaster) 319bbf7dc39SLuboš Luňák { 320bbf7dc39SLuboš Luňák SAL_INFO("vcl.skia", "Using Skia raster mode"); 321bbf7dc39SLuboš Luňák // software, never denylisted 322bbf7dc39SLuboš Luňák writeSkiaRasterInfo(); 323bbf7dc39SLuboš Luňák } 324f45ff1a7SNoel Grandin done = true; 32500dfb826SLuboš Luňák } 32600dfb826SLuboš Luňák 327a3d3e076SLuboš Luňák static bool skiaSupportedByBackend = false; 328a3d3e076SLuboš Luňák static bool supportsVCLSkia() 329a3d3e076SLuboš Luňák { 330a3d3e076SLuboš Luňák if (!skiaSupportedByBackend) 331a3d3e076SLuboš Luňák { 332a3d3e076SLuboš Luňák SAL_INFO("vcl.skia", "Skia not supported by VCL backend, disabling"); 333a3d3e076SLuboš Luňák return false; 334a3d3e076SLuboš Luňák } 335a3d3e076SLuboš Luňák return getenv("SAL_DISABLESKIA") == nullptr; 336a3d3e076SLuboš Luňák } 3373081998aSLuboš Luňák 3386c58b01cSLuboš Luňák static void initInternal(); 3396c58b01cSLuboš Luňák 340ea5eb463SLuboš Luňák bool isVCLSkiaEnabled() 3411e9fae67SLuboš Luňák { 3421e9fae67SLuboš Luňák /** 3431e9fae67SLuboš Luňák * The !bSet part should only be called once! Changing the results in the same 3441e9fae67SLuboš Luňák * run will mix Skia and normal rendering. 3451e9fae67SLuboš Luňák */ 3461e9fae67SLuboš Luňák 3471e9fae67SLuboš Luňák static bool bSet = false; 3481e9fae67SLuboš Luňák static bool bEnable = false; 3491e9fae67SLuboš Luňák static bool bForceSkia = false; 3501e9fae67SLuboš Luňák 3515f29cb2fSArmin Le Grand (Allotropia) // allow global disable when testing SystemPrimitiveRenderer since current Skia on Win does not 3525f29cb2fSArmin Le Grand (Allotropia) // harmonize with using Direct2D and D2DPixelProcessor2D 3535f29cb2fSArmin Le Grand (Allotropia) static const bool bTestSystemPrimitiveRenderer( 3545f29cb2fSArmin Le Grand (Allotropia) nullptr != std::getenv("TEST_SYSTEM_PRIMITIVE_RENDERER")); 3555f29cb2fSArmin Le Grand (Allotropia) if (bTestSystemPrimitiveRenderer) 3565f29cb2fSArmin Le Grand (Allotropia) return false; 3575f29cb2fSArmin Le Grand (Allotropia) 3581e9fae67SLuboš Luňák if (bSet) 3591e9fae67SLuboš Luňák { 3601e9fae67SLuboš Luňák return bForceSkia || bEnable; 3611e9fae67SLuboš Luňák } 3621e9fae67SLuboš Luňák 3631e9fae67SLuboš Luňák /* 3641e9fae67SLuboš Luňák * There are a number of cases that these environment variables cover: 365493ae7a6SThorsten Behrens * * SAL_FORCESKIA forces Skia if disabled by UI options or denylisted 366a65cba8eSLuboš Luňák * * SAL_DISABLESKIA avoids the use of Skia regardless of any option 3671e9fae67SLuboš Luňák */ 3681e9fae67SLuboš Luňák 3691e9fae67SLuboš Luňák bSet = true; 37099bff8cfSLuboš Luňák bForceSkia = !!getenv("SAL_FORCESKIA") || officecfg::Office::Common::VCL::ForceSkia::get(); 3711e9fae67SLuboš Luňák 3721e9fae67SLuboš Luňák bool bRet = false; 37300dfb826SLuboš Luňák bool bSupportsVCLSkia = supportsVCLSkia(); 374a65cba8eSLuboš Luňák if (bForceSkia && bSupportsVCLSkia) 3751e9fae67SLuboš Luňák { 3761e9fae67SLuboš Luňák bRet = true; 3776c58b01cSLuboš Luňák initInternal(); 378493ae7a6SThorsten Behrens // don't actually block if denylisted, but log it if enabled, and also get the vendor id 379493ae7a6SThorsten Behrens checkDeviceDenylisted(true); 3801e9fae67SLuboš Luňák } 38167770fc4SLuboš Luňák else if (getenv("SAL_FORCEGL")) 38267770fc4SLuboš Luňák { 38367770fc4SLuboš Luňák // Skia usage is checked before GL usage, so if GL is forced (and Skia is not), do not 38467770fc4SLuboš Luňák // enable Skia in order to allow GL. 38567770fc4SLuboš Luňák bRet = false; 38667770fc4SLuboš Luňák } 3871e9fae67SLuboš Luňák else if (bSupportsVCLSkia) 3881e9fae67SLuboš Luňák { 3891e9fae67SLuboš Luňák static bool bEnableSkiaEnv = !!getenv("SAL_ENABLESKIA"); 3901e9fae67SLuboš Luňák 3911e9fae67SLuboš Luňák bEnable = bEnableSkiaEnv; 3921e9fae67SLuboš Luňák 39399bff8cfSLuboš Luňák if (officecfg::Office::Common::VCL::UseSkia::get()) 3949245fd02STomoyuki Kubota bEnable = true; 3951e9fae67SLuboš Luňák 3961e9fae67SLuboš Luňák // Force disable in safe mode 3971e9fae67SLuboš Luňák if (Application::IsSafeModeEnabled()) 3981e9fae67SLuboš Luňák bEnable = false; 3991e9fae67SLuboš Luňák 4003081998aSLuboš Luňák if (bEnable) 401a3020001SLuboš Luňák { 4026c58b01cSLuboš Luňák initInternal(); 403493ae7a6SThorsten Behrens checkDeviceDenylisted(); // switch to raster if driver is denylisted 404a3020001SLuboš Luňák } 4053081998aSLuboš Luňák 4061e9fae67SLuboš Luňák bRet = bEnable; 4071e9fae67SLuboš Luňák } 4081e9fae67SLuboš Luňák 4098a2a9d28SLuboš Luňák if (bRet) 4108a2a9d28SLuboš Luňák WatchdogThread::start(); 4118a2a9d28SLuboš Luňák 41299bff8cfSLuboš Luňák CrashReporter::addKeyValue("UseSkia", OUString::boolean(bRet), CrashReporter::Write); 4131e9fae67SLuboš Luňák 4141e9fae67SLuboš Luňák return bRet; 4151e9fae67SLuboš Luňák } 4161e9fae67SLuboš Luňák 4173c63cb12SPatrick Luby bool isAlphaMaskBlendingEnabled() { return false; } 4183c63cb12SPatrick Luby 419ea5eb463SLuboš Luňák static RenderMethod methodToUse = RenderRaster; 420241730a7SLuboš Luňák 421241730a7SLuboš Luňák static bool initRenderMethodToUse() 422241730a7SLuboš Luňák { 42391b4bfeeSMike Kaganski if (Application::IsBitmapRendering()) 42491b4bfeeSMike Kaganski { 42591b4bfeeSMike Kaganski methodToUse = RenderRaster; 42691b4bfeeSMike Kaganski return true; 42791b4bfeeSMike Kaganski } 42891b4bfeeSMike Kaganski 429241730a7SLuboš Luňák if (const char* env = getenv("SAL_SKIA")) 430241730a7SLuboš Luňák { 431241730a7SLuboš Luňák if (strcmp(env, "raster") == 0) 432241730a7SLuboš Luňák { 433ea5eb463SLuboš Luňák methodToUse = RenderRaster; 434241730a7SLuboš Luňák return true; 435241730a7SLuboš Luňák } 436d4afd3aeSLuboš Luňák #ifdef MACOSX 437d4afd3aeSLuboš Luňák if (strcmp(env, "metal") == 0) 438d4afd3aeSLuboš Luňák { 439d4afd3aeSLuboš Luňák methodToUse = RenderMetal; 440d4afd3aeSLuboš Luňák return true; 441d4afd3aeSLuboš Luňák } 442d4afd3aeSLuboš Luňák #else 443e76bb4eaSLuboš Luňák if (strcmp(env, "vulkan") == 0) 444e76bb4eaSLuboš Luňák { 445e76bb4eaSLuboš Luňák methodToUse = RenderVulkan; 446e76bb4eaSLuboš Luňák return true; 447e76bb4eaSLuboš Luňák } 448d4afd3aeSLuboš Luňák #endif 449e76bb4eaSLuboš Luňák SAL_WARN("vcl.skia", "Unrecognized value of SAL_SKIA"); 450e76bb4eaSLuboš Luňák abort(); 451241730a7SLuboš Luňák } 452d4afd3aeSLuboš Luňák methodToUse = RenderRaster; 45390b02dd4SLuboš Luňák if (officecfg::Office::Common::VCL::ForceSkiaRaster::get()) 45490b02dd4SLuboš Luňák return true; 455d4afd3aeSLuboš Luňák #ifdef SK_METAL 456d4afd3aeSLuboš Luňák methodToUse = RenderMetal; 457d4afd3aeSLuboš Luňák #endif 458d4afd3aeSLuboš Luňák #ifdef SK_VULKAN 459ea5eb463SLuboš Luňák methodToUse = RenderVulkan; 460d4afd3aeSLuboš Luňák #endif 461241730a7SLuboš Luňák return true; 462241730a7SLuboš Luňák } 463241730a7SLuboš Luňák 464ea5eb463SLuboš Luňák RenderMethod renderMethodToUse() 465241730a7SLuboš Luňák { 466241730a7SLuboš Luňák static bool methodToUseInited = initRenderMethodToUse(); 467c2709d9fSLuboš Luňák if (methodToUseInited) // Used just to ensure thread-safe one-time init. 468c2709d9fSLuboš Luňák return methodToUse; 469c2709d9fSLuboš Luňák abort(); 470241730a7SLuboš Luňák } 471241730a7SLuboš Luňák 472ea5eb463SLuboš Luňák void disableRenderMethod(RenderMethod method) 473241730a7SLuboš Luňák { 474241730a7SLuboš Luňák if (renderMethodToUse() != method) 475241730a7SLuboš Luňák return; 476241730a7SLuboš Luňák // Choose a fallback, right now always raster. 477241730a7SLuboš Luňák methodToUse = RenderRaster; 478241730a7SLuboš Luňák } 479241730a7SLuboš Luňák 480d4afd3aeSLuboš Luňák // If needed, we'll allocate one extra window context so that we have a valid GrDirectContext 481d4afd3aeSLuboš Luňák // from Vulkan/MetalWindowContext. 482d4afd3aeSLuboš Luňák static std::unique_ptr<sk_app::WindowContext> sharedWindowContext; 483ea5eb463SLuboš Luňák 484d4afd3aeSLuboš Luňák static std::unique_ptr<sk_app::WindowContext> (*createGpuWindowContextFunction)(bool) = nullptr; 485d4afd3aeSLuboš Luňák static void setCreateGpuWindowContext(std::unique_ptr<sk_app::WindowContext> (*function)(bool)) 4863081998aSLuboš Luňák { 487d4afd3aeSLuboš Luňák createGpuWindowContextFunction = function; 4883081998aSLuboš Luňák } 4893081998aSLuboš Luňák 490eaf4f6d3SLuboš Luňák GrDirectContext* getSharedGrDirectContext() 491ea5eb463SLuboš Luňák { 4928a2a9d28SLuboš Luňák SkiaZone zone; 493d4afd3aeSLuboš Luňák assert(renderMethodToUse() != RenderRaster); 494d4afd3aeSLuboš Luňák if (sharedWindowContext) 495d4afd3aeSLuboš Luňák return sharedWindowContext->directContext(); 496ea5eb463SLuboš Luňák // TODO mutex? 497d4afd3aeSLuboš Luňák // Set up the shared GrDirectContext from Skia's (patched) Vulkan/MetalWindowContext, if it's been 498ea5eb463SLuboš Luňák // already set up. 499d4afd3aeSLuboš Luňák switch (renderMethodToUse()) 500ea5eb463SLuboš Luňák { 501d4afd3aeSLuboš Luňák case RenderVulkan: 502d4afd3aeSLuboš Luňák #ifdef SK_VULKAN 503d4afd3aeSLuboš Luňák if (GrDirectContext* context = sk_app::VulkanWindowContext::getSharedGrDirectContext()) 504d4afd3aeSLuboš Luňák return context; 505d4afd3aeSLuboš Luňák #endif 506d4afd3aeSLuboš Luňák break; 507d4afd3aeSLuboš Luňák case RenderMetal: 508d4afd3aeSLuboš Luňák #ifdef SK_METAL 509d4afd3aeSLuboš Luňák if (GrDirectContext* context = sk_app::getMetalSharedGrDirectContext()) 510d4afd3aeSLuboš Luňák return context; 511d4afd3aeSLuboš Luňák #endif 512d4afd3aeSLuboš Luňák break; 513d4afd3aeSLuboš Luňák case RenderRaster: 514d4afd3aeSLuboš Luňák abort(); 515ea5eb463SLuboš Luňák } 5163081998aSLuboš Luňák static bool done = false; 5173081998aSLuboš Luňák if (done) 5183081998aSLuboš Luňák return nullptr; 5193081998aSLuboš Luňák done = true; 520d4afd3aeSLuboš Luňák if (createGpuWindowContextFunction == nullptr) 521a3d3e076SLuboš Luňák return nullptr; // not initialized properly (e.g. used from a VCL backend with no Skia support) 522d4afd3aeSLuboš Luňák sharedWindowContext = createGpuWindowContextFunction(false); 523d4afd3aeSLuboš Luňák GrDirectContext* grDirectContext 524d4afd3aeSLuboš Luňák = sharedWindowContext ? sharedWindowContext->directContext() : nullptr; 525eaf4f6d3SLuboš Luňák if (grDirectContext) 526eaf4f6d3SLuboš Luňák return grDirectContext; 527d4afd3aeSLuboš Luňák SAL_WARN_IF(renderMethodToUse() == RenderVulkan, "vcl.skia", 528d4afd3aeSLuboš Luňák "Cannot create Vulkan GPU context, falling back to Raster"); 529d4afd3aeSLuboš Luňák SAL_WARN_IF(renderMethodToUse() == RenderMetal, "vcl.skia", 530d4afd3aeSLuboš Luňák "Cannot create Metal GPU context, falling back to Raster"); 531d4afd3aeSLuboš Luňák disableRenderMethod(renderMethodToUse()); 532ea5eb463SLuboš Luňák return nullptr; 533ea5eb463SLuboš Luňák } 534ea5eb463SLuboš Luňák 535bbf7dc39SLuboš Luňák #if defined(SK_VULKAN) || defined(SK_METAL) 536d4afd3aeSLuboš Luňák static std::unique_ptr<sk_app::WindowContext> getTemporaryWindowContext() 537b275cce0SLuboš Luňák { 538d4afd3aeSLuboš Luňák if (createGpuWindowContextFunction == nullptr) 539d4afd3aeSLuboš Luňák return nullptr; 540d4afd3aeSLuboš Luňák return createGpuWindowContextFunction(true); 541b275cce0SLuboš Luňák } 542d4afd3aeSLuboš Luňák #endif 543b275cce0SLuboš Luňák 5448ca549ebSLuboš Luňák static RenderMethod renderMethodToUseForSize(const SkISize& size) 5458ca549ebSLuboš Luňák { 5468ca549ebSLuboš Luňák // Do not use GPU for small surfaces. The problem is that due to the separate alpha hack 5478ca549ebSLuboš Luňák // we quite often may call GetBitmap() on VirtualDevice, which is relatively slow 5488ca549ebSLuboš Luňák // when the pixels need to be fetched from the GPU. And there are documents that use 5498ca549ebSLuboš Luňák // many tiny surfaces (bsc#1183308 for example), where this slowness adds up too much. 5508ca549ebSLuboš Luňák // This should be re-evaluated once the separate alpha hack is removed (SKIA_USE_BITMAP32) 5518ca549ebSLuboš Luňák // and we no longer (hopefully) fetch pixels that often. 5528ca549ebSLuboš Luňák if (size.width() <= 32 && size.height() <= 32) 5538ca549ebSLuboš Luňák return RenderRaster; 5548ca549ebSLuboš Luňák return renderMethodToUse(); 5558ca549ebSLuboš Luňák } 5568ca549ebSLuboš Luňák 5573f359a03SLuboš Luňák sk_sp<SkSurface> createSkSurface(int width, int height, SkColorType type, SkAlphaType alpha) 5588fede4e7SLuboš Luňák { 5598a2a9d28SLuboš Luňák SkiaZone zone; 5608fede4e7SLuboš Luňák assert(type == kN32_SkColorType || type == kAlpha_8_SkColorType); 5618fede4e7SLuboš Luňák sk_sp<SkSurface> surface; 5628ca549ebSLuboš Luňák switch (renderMethodToUseForSize({ width, height })) 5638fede4e7SLuboš Luňák { 564715fe00aSLuboš Luňák case RenderVulkan: 565d4afd3aeSLuboš Luňák case RenderMetal: 5668fede4e7SLuboš Luňák { 567eaf4f6d3SLuboš Luňák if (GrDirectContext* grDirectContext = getSharedGrDirectContext()) 5688fede4e7SLuboš Luňák { 569f8c15850SNoel Grandin surface = SkSurfaces::RenderTarget(grDirectContext, skgpu::Budgeted::kNo, 570f8c15850SNoel Grandin SkImageInfo::Make(width, height, type, alpha), 0, 571f8c15850SNoel Grandin surfaceProps()); 57219365e6eSLuboš Luňák if (surface) 57319365e6eSLuboš Luňák { 5748fede4e7SLuboš Luňák #ifdef DBG_UTIL 57519365e6eSLuboš Luňák prefillSurface(surface); 5768fede4e7SLuboš Luňák #endif 57719365e6eSLuboš Luňák return surface; 57819365e6eSLuboš Luňák } 579d4afd3aeSLuboš Luňák SAL_WARN_IF(renderMethodToUse() == RenderVulkan, "vcl.skia", 580d4afd3aeSLuboš Luňák "Cannot create Vulkan GPU offscreen surface, falling back to Raster"); 581d4afd3aeSLuboš Luňák SAL_WARN_IF(renderMethodToUse() == RenderMetal, "vcl.skia", 582d4afd3aeSLuboš Luňák "Cannot create Metal GPU offscreen surface, falling back to Raster"); 5838fede4e7SLuboš Luňák } 5848fede4e7SLuboš Luňák break; 5858fede4e7SLuboš Luňák } 5868fede4e7SLuboš Luňák default: 5878fede4e7SLuboš Luňák break; 5888fede4e7SLuboš Luňák } 5898fede4e7SLuboš Luňák // Create raster surface as a fallback. 590f8c15850SNoel Grandin surface = SkSurfaces::Raster(SkImageInfo::Make(width, height, type, alpha), surfaceProps()); 5918fede4e7SLuboš Luňák assert(surface); 592797315f2SLuboš Luňák if (surface) 593797315f2SLuboš Luňák { 5948fede4e7SLuboš Luňák #ifdef DBG_UTIL 595797315f2SLuboš Luňák prefillSurface(surface); 5968fede4e7SLuboš Luňák #endif 597797315f2SLuboš Luňák return surface; 598797315f2SLuboš Luňák } 599797315f2SLuboš Luňák // In non-debug builds we could return SkSurface::MakeNull() and try to cope with the situation, 600797315f2SLuboš Luňák // but that can lead to unnoticed data loss, so better fail clearly. 601797315f2SLuboš Luňák abort(); 6028fede4e7SLuboš Luňák } 6038fede4e7SLuboš Luňák 604fafc059aSLuboš Luňák sk_sp<SkImage> createSkImage(const SkBitmap& bitmap) 605fafc059aSLuboš Luňák { 606fafc059aSLuboš Luňák SkiaZone zone; 607fafc059aSLuboš Luňák assert(bitmap.colorType() == kN32_SkColorType || bitmap.colorType() == kAlpha_8_SkColorType); 6088ca549ebSLuboš Luňák switch (renderMethodToUseForSize(bitmap.dimensions())) 609fafc059aSLuboš Luňák { 610715fe00aSLuboš Luňák case RenderVulkan: 611d4afd3aeSLuboš Luňák case RenderMetal: 612fafc059aSLuboš Luňák { 613eaf4f6d3SLuboš Luňák if (GrDirectContext* grDirectContext = getSharedGrDirectContext()) 614fafc059aSLuboš Luňák { 615f8c15850SNoel Grandin sk_sp<SkSurface> surface = SkSurfaces::RenderTarget( 6169c9a711aSNoel Grandin grDirectContext, skgpu::Budgeted::kNo, 6171724b7a2SLuboš Luňák bitmap.info().makeAlphaType(kPremul_SkAlphaType), 0, surfaceProps()); 61819365e6eSLuboš Luňák if (surface) 61919365e6eSLuboš Luňák { 62019365e6eSLuboš Luňák SkPaint paint; 62119365e6eSLuboš Luňák paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha 622ad8bff9dSLuboš Luňák surface->getCanvas()->drawImage(bitmap.asImage(), 0, 0, SkSamplingOptions(), 623ad8bff9dSLuboš Luňák &paint); 624797315f2SLuboš Luňák return makeCheckedImageSnapshot(surface); 62519365e6eSLuboš Luňák } 62619365e6eSLuboš Luňák // Try to fall back in non-debug builds. 627d4afd3aeSLuboš Luňák SAL_WARN_IF(renderMethodToUse() == RenderVulkan, "vcl.skia", 628d4afd3aeSLuboš Luňák "Cannot create Vulkan GPU offscreen surface, falling back to Raster"); 629d4afd3aeSLuboš Luňák SAL_WARN_IF(renderMethodToUse() == RenderMetal, "vcl.skia", 630d4afd3aeSLuboš Luňák "Cannot create Metal GPU offscreen surface, falling back to Raster"); 631fafc059aSLuboš Luňák } 632fafc059aSLuboš Luňák break; 633fafc059aSLuboš Luňák } 634fafc059aSLuboš Luňák default: 635fafc059aSLuboš Luňák break; 636fafc059aSLuboš Luňák } 637fafc059aSLuboš Luňák // Create raster image as a fallback. 638f8c15850SNoel Grandin sk_sp<SkImage> image = SkImages::RasterFromBitmap(bitmap); 639fafc059aSLuboš Luňák assert(image); 640fafc059aSLuboš Luňák return image; 641fafc059aSLuboš Luňák } 642fafc059aSLuboš Luňák 643797315f2SLuboš Luňák sk_sp<SkImage> makeCheckedImageSnapshot(sk_sp<SkSurface> surface) 644797315f2SLuboš Luňák { 645797315f2SLuboš Luňák sk_sp<SkImage> ret = surface->makeImageSnapshot(); 646797315f2SLuboš Luňák assert(ret); 647797315f2SLuboš Luňák if (ret) 648797315f2SLuboš Luňák return ret; 649797315f2SLuboš Luňák abort(); 650797315f2SLuboš Luňák } 651797315f2SLuboš Luňák 652797315f2SLuboš Luňák sk_sp<SkImage> makeCheckedImageSnapshot(sk_sp<SkSurface> surface, const SkIRect& bounds) 653797315f2SLuboš Luňák { 654797315f2SLuboš Luňák sk_sp<SkImage> ret = surface->makeImageSnapshot(bounds); 655797315f2SLuboš Luňák assert(ret); 656797315f2SLuboš Luňák if (ret) 657797315f2SLuboš Luňák return ret; 658797315f2SLuboš Luňák abort(); 659797315f2SLuboš Luňák } 660797315f2SLuboš Luňák 661fc0bff85SLuboš Luňák namespace 662fc0bff85SLuboš Luňák { 663fc0bff85SLuboš Luňák // Image cache, for saving results of complex operations such as drawTransformedBitmap(). 664fc0bff85SLuboš Luňák struct ImageCacheItem 665fc0bff85SLuboš Luňák { 666fc0bff85SLuboš Luňák OString key; 667fc0bff85SLuboš Luňák sk_sp<SkImage> image; 668fae487b7SLuboš Luňák tools::Long size; // cost of the item 669fc0bff85SLuboš Luňák }; 670fc0bff85SLuboš Luňák } //namespace 671fc0bff85SLuboš Luňák 672fc0bff85SLuboš Luňák // LRU cache, last item is the least recently used. Hopefully there won't be that many items 6732c86b79eSLuboš Luňák // to require a hash/map. Using o3tl::lru_map would be simpler, but it doesn't support 674fc0bff85SLuboš Luňák // calculating cost of each item. 6754e512171SNoel Grandin static std::list<ImageCacheItem> imageCache; 676fae487b7SLuboš Luňák static tools::Long imageCacheSize = 0; // sum of all ImageCacheItem.size 677fc0bff85SLuboš Luňák 678fc0bff85SLuboš Luňák void addCachedImage(const OString& key, sk_sp<SkImage> image) 679fc0bff85SLuboš Luňák { 680fc0bff85SLuboš Luňák static bool disabled = getenv("SAL_DISABLE_SKIA_CACHE") != nullptr; 681fc0bff85SLuboš Luňák if (disabled) 682fc0bff85SLuboš Luňák return; 683fae487b7SLuboš Luňák tools::Long size = static_cast<tools::Long>(image->width()) * image->height() 684fae487b7SLuboš Luňák * SkColorTypeBytesPerPixel(image->imageInfo().colorType()); 6854e512171SNoel Grandin imageCache.push_front({ key, image, size }); 686fc0bff85SLuboš Luňák imageCacheSize += size; 687fc0bff85SLuboš Luňák SAL_INFO("vcl.skia.trace", "addcachedimage " << image << " :" << size << "/" << imageCacheSize); 688fae487b7SLuboš Luňák const tools::Long maxSize = maxImageCacheSize(); 689fae487b7SLuboš Luňák while (imageCacheSize > maxSize) 690fc0bff85SLuboš Luňák { 6914e512171SNoel Grandin assert(!imageCache.empty()); 6924e512171SNoel Grandin imageCacheSize -= imageCache.back().size; 6934e512171SNoel Grandin SAL_INFO("vcl.skia.trace", 6944e512171SNoel Grandin "least used removal " << imageCache.back().image << ":" << imageCache.back().size); 6954e512171SNoel Grandin imageCache.pop_back(); 696fc0bff85SLuboš Luňák } 697fc0bff85SLuboš Luňák } 698fc0bff85SLuboš Luňák 699fc0bff85SLuboš Luňák sk_sp<SkImage> findCachedImage(const OString& key) 700fc0bff85SLuboš Luňák { 7014e512171SNoel Grandin for (auto it = imageCache.begin(); it != imageCache.end(); ++it) 702fc0bff85SLuboš Luňák { 7034e512171SNoel Grandin if (it->key == key) 704fc0bff85SLuboš Luňák { 7054e512171SNoel Grandin sk_sp<SkImage> ret = it->image; 7064e512171SNoel Grandin SAL_INFO("vcl.skia.trace", "findcachedimage " << key << " : " << it->image << " found"); 7074e512171SNoel Grandin imageCache.splice(imageCache.begin(), imageCache, it); 7084e512171SNoel Grandin return ret; 709fc0bff85SLuboš Luňák } 710fc0bff85SLuboš Luňák } 7111b5bd34dSLuboš Luňák SAL_INFO("vcl.skia.trace", "findcachedimage " << key << " not found"); 712fc0bff85SLuboš Luňák return nullptr; 713fc0bff85SLuboš Luňák } 714fc0bff85SLuboš Luňák 715fc0bff85SLuboš Luňák void removeCachedImage(sk_sp<SkImage> image) 716fc0bff85SLuboš Luňák { 7174e512171SNoel Grandin for (auto it = imageCache.begin(); it != imageCache.end();) 718fc0bff85SLuboš Luňák { 719fc0bff85SLuboš Luňák if (it->image == image) 720fc0bff85SLuboš Luňák { 721fc0bff85SLuboš Luňák imageCacheSize -= it->size; 722fc0bff85SLuboš Luňák assert(imageCacheSize >= 0); 7234e512171SNoel Grandin it = imageCache.erase(it); 724fc0bff85SLuboš Luňák } 725fc0bff85SLuboš Luňák else 726fc0bff85SLuboš Luňák ++it; 727fc0bff85SLuboš Luňák } 728fc0bff85SLuboš Luňák } 729fc0bff85SLuboš Luňák 730fae487b7SLuboš Luňák tools::Long maxImageCacheSize() 731fae487b7SLuboš Luňák { 732fae487b7SLuboš Luňák // Defaults to 4x 2000px 32bpp images, 64MiB. 733fae487b7SLuboš Luňák return officecfg::Office::Common::Cache::Skia::ImageCacheSize::get(); 734fae487b7SLuboš Luňák } 735fae487b7SLuboš Luňák 7362c86b79eSLuboš Luňák static o3tl::lru_map<uint32_t, uint32_t> checksumCache(256); 7372c86b79eSLuboš Luňák 738e369efa0SJulien Nabet static uint32_t computeSkPixmapChecksum(const SkPixmap& pixmap) 7392c86b79eSLuboš Luňák { 740f8c15850SNoel Grandin // Use uint32_t because that's what SkChecksum::Hash32() returns. 741f8c15850SNoel Grandin static_assert(std::is_same_v<uint32_t, decltype(SkChecksum::Hash32(nullptr, 0, 0))>); 7422c86b79eSLuboš Luňák const size_t dataRowBytes = pixmap.width() << pixmap.shiftPerPixel(); 7432c86b79eSLuboš Luňák if (dataRowBytes == pixmap.rowBytes()) 744f8c15850SNoel Grandin return SkChecksum::Hash32(pixmap.addr(), pixmap.height() * dataRowBytes, 0); 7452c86b79eSLuboš Luňák uint32_t sum = 0; 7462c86b79eSLuboš Luňák for (int row = 0; row < pixmap.height(); ++row) 747f8c15850SNoel Grandin sum = SkChecksum::Hash32(pixmap.addr(0, row), dataRowBytes, sum); 7482c86b79eSLuboš Luňák return sum; 7492c86b79eSLuboš Luňák } 7502c86b79eSLuboš Luňák 7512c86b79eSLuboš Luňák uint32_t getSkImageChecksum(sk_sp<SkImage> image) 7522c86b79eSLuboš Luňák { 7532c86b79eSLuboš Luňák // Cache the checksums based on the uniqueID() (which should stay the same 7542c86b79eSLuboš Luňák // for the same image), because it may be still somewhat expensive. 7552c86b79eSLuboš Luňák uint32_t id = image->uniqueID(); 7562c86b79eSLuboš Luňák auto it = checksumCache.find(id); 7572c86b79eSLuboš Luňák if (it != checksumCache.end()) 7582c86b79eSLuboš Luňák return it->second; 7592c86b79eSLuboš Luňák SkPixmap pixmap; 7602c86b79eSLuboš Luňák if (!image->peekPixels(&pixmap)) 7612c86b79eSLuboš Luňák abort(); // Fetching of GPU-based pixels is expensive, and shouldn't(?) be needed anyway. 7622c86b79eSLuboš Luňák uint32_t checksum = computeSkPixmapChecksum(pixmap); 7632c86b79eSLuboš Luňák checksumCache.insert({ id, checksum }); 7642c86b79eSLuboš Luňák return checksum; 7652c86b79eSLuboš Luňák } 7662c86b79eSLuboš Luňák 767f7a628f8SLuboš Luňák static sk_sp<SkBlender> invertBlender; 768f7a628f8SLuboš Luňák static sk_sp<SkBlender> xorBlender; 769f7a628f8SLuboš Luňák 770f7a628f8SLuboš Luňák // This does the invert operation, i.e. result = color(255-R,255-G,255-B,A). 771f7a628f8SLuboš Luňák void setBlenderInvert(SkPaint* paint) 772f7a628f8SLuboš Luňák { 773f7a628f8SLuboš Luňák if (!invertBlender) 774f7a628f8SLuboš Luňák { 775f7a628f8SLuboš Luňák // Note that the colors are premultiplied, so '1 - dst.r' must be 776f7a628f8SLuboš Luňák // written as 'dst.a - dst.r', since premultiplied R is in the range (0-A). 777f7a628f8SLuboš Luňák const char* const diff = R"( 778f7a628f8SLuboš Luňák vec4 main( vec4 src, vec4 dst ) 779f7a628f8SLuboš Luňák { 780f7a628f8SLuboš Luňák return vec4( dst.a - dst.r, dst.a - dst.g, dst.a - dst.b, dst.a ); 781f7a628f8SLuboš Luňák } 782f7a628f8SLuboš Luňák )"; 783f7a628f8SLuboš Luňák auto effect = SkRuntimeEffect::MakeForBlender(SkString(diff)); 784f7a628f8SLuboš Luňák if (!effect.effect) 785f7a628f8SLuboš Luňák { 786f7a628f8SLuboš Luňák SAL_WARN("vcl.skia", 787f7a628f8SLuboš Luňák "SKRuntimeEffect::MakeForBlender failed: " << effect.errorText.c_str()); 788f7a628f8SLuboš Luňák abort(); 789f7a628f8SLuboš Luňák } 790f7a628f8SLuboš Luňák invertBlender = effect.effect->makeBlender(nullptr); 791f7a628f8SLuboš Luňák } 792f7a628f8SLuboš Luňák paint->setBlender(invertBlender); 793f7a628f8SLuboš Luňák } 794f7a628f8SLuboš Luňák 795f7a628f8SLuboš Luňák // This does the xor operation, i.e. bitwise xor of RGB values of both colors. 796f7a628f8SLuboš Luňák void setBlenderXor(SkPaint* paint) 797f7a628f8SLuboš Luňák { 798f7a628f8SLuboš Luňák if (!xorBlender) 799f7a628f8SLuboš Luňák { 800f7a628f8SLuboš Luňák // Note that the colors are premultiplied, converting to 0-255 range 801f7a628f8SLuboš Luňák // must also unpremultiply. 802f7a628f8SLuboš Luňák const char* const diff = R"( 803f7a628f8SLuboš Luňák vec4 main( vec4 src, vec4 dst ) 804f7a628f8SLuboš Luňák { 805f7a628f8SLuboš Luňák return vec4( 806f7a628f8SLuboš Luňák float(int(src.r * src.a * 255.0) ^ int(dst.r * dst.a * 255.0)) / 255.0 / dst.a, 807f7a628f8SLuboš Luňák float(int(src.g * src.a * 255.0) ^ int(dst.g * dst.a * 255.0)) / 255.0 / dst.a, 808f7a628f8SLuboš Luňák float(int(src.b * src.a * 255.0) ^ int(dst.b * dst.a * 255.0)) / 255.0 / dst.a, 809f7a628f8SLuboš Luňák dst.a ); 810f7a628f8SLuboš Luňák } 811f7a628f8SLuboš Luňák )"; 812f7a628f8SLuboš Luňák SkRuntimeEffect::Options opts; 813f7a628f8SLuboš Luňák // Skia does not allow binary operators in the default ES2Strict mode, but that's only 814f7a628f8SLuboš Luňák // because of OpenGL support. We don't use OpenGL, and it's safe for all modes that we do use. 815f7a628f8SLuboš Luňák // https://groups.google.com/g/skia-discuss/c/EPLuQbg64Kc/m/2uDXFIGhAwAJ 8169c9a711aSNoel Grandin opts.maxVersionAllowed = SkSL::Version::k300; 817f7a628f8SLuboš Luňák auto effect = SkRuntimeEffect::MakeForBlender(SkString(diff), opts); 818f7a628f8SLuboš Luňák if (!effect.effect) 819f7a628f8SLuboš Luňák { 820f7a628f8SLuboš Luňák SAL_WARN("vcl.skia", 821f7a628f8SLuboš Luňák "SKRuntimeEffect::MakeForBlender failed: " << effect.errorText.c_str()); 822f7a628f8SLuboš Luňák abort(); 823f7a628f8SLuboš Luňák } 824f7a628f8SLuboš Luňák xorBlender = effect.effect->makeBlender(nullptr); 825f7a628f8SLuboš Luňák } 826f7a628f8SLuboš Luňák paint->setBlender(xorBlender); 827f7a628f8SLuboš Luňák } 828f7a628f8SLuboš Luňák 8296c58b01cSLuboš Luňák static void initInternal() 8306c58b01cSLuboš Luňák { 8316c58b01cSLuboš Luňák // Set up all things needed for using Skia. 8326c58b01cSLuboš Luňák SkGraphics::Init(); 8336c58b01cSLuboš Luňák SkLoOpts::Init(); 8346c58b01cSLuboš Luňák } 8356c58b01cSLuboš Luňák 836ea5eb463SLuboš Luňák void cleanup() 837ea5eb463SLuboš Luňák { 838d4afd3aeSLuboš Luňák sharedWindowContext.reset(); 8394e512171SNoel Grandin imageCache.clear(); 840fc0bff85SLuboš Luňák imageCacheSize = 0; 841f7a628f8SLuboš Luňák invertBlender.reset(); 842f7a628f8SLuboš Luňák xorBlender.reset(); 843ea5eb463SLuboš Luňák } 844ea5eb463SLuboš Luňák 8451724b7a2SLuboš Luňák static SkSurfaceProps commonSurfaceProps; 8461724b7a2SLuboš Luňák const SkSurfaceProps* surfaceProps() { return &commonSurfaceProps; } 8471724b7a2SLuboš Luňák 8481724b7a2SLuboš Luňák void setPixelGeometry(SkPixelGeometry pixelGeometry) 8491724b7a2SLuboš Luňák { 8501724b7a2SLuboš Luňák commonSurfaceProps = SkSurfaceProps(commonSurfaceProps.flags(), pixelGeometry); 8511724b7a2SLuboš Luňák } 8521724b7a2SLuboš Luňák 853a3d3e076SLuboš Luňák // Skia should not be used from VCL backends that do not actually support it, as there will be setup missing. 854d4afd3aeSLuboš Luňák // The code here (that is in the vcl lib) needs a function for creating Vulkan/Metal context that is 855a3d3e076SLuboš Luňák // usually available only in the backend libs. 856d4afd3aeSLuboš Luňák void prepareSkia(std::unique_ptr<sk_app::WindowContext> (*createGpuWindowContext)(bool)) 857a3d3e076SLuboš Luňák { 858d4afd3aeSLuboš Luňák setCreateGpuWindowContext(createGpuWindowContext); 859a3d3e076SLuboš Luňák skiaSupportedByBackend = true; 860a3d3e076SLuboš Luňák } 861a3d3e076SLuboš Luňák 862f8c15850SNoel Grandin void dump(const SkBitmap& bitmap, const char* file) 863f8c15850SNoel Grandin { 864f8c15850SNoel Grandin dump(SkImages::RasterFromBitmap(bitmap), file); 865f8c15850SNoel Grandin } 866f33b76b4SLuboš Luňák 867f33b76b4SLuboš Luňák void dump(const sk_sp<SkSurface>& surface, const char* file) 868f33b76b4SLuboš Luňák { 869f33b76b4SLuboš Luňák surface->getCanvas()->flush(); 870f33b76b4SLuboš Luňák dump(makeCheckedImageSnapshot(surface), file); 871f33b76b4SLuboš Luňák } 872f33b76b4SLuboš Luňák 873f33b76b4SLuboš Luňák void dump(const sk_sp<SkImage>& image, const char* file) 874f33b76b4SLuboš Luňák { 875f8c15850SNoel Grandin SkBitmap bm; 876f8c15850SNoel Grandin if (!as_IB(image)->getROPixels(getSharedGrDirectContext(), &bm)) 877f8c15850SNoel Grandin return; 878f8c15850SNoel Grandin SkPixmap pixmap; 879f8c15850SNoel Grandin if (!bm.peekPixels(&pixmap)) 880f8c15850SNoel Grandin return; 881f8c15850SNoel Grandin SkPngEncoder::Options opts; 882f8c15850SNoel Grandin opts.fFilterFlags = SkPngEncoder::FilterFlag::kNone; 883f8c15850SNoel Grandin opts.fZLibLevel = 1; 884f8c15850SNoel Grandin SkDynamicMemoryWStream stream; 885f8c15850SNoel Grandin if (!SkPngEncoder::Encode(&stream, pixmap, opts)) 886f8c15850SNoel Grandin return; 887f8c15850SNoel Grandin sk_sp<SkData> data = stream.detachAsData(); 888f33b76b4SLuboš Luňák std::ofstream ostream(file, std::ios::binary); 889f33b76b4SLuboš Luňák ostream.write(static_cast<const char*>(data->data()), data->size()); 890f33b76b4SLuboš Luňák } 891f33b76b4SLuboš Luňák 8928fede4e7SLuboš Luňák #ifdef DBG_UTIL 89322d94ce0SNoel void prefillSurface(const sk_sp<SkSurface>& surface) 8948fede4e7SLuboš Luňák { 8958fede4e7SLuboš Luňák // Pre-fill the surface with deterministic garbage. 8968fede4e7SLuboš Luňák SkBitmap bitmap; 8978fede4e7SLuboš Luňák bitmap.allocN32Pixels(2, 2); 8988fede4e7SLuboš Luňák SkPMColor* scanline; 8998fede4e7SLuboš Luňák scanline = bitmap.getAddr32(0, 0); 9008fede4e7SLuboš Luňák *scanline++ = SkPreMultiplyARGB(0xFF, 0xBF, 0x80, 0x40); 9018fede4e7SLuboš Luňák *scanline++ = SkPreMultiplyARGB(0xFF, 0x40, 0x80, 0xBF); 9028fede4e7SLuboš Luňák scanline = bitmap.getAddr32(0, 1); 9038fede4e7SLuboš Luňák *scanline++ = SkPreMultiplyARGB(0xFF, 0xE3, 0x5C, 0x13); 9048fede4e7SLuboš Luňák *scanline++ = SkPreMultiplyARGB(0xFF, 0x13, 0x5C, 0xE3); 9051f6cb097SLuboš Luňák bitmap.setImmutable(); 9068fede4e7SLuboš Luňák SkPaint paint; 9078fede4e7SLuboš Luňák paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha 908ad8bff9dSLuboš Luňák paint.setShader( 909ad8bff9dSLuboš Luňák bitmap.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, SkSamplingOptions())); 9108fede4e7SLuboš Luňák surface->getCanvas()->drawPaint(paint); 9118fede4e7SLuboš Luňák } 9128fede4e7SLuboš Luňák #endif 9138fede4e7SLuboš Luňák 914ea5eb463SLuboš Luňák } // namespace 915ea5eb463SLuboš Luňák 9165c4f872aSLuboš Luňák #endif // HAVE_FEATURE_SKIA 9175c4f872aSLuboš Luňák 9181e9fae67SLuboš Luňák /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 919
