summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuboš Luňák <l.lunak@collabora.com>2021-08-23 17:57:48 +0200
committerLuboš Luňák <l.lunak@collabora.com>2021-08-24 13:17:23 +0200
commitd4afd3aeb4ef2727986551816c1ff9ad0ed12d04 (patch)
tree40694dde66cac18d071acc647ea107aea5c61c9f
parentddcdc2077fab2b6d9c4def4e4615185411cbe80a (diff)
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@collabora.com>
-rw-r--r--config_host/config_skia.h.in3
-rw-r--r--external/skia/Library_skia.mk12
-rw-r--r--external/skia/share-grcontext.patch.1297
-rw-r--r--external/skia/swap-buffers-rect.patch.116
-rw-r--r--include/vcl/skia/SkiaHelper.hxx3
-rw-r--r--vcl/Library_vclplug_osx.mk1
-rw-r--r--vcl/README.vars2
-rw-r--r--vcl/inc/skia/gdiimpl.hxx5
-rw-r--r--vcl/inc/skia/osx/gdiimpl.hxx9
-rw-r--r--vcl/inc/skia/osx/rastercontext.hxx42
-rw-r--r--vcl/inc/skia/utils.hxx4
-rw-r--r--vcl/inc/skia/win/gdiimpl.hxx6
-rw-r--r--vcl/inc/skia/x11/gdiimpl.hxx4
-rw-r--r--vcl/inc/strings.hrc1
-rw-r--r--vcl/skia/README1
-rw-r--r--vcl/skia/SkiaHelper.cxx203
-rw-r--r--vcl/skia/gdiimpl.cxx20
-rw-r--r--vcl/skia/osx/gdiimpl.cxx99
-rw-r--r--vcl/skia/osx/rastercontext.cxx51
-rw-r--r--vcl/skia/win/gdiimpl.cxx17
-rw-r--r--vcl/skia/x11/gdiimpl.cxx17
-rw-r--r--vcl/skia/zone.cxx7
-rw-r--r--vcl/source/app/svapp.cxx3
23 files changed, 570 insertions, 253 deletions
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;