[Libreoffice-commits] core.git: 2 commits - config_host/config_skia.h.in external/skia include/vcl vcl/inc vcl/Library_vclplug_osx.mk vcl/osx vcl/quartz vcl/README.vars vcl/skia vcl/source

Luboš Luňák (via logerrit) logerrit at kemper.freedesktop.org
Tue Aug 24 11:17:59 UTC 2021


 config_host/config_skia.h.in            |    3 
 external/skia/Library_skia.mk           |   12 -
 external/skia/share-grcontext.patch.1   |  297 +++++++++++++++++++++++++++++---
 external/skia/swap-buffers-rect.patch.1 |   16 -
 include/vcl/skia/SkiaHelper.hxx         |    3 
 vcl/Library_vclplug_osx.mk              |    1 
 vcl/README.vars                         |    2 
 vcl/inc/quartz/salgdi.h                 |    5 
 vcl/inc/skia/gdiimpl.hxx                |    5 
 vcl/inc/skia/osx/gdiimpl.hxx            |   12 -
 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/osx/salframe.cxx                    |    2 
 vcl/quartz/salgdi.cxx                   |   10 +
 vcl/skia/README                         |    1 
 vcl/skia/SkiaHelper.cxx                 |  203 ++++++++++++++-------
 vcl/skia/gdiimpl.cxx                    |   20 --
 vcl/skia/osx/gdiimpl.cxx                |  103 ++++++++---
 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 
 26 files changed, 593 insertions(+), 254 deletions(-)

New commits:
commit baed91bab05546f7f5233701bd7aaed44f25a364
Author:     Luboš Luňák <l.lunak at collabora.com>
AuthorDate: Mon Aug 23 18:45:12 2021 +0200
Commit:     Luboš Luňák <l.lunak at collabora.com>
CommitDate: Tue Aug 24 13:17:37 2021 +0200

    implement explicit screen flushing also for Skia/Mac
    
    Change-Id: I29b9f54d24aece32949ac3ba916f1d6588cfd85f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120910
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lunak at collabora.com>

diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h
index 5316db0ac1a3..211708dfd576 100644
--- a/vcl/inc/quartz/salgdi.h
+++ b/vcl/inc/quartz/salgdi.h
@@ -297,6 +297,8 @@ public:
                                    ControlState nState,
                                    const ImplControlValue &aValue) = 0;
     virtual void drawTextLayout(const GenericSalLayout& layout) = 0;
+    virtual void Flush() {}
+    virtual void Flush( const tools::Rectangle& ) {}
 protected:
     static bool performDrawNativeControl(ControlType nType,
                                          ControlPart nPart,
@@ -487,6 +489,9 @@ public:
     void                    RefreshRect( const CGRect& ) {}
 #endif
 
+    void                    Flush();
+    void                    Flush( const tools::Rectangle& );
+
     void                    UnsetState();
     // InvalidateContext does an UnsetState and sets mrContext to 0
     void                    InvalidateContext();
diff --git a/vcl/inc/skia/osx/gdiimpl.hxx b/vcl/inc/skia/osx/gdiimpl.hxx
index ef1928bb3a0a..b9adccb370ea 100644
--- a/vcl/inc/skia/osx/gdiimpl.hxx
+++ b/vcl/inc/skia/osx/gdiimpl.hxx
@@ -42,6 +42,9 @@ public:
 
     virtual void drawTextLayout(const GenericSalLayout& layout) override;
 
+    virtual void Flush() override;
+    virtual void Flush(const tools::Rectangle&) override;
+
 private:
     virtual void createWindowSurfaceInternal(bool forceRaster = false) override;
     virtual void destroyWindowSurfaceInternal() override;
diff --git a/vcl/osx/salframe.cxx b/vcl/osx/salframe.cxx
index 18263fa5ea46..5b3a7e665ff5 100644
--- a/vcl/osx/salframe.cxx
+++ b/vcl/osx/salframe.cxx
@@ -988,6 +988,7 @@ void AquaSalFrame::Flush()
     // => fall back to synchronous painting
     if( ImplGetSVData()->maAppData.mnDispatchLevel <= 0 )
     {
+        mpGraphics->Flush();
         [mpNSView display];
     }
 }
@@ -1008,6 +1009,7 @@ void AquaSalFrame::Flush( const tools::Rectangle& rRect )
     // => fall back to synchronous painting
     if( ImplGetSVData()->maAppData.mnDispatchLevel <= 0 )
     {
+        mpGraphics->Flush( rRect );
         [mpNSView display];
     }
 }
diff --git a/vcl/quartz/salgdi.cxx b/vcl/quartz/salgdi.cxx
index 1480c0fda6ab..358ae3881983 100644
--- a/vcl/quartz/salgdi.cxx
+++ b/vcl/quartz/salgdi.cxx
@@ -780,6 +780,16 @@ void AquaSalGraphics::FreeEmbedFontData( const void* pData, tools::Long /*nDataL
     SAL_WARN_IF( (pData==nullptr), "vcl", "AquaSalGraphics::FreeEmbedFontData() is not implemented");
 }
 
+void AquaSalGraphics::Flush()
+{
+    mpBackend->Flush();
+}
+
+void AquaSalGraphics::Flush( const tools::Rectangle& rRect )
+{
+    mpBackend->Flush( rRect );
+}
+
 #ifdef IOS
 
 bool AquaSharedAttributes::checkContext()
diff --git a/vcl/skia/osx/gdiimpl.cxx b/vcl/skia/osx/gdiimpl.cxx
index 0014a98a404d..4b771ad372bb 100644
--- a/vcl/skia/osx/gdiimpl.cxx
+++ b/vcl/skia/osx/gdiimpl.cxx
@@ -93,7 +93,9 @@ void AquaSkiaSalGraphicsImpl::destroyWindowSurfaceInternal()
     mSurface.reset();
 }
 
-//void AquaSkiaSalGraphicsImpl::Flush() { performFlush(); }
+void AquaSkiaSalGraphicsImpl::Flush() { performFlush(); }
+
+void AquaSkiaSalGraphicsImpl::Flush(const tools::Rectangle&) { performFlush(); }
 
 void AquaSkiaSalGraphicsImpl::performFlush()
 {
commit d4afd3aeb4ef2727986551816c1ff9ad0ed12d04
Author:     Luboš Luňák <l.lunak at collabora.com>
AuthorDate: Mon Aug 23 17:57:48 2021 +0200
Commit:     Luboš Luňák <l.lunak at collabora.com>
CommitDate: Tue Aug 24 13:17:23 2021 +0200

    initial Metal support for Mac/Skia
    
    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 <l.lunak at collabora.com>

diff --git a/config_host/config_skia.h.in b/config_host/config_skia.h.in
index 1e003179291b..367920665704 100644
--- a/config_host/config_skia.h.in
+++ b/config_host/config_skia.h.in
@@ -33,8 +33,7 @@ are the same.
 // GPU support (set by configure).
 #undef SK_SUPPORT_GPU
 // Vulkan support enabled (set by configure).
-// temporary override
-#define SK_VULKAN 1
+#undef SK_VULKAN
 // Metal support enabled (set by configure).
 #undef SK_METAL
 
diff --git a/external/skia/Library_skia.mk b/external/skia/Library_skia.mk
index 65006ca5db61..a749bac5d575 100644
--- a/external/skia/Library_skia.mk
+++ b/external/skia/Library_skia.mk
@@ -796,7 +796,7 @@ $(eval $(call gb_Library_add_generated_exception_objects,skia,\
     UnpackedTarball/skia/src/image/SkSurface_Gpu \
 ))
 
-#ifeq ($(SKIA_GPU),VULKAN)
+ifeq ($(SKIA_GPU),VULKAN)
 $(eval $(call gb_Library_add_generated_exception_objects,skia,\
     UnpackedTarball/skia/src/gpu/vk/GrVkAMDMemoryAllocator \
     UnpackedTarball/skia/src/gpu/vk/GrVkAttachment \
@@ -842,7 +842,7 @@ $(eval $(call gb_Library_add_generated_exception_objects,skia,\
     UnpackedTarball/skia/third_party/vulkanmemoryallocator/GrVulkanMemoryAllocator \
 ))
 
-#endif
+endif
 endif
 
 $(eval $(call gb_Library_add_generated_exception_objects,skia,\
@@ -903,11 +903,11 @@ $(eval $(call gb_Library_add_generated_exception_objects,skia,\
     UnpackedTarball/skia/tools/sk_app/win/RasterWindowContext_win \
 ))
 
-#ifeq ($(SKIA_GPU),VULKAN)
+ifeq ($(SKIA_GPU),VULKAN)
 $(eval $(call gb_Library_add_generated_exception_objects,skia,\
     UnpackedTarball/skia/tools/sk_app/win/VulkanWindowContext_win \
 ))
-#endif
+endif
 
 else ifeq ($(OS),MACOSX)
 $(eval $(call gb_Library_add_generated_exception_objects,skia,\
@@ -976,11 +976,11 @@ $(eval $(call gb_Library_add_generated_exception_objects,skia,\
 $(eval $(call gb_Library_add_generated_exception_objects,skia,\
     UnpackedTarball/skia/tools/sk_app/unix/RasterWindowContext_unix \
 ))
-#ifeq ($(SKIA_GPU),VULKAN)
+ifeq ($(SKIA_GPU),VULKAN)
 $(eval $(call gb_Library_add_generated_exception_objects,skia,\
     UnpackedTarball/skia/tools/sk_app/unix/VulkanWindowContext_unix \
 ))
-#endif
+endif
 
 endif
 
diff --git a/external/skia/share-grcontext.patch.1 b/external/skia/share-grcontext.patch.1
index 3cf2133aa1a9..984dbdbd123e 100644
--- a/external/skia/share-grcontext.patch.1
+++ b/external/skia/share-grcontext.patch.1
@@ -1,3 +1,246 @@
+diff --git a/tools/sk_app/MetalWindowContext.h b/tools/sk_app/MetalWindowContext.h
+index e8c8392a15..fbf35c3c2b 100644
+--- a/tools/sk_app/MetalWindowContext.h
++++ b/tools/sk_app/MetalWindowContext.h
+@@ -13,13 +13,18 @@
+ 
+ #include "tools/sk_app/WindowContext.h"
+ 
++#ifdef __OBJC__
+ #import <Metal/Metal.h>
+ #import <QuartzCore/CAMetalLayer.h>
++#endif
+ 
+ namespace sk_app {
+ 
++#ifdef __OBJC__
+ class MetalWindowContext : public WindowContext {
+ public:
++    static GrDirectContext* getSharedGrDirectContext() { return fGlobalShared ? fGlobalShared->fContext.get() : nullptr; }
++
+     sk_sp<SkSurface> getBackbufferSurface() override;
+ 
+     bool isValid() override { return fValid; }
+@@ -45,16 +50,34 @@ protected:
+     void destroyContext();
+     virtual void onDestroyContext() = 0;
+ 
++    static void checkDestroyShared();
++
+     bool                        fValid;
++
++    // We need to use just one GrDirectContext, so share all the relevant data.
++    struct Shared : public SkRefCnt
++    {
+     sk_cfp<id<MTLDevice>>       fDevice;
+     sk_cfp<id<MTLCommandQueue>> fQueue;
+-    CAMetalLayer*               fMetalLayer;
+-    GrMTLHandle                 fDrawableHandle;
+ #if GR_METAL_SDK_VERSION >= 230
+     // wrapping this in sk_cfp throws up an availability warning, so we'll track lifetime manually
+     id<MTLBinaryArchive>        fPipelineArchive SK_API_AVAILABLE(macos(11.0), ios(14.0));
+ #endif
++
++    sk_sp<GrDirectContext> fContext;
++    };
++
++    sk_sp<Shared> fShared;
++
++    static sk_sp<Shared> fGlobalShared;
++
++    CAMetalLayer*               fMetalLayer;
++    GrMTLHandle                 fDrawableHandle;
+ };
++#endif // __OBJC__
++
++// Access function when header is used from C++ code that wouldn't handle ObjC++ headers.
++extern "C" SK_API GrDirectContext* getMetalSharedGrDirectContext();
+ 
+ }   // namespace sk_app
+ 
+diff --git a/tools/sk_app/MetalWindowContext.mm b/tools/sk_app/MetalWindowContext.mm
+index 5b623811ed..49dc77b74d 100644
+--- a/tools/sk_app/MetalWindowContext.mm
++++ b/tools/sk_app/MetalWindowContext.mm
+@@ -37,24 +37,30 @@
+ }
+ 
+ void MetalWindowContext::initializeContext() {
++    fShared = fGlobalShared;
++    if( !fShared )
++    {
++    // TODO do we need a mutex?
++
++    fGlobalShared = sk_make_sp<Shared>();
++    Shared* d = fGlobalShared.get(); // shorter variable name
++
+     SkASSERT(!fContext);
+ 
+-    fDevice.reset(MTLCreateSystemDefaultDevice());
+-    fQueue.reset([*fDevice newCommandQueue]);
++    d->fDevice.reset(MTLCreateSystemDefaultDevice());
++    d->fQueue.reset([*d->fDevice newCommandQueue]);
+ 
+     if (fDisplayParams.fMSAASampleCount > 1) {
+         if (@available(macOS 10.11, iOS 9.0, *)) {
+-            if (![*fDevice supportsTextureSampleCount:fDisplayParams.fMSAASampleCount]) {
++            if (![*d->fDevice supportsTextureSampleCount:fDisplayParams.fMSAASampleCount]) {
++                fGlobalShared.reset();
+                 return;
+             }
+         } else {
++            fGlobalShared.reset();
+             return;
+         }
+     }
+-    fSampleCount = fDisplayParams.fMSAASampleCount;
+-    fStencilBits = 8;
+-
+-    fValid = this->onInitializeContext();
+ 
+ #if GR_METAL_SDK_VERSION >= 230
+     if (fDisplayParams.fEnableBinaryArchive) {
+@@ -62,12 +68,12 @@
+             sk_cfp<MTLBinaryArchiveDescriptor*> desc([MTLBinaryArchiveDescriptor new]);
+             (*desc).url = CacheURL(); // try to load
+             NSError* error;
+-            fPipelineArchive = [*fDevice newBinaryArchiveWithDescriptor:*desc error:&error];
+-            if (!fPipelineArchive) {
++            d->fPipelineArchive = [*d->fDevice newBinaryArchiveWithDescriptor:*desc error:&error];
++            if (!d->fPipelineArchive) {
+                 (*desc).url = nil; // create new
+                 NSError* error;
+-                fPipelineArchive = [*fDevice newBinaryArchiveWithDescriptor:*desc error:&error];
+-                if (!fPipelineArchive) {
++                d->fPipelineArchive = [*d->fDevice newBinaryArchiveWithDescriptor:*desc error:&error];
++                if (!d->fPipelineArchive) {
+                     SkDebugf("Error creating MTLBinaryArchive:\n%s\n",
+                              error.debugDescription.UTF8String);
+                 }
+@@ -75,46 +81,75 @@
+         }
+     } else {
+         if (@available(macOS 11.0, iOS 14.0, *)) {
+-            fPipelineArchive = nil;
++            d->fPipelineArchive = nil;
+         }
+     }
+ #endif
+ 
+     GrMtlBackendContext backendContext = {};
+-    backendContext.fDevice.retain((GrMTLHandle)fDevice.get());
+-    backendContext.fQueue.retain((GrMTLHandle)fQueue.get());
++    backendContext.fDevice.retain((GrMTLHandle)d->fDevice.get());
++    backendContext.fQueue.retain((GrMTLHandle)d->fQueue.get());
+ #if GR_METAL_SDK_VERSION >= 230
+     if (@available(macOS 11.0, iOS 14.0, *)) {
+-        backendContext.fBinaryArchive.retain((__bridge GrMTLHandle)fPipelineArchive);
++        backendContext.fBinaryArchive.retain((__bridge GrMTLHandle)d->fPipelineArchive);
+     }
+ #endif
+-    fContext = GrDirectContext::MakeMetal(backendContext, fDisplayParams.fGrContextOptions);
+-    if (!fContext && fDisplayParams.fMSAASampleCount > 1) {
++    d->fContext = GrDirectContext::MakeMetal(backendContext, fDisplayParams.fGrContextOptions);
++    if (!d->fContext && fDisplayParams.fMSAASampleCount > 1) {
+         fDisplayParams.fMSAASampleCount /= 2;
++        fGlobalShared.reset();
+         this->initializeContext();
+         return;
+     }
++
++    fShared = fGlobalShared;
++    } // if( !fShared )
++
++    fContext = fShared->fContext;
++
++    fSampleCount = fDisplayParams.fMSAASampleCount;
++    fStencilBits = 8;
++
++    fValid = this->onInitializeContext();
+ }
+ 
+ void MetalWindowContext::destroyContext() {
+-    if (fContext) {
+-        // in case we have outstanding refs to this (lua?)
+-        fContext->abandonContext();
+-        fContext.reset();
+-    }
+-
+     this->onDestroyContext();
+ 
+     fMetalLayer = nil;
+     fValid = false;
+ 
++    fContext.reset();
++    fShared.reset();
++
++    checkDestroyShared();
++}
++
++void MetalWindowContext::checkDestroyShared()
++{
++    if(!fGlobalShared || !fGlobalShared->unique()) // TODO mutex?
++        return;
++#ifndef SK_TRACE_VK_RESOURCES
++    if(!fGlobalShared->fContext->unique())
++        return;
++#endif
++    SkASSERT(fGlobalShared->fContext->unique());
++
++    if (fGlobalShared->fContext) {
++        // in case we have outstanding refs to this (lua?)
++        fGlobalShared->fContext->abandonContext();
++        fGlobalShared->fContext.reset();
++    }
++
+ #if GR_METAL_SDK_VERSION >= 230
+     if (@available(macOS 11.0, iOS 14.0, *)) {
+-        [fPipelineArchive release];
++        [fGlobalShared->fPipelineArchive release];
+     }
+ #endif
+-    fQueue.reset();
+-    fDevice.reset();
++    fGlobalShared->fQueue.reset();
++    fGlobalShared->fDevice.reset();
++
++    fGlobalShared.reset();
+ }
+ 
+ sk_sp<SkSurface> MetalWindowContext::getBackbufferSurface() {
+@@ -155,7 +190,7 @@ GrBackendRenderTarget backendRT(fWidth,
+ void MetalWindowContext::swapBuffers() {
+     id<CAMetalDrawable> currentDrawable = (id<CAMetalDrawable>)fDrawableHandle;
+ 
+-    id<MTLCommandBuffer> commandBuffer([*fQueue commandBuffer]);
++    id<MTLCommandBuffer> commandBuffer([*fShared->fQueue commandBuffer]);
+     commandBuffer.label = @"Present";
+ 
+     [commandBuffer presentDrawable:currentDrawable];
+@@ -176,9 +211,9 @@ GrBackendRenderTarget backendRT(fWidth,
+     if (!isActive) {
+ #if GR_METAL_SDK_VERSION >= 230
+         if (@available(macOS 11.0, iOS 14.0, *)) {
+-            if (fPipelineArchive) {
++            if (fShared->fPipelineArchive) {
+                 NSError* error;
+-                [fPipelineArchive serializeToURL:CacheURL() error:&error];
++                [fShared->fPipelineArchive serializeToURL:CacheURL() error:&error];
+                 if (error) {
+                     SkDebugf("Error storing MTLBinaryArchive:\n%s\n",
+                              error.debugDescription.UTF8String);
+@@ -189,4 +224,11 @@ GrBackendRenderTarget backendRT(fWidth,
+     }
+ }
+ 
++SK_API sk_sp<MetalWindowContext::Shared> MetalWindowContext::fGlobalShared;
++
++GrDirectContext* getMetalSharedGrDirectContext()
++{
++    return MetalWindowContext::getSharedGrDirectContext();
++}
++
+ }   //namespace sk_app
 diff --git a/tools/sk_app/VulkanWindowContext.cpp b/tools/sk_app/VulkanWindowContext.cpp
 index d07d5a4274..2b36d60076 100644
 --- a/tools/sk_app/VulkanWindowContext.cpp
@@ -417,10 +660,10 @@ index d07d5a4274..2b36d60076 100644
 +
  }   //namespace sk_app
 diff --git a/tools/sk_app/VulkanWindowContext.h b/tools/sk_app/VulkanWindowContext.h
-index 580dba2733..16f6b3fd51 100644
+index 580dba2733..92bfba6dff 100644
 --- a/tools/sk_app/VulkanWindowContext.h
 +++ b/tools/sk_app/VulkanWindowContext.h
-@@ -19,18 +19,38 @@
+@@ -19,18 +19,22 @@
  #include "tools/gpu/vk/VkTestUtils.h"
  #include "tools/sk_app/WindowContext.h"
  
@@ -432,26 +675,10 @@ index 580dba2733..16f6b3fd51 100644
  
 -class VulkanWindowContext : public WindowContext {
 +class SK_API VulkanWindowContext : public WindowContext {
-+    struct Shared;
  public:
      ~VulkanWindowContext() override;
  
-+    class SharedGrDirectContext {
-+    public:
-+        SharedGrDirectContext() {}
-+        GrDirectContext* getGrDirectContext() { return shared ? shared->fContext.get() : nullptr; }
-+        ~SharedGrDirectContext() { shared.reset(); checkDestroyShared(); }
-+        SharedGrDirectContext(SharedGrDirectContext const &) = default;
-+        SharedGrDirectContext & operator =(SharedGrDirectContext const &) = default;
-+        bool operator!() const { return !shared; }
-+        void reset() { shared.reset(); }
-+    private:
-+        friend class VulkanWindowContext;
-+        SharedGrDirectContext(sk_sp<Shared>& sh ) : shared( sh ) {}
-+        sk_sp<Shared> shared;
-+    };
-+
-+    static SharedGrDirectContext getSharedGrDirectContext() { return SharedGrDirectContext( fGlobalShared ); }
++    static GrDirectContext* getSharedGrDirectContext() { return fGlobalShared ? fGlobalShared->fContext.get() : nullptr; }
 +
      sk_sp<SkSurface> getBackbufferSurface() override;
      void swapBuffers() override;
@@ -461,7 +688,7 @@ index 580dba2733..16f6b3fd51 100644
  
      void resize(int w, int h) override {
          this->createSwapchain(w, h, fDisplayParams);
-@@ -50,9 +70,15 @@ public:
+@@ -50,9 +54,15 @@ public:
      VulkanWindowContext(const DisplayParams&, CreateVkSurfaceFn, CanPresentFn,
                          PFN_vkGetInstanceProcAddr, PFN_vkGetDeviceProcAddr);
  
@@ -477,7 +704,7 @@ index 580dba2733..16f6b3fd51 100644
  
      struct BackbufferInfo {
          uint32_t        fImageIndex;          // image this is associated with
-@@ -64,11 +90,6 @@ private:
+@@ -64,11 +74,6 @@ private:
      bool createBuffers(VkFormat format, VkImageUsageFlags, SkColorType colorType, VkSharingMode);
      void destroyBuffers();
  
@@ -489,7 +716,7 @@ index 580dba2733..16f6b3fd51 100644
      // Create functions
      CreateVkSurfaceFn fCreateVkSurfaceFn;
      CanPresentFn      fCanPresentFn;
-@@ -90,20 +111,44 @@ private:
+@@ -90,20 +95,44 @@ private:
      PFN_vkAcquireNextImageKHR fAcquireNextImageKHR = nullptr;
      PFN_vkQueuePresentKHR fQueuePresentKHR = nullptr;
  
@@ -540,7 +767,7 @@ index 580dba2733..16f6b3fd51 100644
  
      uint32_t               fImageCount;
 diff --git a/tools/sk_app/WindowContext.h b/tools/sk_app/WindowContext.h
-index 0fec5e7366..1d62cea433 100644
+index f143dab013..68bb84b988 100644
 --- a/tools/sk_app/WindowContext.h
 +++ b/tools/sk_app/WindowContext.h
 @@ -10,9 +10,9 @@
@@ -554,6 +781,30 @@ index 0fec5e7366..1d62cea433 100644
  class SkSurface;
  
  namespace sk_app {
+diff --git a/tools/sk_app/mac/MetalWindowContext_mac.mm b/tools/sk_app/mac/MetalWindowContext_mac.mm
+index 5bea8578fa..058c3994be 100644
+--- a/tools/sk_app/mac/MetalWindowContext_mac.mm
++++ b/tools/sk_app/mac/MetalWindowContext_mac.mm
+@@ -49,6 +49,10 @@
+ }
+ 
+ bool MetalWindowContext_mac::onInitializeContext() {
++    // Allow creating just the shared context, without an associated window.
++    if(fMainView == nil)
++        return true;
++
+     SkASSERT(nil != fMainView);
+ 
+     fMetalLayer = [CAMetalLayer layer];
+@@ -56,7 +56,7 @@
+     SkASSERT(nil != fMainView);
+ 
+     fMetalLayer = [CAMetalLayer layer];
+-    fMetalLayer.device = fDevice.get();
++    fMetalLayer.device = fShared->fDevice.get();
+     fMetalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
+ 
+     // resize ignores the passed values and uses the fMainView directly.
 diff --git a/tools/sk_app/unix/VulkanWindowContext_unix.cpp b/tools/sk_app/unix/VulkanWindowContext_unix.cpp
 index 34f6640c76..5478b75dac 100644
 --- a/tools/sk_app/unix/VulkanWindowContext_unix.cpp
diff --git a/external/skia/swap-buffers-rect.patch.1 b/external/skia/swap-buffers-rect.patch.1
index a6e6fd4904a7..628a7e26cd80 100644
--- a/external/skia/swap-buffers-rect.patch.1
+++ b/external/skia/swap-buffers-rect.patch.1
@@ -12,10 +12,10 @@ index c519903006..5dc5bcd180 100644
      void setDisplayParams(const DisplayParams& params) override;
  
 diff --git a/tools/sk_app/MetalWindowContext.h b/tools/sk_app/MetalWindowContext.h
-index e8c8392a15..0d8fd1df6e 100644
+index fbf35c3c2b..2194277922 100644
 --- a/tools/sk_app/MetalWindowContext.h
 +++ b/tools/sk_app/MetalWindowContext.h
-@@ -24,7 +24,7 @@ public:
+@@ -29,7 +29,7 @@ public:
  
      bool isValid() override { return fValid; }
  
@@ -25,10 +25,10 @@ index e8c8392a15..0d8fd1df6e 100644
      void setDisplayParams(const DisplayParams& params) override;
  
 diff --git a/tools/sk_app/MetalWindowContext.mm b/tools/sk_app/MetalWindowContext.mm
-index 5b623811ed..bae6b24138 100644
+index 49dc77b74d..ca1d74dc6c 100644
 --- a/tools/sk_app/MetalWindowContext.mm
 +++ b/tools/sk_app/MetalWindowContext.mm
-@@ -152,7 +152,7 @@ GrBackendRenderTarget backendRT(fWidth,
+@@ -187,7 +187,7 @@ GrBackendRenderTarget backendRT(fWidth,
      return surface;
  }
  
@@ -36,7 +36,7 @@ index 5b623811ed..bae6b24138 100644
 +void MetalWindowContext::swapBuffers(const SkIRect*) {
      id<CAMetalDrawable> currentDrawable = (id<CAMetalDrawable>)fDrawableHandle;
  
-     id<MTLCommandBuffer> commandBuffer([*fQueue commandBuffer]);
+     id<MTLCommandBuffer> commandBuffer([*fShared->fQueue commandBuffer]);
 diff --git a/tools/sk_app/VulkanWindowContext.cpp b/tools/sk_app/VulkanWindowContext.cpp
 index 2b36d60076..d73978c9e4 100644
 --- a/tools/sk_app/VulkanWindowContext.cpp
@@ -51,11 +51,11 @@ index 2b36d60076..d73978c9e4 100644
      BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
      SkSurface* surface = fSurfaces[backbuffer->fImageIndex].get();
 diff --git a/tools/sk_app/VulkanWindowContext.h b/tools/sk_app/VulkanWindowContext.h
-index 16f6b3fd51..39b035215b 100644
+index 92bfba6dff..46f7fd97bd 100644
 --- a/tools/sk_app/VulkanWindowContext.h
 +++ b/tools/sk_app/VulkanWindowContext.h
-@@ -48,7 +48,7 @@ public:
-     static SharedGrDirectContext getSharedGrDirectContext() { return SharedGrDirectContext( fGlobalShared ); }
+@@ -32,7 +32,7 @@ public:
+     static GrDirectContext* getSharedGrDirectContext() { return fGlobalShared ? fGlobalShared->fContext.get() : nullptr; }
  
      sk_sp<SkSurface> getBackbufferSurface() override;
 -    void swapBuffers() override;
diff --git a/include/vcl/skia/SkiaHelper.hxx b/include/vcl/skia/SkiaHelper.hxx
index 8cb76c653600..128c758fa46f 100644
--- a/include/vcl/skia/SkiaHelper.hxx
+++ b/include/vcl/skia/SkiaHelper.hxx
@@ -24,7 +24,8 @@ VCL_DLLPUBLIC bool isVCLSkiaEnabled();
 enum RenderMethod
 {
     RenderRaster,
-    RenderVulkan
+    RenderVulkan,
+    RenderMetal
 };
 
 VCL_DLLPUBLIC RenderMethod renderMethodToUse();
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 <postmac.h>
 
 #include <prewin.h>
-#include <tools/sk_app/WindowContext.h>
 #include <postwin.h>
 
 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<sk_app::WindowContext> mWindowContext;
     // The Skia surface that is target of all the rendering.
     sk_sp<SkSurface> 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<sk_app::WindowContext> createVulkanWindowContext(bool);
+    void flushToScreenRaster(const SkIRect& rect);
+    void flushToScreenMetal(const SkIRect& rect);
     static inline sk_sp<SkFontMgr> fontManager;
+    // This one is used only for Metal, and only indirectly.
+    std::unique_ptr<sk_app::WindowContext> 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 <tools/sk_app/WindowContext.h>
-
-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<SkSurface> 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<SkSurface> 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 <premac.h>
 #include <SkRegion.h>
 #include <SkSurface.h>
-#include <tools/sk_app/VulkanWindowContext.h>
+#include <tools/sk_app/WindowContext.h>
 #include <postmac.h>
 
 namespace SkiaHelper
@@ -74,7 +74,7 @@ inline Size imageSize(const sk_sp<SkImage>& 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<sk_app::WindowContext> (*createVulkanWindowContext)(bool));
+    prepareSkia(std::unique_ptr<sk_app::WindowContext> (*createGpuWindowContext)(bool));
 
 // Shared cache of images.
 void addCachedImage(const OString& key, sk_sp<SkImage> 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 <vcl/dllapi.h>
 #include <skia/gdiimpl.hxx>
+#include <skia/utils.hxx>
 #include <win/salgdi.h>
 #include <win/wingdiimpl.hxx>
 #include <o3tl/lru_map.hxx>
@@ -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<SkTypeface> createDirectWriteTypeface(HDC hdc, HFONT hfont);
     static void initFontInfo();
@@ -69,6 +72,7 @@ protected:
     inline static sk_sp<SkFontMgr> dwriteFontMgr;
     inline static bool dwriteDone = false;
     static SkFont::Edging fontEdging;
+    std::unique_ptr<sk_app::WindowContext> mWindowContext;
 };
 
 typedef std::pair<ControlCacheKey, sk_sp<SkImage>> 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<sk_app::WindowContext>
     createWindowContext(Display* display, Drawable drawable, const XVisualInfo* visual, int width,
                         int height, SkiaHelper::RenderMethod renderMethod, bool temporary);
     friend std::unique_ptr<sk_app::WindowContext> createVulkanWindowContext(bool);
+    std::unique_ptr<sk_app::WindowContext> 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 <GrDirectContext.h>
 #include <skia_compiler.hxx>
 #include <skia_opts.hxx>
-
+#include <tools/sk_app/VulkanWindowContext.h>
+#include <tools/sk_app/MetalWindowContext.h>
 #ifdef DBG_UTIL
 #include <fstream>
 #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<sk_app::WindowContext> 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<sk_app::WindowContext> 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<sk_app::WindowContext> sharedWindowContext;
 
-static std::unique_ptr<sk_app::WindowContext> (*createVulkanWindowContextFunction)(bool) = nullptr;
-static void setCreateVulkanWindowContext(std::unique_ptr<sk_app::WindowContext> (*function)(bool))
+static std::unique_ptr<sk_app::WindowContext> (*createGpuWindowContextFunction)(bool) = nullptr;
+static void setCreateGpuWindowContext(std::unique_ptr<sk_app::WindowContext> (*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<sk_app::WindowContext> 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<sk_app::WindowContext> getTemporaryWindowContext()
 {
-    if (createVulkanWindowContextFunction == nullptr)
-        return sk_app::VulkanWindowContext::SharedGrDirectContext();
-    std::unique_ptr<sk_app::WindowContext> 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<SkSurface> createSkSurface(int width, int height, SkColorType type, SkAlphaType alpha)
 {
@@ -392,6 +448,7 @@ sk_sp<SkSurface> createSkSurface(int width, int height, SkColorType type, SkAlph
     switch (renderMethodToUse())
     {
         case RenderVulkan:
+        case RenderMetal:
         {
             if (GrDirectContext* grDirectContext = getSharedGrDirectContext())
             {
@@ -405,8 +462,10 @@ sk_sp<SkSurface> 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<SkImage> createSkImage(const SkBitmap& bitmap)
     switch (renderMethodToUse())
     {
         case RenderVulkan:
+        case RenderMetal:
         {
             if (GrDirectContext* grDirectContext = getSharedGrDirectContext())
             {
@@ -450,8 +510,10 @@ sk_sp<SkImage> 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<sk_app::WindowContext> (*createVulkanWindowContext)(bool))
+void prepareSkia(std::unique_ptr<sk_app::WindowContext> (*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 <skia/utils.hxx>
 #include <skia/zone.hxx>
 
-#include <skia/osx/rastercontext.hxx>
+#include <tools/sk_app/mac/WindowContextFactory_mac.h>
 
 #include <quartz/ctfonts.hxx>
 
@@ -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<SkSurface> 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<sal_uInt8[]> 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<sk_app::WindowContext> createVulkanWindowContext(bool /*temporary*/)
+std::unique_ptr<sk_app::WindowContext> 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 <skia/osx/rastercontext.hxx>
-
-#include <SkSurface.h>
-
-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<sk_app::WindowContext>
@@ -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;


More information about the Libreoffice-commits mailing list