diff options
author | Luboš Luňák <l.lunak@collabora.com> | 2021-08-23 17:57:48 +0200 |
---|---|---|
committer | Luboš Luňák <l.lunak@collabora.com> | 2021-08-24 13:17:23 +0200 |
commit | d4afd3aeb4ef2727986551816c1ff9ad0ed12d04 (patch) | |
tree | 40694dde66cac18d071acc647ea107aea5c61c9f | |
parent | ddcdc2077fab2b6d9c4def4e4615185411cbe80a (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.in | 3 | ||||
-rw-r--r-- | external/skia/Library_skia.mk | 12 | ||||
-rw-r--r-- | external/skia/share-grcontext.patch.1 | 297 | ||||
-rw-r--r-- | external/skia/swap-buffers-rect.patch.1 | 16 | ||||
-rw-r--r-- | include/vcl/skia/SkiaHelper.hxx | 3 | ||||
-rw-r--r-- | vcl/Library_vclplug_osx.mk | 1 | ||||
-rw-r--r-- | vcl/README.vars | 2 | ||||
-rw-r--r-- | vcl/inc/skia/gdiimpl.hxx | 5 | ||||
-rw-r--r-- | vcl/inc/skia/osx/gdiimpl.hxx | 9 | ||||
-rw-r--r-- | vcl/inc/skia/osx/rastercontext.hxx | 42 | ||||
-rw-r--r-- | vcl/inc/skia/utils.hxx | 4 | ||||
-rw-r--r-- | vcl/inc/skia/win/gdiimpl.hxx | 6 | ||||
-rw-r--r-- | vcl/inc/skia/x11/gdiimpl.hxx | 4 | ||||
-rw-r--r-- | vcl/inc/strings.hrc | 1 | ||||
-rw-r--r-- | vcl/skia/README | 1 | ||||
-rw-r--r-- | vcl/skia/SkiaHelper.cxx | 203 | ||||
-rw-r--r-- | vcl/skia/gdiimpl.cxx | 20 | ||||
-rw-r--r-- | vcl/skia/osx/gdiimpl.cxx | 99 | ||||
-rw-r--r-- | vcl/skia/osx/rastercontext.cxx | 51 | ||||
-rw-r--r-- | vcl/skia/win/gdiimpl.cxx | 17 | ||||
-rw-r--r-- | vcl/skia/x11/gdiimpl.cxx | 17 | ||||
-rw-r--r-- | vcl/skia/zone.cxx | 7 | ||||
-rw-r--r-- | vcl/source/app/svapp.cxx | 3 |
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; |