summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuboš Luňák <l.lunak@collabora.com>2019-11-12 16:10:50 +0100
committerLuboš Luňák <l.lunak@collabora.com>2019-11-27 09:55:16 +0100
commit178079863415bd3618b2008c20aa42ed50ca90ea (patch)
tree36fe5da41410ed14176665e927f245f8596ac45e
parenta51d335b72551940e037fb7abaa5f0c1ec405a99 (diff)
fix Skia offscreen GPU-backed drawing
The previous approach of using multiple GrContext instances apparently does not work, it's not possible to do drawing operations that involve objects using two different GrContext's. So patch Skia to use just one GrContext for our needs. See vcl/skia/README for details. Change-Id: I2bd3d3c618bf7f8ff45b2f37cbd086d2289940aa
-rw-r--r--external/skia/README6
-rw-r--r--external/skia/UnpackedTarball_skia.mk8
-rw-r--r--external/skia/share-grcontext.patch.1499
-rw-r--r--vcl/Library_vcl.mk3
-rw-r--r--vcl/inc/skia/gdiimpl.hxx4
-rw-r--r--vcl/inc/skia/vulkan.hxx29
-rw-r--r--vcl/skia/README16
-rw-r--r--vcl/skia/gdiimpl.cxx59
-rw-r--r--vcl/skia/vulkan.cxx39
-rw-r--r--vcl/skia/win/gdiimpl.cxx22
-rw-r--r--vcl/skia/x11/gdiimpl.cxx32
11 files changed, 601 insertions, 116 deletions
diff --git a/external/skia/README b/external/skia/README
index 767f12e91936..95be1561d4fb 100644
--- a/external/skia/README
+++ b/external/skia/README
@@ -17,3 +17,9 @@ tar cvJf skia-mXX-$id.tar.xz skia
(where XX refers to the branch version)
And review differences for BUILD.gn and relevant files in gn/ .
+
+
+GrContext sharing
+=================
+
+For details about the share-grcontext patch, see vcl/skia/README.
diff --git a/external/skia/UnpackedTarball_skia.mk b/external/skia/UnpackedTarball_skia.mk
index e9905f5abfa7..c3988042a012 100644
--- a/external/skia/UnpackedTarball_skia.mk
+++ b/external/skia/UnpackedTarball_skia.mk
@@ -12,7 +12,13 @@ $(eval $(call gb_UnpackedTarball_UnpackedTarball,skia))
$(eval $(call gb_UnpackedTarball_set_tarball,skia,$(SKIA_TARBALL)))
# TODO
-skia_patches := lerp.patch fix-pch.patch fix-ddi.patch make-api-visible.patch.1 fix-shader-locale.patch.1
+skia_patches := \
+ lerp.patch \
+ fix-pch.patch \
+ fix-ddi.patch \
+ make-api-visible.patch.1 \
+ fix-shader-locale.patch.1 \
+ share-grcontext.patch.1
$(eval $(call gb_UnpackedTarball_set_patchlevel,skia,1))
diff --git a/external/skia/share-grcontext.patch.1 b/external/skia/share-grcontext.patch.1
new file mode 100644
index 000000000000..0492bd948240
--- /dev/null
+++ b/external/skia/share-grcontext.patch.1
@@ -0,0 +1,499 @@
+diff --git a/tools/sk_app/VulkanWindowContext.cpp b/tools/sk_app/VulkanWindowContext.cpp
+index 793c88c158..21164cac67 100644
+--- a/tools/sk_app/VulkanWindowContext.cpp
++++ b/tools/sk_app/VulkanWindowContext.cpp
+@@ -1,4 +1,3 @@
+-
+ /*
+ * Copyright 2015 Google Inc.
+ *
+@@ -24,8 +23,10 @@
+ #undef CreateSemaphore
+ #endif
+
+-#define GET_PROC(F) f ## F = (PFN_vk ## F) fGetInstanceProcAddr(fInstance, "vk" #F)
+-#define GET_DEV_PROC(F) f ## F = (PFN_vk ## F) fGetDeviceProcAddr(fDevice, "vk" #F)
++#define GET_PROC(F) f ## F = (PFN_vk ## F) fGetInstanceProcAddr(fShared->fInstance, "vk" #F)
++#define GET_DEV_PROC(F) f ## F = (PFN_vk ## F) fGetDeviceProcAddr(fShared->fDevice, "vk" #F)
++#define GET_PROC_GLOBAL(F) fGlobalShared->f ## F = (PFN_vk ## F) fGetInstanceProcAddr(fGlobalShared->fInstance, "vk" #F)
++#define GET_DEV_PROC_GLOBAL(F) fGlobalShared->f ## F = (PFN_vk ## F) fGetDeviceProcAddr(fGlobalShared->fDevice, "vk" #F)
+
+ namespace sk_app {
+
+@@ -49,6 +50,14 @@ VulkanWindowContext::VulkanWindowContext(const DisplayParams& params,
+ }
+
+ void VulkanWindowContext::initializeContext() {
++ fShared = fGlobalShared;
++ if( !fShared )
++ {
++ // TODO do we need a mutex?
++
++ fGlobalShared = sk_make_sp<Shared>();
++ Shared* d = fGlobalShared.get(); // shorter variable name
++
+ // any config code here (particularly for msaa)?
+
+ PFN_vkGetInstanceProcAddr getInstanceProc = fGetInstanceProcAddr;
+@@ -62,24 +71,25 @@ void VulkanWindowContext::initializeContext() {
+ };
+ GrVkBackendContext backendContext;
+ GrVkExtensions extensions;
+- VkPhysicalDeviceFeatures2 features;
+- if (!sk_gpu_test::CreateVkBackendContext(getProc, &backendContext, &extensions, &features,
+- &fDebugCallback, &fPresentQueueIndex, fCanPresentFn)) {
+- sk_gpu_test::FreeVulkanFeaturesStructs(&features);
++ if (!sk_gpu_test::CreateVkBackendContext(getProc, &backendContext, &extensions, &d->features,
++ &d->fDebugCallback, &d->fPresentQueueIndex, fCanPresentFn)) {
++ sk_gpu_test::FreeVulkanFeaturesStructs(&d->features);
++ fGlobalShared.reset();
+ return;
+ }
+
+ if (!extensions.hasExtension(VK_KHR_SURFACE_EXTENSION_NAME, 25) ||
+ !extensions.hasExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, 68)) {
+- sk_gpu_test::FreeVulkanFeaturesStructs(&features);
++ sk_gpu_test::FreeVulkanFeaturesStructs(&d->features);
++ fGlobalShared.reset();
+ return;
+ }
+
+- fInstance = backendContext.fInstance;
+- fPhysicalDevice = backendContext.fPhysicalDevice;
+- fDevice = backendContext.fDevice;
+- fGraphicsQueueIndex = backendContext.fGraphicsQueueIndex;
+- fGraphicsQueue = backendContext.fQueue;
++ d->fInstance = backendContext.fInstance;
++ d->fPhysicalDevice = backendContext.fPhysicalDevice;
++ d->fDevice = backendContext.fDevice;
++ d->fGraphicsQueueIndex = backendContext.fGraphicsQueueIndex;
++ d->fGraphicsQueue = backendContext.fQueue;
+
+ PFN_vkGetPhysicalDeviceProperties localGetPhysicalDeviceProperties =
+ reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(
+@@ -87,21 +97,31 @@ void VulkanWindowContext::initializeContext() {
+ backendContext.fInstance,
+ VK_NULL_HANDLE));
+ if (!localGetPhysicalDeviceProperties) {
+- sk_gpu_test::FreeVulkanFeaturesStructs(&features);
++ sk_gpu_test::FreeVulkanFeaturesStructs(&d->features);
++ fGlobalShared.reset();
+ return;
+ }
+ VkPhysicalDeviceProperties physDeviceProperties;
+ localGetPhysicalDeviceProperties(backendContext.fPhysicalDevice, &physDeviceProperties);
+ uint32_t physDevVersion = physDeviceProperties.apiVersion;
+
+- fInterface.reset(new GrVkInterface(backendContext.fGetProc, fInstance, fDevice,
++ d->fInterface.reset(new GrVkInterface(backendContext.fGetProc, d->fInstance, d->fDevice,
+ backendContext.fInstanceVersion, physDevVersion,
+ &extensions));
+
+- GET_PROC(DestroyInstance);
+- if (fDebugCallback != VK_NULL_HANDLE) {
+- GET_PROC(DestroyDebugReportCallbackEXT);
++ d->fContext = GrContext::MakeVulkan(backendContext, fDisplayParams.fGrContextOptions);
++
++ GET_PROC_GLOBAL(DestroyInstance);
++ GET_DEV_PROC_GLOBAL(DestroyDevice);
++ if (fGlobalShared->fDebugCallback != VK_NULL_HANDLE) {
++ GET_PROC_GLOBAL(DestroyDebugReportCallbackEXT);
+ }
++
++ fShared = fGlobalShared;
++ } // if( !fShared )
++
++ fContext = fShared->fContext;
++
+ GET_PROC(DestroySurfaceKHR);
+ GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
+ GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
+@@ -109,7 +129,6 @@ void VulkanWindowContext::initializeContext() {
+ GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
+ GET_DEV_PROC(DeviceWaitIdle);
+ GET_DEV_PROC(QueueWaitIdle);
+- GET_DEV_PROC(DestroyDevice);
+ GET_DEV_PROC(CreateSwapchainKHR);
+ GET_DEV_PROC(DestroySwapchainKHR);
+ GET_DEV_PROC(GetSwapchainImagesKHR);
+@@ -117,46 +136,40 @@ void VulkanWindowContext::initializeContext() {
+ GET_DEV_PROC(QueuePresentKHR);
+ GET_DEV_PROC(GetDeviceQueue);
+
+- fContext = GrContext::MakeVulkan(backendContext, fDisplayParams.fGrContextOptions);
+-
+- fSurface = fCreateVkSurfaceFn(fInstance);
++ fSurface = fCreateVkSurfaceFn(fShared->fInstance);
+ if (VK_NULL_HANDLE == fSurface) {
+ this->destroyContext();
+- sk_gpu_test::FreeVulkanFeaturesStructs(&features);
+ return;
+ }
+
+ VkBool32 supported;
+- VkResult res = fGetPhysicalDeviceSurfaceSupportKHR(fPhysicalDevice, fPresentQueueIndex,
++ VkResult res = fGetPhysicalDeviceSurfaceSupportKHR(fShared->fPhysicalDevice, fShared->fPresentQueueIndex,
+ fSurface, &supported);
+ if (VK_SUCCESS != res) {
+ this->destroyContext();
+- sk_gpu_test::FreeVulkanFeaturesStructs(&features);
+ return;
+ }
+
+ if (!this->createSwapchain(-1, -1, fDisplayParams)) {
+ this->destroyContext();
+- sk_gpu_test::FreeVulkanFeaturesStructs(&features);
+ return;
+ }
+
+ // create presentQueue
+- fGetDeviceQueue(fDevice, fPresentQueueIndex, 0, &fPresentQueue);
+- sk_gpu_test::FreeVulkanFeaturesStructs(&features);
++ fGetDeviceQueue(fShared->fDevice, fShared->fPresentQueueIndex, 0, &fPresentQueue);
+ }
+
+ bool VulkanWindowContext::createSwapchain(int width, int height,
+ const DisplayParams& params) {
+ // check for capabilities
+ VkSurfaceCapabilitiesKHR caps;
+- VkResult res = fGetPhysicalDeviceSurfaceCapabilitiesKHR(fPhysicalDevice, fSurface, &caps);
++ VkResult res = fGetPhysicalDeviceSurfaceCapabilitiesKHR(fShared->fPhysicalDevice, fSurface, &caps);
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ uint32_t surfaceFormatCount;
+- res = fGetPhysicalDeviceSurfaceFormatsKHR(fPhysicalDevice, fSurface, &surfaceFormatCount,
++ res = fGetPhysicalDeviceSurfaceFormatsKHR(fShared->fPhysicalDevice, fSurface, &surfaceFormatCount,
+ nullptr);
+ if (VK_SUCCESS != res) {
+ return false;
+@@ -164,14 +177,14 @@ bool VulkanWindowContext::createSwapchain(int width, int height,
+
+ SkAutoMalloc surfaceFormatAlloc(surfaceFormatCount * sizeof(VkSurfaceFormatKHR));
+ VkSurfaceFormatKHR* surfaceFormats = (VkSurfaceFormatKHR*)surfaceFormatAlloc.get();
+- res = fGetPhysicalDeviceSurfaceFormatsKHR(fPhysicalDevice, fSurface, &surfaceFormatCount,
++ res = fGetPhysicalDeviceSurfaceFormatsKHR(fShared->fPhysicalDevice, fSurface, &surfaceFormatCount,
+ surfaceFormats);
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ uint32_t presentModeCount;
+- res = fGetPhysicalDeviceSurfacePresentModesKHR(fPhysicalDevice, fSurface, &presentModeCount,
++ res = fGetPhysicalDeviceSurfacePresentModesKHR(fShared->fPhysicalDevice, fSurface, &presentModeCount,
+ nullptr);
+ if (VK_SUCCESS != res) {
+ return false;
+@@ -179,7 +192,7 @@ bool VulkanWindowContext::createSwapchain(int width, int height,
+
+ SkAutoMalloc presentModeAlloc(presentModeCount * sizeof(VkPresentModeKHR));
+ VkPresentModeKHR* presentModes = (VkPresentModeKHR*)presentModeAlloc.get();
+- res = fGetPhysicalDeviceSurfacePresentModesKHR(fPhysicalDevice, fSurface, &presentModeCount,
++ res = fGetPhysicalDeviceSurfacePresentModesKHR(fShared->fPhysicalDevice, fSurface, &presentModeCount,
+ presentModes);
+ if (VK_SUCCESS != res) {
+ return false;
+@@ -286,8 +299,8 @@ bool VulkanWindowContext::createSwapchain(int width, int height,
+ swapchainCreateInfo.imageArrayLayers = 1;
+ swapchainCreateInfo.imageUsage = usageFlags;
+
+- uint32_t queueFamilies[] = { fGraphicsQueueIndex, fPresentQueueIndex };
+- if (fGraphicsQueueIndex != fPresentQueueIndex) {
++ uint32_t queueFamilies[] = { fShared->fGraphicsQueueIndex, fShared->fPresentQueueIndex };
++ if (fShared->fGraphicsQueueIndex != fShared->fPresentQueueIndex) {
+ swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
+ swapchainCreateInfo.queueFamilyIndexCount = 2;
+ swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
+@@ -303,18 +316,18 @@ bool VulkanWindowContext::createSwapchain(int width, int height,
+ swapchainCreateInfo.clipped = true;
+ swapchainCreateInfo.oldSwapchain = fSwapchain;
+
+- res = fCreateSwapchainKHR(fDevice, &swapchainCreateInfo, nullptr, &fSwapchain);
++ res = fCreateSwapchainKHR(fShared->fDevice, &swapchainCreateInfo, nullptr, &fSwapchain);
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ // destroy the old swapchain
+ if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
+- fDeviceWaitIdle(fDevice);
++ fDeviceWaitIdle(fShared->fDevice);
+
+ this->destroyBuffers();
+
+- fDestroySwapchainKHR(fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
++ fDestroySwapchainKHR(fShared->fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
+ }
+
+ this->createBuffers(swapchainCreateInfo.imageFormat, colorType);
+@@ -323,10 +336,10 @@ bool VulkanWindowContext::createSwapchain(int width, int height,
+ }
+
+ void VulkanWindowContext::createBuffers(VkFormat format, SkColorType colorType) {
+- fGetSwapchainImagesKHR(fDevice, fSwapchain, &fImageCount, nullptr);
++ fGetSwapchainImagesKHR(fShared->fDevice, fSwapchain, &fImageCount, nullptr);
+ SkASSERT(fImageCount);
+ fImages = new VkImage[fImageCount];
+- fGetSwapchainImagesKHR(fDevice, fSwapchain, &fImageCount, fImages);
++ fGetSwapchainImagesKHR(fShared->fDevice, fSwapchain, &fImageCount, fImages);
+
+ // set up initial image layouts and create surfaces
+ fImageLayouts = new VkImageLayout[fImageCount];
+@@ -341,7 +354,7 @@ void VulkanWindowContext::createBuffers(VkFormat format, SkColorType colorType)
+ info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
+ info.fFormat = format;
+ info.fLevelCount = 1;
+- info.fCurrentQueueFamily = fPresentQueueIndex;
++ info.fCurrentQueueFamily = fShared->fPresentQueueIndex;
+
+ if (fSampleCount == 1) {
+ GrBackendRenderTarget backendRT(fWidth, fHeight, fSampleCount, info);
+@@ -372,8 +385,8 @@ void VulkanWindowContext::createBuffers(VkFormat format, SkColorType colorType)
+ fBackbuffers = new BackbufferInfo[fImageCount + 1];
+ for (uint32_t i = 0; i < fImageCount + 1; ++i) {
+ fBackbuffers[i].fImageIndex = -1;
+- GR_VK_CALL_ERRCHECK(fInterface,
+- CreateSemaphore(fDevice, &semaphoreInfo,
++ GR_VK_CALL_ERRCHECK(fShared->fInterface,
++ CreateSemaphore(fShared->fDevice, &semaphoreInfo,
+ nullptr, &fBackbuffers[i].fRenderSemaphore));
+ }
+ fCurrentBackbufferIndex = fImageCount;
+@@ -384,8 +397,8 @@ void VulkanWindowContext::destroyBuffers() {
+ if (fBackbuffers) {
+ for (uint32_t i = 0; i < fImageCount + 1; ++i) {
+ fBackbuffers[i].fImageIndex = -1;
+- GR_VK_CALL(fInterface,
+- DestroySemaphore(fDevice,
++ GR_VK_CALL(fShared->fInterface,
++ DestroySemaphore(fShared->fDevice,
+ fBackbuffers[i].fRenderSemaphore,
+ nullptr));
+ }
+@@ -410,41 +423,55 @@ VulkanWindowContext::~VulkanWindowContext() {
+ void VulkanWindowContext::destroyContext() {
+ if (this->isValid()) {
+ fQueueWaitIdle(fPresentQueue);
+- fDeviceWaitIdle(fDevice);
++ fDeviceWaitIdle(fShared->fDevice);
+
+ this->destroyBuffers();
+
+ if (VK_NULL_HANDLE != fSwapchain) {
+- fDestroySwapchainKHR(fDevice, fSwapchain, nullptr);
++ fDestroySwapchainKHR(fShared->fDevice, fSwapchain, nullptr);
+ fSwapchain = VK_NULL_HANDLE;
+ }
+
+ if (VK_NULL_HANDLE != fSurface) {
+- fDestroySurfaceKHR(fInstance, fSurface, nullptr);
++ fDestroySurfaceKHR(fShared->fInstance, fSurface, nullptr);
+ fSurface = VK_NULL_HANDLE;
+ }
+ }
+
+ fContext.reset();
+- fInterface.reset();
++ fShared.reset();
++
++ checkDestroyShared();
++}
+
+- if (VK_NULL_HANDLE != fDevice) {
+- fDestroyDevice(fDevice, nullptr);
+- fDevice = VK_NULL_HANDLE;
++void VulkanWindowContext::checkDestroyShared()
++{
++ if(!fGlobalShared || !fGlobalShared->unique()) // TODO mutex?
++ return;
++ SkASSERT(fGlobalShared->fContext->unique());
++ fGlobalShared->fContext.reset();
++ fGlobalShared->fInterface.reset();
++
++ if (VK_NULL_HANDLE != fGlobalShared->fDevice) {
++ fGlobalShared->fDestroyDevice(fGlobalShared->fDevice, nullptr);
++ fGlobalShared->fDevice = VK_NULL_HANDLE;
+ }
+
+ #ifdef SK_ENABLE_VK_LAYERS
+- if (fDebugCallback != VK_NULL_HANDLE) {
+- fDestroyDebugReportCallbackEXT(fInstance, fDebugCallback, nullptr);
++ if (fGlobalShared->fDebugCallback != VK_NULL_HANDLE) {
++ fGlobalShared->fDestroyDebugReportCallbackEXT(fGlobalShared->fInstance, fDebugCallback, nullptr);
+ }
+ #endif
+
+- fPhysicalDevice = VK_NULL_HANDLE;
++ fGlobalShared->fPhysicalDevice = VK_NULL_HANDLE;
+
+- if (VK_NULL_HANDLE != fInstance) {
+- fDestroyInstance(fInstance, nullptr);
+- fInstance = VK_NULL_HANDLE;
++ if (VK_NULL_HANDLE != fGlobalShared->fInstance) {
++ fGlobalShared->fDestroyInstance(fGlobalShared->fInstance, nullptr);
++ fGlobalShared->fInstance = VK_NULL_HANDLE;
+ }
++
++ sk_gpu_test::FreeVulkanFeaturesStructs(&fGlobalShared->features);
++ fGlobalShared.reset();
+ }
+
+ VulkanWindowContext::BackbufferInfo* VulkanWindowContext::getAvailableBackbuffer() {
+@@ -470,34 +497,34 @@ sk_sp<SkSurface> VulkanWindowContext::getBackbufferSurface() {
+ semaphoreInfo.pNext = nullptr;
+ semaphoreInfo.flags = 0;
+ VkSemaphore semaphore;
+- GR_VK_CALL_ERRCHECK(fInterface, CreateSemaphore(fDevice, &semaphoreInfo,
++ GR_VK_CALL_ERRCHECK(fShared->fInterface, CreateSemaphore(fShared->fDevice, &semaphoreInfo,
+ nullptr, &semaphore));
+
+ // acquire the image
+- VkResult res = fAcquireNextImageKHR(fDevice, fSwapchain, UINT64_MAX,
++ VkResult res = fAcquireNextImageKHR(fShared->fDevice, fSwapchain, UINT64_MAX,
+ semaphore, VK_NULL_HANDLE,
+ &backbuffer->fImageIndex);
+ if (VK_ERROR_SURFACE_LOST_KHR == res) {
+ // need to figure out how to create a new vkSurface without the platformData*
+ // maybe use attach somehow? but need a Window
+- GR_VK_CALL(fInterface, DestroySemaphore(fDevice, semaphore, nullptr));
++ GR_VK_CALL(fShared->fInterface, DestroySemaphore(fShared->fDevice, semaphore, nullptr));
+ return nullptr;
+ }
+ if (VK_ERROR_OUT_OF_DATE_KHR == res) {
+ // tear swapchain down and try again
+ if (!this->createSwapchain(-1, -1, fDisplayParams)) {
+- GR_VK_CALL(fInterface, DestroySemaphore(fDevice, semaphore, nullptr));
++ GR_VK_CALL(fShared->fInterface, DestroySemaphore(fShared->fDevice, semaphore, nullptr));
+ return nullptr;
+ }
+ backbuffer = this->getAvailableBackbuffer();
+
+ // acquire the image
+- res = fAcquireNextImageKHR(fDevice, fSwapchain, UINT64_MAX,
++ res = fAcquireNextImageKHR(fShared->fDevice, fSwapchain, UINT64_MAX,
+ semaphore, VK_NULL_HANDLE,
+ &backbuffer->fImageIndex);
+
+ if (VK_SUCCESS != res) {
+- GR_VK_CALL(fInterface, DestroySemaphore(fDevice, semaphore, nullptr));
++ GR_VK_CALL(fShared->fInterface, DestroySemaphore(fShared->fDevice, semaphore, nullptr));
+ return nullptr;
+ }
+ }
+@@ -541,4 +568,6 @@ void VulkanWindowContext::swapBuffers() {
+ fQueuePresentKHR(fPresentQueue, &presentInfo);
+ }
+
++SK_API sk_sp<VulkanWindowContext::Shared> VulkanWindowContext::fGlobalShared;
++
+ } //namespace sk_app
+diff --git a/tools/sk_app/VulkanWindowContext.h b/tools/sk_app/VulkanWindowContext.h
+index 2db9e79ae6..7950dc159b 100644
+--- a/tools/sk_app/VulkanWindowContext.h
++++ b/tools/sk_app/VulkanWindowContext.h
+@@ -1,4 +1,3 @@
+-
+ /*
+ * Copyright 2016 Google Inc.
+ *
+@@ -23,14 +22,30 @@ class GrRenderTarget;
+
+ namespace sk_app {
+
+-class VulkanWindowContext : public WindowContext {
++class SK_API VulkanWindowContext : public WindowContext {
++ struct Shared;
+ public:
+ ~VulkanWindowContext() override;
+
++ class SharedGrContext {
++ public:
++ SharedGrContext() {}
++ GrContext* getGrContext() { return shared ? shared->fContext.get() : nullptr; }
++ ~SharedGrContext() { shared.reset(); checkDestroyShared(); }
++ bool operator!() const { return !shared; }
++ void reset() { shared.reset(); }
++ private:
++ friend class VulkanWindowContext;
++ SharedGrContext(sk_sp<Shared>& sh ) : shared( sh ) {}
++ sk_sp<Shared> shared;
++ };
++
++ static SharedGrContext getSharedGrContext() { return SharedGrContext( fGlobalShared ); }
++
+ sk_sp<SkSurface> getBackbufferSurface() override;
+ void swapBuffers() override;
+
+- bool isValid() override { return fDevice != VK_NULL_HANDLE; }
++ bool isValid() override { return fShared->fDevice != VK_NULL_HANDLE; }
+
+ void resize(int w, int h) override {
+ this->createSwapchain(w, h, fDisplayParams);
+@@ -53,6 +68,7 @@ public:
+ private:
+ void initializeContext();
+ void destroyContext();
++ static void checkDestroyShared();
+
+ struct BackbufferInfo {
+ uint32_t fImageIndex; // image this is associated with
+@@ -64,11 +80,6 @@ private:
+ void createBuffers(VkFormat format, SkColorType colorType);
+ void destroyBuffers();
+
+- VkInstance fInstance = VK_NULL_HANDLE;
+- VkPhysicalDevice fPhysicalDevice = VK_NULL_HANDLE;
+- VkDevice fDevice = VK_NULL_HANDLE;
+- VkDebugReportCallbackEXT fDebugCallback = VK_NULL_HANDLE;
+-
+ // Create functions
+ CreateVkSurfaceFn fCreateVkSurfaceFn;
+ CanPresentFn fCanPresentFn;
+@@ -90,20 +101,41 @@ private:
+ PFN_vkAcquireNextImageKHR fAcquireNextImageKHR = nullptr;
+ PFN_vkQueuePresentKHR fQueuePresentKHR = nullptr;
+
+- PFN_vkDestroyInstance fDestroyInstance = nullptr;
+ PFN_vkDeviceWaitIdle fDeviceWaitIdle = nullptr;
+- PFN_vkDestroyDebugReportCallbackEXT fDestroyDebugReportCallbackEXT = nullptr;
+ PFN_vkQueueWaitIdle fQueueWaitIdle = nullptr;
+- PFN_vkDestroyDevice fDestroyDevice = nullptr;
+ PFN_vkGetDeviceQueue fGetDeviceQueue = nullptr;
+
++ // We need to use just one GrContext, so share all the relevant data.
++ struct Shared : public SkRefCnt
++ {
++ PFN_vkDestroyInstance fDestroyInstance = nullptr;
++ PFN_vkDestroyDevice fDestroyDevice = nullptr;
++ PFN_vkDestroyDebugReportCallbackEXT fDestroyDebugReportCallbackEXT = nullptr;
++
++ VkInstance fInstance = VK_NULL_HANDLE;
++ VkPhysicalDevice fPhysicalDevice = VK_NULL_HANDLE;
++ VkDevice fDevice = VK_NULL_HANDLE;
++ VkDebugReportCallbackEXT fDebugCallback = VK_NULL_HANDLE;
++
+ sk_sp<const GrVkInterface> fInterface;
+
+- VkSurfaceKHR fSurface;
+- VkSwapchainKHR fSwapchain;
++ // Original code had this as a function-local variable, but that seems wrong.
++ // It should exist as long as the context exists.
++ VkPhysicalDeviceFeatures2 features;
++
+ uint32_t fGraphicsQueueIndex;
+ VkQueue fGraphicsQueue;
+ uint32_t fPresentQueueIndex;
++
++ sk_sp<GrContext> fContext;
++ };
++
++ sk_sp<Shared> fShared;
++
++ static sk_sp<Shared> fGlobalShared;
++
++ VkSurfaceKHR fSurface;
++ VkSwapchainKHR fSwapchain;
+ VkQueue fPresentQueue;
+
+ uint32_t fImageCount;
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 1f05f7aa9dd2..c054a2767a3c 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -586,8 +586,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
vcl/skia/SkiaHelper \
$(if $(filter SKIA,$(BUILD_TYPE)), \
vcl/skia/salbmp \
- vcl/skia/gdiimpl \
- vcl/skia/vulkan) \
+ vcl/skia/gdiimpl) \
))
# runtime dependency
diff --git a/vcl/inc/skia/gdiimpl.hxx b/vcl/inc/skia/gdiimpl.hxx
index 04f96cb7bd11..195b5d877eed 100644
--- a/vcl/inc/skia/gdiimpl.hxx
+++ b/vcl/inc/skia/gdiimpl.hxx
@@ -26,6 +26,7 @@
#include <salgeom.hxx>
#include <SkSurface.h>
+#include <tools/sk_app/VulkanWindowContext.h>
class SkiaFlushIdle;
@@ -210,6 +211,7 @@ protected:
void destroySurface();
// Reimplemented for X11.
virtual bool avoidRecreateByResize() const { return false; }
+ void createOffscreenSurface();
void privateDrawAlphaRect(long nX, long nY, long nWidth, long nHeight, double nTransparency,
bool blockAA = false);
@@ -265,6 +267,8 @@ protected:
// The Skia surface that is target of all the rendering.
sk_sp<SkSurface> mSurface;
bool mIsGPU; // whether the surface is GPU-backed
+ // Keep reference to shared GrContext.
+ sk_app::VulkanWindowContext::SharedGrContext mOffscreenGrContext;
vcl::Region mClipRegion;
Color mLineColor;
Color mFillColor;
diff --git a/vcl/inc/skia/vulkan.hxx b/vcl/inc/skia/vulkan.hxx
deleted file mode 100644
index 6c8e3c7bfa44..000000000000
--- a/vcl/inc/skia/vulkan.hxx
+++ /dev/null
@@ -1,29 +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_VULKAN_HXX
-#define INCLUDED_VCL_INC_SKIA_VULKAN_HXX
-
-#include <GrContext.h>
-
-#include <vcl/dllapi.h>
-
-// Create and handle GrContext for Vulkan drawing to offscreen surfaces.
-// Skia already provides WindowContext class that does this for surfaces
-// used for drawing to windows, but it does not seem to provide a simple
-// way to get GrContext without a window.
-class VCL_PLUGIN_PUBLIC SkiaVulkanGrContext
-{
-public:
- static GrContext* getGrContext();
-};
-
-#endif // INCLUDED_VCL_INC_SKIA_VULKAN_HXX
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/skia/README b/vcl/skia/README
index 793c16e8dd39..f1248d90c3ea 100644
--- a/vcl/skia/README
+++ b/vcl/skia/README
@@ -17,3 +17,19 @@ Skia supports several methods to draw:
- Vulkan - Vulkan-based GPU drawing, this is the default
There are more (OpenGL, Metal on Mac, etc.), but (as of now) they are not supported by VCL.
+
+GrContext sharing:
+==================
+
+We use Skia's sk_app::WindowContext class for creating surfaces for windows, that class
+takes care of the internals. But of offscreen drawing, we need an instance of class
+GrContext. There is sk_app::WindowContext::getGrContext(), but each instance creates
+its own GrContext, and apparently it does not work to mix them. Which means that
+for offscreen drawing we would need to know which window (and only that window)
+the contents will be eventually painted to, which is not possible (it may not even
+be known at the time).
+
+To solve this problem we patch sk_app::WindowContext to create just one GrContext object
+and share it between instances. Additionally, using sk_app::WindowContext::SharedGrContext
+it is possible to share it also for offscreen drawing, including keeping proper reference
+count.
diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx
index 776e44216c45..ac47d254cc03 100644
--- a/vcl/skia/gdiimpl.cxx
+++ b/vcl/skia/gdiimpl.cxx
@@ -23,12 +23,14 @@
#include <skia/salbmp.hxx>
#include <vcl/idle.hxx>
#include <vcl/svapp.hxx>
+#include <vcl/lazydelete.hxx>
#include <SkCanvas.h>
#include <SkPath.h>
#include <SkRegion.h>
#include <SkDashPathEffect.h>
#include <GrBackendSurface.h>
+#include <GrContextFactory.h>
#include <basegfx/polygon/b2dpolygontools.hxx>
@@ -190,7 +192,11 @@ SkiaSalGraphicsImpl::SkiaSalGraphicsImpl(SalGraphics& rParent, SalGeometryProvid
{
}
-SkiaSalGraphicsImpl::~SkiaSalGraphicsImpl() {}
+SkiaSalGraphicsImpl::~SkiaSalGraphicsImpl()
+{
+ assert(!mSurface);
+ assert(!mOffscreenGrContext);
+}
void SkiaSalGraphicsImpl::Init() {}
@@ -208,8 +214,7 @@ void SkiaSalGraphicsImpl::recreateSurface()
void SkiaSalGraphicsImpl::createSurface()
{
- // Create surface for offscreen graphics. Subclasses will create GPU-backed
- // surfaces as appropriate.
+ // Create raster surface. Subclasses will create GPU-backed surfaces as appropriate.
mSurface = SkSurface::MakeRasterN32Premul(GetWidth(), GetHeight());
mIsGPU = false;
#ifdef DBG_UTIL
@@ -217,6 +222,53 @@ void SkiaSalGraphicsImpl::createSurface()
#endif
}
+void SkiaSalGraphicsImpl::createOffscreenSurface()
+{
+ assert(isOffscreen());
+ destroySurface();
+ switch (renderMethodToUse())
+ {
+ case RenderVulkan:
+ {
+ mOffscreenGrContext = sk_app::VulkanWindowContext::getSharedGrContext();
+ GrContext* grContext = mOffscreenGrContext.getGrContext();
+ // We may not get a GrContext if called before any onscreen window is created,
+ // but that happens very early, so this should be rare and insignificant.
+ // Unittests are an exception, they usually do not create any windows,
+ // so in that case do create GrContext that has no window associated.
+ if (!grContext)
+ {
+ static bool isUnitTest = (getenv("LO_TESTNAME") != nullptr);
+ if (isUnitTest)
+ {
+ static vcl::DeleteOnDeinit<sk_gpu_test::GrContextFactory> factory(
+ new sk_gpu_test::GrContextFactory);
+ // The factory owns the context.
+ grContext
+ = factory.get()->get(sk_gpu_test::GrContextFactory::kVulkan_ContextType);
+ }
+ }
+ if (grContext)
+ {
+ mSurface = SkSurface::MakeRenderTarget(
+ grContext, SkBudgeted::kNo,
+ SkImageInfo::MakeN32Premul(GetWidth(), GetHeight()));
+ mIsGPU = true;
+ assert(mSurface.get());
+#ifdef DBG_UTIL
+ prefillSurface();
+#endif
+ return;
+ }
+ SAL_WARN("vcl.skia", "cannot create Vulkan GPU offscreen surface");
+ break;
+ }
+ default:
+ break;
+ }
+ return SkiaSalGraphicsImpl::createSurface(); // create a raster one
+}
+
void SkiaSalGraphicsImpl::destroySurface()
{
if (mSurface)
@@ -237,6 +289,7 @@ void SkiaSalGraphicsImpl::destroySurface()
mSurface->flush();
mSurface.reset();
mIsGPU = false;
+ mOffscreenGrContext.reset();
}
void SkiaSalGraphicsImpl::DeInit() { destroySurface(); }
diff --git a/vcl/skia/vulkan.cxx b/vcl/skia/vulkan.cxx
deleted file mode 100644
index c5c9093739fc..000000000000
--- a/vcl/skia/vulkan.cxx
+++ /dev/null
@@ -1,39 +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/vulkan.hxx>
-
-#include <GrContextFactory.h>
-
-#include <vcl/lazydelete.hxx>
-
-static GrContext* createGrContext()
-{
- static vcl::DeleteOnDeinit<sk_gpu_test::GrContextFactory> factory(
- new sk_gpu_test::GrContextFactory);
- // The factory owns the context.
- return factory.get()->get(sk_gpu_test::GrContextFactory::kVulkan_ContextType);
-}
-
-GrContext* SkiaVulkanGrContext::getGrContext()
-{
- static GrContext* context = createGrContext();
- return context;
-}
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/skia/win/gdiimpl.cxx b/vcl/skia/win/gdiimpl.cxx
index e9db555c25c0..8f30c3486dac 100644
--- a/vcl/skia/win/gdiimpl.cxx
+++ b/vcl/skia/win/gdiimpl.cxx
@@ -12,7 +12,6 @@
#include <tools/sk_app/win/WindowContextFactory_win.h>
#include <tools/sk_app/WindowContext.h>
#include <win/saldata.hxx>
-#include <skia/vulkan.hxx>
#include <SkColorFilter.h>
#include <SkPixelRef.h>
@@ -45,26 +44,9 @@ void WinSkiaSalGraphicsImpl::Init()
void WinSkiaSalGraphicsImpl::createSurface()
{
- destroySurface();
if (isOffscreen())
- {
- switch (renderMethodToUse())
- {
- case RenderVulkan:
- mSurface = SkSurface::MakeRenderTarget(
- SkiaVulkanGrContext::getGrContext(), SkBudgeted::kNo,
- SkImageInfo::MakeN32Premul(GetWidth(), GetHeight()));
- mIsGPU = true;
- assert(mSurface.get());
-#ifdef DBG_UTIL
- prefillSurface();
-#endif
- return;
- default:
- break;
- }
- return SkiaSalGraphicsImpl::createSurface();
- }
+ return createOffscreenSurface();
+ destroySurface();
// When created, Init() gets called with size (0,0), which is invalid size
// for Skia. Creating the actual surface is delayed, so the size should be always
// valid here, but better check.
diff --git a/vcl/skia/x11/gdiimpl.cxx b/vcl/skia/x11/gdiimpl.cxx
index e349074e70e1..ef381c5e56d3 100644
--- a/vcl/skia/x11/gdiimpl.cxx
+++ b/vcl/skia/x11/gdiimpl.cxx
@@ -21,8 +21,6 @@
#include <tools/sk_app/unix/WindowContextFactory_unix.h>
#include <tools/sk_app/WindowContext.h>
-#include <skia/vulkan.hxx>
-
X11SkiaSalGraphicsImpl::X11SkiaSalGraphicsImpl(X11SalGraphics& rParent)
: SkiaSalGraphicsImpl(rParent, rParent.GetGeometryProvider())
, mX11Parent(rParent)
@@ -40,29 +38,12 @@ void X11SkiaSalGraphicsImpl::Init()
void X11SkiaSalGraphicsImpl::createSurface()
{
- destroySurface();
if (isOffscreen())
- {
- switch (renderMethodToUse())
- {
- case RenderVulkan:
- mSurface = SkSurface::MakeRenderTarget(
- SkiaVulkanGrContext::getGrContext(), SkBudgeted::kNo,
- SkImageInfo::MakeN32Premul(GetWidth(), GetHeight()));
- mIsGPU = true;
- assert(mSurface.get());
-#ifdef DBG_UTIL
- prefillSurface();
-#endif
- return;
- default:
- break;
- }
- return SkiaSalGraphicsImpl::createSurface();
- }
+ return createOffscreenSurface();
+ destroySurface();
sk_app::DisplayParams displayParams;
- // TODO The Skia Xlib code actually requires the non-native color type to work properly.
// Use a macro to hide an unreachable code warning.
+ // TODO The Skia Xlib code actually requires the non-native color type to work properly.
#define GET_FORMAT \
kN32_SkColorType == kBGRA_8888_SkColorType ? kRGBA_8888_SkColorType : kBGRA_8888_SkColorType
displayParams.fColorType = GET_FORMAT;
@@ -73,6 +54,13 @@ void X11SkiaSalGraphicsImpl::createSurface()
assert(winInfo.fDisplay && winInfo.fWindow != None);
winInfo.fFBConfig = nullptr; // not used
winInfo.fVisualInfo = const_cast<SalVisual*>(&mX11Parent.GetVisual());
+#ifdef DBG_UTIL
+ // Our patched Skia has VulkanWindowContext that shares GrContext, which requires
+ // that the X11 visual is always the same. Ensure it is so.
+ static VisualID checkVisualID = -1U;
+ assert(checkVisualID == -1U || winInfo.fVisualInfo->visualid == checkVisualID);
+ checkVisualID = winInfo.fVisualInfo->visualid;
+#endif
winInfo.fWidth = GetWidth();
winInfo.fHeight = GetHeight();
switch (renderMethodToUse())