From d4afd3aeb4ef2727986551816c1ff9ad0ed12d04 Mon Sep 17 00:00:00 2001 From: Luboš Luňák Date: Mon, 23 Aug 2021 17:57:48 +0200 Subject: initial Metal support for Mac/Skia MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also required changing SkiaSalGraphicsImpl to have sk_app::WindowContext as an internal detail inaccessible to the base class, since the Mac implementations cannot use it as is. Change-Id: I2424f0b887c79ee91c3bd0f1477b0745f9540247 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120909 Tested-by: Jenkins Reviewed-by: Luboš Luňák --- vcl/Library_vclplug_osx.mk | 1 - vcl/README.vars | 2 +- vcl/inc/skia/gdiimpl.hxx | 5 +- vcl/inc/skia/osx/gdiimpl.hxx | 9 +- vcl/inc/skia/osx/rastercontext.hxx | 42 -------- vcl/inc/skia/utils.hxx | 4 +- vcl/inc/skia/win/gdiimpl.hxx | 6 +- vcl/inc/skia/x11/gdiimpl.hxx | 4 +- vcl/inc/strings.hrc | 1 + vcl/skia/README | 1 + vcl/skia/SkiaHelper.cxx | 203 ++++++++++++++++++++++++------------- vcl/skia/gdiimpl.cxx | 20 ++-- vcl/skia/osx/gdiimpl.cxx | 99 ++++++++++++++---- vcl/skia/osx/rastercontext.cxx | 51 ---------- vcl/skia/win/gdiimpl.cxx | 17 +++- vcl/skia/x11/gdiimpl.cxx | 17 +++- vcl/skia/zone.cxx | 7 +- vcl/source/app/svapp.cxx | 3 + 18 files changed, 279 insertions(+), 213 deletions(-) delete mode 100644 vcl/inc/skia/osx/rastercontext.hxx delete mode 100644 vcl/skia/osx/rastercontext.cxx (limited to 'vcl') diff --git a/vcl/Library_vclplug_osx.mk b/vcl/Library_vclplug_osx.mk index ddc3a3608f1b..e2a1a5161bdd 100644 --- a/vcl/Library_vclplug_osx.mk +++ b/vcl/Library_vclplug_osx.mk @@ -144,7 +144,6 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_osx,\ $(if $(filter SKIA,$(BUILD_TYPE)), \ vcl/skia/osx/bitmap \ vcl/skia/osx/gdiimpl \ - vcl/skia/osx/rastercontext \ ) \ )) diff --git a/vcl/README.vars b/vcl/README.vars index 60551e1e4173..f9e0c600d2c7 100644 --- a/vcl/README.vars +++ b/vcl/README.vars @@ -44,7 +44,7 @@ Skia SAL_DISABLESKIA=1 - force disabled Skia SAL_ENABLESKIA=1 - enable Skia, unless denylisted (and if the VCL backend supports Skia) SAL_FORCESKIA=1 - force using Skia, even if denylisted -SAL_SKIA=raster|vulkan - select Skia's drawing method, by default Vulkan is used +SAL_SKIA=raster|vulkan|metal - select Skia's drawing method, by default Vulkan or Metal are used if available SAL_DISABLE_SKIA_CACHE=1 - disable caching of complex images SAL_SKIA_KEEP_BITMAP_BUFFER=1 - SkiaSalBitmap will keep its bitmap buffer even after storing in SkImage diff --git a/vcl/inc/skia/gdiimpl.hxx b/vcl/inc/skia/gdiimpl.hxx index 1d5bfd071ea5..a7ba10c3273b 100644 --- a/vcl/inc/skia/gdiimpl.hxx +++ b/vcl/inc/skia/gdiimpl.hxx @@ -31,7 +31,6 @@ #include #include -#include #include class SkiaFlushIdle; @@ -240,7 +239,8 @@ protected: // Reimplemented for X11. virtual bool avoidRecreateByResize() const; void createWindowSurface(bool forceRaster = false); - virtual void createWindowContext(bool forceRaster = false) = 0; + virtual void createWindowSurfaceInternal(bool forceRaster = false) = 0; + virtual void destroyWindowSurfaceInternal() = 0; void createOffscreenSurface(); void privateDrawAlphaRect(tools::Long nX, tools::Long nY, tools::Long nWidth, @@ -319,7 +319,6 @@ protected: SalGraphics& mParent; /// Pointer to the SalFrame or SalVirtualDevice SalGeometryProvider* mProvider; - std::unique_ptr mWindowContext; // The Skia surface that is target of all the rendering. sk_sp mSurface; bool mIsGPU; // whether the surface is GPU-backed diff --git a/vcl/inc/skia/osx/gdiimpl.hxx b/vcl/inc/skia/osx/gdiimpl.hxx index cc291bd38764..ef1928bb3a0a 100644 --- a/vcl/inc/skia/osx/gdiimpl.hxx +++ b/vcl/inc/skia/osx/gdiimpl.hxx @@ -43,11 +43,14 @@ public: virtual void drawTextLayout(const GenericSalLayout& layout) override; private: - virtual void createWindowContext(bool forceRaster = false) override; + virtual void createWindowSurfaceInternal(bool forceRaster = false) override; + virtual void destroyWindowSurfaceInternal() override; virtual void performFlush() override; - void flushToScreen(const SkIRect& rect); - friend std::unique_ptr createVulkanWindowContext(bool); + void flushToScreenRaster(const SkIRect& rect); + void flushToScreenMetal(const SkIRect& rect); static inline sk_sp fontManager; + // This one is used only for Metal, and only indirectly. + std::unique_ptr mWindowContext; }; #endif // INCLUDED_VCL_INC_SKIA_OSX_GDIIMPL_HXX diff --git a/vcl/inc/skia/osx/rastercontext.hxx b/vcl/inc/skia/osx/rastercontext.hxx deleted file mode 100644 index 84891d4b8642..000000000000 --- a/vcl/inc/skia/osx/rastercontext.hxx +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- 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/. - */ - -#ifndef INCLUDED_VCL_INC_SKIA_OSX_RASTERCONTEXT_HXX -#define INCLUDED_VCL_INC_SKIA_OSX_RASTERCONTEXT_HXX - -#include - -class AquaSkiaSalGraphicsImpl; - -// RasterWindowContext_mac uses OpenGL internally, which -// we don't want, so make our own raster window context -// based on SkBitmap, and our code will handle things like flush. - -class AquaSkiaWindowContextRaster : public sk_app::WindowContext -{ -public: - AquaSkiaWindowContextRaster(int w, int h, const sk_app::DisplayParams& params); - virtual sk_sp getBackbufferSurface() override { return mSurface; } - // Not to be called, our mac code should be used. - virtual void swapBuffers(const SkIRect* = nullptr) override { abort(); } - virtual bool isValid() override { return mSurface.get(); }; - virtual void resize(int w, int h) override; - virtual void setDisplayParams(const sk_app::DisplayParams& params) override; - -protected: - virtual bool isGpuContext() override { return false; } - -private: - void createSurface(); - sk_sp mSurface; -}; - -#endif // INCLUDED_VCL_INC_SKIA_OSX_RASTERCONTEXT_HXX - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/skia/utils.hxx b/vcl/inc/skia/utils.hxx index b1987024c616..0bcc5989493e 100644 --- a/vcl/inc/skia/utils.hxx +++ b/vcl/inc/skia/utils.hxx @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include namespace SkiaHelper @@ -74,7 +74,7 @@ inline Size imageSize(const sk_sp& image) { return Size(image->width(), // Must be called in any VCL backend before any Skia functionality is used. // If not set, Skia will be disabled. VCL_DLLPUBLIC void - prepareSkia(std::unique_ptr (*createVulkanWindowContext)(bool)); + prepareSkia(std::unique_ptr (*createGpuWindowContext)(bool)); // Shared cache of images. void addCachedImage(const OString& key, sk_sp image); diff --git a/vcl/inc/skia/win/gdiimpl.hxx b/vcl/inc/skia/win/gdiimpl.hxx index fb82731ad533..2f85a9e3cf66 100644 --- a/vcl/inc/skia/win/gdiimpl.hxx +++ b/vcl/inc/skia/win/gdiimpl.hxx @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -42,6 +43,7 @@ private: public: WinSkiaSalGraphicsImpl(WinSalGraphics& rGraphics, SalGeometryProvider* mpProvider); + virtual ~WinSkiaSalGraphicsImpl() override; virtual void DeInit() override; virtual void freeResources() override; @@ -60,7 +62,8 @@ public: static void prepareSkia(); protected: - virtual void createWindowContext(bool forceRaster = false) override; + virtual void createWindowSurfaceInternal(bool forceRaster = false) override; + virtual void destroyWindowSurfaceInternal() override; virtual void performFlush() override; static sk_sp createDirectWriteTypeface(HDC hdc, HFONT hfont); static void initFontInfo(); @@ -69,6 +72,7 @@ protected: inline static sk_sp dwriteFontMgr; inline static bool dwriteDone = false; static SkFont::Edging fontEdging; + std::unique_ptr mWindowContext; }; typedef std::pair> SkiaControlCachePair; diff --git a/vcl/inc/skia/x11/gdiimpl.hxx b/vcl/inc/skia/x11/gdiimpl.hxx index 10c6c5fcb972..df9421f54720 100644 --- a/vcl/inc/skia/x11/gdiimpl.hxx +++ b/vcl/inc/skia/x11/gdiimpl.hxx @@ -35,13 +35,15 @@ public: static void prepareSkia(); private: - virtual void createWindowContext(bool forceRaster = false) override; + virtual void createWindowSurfaceInternal(bool forceRaster = false) override; + virtual void destroyWindowSurfaceInternal() override; virtual void performFlush() override; virtual bool avoidRecreateByResize() const override; static std::unique_ptr createWindowContext(Display* display, Drawable drawable, const XVisualInfo* visual, int width, int height, SkiaHelper::RenderMethod renderMethod, bool temporary); friend std::unique_ptr createVulkanWindowContext(bool); + std::unique_ptr mWindowContext; }; #endif // INCLUDED_VCL_INC_SKIA_X11_GDIIMPL_HXX diff --git a/vcl/inc/strings.hrc b/vcl/inc/strings.hrc index d08446ba44cf..d6e82961e284 100644 --- a/vcl/inc/strings.hrc +++ b/vcl/inc/strings.hrc @@ -106,6 +106,7 @@ #define SV_APP_UIRENDER NC_("SV_APP_UIRENDER", "UI render: ") #define SV_APP_GL NC_("SV_APP_GL", "GL") #define SV_APP_SKIA_VULKAN NC_("SV_APP_SKIA_VULKAN", "Skia/Vulkan") +#define SV_APP_SKIA_METAL NC_("SV_APP_SKIA_METAL", "Skia/Metal") #define SV_APP_SKIA_RASTER NC_("SV_APP_SKIA_RASTER", "Skia/Raster") #define SV_APP_DEFAULT NC_("SV_APP_DEFAULT", "default") diff --git a/vcl/skia/README b/vcl/skia/README index c508beb33cea..8381dd8dca89 100644 --- a/vcl/skia/README +++ b/vcl/skia/README @@ -15,6 +15,7 @@ Skia drawing methods: Skia supports several methods to draw: - Raster - CPU-based drawing (here primarily used for debugging) - Vulkan - Vulkan-based GPU drawing, this is the default +- Metal - MACOSX GPU drawing, this is the Mac default There are more (OpenGL, Metal on Mac, etc.), but (as of now) they are not supported by VCL. diff --git a/vcl/skia/SkiaHelper.cxx b/vcl/skia/SkiaHelper.cxx index 754176787abc..6881ad1cbf83 100644 --- a/vcl/skia/SkiaHelper.cxx +++ b/vcl/skia/SkiaHelper.cxx @@ -44,35 +44,14 @@ bool isVCLSkiaEnabled() { return false; } #include #include #include - +#include +#include #ifdef DBG_UTIL #include #endif namespace SkiaHelper { -static OUString getDenylistFile() -{ - OUString url("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER); - rtl::Bootstrap::expandMacros(url); - - return url + "/skia/skia_denylist_vulkan.xml"; -} - -static uint32_t driverVersion = 0; -uint32_t vendorId = 0; - -static OUString versionAsString(uint32_t version) -{ - return OUString::number(version >> 22) + "." + OUString::number((version >> 12) & 0x3ff) + "." - + OUString::number(version & 0xfff); -} - -static std::string_view vendorAsString(uint32_t vendor) -{ - return DriverBlocklist::GetVendorNameFromId(vendor); -} - static OUString getCacheFolder() { OUString url("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER @@ -90,11 +69,35 @@ static void writeToLog(SvStream& stream, const char* key, const char* value) stream.WriteChar('\n'); } +uint32_t vendorId = 0; + +#ifdef SK_VULKAN static void writeToLog(SvStream& stream, const char* key, std::u16string_view value) { writeToLog(stream, key, OUStringToOString(value, RTL_TEXTENCODING_UTF8).getStr()); } +static OUString getDenylistFile() +{ + OUString url("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER); + rtl::Bootstrap::expandMacros(url); + + return url + "/skia/skia_denylist_vulkan.xml"; +} + +static uint32_t driverVersion = 0; + +static OUString versionAsString(uint32_t version) +{ + return OUString::number(version >> 22) + "." + OUString::number((version >> 12) & 0x3ff) + "." + + OUString::number(version & 0xfff); +} + +static std::string_view vendorAsString(uint32_t vendor) +{ + return DriverBlocklist::GetVendorNameFromId(vendor); +} + // Note that this function also logs system information about Vulkan. static bool isVulkanDenylisted(const VkPhysicalDeviceProperties& props) { @@ -137,6 +140,15 @@ static bool isVulkanDenylisted(const VkPhysicalDeviceProperties& props) writeToLog(logFile, "Denylisted", denylisted ? "yes" : "no"); return denylisted; } +#endif + +#ifdef SK_METAL +static void writeSkiaMetalInfo() +{ + SvFileStream logFile(getCacheFolder() + "/skia.log", StreamMode::WRITE | StreamMode::TRUNC); + writeToLog(logFile, "RenderMethod", "metal"); +} +#endif static void writeSkiaRasterInfo() { @@ -146,7 +158,9 @@ static void writeSkiaRasterInfo() writeToLog(logFile, "Compiler", skia_compiler_name()); } -static sk_app::VulkanWindowContext::SharedGrDirectContext getTemporaryGrDirectContext(); +#ifdef SK_VULKAN +static std::unique_ptr getTemporaryWindowContext(); +#endif static void checkDeviceDenylisted(bool blockDisable = false) { @@ -160,23 +174,26 @@ static void checkDeviceDenylisted(bool blockDisable = false) { case RenderVulkan: { +#ifdef SK_VULKAN // First try if a GrDirectContext already exists. - sk_app::VulkanWindowContext::SharedGrDirectContext grDirectContext + std::unique_ptr temporaryWindowContext; + GrDirectContext* grDirectContext = sk_app::VulkanWindowContext::getSharedGrDirectContext(); - if (!grDirectContext.getGrDirectContext()) + if (!grDirectContext) { // This function is called from isVclSkiaEnabled(), which // may be called when deciding which X11 visual to use, // and that visual is normally needed when creating // Skia's VulkanWindowContext, which is needed for the GrDirectContext. - // Avoid the loop by creating a temporary GrDirectContext + // Avoid the loop by creating a temporary WindowContext // that will use the default X11 visual (that shouldn't matter // for just finding out information about Vulkan) and destroying // the temporary context will clean up again. - grDirectContext = getTemporaryGrDirectContext(); + temporaryWindowContext = getTemporaryWindowContext(); + grDirectContext = sk_app::VulkanWindowContext::getSharedGrDirectContext(); } bool denylisted = true; // assume the worst - if (grDirectContext.getGrDirectContext()) // Vulkan was initialized properly + if (grDirectContext) // Vulkan was initialized properly { denylisted = isVulkanDenylisted(sk_app::VulkanWindowContext::getPhysDeviceProperties()); @@ -190,11 +207,28 @@ static void checkDeviceDenylisted(bool blockDisable = false) writeSkiaRasterInfo(); } break; +#else + SAL_WARN("vcl.skia", "Vulkan support not built in"); + (void)blockDisable; + [[fallthrough]]; +#endif } + case RenderMetal: +#ifdef SK_METAL + // Try to assume Metal always works, given that Mac doesn't have such as wide range of HW vendors as PC. + // If there turns out to be problems, handle it similarly to Vulkan. + SAL_INFO("vcl.skia", "Using Skia Metal mode"); + writeSkiaMetalInfo(); + break; +#else + SAL_WARN("vcl.skia", "Metal support not built in"); + [[fallthrough]]; +#endif case RenderRaster: SAL_INFO("vcl.skia", "Using Skia raster mode"); + // software, never denylisted writeSkiaRasterInfo(); - return; // software, never denylisted + break; } done = true; } @@ -298,20 +332,31 @@ static bool initRenderMethodToUse() methodToUse = RenderRaster; return true; } +#ifdef MACOSX + if (strcmp(env, "metal") == 0) + { + methodToUse = RenderMetal; + return true; + } +#else if (strcmp(env, "vulkan") == 0) { methodToUse = RenderVulkan; return true; } +#endif SAL_WARN("vcl.skia", "Unrecognized value of SAL_SKIA"); abort(); } + methodToUse = RenderRaster; if (officecfg::Office::Common::VCL::ForceSkiaRaster::get()) - { - methodToUse = RenderRaster; return true; - } +#ifdef SK_METAL + methodToUse = RenderMetal; +#endif +#ifdef SK_VULKAN methodToUse = RenderVulkan; +#endif return true; } @@ -331,58 +376,69 @@ void disableRenderMethod(RenderMethod method) methodToUse = RenderRaster; } -static sk_app::VulkanWindowContext::SharedGrDirectContext* sharedGrDirectContext; +// If needed, we'll allocate one extra window context so that we have a valid GrDirectContext +// from Vulkan/MetalWindowContext. +static std::unique_ptr sharedWindowContext; -static std::unique_ptr (*createVulkanWindowContextFunction)(bool) = nullptr; -static void setCreateVulkanWindowContext(std::unique_ptr (*function)(bool)) +static std::unique_ptr (*createGpuWindowContextFunction)(bool) = nullptr; +static void setCreateGpuWindowContext(std::unique_ptr (*function)(bool)) { - createVulkanWindowContextFunction = function; + createGpuWindowContextFunction = function; } GrDirectContext* getSharedGrDirectContext() { SkiaZone zone; - assert(renderMethodToUse() == RenderVulkan); - if (sharedGrDirectContext) - return sharedGrDirectContext->getGrDirectContext(); + assert(renderMethodToUse() != RenderRaster); + if (sharedWindowContext) + return sharedWindowContext->directContext(); // TODO mutex? - // Set up the shared GrDirectContext from Skia's (patched) VulkanWindowContext, if it's been + // Set up the shared GrDirectContext from Skia's (patched) Vulkan/MetalWindowContext, if it's been // already set up. - sk_app::VulkanWindowContext::SharedGrDirectContext context - = sk_app::VulkanWindowContext::getSharedGrDirectContext(); - GrDirectContext* grDirectContext = context.getGrDirectContext(); - if (grDirectContext) + switch (renderMethodToUse()) { - sharedGrDirectContext = new sk_app::VulkanWindowContext::SharedGrDirectContext(context); - return grDirectContext; + case RenderVulkan: +#ifdef SK_VULKAN + if (GrDirectContext* context = sk_app::VulkanWindowContext::getSharedGrDirectContext()) + return context; +#endif + break; + case RenderMetal: +#ifdef SK_METAL + if (GrDirectContext* context = sk_app::getMetalSharedGrDirectContext()) + return context; +#endif + break; + case RenderRaster: + abort(); } static bool done = false; if (done) return nullptr; done = true; - if (createVulkanWindowContextFunction == nullptr) + if (createGpuWindowContextFunction == nullptr) return nullptr; // not initialized properly (e.g. used from a VCL backend with no Skia support) - std::unique_ptr tmpContext = createVulkanWindowContextFunction(false); - // Set up using the shared context created by the call above, if successful. - context = sk_app::VulkanWindowContext::getSharedGrDirectContext(); - grDirectContext = context.getGrDirectContext(); + sharedWindowContext = createGpuWindowContextFunction(false); + GrDirectContext* grDirectContext + = sharedWindowContext ? sharedWindowContext->directContext() : nullptr; if (grDirectContext) - { - sharedGrDirectContext = new sk_app::VulkanWindowContext::SharedGrDirectContext(context); return grDirectContext; - } - disableRenderMethod(RenderVulkan); + SAL_WARN_IF(renderMethodToUse() == RenderVulkan, "vcl.skia", + "Cannot create Vulkan GPU context, falling back to Raster"); + SAL_WARN_IF(renderMethodToUse() == RenderMetal, "vcl.skia", + "Cannot create Metal GPU context, falling back to Raster"); + disableRenderMethod(renderMethodToUse()); return nullptr; } -static sk_app::VulkanWindowContext::SharedGrDirectContext getTemporaryGrDirectContext() +#ifdef SK_VULKAN +static std::unique_ptr getTemporaryWindowContext() { - if (createVulkanWindowContextFunction == nullptr) - return sk_app::VulkanWindowContext::SharedGrDirectContext(); - std::unique_ptr tmpContext = createVulkanWindowContextFunction(true); - // Set up using the shared context created by the call above, if successful. - return sk_app::VulkanWindowContext::getSharedGrDirectContext(); + if (createGpuWindowContextFunction == nullptr) + return nullptr; + return createGpuWindowContextFunction(true); } +#endif sk_sp createSkSurface(int width, int height, SkColorType type, SkAlphaType alpha) { @@ -392,6 +448,7 @@ sk_sp createSkSurface(int width, int height, SkColorType type, SkAlph switch (renderMethodToUse()) { case RenderVulkan: + case RenderMetal: { if (GrDirectContext* grDirectContext = getSharedGrDirectContext()) { @@ -405,8 +462,10 @@ sk_sp createSkSurface(int width, int height, SkColorType type, SkAlph #endif return surface; } - SAL_WARN("vcl.skia", - "cannot create Vulkan GPU offscreen surface, falling back to Raster"); + SAL_WARN_IF(renderMethodToUse() == RenderVulkan, "vcl.skia", + "Cannot create Vulkan GPU offscreen surface, falling back to Raster"); + SAL_WARN_IF(renderMethodToUse() == RenderMetal, "vcl.skia", + "Cannot create Metal GPU offscreen surface, falling back to Raster"); } break; } @@ -435,6 +494,7 @@ sk_sp createSkImage(const SkBitmap& bitmap) switch (renderMethodToUse()) { case RenderVulkan: + case RenderMetal: { if (GrDirectContext* grDirectContext = getSharedGrDirectContext()) { @@ -450,8 +510,10 @@ sk_sp createSkImage(const SkBitmap& bitmap) return makeCheckedImageSnapshot(surface); } // Try to fall back in non-debug builds. - SAL_WARN("vcl.skia", - "cannot create Vulkan GPU offscreen surface, falling back to Raster"); + SAL_WARN_IF(renderMethodToUse() == RenderVulkan, "vcl.skia", + "Cannot create Vulkan GPU offscreen surface, falling back to Raster"); + SAL_WARN_IF(renderMethodToUse() == RenderMetal, "vcl.skia", + "Cannot create Metal GPU offscreen surface, falling back to Raster"); } break; } @@ -559,8 +621,7 @@ tools::Long maxImageCacheSize() void cleanup() { - delete sharedGrDirectContext; - sharedGrDirectContext = nullptr; + sharedWindowContext.reset(); imageCache.clear(); imageCacheSize = 0; } @@ -574,11 +635,11 @@ void setPixelGeometry(SkPixelGeometry pixelGeometry) } // Skia should not be used from VCL backends that do not actually support it, as there will be setup missing. -// The code here (that is in the vcl lib) needs a function for creating Vulkan context that is +// The code here (that is in the vcl lib) needs a function for creating Vulkan/Metal context that is // usually available only in the backend libs. -void prepareSkia(std::unique_ptr (*createVulkanWindowContext)(bool)) +void prepareSkia(std::unique_ptr (*createGpuWindowContext)(bool)) { - setCreateVulkanWindowContext(createVulkanWindowContext); + setCreateGpuWindowContext(createGpuWindowContext); skiaSupportedByBackend = true; } diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx index b2d19755d38c..1a27d3719727 100644 --- a/vcl/skia/gdiimpl.cxx +++ b/vcl/skia/gdiimpl.cxx @@ -289,11 +289,7 @@ SkiaSalGraphicsImpl::SkiaSalGraphicsImpl(SalGraphics& rParent, SalGeometryProvid { } -SkiaSalGraphicsImpl::~SkiaSalGraphicsImpl() -{ - assert(!mSurface); - assert(!mWindowContext); -} +SkiaSalGraphicsImpl::~SkiaSalGraphicsImpl() { assert(!mSurface); } void SkiaSalGraphicsImpl::Init() {} @@ -318,10 +314,7 @@ void SkiaSalGraphicsImpl::createWindowSurface(bool forceRaster) SkiaZone zone; assert(!isOffscreen()); assert(!mSurface); - assert(!mWindowContext); - createWindowContext(forceRaster); - if (mWindowContext) - mSurface = mWindowContext->getBackbufferSurface(); + createWindowSurfaceInternal(forceRaster); if (!mSurface) { switch (renderMethodToUse()) @@ -331,6 +324,11 @@ void SkiaSalGraphicsImpl::createWindowSurface(bool forceRaster) "cannot create Vulkan GPU window surface, falling back to Raster"); destroySurface(); // destroys also WindowContext return createWindowSurface(true); // try again + case RenderMetal: + SAL_WARN("vcl.skia", + "cannot create Metal GPU window surface, falling back to Raster"); + destroySurface(); // destroys also WindowContext + return createWindowSurface(true); // try again case RenderRaster: abort(); // This should not really happen, do not even try to cope with it. } @@ -357,7 +355,6 @@ void SkiaSalGraphicsImpl::createOffscreenSurface() SkiaZone zone; assert(isOffscreen()); assert(!mSurface); - assert(!mWindowContext); // HACK: See isOffscreen(). int width = std::max(1, GetWidth()); int height = std::max(1, GetHeight()); @@ -385,8 +382,9 @@ void SkiaSalGraphicsImpl::destroySurface() // but work around it here. if (mSurface) mSurface->flushAndSubmit(); + if (!isOffscreen()) + destroyWindowSurfaceInternal(); mSurface.reset(); - mWindowContext.reset(); mIsGPU = false; } diff --git a/vcl/skia/osx/gdiimpl.cxx b/vcl/skia/osx/gdiimpl.cxx index 8a879e2f7788..0014a98a404d 100644 --- a/vcl/skia/osx/gdiimpl.cxx +++ b/vcl/skia/osx/gdiimpl.cxx @@ -21,7 +21,7 @@ #include #include -#include +#include #include @@ -43,6 +43,7 @@ AquaSkiaSalGraphicsImpl::AquaSkiaSalGraphicsImpl(AquaSalGraphics& rParent, AquaSkiaSalGraphicsImpl::~AquaSkiaSalGraphicsImpl() { DeInit(); // mac code doesn't call DeInit() + assert(!mWindowContext); } void AquaSkiaSalGraphicsImpl::DeInit() @@ -54,19 +55,31 @@ void AquaSkiaSalGraphicsImpl::DeInit() void AquaSkiaSalGraphicsImpl::freeResources() {} -void AquaSkiaSalGraphicsImpl::createWindowContext(bool forceRaster) +void AquaSkiaSalGraphicsImpl::createWindowSurfaceInternal(bool forceRaster) { SkiaZone zone; sk_app::DisplayParams displayParams; displayParams.fColorType = kN32_SkColorType; - forceRaster = true; // TODO + sk_app::window_context_factory::MacWindowInfo macWindow; + macWindow.fMainView = mrShared.mpFrame->mpNSView; RenderMethod renderMethod = forceRaster ? RenderRaster : renderMethodToUse(); switch (renderMethod) { case RenderRaster: - displayParams.fColorType = kRGBA_8888_SkColorType; // TODO - mWindowContext.reset( - new AquaSkiaWindowContextRaster(GetWidth(), GetHeight(), displayParams)); + // RasterWindowContext_mac uses OpenGL internally, which we don't want, + // so use our own surface and do blitting to the screen ourselves. + mSurface = createSkSurface(GetWidth(), GetHeight()); + break; + case RenderMetal: + // It appears that Metal surfaces cannot be read from, which may break things + // like copyArea(). Additionally sk_app::MetalWindowContext requires + // a new call to getBackbufferSurface() after every swapBuffers(), which + // normally would also require reading contents of the previous surface, + // because we do not redraw the complete area for every draw call. + // Handle that by using an offscreen surface and blit to the onscreen surface as necessary. + mSurface = createSkSurface(GetWidth(), GetHeight()); + mWindowContext + = sk_app::window_context_factory::MakeMetalForMac(macWindow, displayParams); break; case RenderVulkan: abort(); @@ -74,21 +87,50 @@ void AquaSkiaSalGraphicsImpl::createWindowContext(bool forceRaster) } } +void AquaSkiaSalGraphicsImpl::destroyWindowSurfaceInternal() +{ + mWindowContext.reset(); + mSurface.reset(); +} + //void AquaSkiaSalGraphicsImpl::Flush() { performFlush(); } void AquaSkiaSalGraphicsImpl::performFlush() { SkiaZone zone; flushDrawing(); - if (mWindowContext) + if (mSurface) { if (mDirtyRect.intersect(SkIRect::MakeWH(GetWidth(), GetHeight()))) - flushToScreen(mDirtyRect); + { + if (isGPU()) + flushToScreenMetal(mDirtyRect); + else + flushToScreenRaster(mDirtyRect); + } mDirtyRect.setEmpty(); } } -void AquaSkiaSalGraphicsImpl::flushToScreen(const SkIRect& rect) +constexpr static uint32_t toCGBitmapType(SkColorType color, SkAlphaType alpha) +{ + if (alpha == kPremul_SkAlphaType) + { + return color == kBGRA_8888_SkColorType + ? (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little) + : (kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + } + else + { + assert(alpha == kOpaque_SkAlphaType); + return color == kBGRA_8888_SkColorType + ? (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little) + : (kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big); + } +} + +// For Raster we use our own screen blitting (see above). +void AquaSkiaSalGraphicsImpl::flushToScreenRaster(const SkIRect& rect) { // Based on AquaGraphicsBackend::drawBitmap(). if (!mrShared.checkContext()) @@ -105,8 +147,8 @@ void AquaSkiaSalGraphicsImpl::flushToScreen(const SkIRect& rect) // pixel lines will be read from correct positions. CGContextRef context = CGBitmapContextCreate(pixmap.writable_addr32(rect.left(), rect.top()), rect.width(), - rect.height(), 8, pixmap.rowBytes(), // TODO - GetSalData()->mxRGBSpace, kCGImageAlphaNoneSkipLast); // TODO + rect.height(), 8, pixmap.rowBytes(), GetSalData()->mxRGBSpace, + toCGBitmapType(image->colorType(), image->alphaType())); assert(context); // TODO CGImageRef screenImage = CGBitmapContextCreateImage(context); assert(screenImage); // TODO @@ -131,6 +173,19 @@ void AquaSkiaSalGraphicsImpl::flushToScreen(const SkIRect& rect) mrShared.refreshRect(rect.left(), rect.top(), rect.width(), rect.height()); } +// For Metal we flush to the Metal surface and then swap buffers (see above). +void AquaSkiaSalGraphicsImpl::flushToScreenMetal(const SkIRect&) +{ + // The rectangle argument is irrelevant, the whole surface must be used for Metal. + sk_sp screenSurface = mWindowContext->getBackbufferSurface(); + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); // copy as is + screenSurface->getCanvas()->drawImage(makeCheckedImageSnapshot(mSurface), 0, 0, + SkSamplingOptions(), &paint); + screenSurface->flushAndSubmit(); // Otherwise the window is not drawn sometimes. + mWindowContext->swapBuffers(nullptr); +} + bool AquaSkiaSalGraphicsImpl::drawNativeControl(ControlType nType, ControlPart nPart, const tools::Rectangle& rControlRegion, ControlState nState, const ImplControlValue& aValue) @@ -140,9 +195,9 @@ bool AquaSkiaSalGraphicsImpl::drawNativeControl(ControlType nType, ControlPart n const size_t bytes = width * height * 4; std::unique_ptr data(new sal_uInt8[bytes]); memset(data.get(), 0, bytes); - CGContextRef context - = CGBitmapContextCreate(data.get(), width, height, 8, width * 4, // TODO - GetSalData()->mxRGBSpace, kCGImageAlphaPremultipliedLast); // TODO + CGContextRef context = CGBitmapContextCreate( + data.get(), width, height, 8, width * 4, GetSalData()->mxRGBSpace, + toCGBitmapType(mSurface->imageInfo().colorType(), kPremul_SkAlphaType)); assert(context); // TODO // Flip upside down. CGContextTranslateCTM(context, 0, height); @@ -156,9 +211,10 @@ bool AquaSkiaSalGraphicsImpl::drawNativeControl(ControlType nType, ControlPart n if (bOK) { SkBitmap bitmap; - if (!bitmap.installPixels( - SkImageInfo::Make(width, height, kRGBA_8888_SkColorType, kPremul_SkAlphaType), - data.get(), width * 4)) + if (!bitmap.installPixels(SkImageInfo::Make(width, height, + mSurface->imageInfo().colorType(), + kPremul_SkAlphaType), + data.get(), width * 4)) abort(); preDraw(); @@ -224,11 +280,14 @@ void AquaSkiaSalGraphicsImpl::drawTextLayout(const GenericSalLayout& rLayout) drawGenericLayout(rLayout, mrShared.maTextColor, font, verticalFont); } -std::unique_ptr createVulkanWindowContext(bool /*temporary*/) +std::unique_ptr createMetalWindowContext(bool /*temporary*/) { - return nullptr; + sk_app::DisplayParams displayParams; + sk_app::window_context_factory::MacWindowInfo macWindow; + macWindow.fMainView = nullptr; + return sk_app::window_context_factory::MakeMetalForMac(macWindow, displayParams); } -void AquaSkiaSalGraphicsImpl::prepareSkia() { SkiaHelper::prepareSkia(createVulkanWindowContext); } +void AquaSkiaSalGraphicsImpl::prepareSkia() { SkiaHelper::prepareSkia(createMetalWindowContext); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/skia/osx/rastercontext.cxx b/vcl/skia/osx/rastercontext.cxx deleted file mode 100644 index a2a514483710..000000000000 --- a/vcl/skia/osx/rastercontext.cxx +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- 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/. - * - * Some of this code is based on Skia source code, covered by the following - * license notice (see readlicense_oo for the full license): - * - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - * - */ - -#include - -#include - -AquaSkiaWindowContextRaster::AquaSkiaWindowContextRaster(int w, int h, - const sk_app::DisplayParams& params) - : WindowContext(params) -{ - fWidth = w; - fHeight = h; - resize(w, h); -} - -void AquaSkiaWindowContextRaster::resize(int w, int h) -{ - fWidth = w; - fHeight = h; - createSurface(); -} - -void AquaSkiaWindowContextRaster::setDisplayParams(const sk_app::DisplayParams& params) -{ - fDisplayParams = params; -} - -void AquaSkiaWindowContextRaster::createSurface() -{ - SkImageInfo info = SkImageInfo::Make(fWidth, fHeight, fDisplayParams.fColorType, - kPremul_SkAlphaType, fDisplayParams.fColorSpace); - mSurface = SkSurface::MakeRaster(info, &fDisplayParams.fSurfaceProps); -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/skia/win/gdiimpl.cxx b/vcl/skia/win/gdiimpl.cxx index 2e0e7fc8ee12..c7f1fc1f51e6 100644 --- a/vcl/skia/win/gdiimpl.cxx +++ b/vcl/skia/win/gdiimpl.cxx @@ -36,8 +36,12 @@ WinSkiaSalGraphicsImpl::WinSkiaSalGraphicsImpl(WinSalGraphics& rGraphics, { } -void WinSkiaSalGraphicsImpl::createWindowContext(bool forceRaster) +WinSkiaSalGraphicsImpl::~WinSkiaSalGraphicsImpl() { assert(!mWindowContext); } + +void WinSkiaSalGraphicsImpl::createWindowSurfaceInternal(bool forceRaster) { + assert(!mWindowContext); + assert(!mSurface); SkiaZone zone; sk_app::DisplayParams displayParams; assert(GetWidth() > 0 && GetHeight() > 0); @@ -52,7 +56,18 @@ void WinSkiaSalGraphicsImpl::createWindowContext(bool forceRaster) mWindowContext = sk_app::window_context_factory::MakeVulkanForWin(mWinParent.gethWnd(), displayParams); break; + case RenderMetal: + abort(); + break; } + if (mWindowContext) + mSurface = mWindowContext->getBackbufferSurface(); +} + +void WinSkiaSalGraphicsImpl::destroyWindowSurfaceInternal() +{ + mWindowContext.reset(); + mSurface.reset(); } void WinSkiaSalGraphicsImpl::DeInit() diff --git a/vcl/skia/x11/gdiimpl.cxx b/vcl/skia/x11/gdiimpl.cxx index 54a1220bcd17..ab0207fd94e6 100644 --- a/vcl/skia/x11/gdiimpl.cxx +++ b/vcl/skia/x11/gdiimpl.cxx @@ -33,7 +33,7 @@ X11SkiaSalGraphicsImpl::X11SkiaSalGraphicsImpl(X11SalGraphics& rParent) { } -X11SkiaSalGraphicsImpl::~X11SkiaSalGraphicsImpl() {} +X11SkiaSalGraphicsImpl::~X11SkiaSalGraphicsImpl() { assert(!mWindowContext); } void X11SkiaSalGraphicsImpl::Init() { @@ -42,12 +42,22 @@ void X11SkiaSalGraphicsImpl::Init() SkiaSalGraphicsImpl::Init(); } -void X11SkiaSalGraphicsImpl::createWindowContext(bool forceRaster) +void X11SkiaSalGraphicsImpl::createWindowSurfaceInternal(bool forceRaster) { + assert(!mWindowContext); + assert(!mSurface); assert(mX11Parent.GetDrawable() != None); mWindowContext = createWindowContext(mX11Parent.GetXDisplay(), mX11Parent.GetDrawable(), &mX11Parent.GetVisual(), GetWidth(), GetHeight(), forceRaster ? RenderRaster : renderMethodToUse(), false); + if (mWindowContext) + mSurface = mWindowContext->getBackbufferSurface(); +} + +void X11SkiaSalGraphicsImpl::destroyWindowSurfaceInternal() +{ + mWindowContext.reset(); + mSurface.reset(); } std::unique_ptr @@ -103,6 +113,9 @@ X11SkiaSalGraphicsImpl::createWindowContext(Display* display, Drawable drawable, return sk_app::window_context_factory::MakeRasterForXlib(winInfo, displayParams); case RenderVulkan: return sk_app::window_context_factory::MakeVulkanForXlib(winInfo, displayParams); + case RenderMetal: + abort(); + break; } abort(); } diff --git a/vcl/skia/zone.cxx b/vcl/skia/zone.cxx index d9010b7a96b2..f954173662ff 100644 --- a/vcl/skia/zone.cxx +++ b/vcl/skia/zone.cxx @@ -62,15 +62,16 @@ const CrashWatchdogTimingsValues& SkiaZone::getCrashWatchdogTimingsValues() switch (renderMethodToUse()) { case RenderVulkan: + case RenderMetal: { #if defined(SK_RELEASE) - static const CrashWatchdogTimingsValues vulkanValues = { 6, 20 }; /* 1.5s, 5s */ + static const CrashWatchdogTimingsValues gpuValues = { 6, 20 }; /* 1.5s, 5s */ #elif defined(SK_DEBUG) - static const CrashWatchdogTimingsValues vulkanValues = { 60, 200 }; /* 15s, 50s */ + static const CrashWatchdogTimingsValues gpuValues = { 60, 200 }; /* 15s, 50s */ #else #error Unknown Skia debug/release setting. #endif - return vulkanValues; + return gpuValues; } case RenderRaster: { diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx index 4c60886e4503..b65bfade3a25 100644 --- a/vcl/source/app/svapp.cxx +++ b/vcl/source/app/svapp.cxx @@ -1186,6 +1186,9 @@ OUString Application::GetHWOSConfInfo(const int bSelection, const bool bLocalize case SkiaHelper::RenderVulkan: appendDetails(u"", Localize(SV_APP_SKIA_VULKAN, bLocalize)); break; + case SkiaHelper::RenderMetal: + appendDetails(u"", Localize(SV_APP_SKIA_METAL, bLocalize)); + break; case SkiaHelper::RenderRaster: appendDetails(u"", Localize(SV_APP_SKIA_RASTER, bLocalize)); break; -- cgit v1.2.3