bump product version to 7.6.3.2-android
[LibreOffice.git] / external / skia / share-grcontext.patch.1
blobc2f1320479715afa9eee24cd2a83b0fd84bb738e
1 diff --git a/tools/sk_app/MetalWindowContext.h b/tools/sk_app/MetalWindowContext.h
2 index 106d366415..08dc19b5c8 100644
3 --- a/tools/sk_app/MetalWindowContext.h
4 +++ b/tools/sk_app/MetalWindowContext.h
5 @@ -14,13 +14,18 @@
6  
7  #include "tools/sk_app/WindowContext.h"
8  
9 +#ifdef __OBJC__
10  #import <Metal/Metal.h>
11  #import <QuartzCore/CAMetalLayer.h>
12 +#endif
14  namespace sk_app {
16 +#ifdef __OBJC__
17  class MetalWindowContext : public WindowContext {
18  public:
19 +    static GrDirectContext* getSharedGrDirectContext() { return fGlobalShared ? fGlobalShared->fContext.get() : nullptr; }
21      sk_sp<SkSurface> getBackbufferSurface() override;
23      bool isValid() override { return fValid; }
24 @@ -46,16 +51,34 @@ protected:
25      void destroyContext();
26      virtual void onDestroyContext() = 0;
28 +    static void checkDestroyShared();
30      bool                        fValid;
32 +    // We need to use just one GrDirectContext, so share all the relevant data.
33 +    struct Shared : public SkRefCnt
34 +    {
35      sk_cfp<id<MTLDevice>>       fDevice;
36      sk_cfp<id<MTLCommandQueue>> fQueue;
37 -    CAMetalLayer*               fMetalLayer;
38 -    GrMTLHandle                 fDrawableHandle;
39  #if GR_METAL_SDK_VERSION >= 230
40      // wrapping this in sk_cfp throws up an availability warning, so we'll track lifetime manually
41      id<MTLBinaryArchive>        fPipelineArchive SK_API_AVAILABLE(macos(11.0), ios(14.0));
42  #endif
44 +    sk_sp<GrDirectContext> fContext;
45 +    };
47 +    sk_sp<Shared> fShared;
49 +    static sk_sp<Shared> fGlobalShared;
51 +    CAMetalLayer*               fMetalLayer;
52 +    GrMTLHandle                 fDrawableHandle;
53  };
54 +#endif // __OBJC__
56 +// Access function when header is used from C++ code that wouldn't handle ObjC++ headers.
57 +extern "C" SK_API GrDirectContext* getMetalSharedGrDirectContext();
59  }   // namespace sk_app
61 diff --git a/tools/sk_app/MetalWindowContext.mm b/tools/sk_app/MetalWindowContext.mm
62 index d972e321a6..9f576944b7 100644
63 --- a/tools/sk_app/MetalWindowContext.mm
64 +++ b/tools/sk_app/MetalWindowContext.mm
65 @@ -37,24 +37,30 @@ NSURL* MetalWindowContext::CacheURL() {
66  }
68  void MetalWindowContext::initializeContext() {
69 +    fShared = fGlobalShared;
70 +    if( !fShared )
71 +    {
72 +    // TODO do we need a mutex?
74 +    fGlobalShared = sk_make_sp<Shared>();
75 +    Shared* d = fGlobalShared.get(); // shorter variable name
77      SkASSERT(!fContext);
79 -    fDevice.reset(MTLCreateSystemDefaultDevice());
80 -    fQueue.reset([*fDevice newCommandQueue]);
81 +    d->fDevice.reset(MTLCreateSystemDefaultDevice());
82 +    d->fQueue.reset([*d->fDevice newCommandQueue]);
84      if (fDisplayParams.fMSAASampleCount > 1) {
85          if (@available(macOS 10.11, iOS 9.0, *)) {
86 -            if (![*fDevice supportsTextureSampleCount:fDisplayParams.fMSAASampleCount]) {
87 +            if (![*d->fDevice supportsTextureSampleCount:fDisplayParams.fMSAASampleCount]) {
88 +                fGlobalShared.reset();
89                  return;
90              }
91          } else {
92 +            fGlobalShared.reset();
93              return;
94          }
95      }
96 -    fSampleCount = fDisplayParams.fMSAASampleCount;
97 -    fStencilBits = 8;
99 -    fValid = this->onInitializeContext();
101  #if GR_METAL_SDK_VERSION >= 230
102      if (fDisplayParams.fEnableBinaryArchive) {
103 @@ -62,11 +68,11 @@ void MetalWindowContext::initializeContext() {
104              sk_cfp<MTLBinaryArchiveDescriptor*> desc([MTLBinaryArchiveDescriptor new]);
105              (*desc).url = CacheURL(); // try to load
106              NSError* error;
107 -            fPipelineArchive = [*fDevice newBinaryArchiveWithDescriptor:*desc error:&error];
108 -            if (!fPipelineArchive) {
109 +            d->fPipelineArchive = [*d->fDevice newBinaryArchiveWithDescriptor:*desc error:&error];
110 +            if (!d->fPipelineArchive) {
111                  (*desc).url = nil; // create new
112 -                fPipelineArchive = [*fDevice newBinaryArchiveWithDescriptor:*desc error:&error];
113 -                if (!fPipelineArchive) {
114 +                d->fPipelineArchive = [*d->fDevice newBinaryArchiveWithDescriptor:*desc error:&error];
115 +                if (!d->fPipelineArchive) {
116                      SkDebugf("Error creating MTLBinaryArchive:\n%s\n",
117                               error.debugDescription.UTF8String);
118                  }
119 @@ -74,46 +80,75 @@ void MetalWindowContext::initializeContext() {
120          }
121      } else {
122          if (@available(macOS 11.0, iOS 14.0, *)) {
123 -            fPipelineArchive = nil;
124 +            d->fPipelineArchive = nil;
125          }
126      }
127  #endif
129      GrMtlBackendContext backendContext = {};
130 -    backendContext.fDevice.retain((GrMTLHandle)fDevice.get());
131 -    backendContext.fQueue.retain((GrMTLHandle)fQueue.get());
132 +    backendContext.fDevice.retain((GrMTLHandle)d->fDevice.get());
133 +    backendContext.fQueue.retain((GrMTLHandle)d->fQueue.get());
134  #if GR_METAL_SDK_VERSION >= 230
135      if (@available(macOS 11.0, iOS 14.0, *)) {
136 -        backendContext.fBinaryArchive.retain((__bridge GrMTLHandle)fPipelineArchive);
137 +        backendContext.fBinaryArchive.retain((__bridge GrMTLHandle)d->fPipelineArchive);
138      }
139  #endif
140 -    fContext = GrDirectContext::MakeMetal(backendContext, fDisplayParams.fGrContextOptions);
141 -    if (!fContext && fDisplayParams.fMSAASampleCount > 1) {
142 +    d->fContext = GrDirectContext::MakeMetal(backendContext, fDisplayParams.fGrContextOptions);
143 +    if (!d->fContext && fDisplayParams.fMSAASampleCount > 1) {
144          fDisplayParams.fMSAASampleCount /= 2;
145 +        fGlobalShared.reset();
146          this->initializeContext();
147          return;
148      }
150 +    fShared = fGlobalShared;
151 +    } // if( !fShared )
153 +    fContext = fShared->fContext;
155 +    fSampleCount = fDisplayParams.fMSAASampleCount;
156 +    fStencilBits = 8;
158 +    fValid = this->onInitializeContext();
161  void MetalWindowContext::destroyContext() {
162 -    if (fContext) {
163 -        // in case we have outstanding refs to this (lua?)
164 -        fContext->abandonContext();
165 -        fContext.reset();
166 -    }
168      this->onDestroyContext();
170      fMetalLayer = nil;
171      fValid = false;
173 +    fContext.reset();
174 +    fShared.reset();
176 +    checkDestroyShared();
179 +void MetalWindowContext::checkDestroyShared()
181 +    if(!fGlobalShared || !fGlobalShared->unique()) // TODO mutex?
182 +        return;
183 +#ifndef SK_TRACE_VK_RESOURCES
184 +    if(!fGlobalShared->fContext->unique())
185 +        return;
186 +#endif
187 +    SkASSERT(fGlobalShared->fContext->unique());
189 +    if (fGlobalShared->fContext) {
190 +        // in case we have outstanding refs to this (lua?)
191 +        fGlobalShared->fContext->abandonContext();
192 +        fGlobalShared->fContext.reset();
193 +    }
195  #if GR_METAL_SDK_VERSION >= 230
196      if (@available(macOS 11.0, iOS 14.0, *)) {
197 -        [fPipelineArchive release];
198 +        [fGlobalShared->fPipelineArchive release];
199      }
200  #endif
201 -    fQueue.reset();
202 -    fDevice.reset();
203 +    fGlobalShared->fQueue.reset();
204 +    fGlobalShared->fDevice.reset();
206 +    fGlobalShared.reset();
209  sk_sp<SkSurface> MetalWindowContext::getBackbufferSurface() {
210 @@ -154,7 +189,7 @@ sk_sp<SkSurface> MetalWindowContext::getBackbufferSurface() {
211  void MetalWindowContext::swapBuffers() {
212      id<CAMetalDrawable> currentDrawable = (id<CAMetalDrawable>)fDrawableHandle;
214 -    id<MTLCommandBuffer> commandBuffer([*fQueue commandBuffer]);
215 +    id<MTLCommandBuffer> commandBuffer([*fShared->fQueue commandBuffer]);
216      commandBuffer.label = @"Present";
218      [commandBuffer presentDrawable:currentDrawable];
219 @@ -175,9 +210,9 @@ void MetalWindowContext::activate(bool isActive) {
220      if (!isActive) {
221  #if GR_METAL_SDK_VERSION >= 230
222          if (@available(macOS 11.0, iOS 14.0, *)) {
223 -            if (fPipelineArchive) {
224 +            if (fShared->fPipelineArchive) {
225                  NSError* error;
226 -                [fPipelineArchive serializeToURL:CacheURL() error:&error];
227 +                [fShared->fPipelineArchive serializeToURL:CacheURL() error:&error];
228                  if (error) {
229                      SkDebugf("Error storing MTLBinaryArchive:\n%s\n",
230                               error.debugDescription.UTF8String);
231 @@ -188,4 +223,11 @@ void MetalWindowContext::activate(bool isActive) {
232      }
235 +SK_API sk_sp<MetalWindowContext::Shared> MetalWindowContext::fGlobalShared;
237 +GrDirectContext* getMetalSharedGrDirectContext()
239 +    return MetalWindowContext::getSharedGrDirectContext();
242  }   //namespace sk_app
243 diff --git a/tools/sk_app/VulkanWindowContext.cpp b/tools/sk_app/VulkanWindowContext.cpp
244 index c9db528ca4..634034da5a 100644
245 --- a/tools/sk_app/VulkanWindowContext.cpp
246 +++ b/tools/sk_app/VulkanWindowContext.cpp
247 @@ -25,9 +25,13 @@
248  #endif
250  #define GET_PROC(F) f ## F = \
251 -    (PFN_vk ## F) backendContext.fGetProc("vk" #F, fInstance, VK_NULL_HANDLE)
252 +    (PFN_vk ## F) fGlobalShared->backendContext.fGetProc("vk" #F, fShared->fInstance, VK_NULL_HANDLE)
253  #define GET_DEV_PROC(F) f ## F = \
254 -    (PFN_vk ## F) backendContext.fGetProc("vk" #F, VK_NULL_HANDLE, fDevice)
255 +    (PFN_vk ## F) fGlobalShared->backendContext.fGetProc("vk" #F, VK_NULL_HANDLE, fShared->fDevice)
256 +#define GET_PROC_GLOBAL(F) fGlobalShared->f ## F = \
257 +    (PFN_vk ## F) fGlobalShared->backendContext.fGetProc("vk" #F, fGlobalShared->fInstance, VK_NULL_HANDLE)
258 +#define GET_DEV_PROC_GLOBAL(F) fGlobalShared->f ## F = \
259 +    (PFN_vk ## F) fGlobalShared->backendContext.fGetProc("vk" #F, VK_NULL_HANDLE, fGlobalShared->fDevice)
261  namespace sk_app {
263 @@ -49,31 +53,39 @@ VulkanWindowContext::VulkanWindowContext(const DisplayParams& params,
266  void VulkanWindowContext::initializeContext() {
267 +    fShared = fGlobalShared;
268 +    if( !fShared )
269 +    {
270 +    // TODO do we need a mutex?
272 +    fGlobalShared = sk_make_sp<Shared>();
273 +    Shared* d = fGlobalShared.get(); // shorter variable name
275      SkASSERT(!fContext);
276      // any config code here (particularly for msaa)?
278      PFN_vkGetInstanceProcAddr getInstanceProc = fGetInstanceProcAddr;
279 -    GrVkBackendContext backendContext;
280 +    GrVkBackendContext& backendContext = fGlobalShared->backendContext;
281      skgpu::VulkanExtensions extensions;
282 -    VkPhysicalDeviceFeatures2 features;
283 -    if (!sk_gpu_test::CreateVkBackendContext(getInstanceProc, &backendContext, &extensions,
284 -                                             &features, &fDebugCallback, &fPresentQueueIndex,
285 -                                             fCanPresentFn)) {
286 -        sk_gpu_test::FreeVulkanFeaturesStructs(&features);
287 +    if (!sk_gpu_test::CreateVkBackendContext(getInstanceProc, &backendContext, &extensions, &d->features,
288 +                                             &d->fDebugCallback, &d->fPresentQueueIndex, fCanPresentFn)) {
289 +        sk_gpu_test::FreeVulkanFeaturesStructs(&d->features);
290 +        fGlobalShared.reset();
291          return;
292      }
294      if (!extensions.hasExtension(VK_KHR_SURFACE_EXTENSION_NAME, 25) ||
295          !extensions.hasExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, 68)) {
296 -        sk_gpu_test::FreeVulkanFeaturesStructs(&features);
297 +        sk_gpu_test::FreeVulkanFeaturesStructs(&d->features);
298 +        fGlobalShared.reset();
299          return;
300      }
302 -    fInstance = backendContext.fInstance;
303 -    fPhysicalDevice = backendContext.fPhysicalDevice;
304 -    fDevice = backendContext.fDevice;
305 -    fGraphicsQueueIndex = backendContext.fGraphicsQueueIndex;
306 -    fGraphicsQueue = backendContext.fQueue;
307 +    d->fInstance = backendContext.fInstance;
308 +    d->fPhysicalDevice = backendContext.fPhysicalDevice;
309 +    d->fDevice = backendContext.fDevice;
310 +    d->fGraphicsQueueIndex = backendContext.fGraphicsQueueIndex;
311 +    d->fGraphicsQueue = backendContext.fQueue;
313      PFN_vkGetPhysicalDeviceProperties localGetPhysicalDeviceProperties =
314              reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(
315 @@ -81,21 +93,30 @@ void VulkanWindowContext::initializeContext() {
316                                              backendContext.fInstance,
317                                              VK_NULL_HANDLE));
318      if (!localGetPhysicalDeviceProperties) {
319 -        sk_gpu_test::FreeVulkanFeaturesStructs(&features);
320 +        sk_gpu_test::FreeVulkanFeaturesStructs(&d->features);
321 +        fGlobalShared.reset();
322          return;
323      }
324 -    VkPhysicalDeviceProperties physDeviceProperties;
325 -    localGetPhysicalDeviceProperties(backendContext.fPhysicalDevice, &physDeviceProperties);
326 -    uint32_t physDevVersion = physDeviceProperties.apiVersion;
327 +    localGetPhysicalDeviceProperties(backendContext.fPhysicalDevice, &d->physDeviceProperties);
328 +    uint32_t physDevVersion = d->physDeviceProperties.apiVersion;
330 -    fInterface.reset(new skgpu::VulkanInterface(backendContext.fGetProc, fInstance, fDevice,
331 +    d->fInterface.reset(new skgpu::VulkanInterface(backendContext.fGetProc, d->fInstance, d->fDevice,
332                                                  backendContext.fInstanceVersion, physDevVersion,
333                                                  &extensions));
335 -    GET_PROC(DestroyInstance);
336 -    if (fDebugCallback != VK_NULL_HANDLE) {
337 -        GET_PROC(DestroyDebugReportCallbackEXT);
338 +    d->fContext = GrDirectContext::MakeVulkan(backendContext, fDisplayParams.fGrContextOptions);
340 +    GET_PROC_GLOBAL(DestroyInstance);
341 +    GET_DEV_PROC_GLOBAL(DestroyDevice);
342 +    if (fGlobalShared->fDebugCallback != VK_NULL_HANDLE) {
343 +        GET_PROC_GLOBAL(DestroyDebugReportCallbackEXT);
344      }
346 +    fShared = fGlobalShared;
347 +    } // if( !fShared )
349 +    fContext = fShared->fContext;
351      GET_PROC(DestroySurfaceKHR);
352      GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
353      GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
354 @@ -103,7 +124,6 @@ void VulkanWindowContext::initializeContext() {
355      GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
356      GET_DEV_PROC(DeviceWaitIdle);
357      GET_DEV_PROC(QueueWaitIdle);
358 -    GET_DEV_PROC(DestroyDevice);
359      GET_DEV_PROC(CreateSwapchainKHR);
360      GET_DEV_PROC(DestroySwapchainKHR);
361      GET_DEV_PROC(GetSwapchainImagesKHR);
362 @@ -111,46 +131,44 @@ void VulkanWindowContext::initializeContext() {
363      GET_DEV_PROC(QueuePresentKHR);
364      GET_DEV_PROC(GetDeviceQueue);
366 -    fContext = GrDirectContext::MakeVulkan(backendContext, fDisplayParams.fGrContextOptions);
367 +    // No actual window, used just to create the shared GrContext.
368 +    if(fCreateVkSurfaceFn == nullptr)
369 +        return;
371 -    fSurface = fCreateVkSurfaceFn(fInstance);
372 +    fSurface = fCreateVkSurfaceFn(fShared->fInstance);
373      if (VK_NULL_HANDLE == fSurface) {
374          this->destroyContext();
375 -        sk_gpu_test::FreeVulkanFeaturesStructs(&features);
376          return;
377      }
379 +    // create presentQueue
380 +    fGetDeviceQueue(fShared->fDevice, fShared->fPresentQueueIndex, 0, &fPresentQueue);
382      VkBool32 supported;
383 -    VkResult res = fGetPhysicalDeviceSurfaceSupportKHR(fPhysicalDevice, fPresentQueueIndex,
384 +    VkResult res = fGetPhysicalDeviceSurfaceSupportKHR(fShared->fPhysicalDevice, fShared->fPresentQueueIndex,
385                                                         fSurface, &supported);
386      if (VK_SUCCESS != res) {
387          this->destroyContext();
388 -        sk_gpu_test::FreeVulkanFeaturesStructs(&features);
389          return;
390      }
392      if (!this->createSwapchain(-1, -1, fDisplayParams)) {
393          this->destroyContext();
394 -        sk_gpu_test::FreeVulkanFeaturesStructs(&features);
395          return;
396      }
398 -    // create presentQueue
399 -    fGetDeviceQueue(fDevice, fPresentQueueIndex, 0, &fPresentQueue);
400 -    sk_gpu_test::FreeVulkanFeaturesStructs(&features);
403  bool VulkanWindowContext::createSwapchain(int width, int height,
404                                            const DisplayParams& params) {
405      // check for capabilities
406      VkSurfaceCapabilitiesKHR caps;
407 -    VkResult res = fGetPhysicalDeviceSurfaceCapabilitiesKHR(fPhysicalDevice, fSurface, &caps);
408 +    VkResult res = fGetPhysicalDeviceSurfaceCapabilitiesKHR(fShared->fPhysicalDevice, fSurface, &caps);
409      if (VK_SUCCESS != res) {
410          return false;
411      }
413      uint32_t surfaceFormatCount;
414 -    res = fGetPhysicalDeviceSurfaceFormatsKHR(fPhysicalDevice, fSurface, &surfaceFormatCount,
415 +    res = fGetPhysicalDeviceSurfaceFormatsKHR(fShared->fPhysicalDevice, fSurface, &surfaceFormatCount,
416                                                nullptr);
417      if (VK_SUCCESS != res) {
418          return false;
419 @@ -158,14 +176,14 @@ bool VulkanWindowContext::createSwapchain(int width, int height,
421      SkAutoMalloc surfaceFormatAlloc(surfaceFormatCount * sizeof(VkSurfaceFormatKHR));
422      VkSurfaceFormatKHR* surfaceFormats = (VkSurfaceFormatKHR*)surfaceFormatAlloc.get();
423 -    res = fGetPhysicalDeviceSurfaceFormatsKHR(fPhysicalDevice, fSurface, &surfaceFormatCount,
424 +    res = fGetPhysicalDeviceSurfaceFormatsKHR(fShared->fPhysicalDevice, fSurface, &surfaceFormatCount,
425                                                surfaceFormats);
426      if (VK_SUCCESS != res) {
427          return false;
428      }
430      uint32_t presentModeCount;
431 -    res = fGetPhysicalDeviceSurfacePresentModesKHR(fPhysicalDevice, fSurface, &presentModeCount,
432 +    res = fGetPhysicalDeviceSurfacePresentModesKHR(fShared->fPhysicalDevice, fSurface, &presentModeCount,
433                                                     nullptr);
434      if (VK_SUCCESS != res) {
435          return false;
436 @@ -173,7 +191,7 @@ bool VulkanWindowContext::createSwapchain(int width, int height,
438      SkAutoMalloc presentModeAlloc(presentModeCount * sizeof(VkPresentModeKHR));
439      VkPresentModeKHR* presentModes = (VkPresentModeKHR*)presentModeAlloc.get();
440 -    res = fGetPhysicalDeviceSurfacePresentModesKHR(fPhysicalDevice, fSurface, &presentModeCount,
441 +    res = fGetPhysicalDeviceSurfacePresentModesKHR(fShared->fPhysicalDevice, fSurface, &presentModeCount,
442                                                     presentModes);
443      if (VK_SUCCESS != res) {
444          return false;
445 @@ -286,8 +304,8 @@ bool VulkanWindowContext::createSwapchain(int width, int height,
446      swapchainCreateInfo.imageArrayLayers = 1;
447      swapchainCreateInfo.imageUsage = usageFlags;
449 -    uint32_t queueFamilies[] = { fGraphicsQueueIndex, fPresentQueueIndex };
450 -    if (fGraphicsQueueIndex != fPresentQueueIndex) {
451 +    uint32_t queueFamilies[] = { fShared->fGraphicsQueueIndex, fShared->fPresentQueueIndex };
452 +    if (fShared->fGraphicsQueueIndex != fShared->fPresentQueueIndex) {
453          swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
454          swapchainCreateInfo.queueFamilyIndexCount = 2;
455          swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
456 @@ -303,27 +321,27 @@ bool VulkanWindowContext::createSwapchain(int width, int height,
457      swapchainCreateInfo.clipped = true;
458      swapchainCreateInfo.oldSwapchain = fSwapchain;
460 -    res = fCreateSwapchainKHR(fDevice, &swapchainCreateInfo, nullptr, &fSwapchain);
461 +    res = fCreateSwapchainKHR(fShared->fDevice, &swapchainCreateInfo, nullptr, &fSwapchain);
462      if (VK_SUCCESS != res) {
463          return false;
464      }
466      // destroy the old swapchain
467      if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
468 -        fDeviceWaitIdle(fDevice);
469 +        fDeviceWaitIdle(fShared->fDevice);
471          this->destroyBuffers();
473 -        fDestroySwapchainKHR(fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
474 +        fDestroySwapchainKHR(fShared->fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
475      }
477      if (!this->createBuffers(swapchainCreateInfo.imageFormat, usageFlags, colorType,
478                               swapchainCreateInfo.imageSharingMode)) {
479 -        fDeviceWaitIdle(fDevice);
480 +        fDeviceWaitIdle(fShared->fDevice);
482          this->destroyBuffers();
484 -        fDestroySwapchainKHR(fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
485 +        fDestroySwapchainKHR(fShared->fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
486      }
488      return true;
489 @@ -332,10 +350,10 @@ bool VulkanWindowContext::createSwapchain(int width, int height,
490  bool VulkanWindowContext::createBuffers(VkFormat format, VkImageUsageFlags usageFlags,
491                                          SkColorType colorType,
492                                          VkSharingMode sharingMode) {
493 -    fGetSwapchainImagesKHR(fDevice, fSwapchain, &fImageCount, nullptr);
494 +    fGetSwapchainImagesKHR(fShared->fDevice, fSwapchain, &fImageCount, nullptr);
495      SkASSERT(fImageCount);
496      fImages = new VkImage[fImageCount];
497 -    fGetSwapchainImagesKHR(fDevice, fSwapchain, &fImageCount, fImages);
498 +    fGetSwapchainImagesKHR(fShared->fDevice, fSwapchain, &fImageCount, fImages);
500      // set up initial image layouts and create surfaces
501      fImageLayouts = new VkImageLayout[fImageCount];
502 @@ -351,7 +369,7 @@ bool VulkanWindowContext::createBuffers(VkFormat format, VkImageUsageFlags usage
503          info.fFormat = format;
504          info.fImageUsageFlags = usageFlags;
505          info.fLevelCount = 1;
506 -        info.fCurrentQueueFamily = fPresentQueueIndex;
507 +        info.fCurrentQueueFamily = fShared->fPresentQueueIndex;
508          info.fSharingMode = sharingMode;
510          if (usageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) {
511 @@ -387,8 +405,8 @@ bool VulkanWindowContext::createBuffers(VkFormat format, VkImageUsageFlags usage
512      fBackbuffers = new BackbufferInfo[fImageCount + 1];
513      for (uint32_t i = 0; i < fImageCount + 1; ++i) {
514          fBackbuffers[i].fImageIndex = -1;
515 -        SkDEBUGCODE(VkResult result = )GR_VK_CALL(fInterface,
516 -                CreateSemaphore(fDevice, &semaphoreInfo, nullptr,
517 +        SkDEBUGCODE(VkResult result = )GR_VK_CALL(fShared->fInterface,
518 +                CreateSemaphore(fShared->fDevice, &semaphoreInfo, nullptr,
519                                  &fBackbuffers[i].fRenderSemaphore));
520          SkASSERT(result == VK_SUCCESS);
521      }
522 @@ -401,8 +419,8 @@ void VulkanWindowContext::destroyBuffers() {
523      if (fBackbuffers) {
524          for (uint32_t i = 0; i < fImageCount + 1; ++i) {
525              fBackbuffers[i].fImageIndex = -1;
526 -            GR_VK_CALL(fInterface,
527 -                       DestroySemaphore(fDevice,
528 +            GR_VK_CALL(fShared->fInterface,
529 +                       DestroySemaphore(fShared->fDevice,
530                                          fBackbuffers[i].fRenderSemaphore,
531                                          nullptr));
532          }
533 @@ -427,42 +445,59 @@ VulkanWindowContext::~VulkanWindowContext() {
534  void VulkanWindowContext::destroyContext() {
535      if (this->isValid()) {
536          fQueueWaitIdle(fPresentQueue);
537 -        fDeviceWaitIdle(fDevice);
538 +        fDeviceWaitIdle(fShared->fDevice);
540          this->destroyBuffers();
542          if (VK_NULL_HANDLE != fSwapchain) {
543 -            fDestroySwapchainKHR(fDevice, fSwapchain, nullptr);
544 +            fDestroySwapchainKHR(fShared->fDevice, fSwapchain, nullptr);
545              fSwapchain = VK_NULL_HANDLE;
546          }
548          if (VK_NULL_HANDLE != fSurface) {
549 -            fDestroySurfaceKHR(fInstance, fSurface, nullptr);
550 +            fDestroySurfaceKHR(fShared->fInstance, fSurface, nullptr);
551              fSurface = VK_NULL_HANDLE;
552          }
553      }
555 -    SkASSERT(fContext->unique());
556      fContext.reset();
557 -    fInterface.reset();
558 +    fShared.reset();
560 +    checkDestroyShared();
563 +void VulkanWindowContext::checkDestroyShared()
565 +    if(!fGlobalShared || !fGlobalShared->unique()) // TODO mutex?
566 +        return;
567 +#ifndef SK_TRACE_VK_RESOURCES
568 +    if(!fGlobalShared->fContext->unique())
569 +        return;
570 +#endif
571 +    SkASSERT(fGlobalShared->fContext->unique());
572 +    fGlobalShared->fContext.reset();
573 +    fGlobalShared->fInterface.reset();
575 -    if (VK_NULL_HANDLE != fDevice) {
576 -        fDestroyDevice(fDevice, nullptr);
577 -        fDevice = VK_NULL_HANDLE;
578 +    if (VK_NULL_HANDLE != fGlobalShared->fDevice) {
579 +        fGlobalShared->fDestroyDevice(fGlobalShared->fDevice, nullptr);
580 +        fGlobalShared->fDevice = VK_NULL_HANDLE;
581      }
583  #ifdef SK_ENABLE_VK_LAYERS
584 -    if (fDebugCallback != VK_NULL_HANDLE) {
585 -        fDestroyDebugReportCallbackEXT(fInstance, fDebugCallback, nullptr);
586 +    if (fGlobalShared->fDebugCallback != VK_NULL_HANDLE) {
587 +        fGlobalShared->fDestroyDebugReportCallbackEXT(fGlobalShared->fInstance, fDebugCallback, nullptr);
588      }
589  #endif
591 -    fPhysicalDevice = VK_NULL_HANDLE;
592 +    fGlobalShared->fPhysicalDevice = VK_NULL_HANDLE;
594 -    if (VK_NULL_HANDLE != fInstance) {
595 -        fDestroyInstance(fInstance, nullptr);
596 -        fInstance = VK_NULL_HANDLE;
597 +    if (VK_NULL_HANDLE != fGlobalShared->fInstance) {
598 +        fGlobalShared->fDestroyInstance(fGlobalShared->fInstance, nullptr);
599 +        fGlobalShared->fInstance = VK_NULL_HANDLE;
600      }
602 +    sk_gpu_test::FreeVulkanFeaturesStructs(&fGlobalShared->features);
603 +    fGlobalShared.reset();
606  VulkanWindowContext::BackbufferInfo* VulkanWindowContext::getAvailableBackbuffer() {
607 @@ -488,35 +523,35 @@ sk_sp<SkSurface> VulkanWindowContext::getBackbufferSurface() {
608      semaphoreInfo.pNext = nullptr;
609      semaphoreInfo.flags = 0;
610      VkSemaphore semaphore;
611 -    SkDEBUGCODE(VkResult result = )GR_VK_CALL(fInterface, CreateSemaphore(fDevice, &semaphoreInfo,
612 +    SkDEBUGCODE(VkResult result = )GR_VK_CALL(fShared->fInterface, CreateSemaphore(fShared->fDevice, &semaphoreInfo,
613                                                                            nullptr, &semaphore));
614      SkASSERT(result == VK_SUCCESS);
616      // acquire the image
617 -    VkResult res = fAcquireNextImageKHR(fDevice, fSwapchain, UINT64_MAX,
618 +    VkResult res = fAcquireNextImageKHR(fShared->fDevice, fSwapchain, UINT64_MAX,
619                                          semaphore, VK_NULL_HANDLE,
620                                          &backbuffer->fImageIndex);
621      if (VK_ERROR_SURFACE_LOST_KHR == res) {
622          // need to figure out how to create a new vkSurface without the platformData*
623          // maybe use attach somehow? but need a Window
624 -        GR_VK_CALL(fInterface, DestroySemaphore(fDevice, semaphore, nullptr));
625 +        GR_VK_CALL(fShared->fInterface, DestroySemaphore(fShared->fDevice, semaphore, nullptr));
626          return nullptr;
627      }
628      if (VK_ERROR_OUT_OF_DATE_KHR == res) {
629          // tear swapchain down and try again
630          if (!this->createSwapchain(-1, -1, fDisplayParams)) {
631 -            GR_VK_CALL(fInterface, DestroySemaphore(fDevice, semaphore, nullptr));
632 +            GR_VK_CALL(fShared->fInterface, DestroySemaphore(fShared->fDevice, semaphore, nullptr));
633              return nullptr;
634          }
635          backbuffer = this->getAvailableBackbuffer();
637          // acquire the image
638 -        res = fAcquireNextImageKHR(fDevice, fSwapchain, UINT64_MAX,
639 +        res = fAcquireNextImageKHR(fShared->fDevice, fSwapchain, UINT64_MAX,
640                                     semaphore, VK_NULL_HANDLE,
641                                     &backbuffer->fImageIndex);
643          if (VK_SUCCESS != res) {
644 -            GR_VK_CALL(fInterface, DestroySemaphore(fDevice, semaphore, nullptr));
645 +            GR_VK_CALL(fShared->fInterface, DestroySemaphore(fShared->fDevice, semaphore, nullptr));
646              return nullptr;
647          }
648      }
649 @@ -542,7 +577,7 @@ void VulkanWindowContext::swapBuffers() {
650      GrFlushInfo info;
651      info.fNumSemaphores = 1;
652      info.fSignalSemaphores = &beSemaphore;
653 -    skgpu::MutableTextureState presentState(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, fPresentQueueIndex);
654 +    skgpu::MutableTextureState presentState(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, fShared->fPresentQueueIndex);
655      surface->flush(info, &presentState);
656      surface->recordingContext()->asDirectContext()->submit();
658 @@ -562,4 +597,6 @@ void VulkanWindowContext::swapBuffers() {
659      fQueuePresentKHR(fPresentQueue, &presentInfo);
662 +SK_API sk_sp<VulkanWindowContext::Shared> VulkanWindowContext::fGlobalShared;
664  }   //namespace sk_app
665 diff --git a/tools/sk_app/VulkanWindowContext.h b/tools/sk_app/VulkanWindowContext.h
666 index 7e1fdd9af5..946bca7522 100644
667 --- a/tools/sk_app/VulkanWindowContext.h
668 +++ b/tools/sk_app/VulkanWindowContext.h
669 @@ -19,20 +19,24 @@
670  #include "tools/gpu/vk/VkTestUtils.h"
671  #include "tools/sk_app/WindowContext.h"
673 +#include <cassert>
675  class GrRenderTarget;
677  namespace skgpu { struct VulkanInterface; }
679  namespace sk_app {
681 -class VulkanWindowContext : public WindowContext {
682 +class SK_API VulkanWindowContext : public WindowContext {
683  public:
684      ~VulkanWindowContext() override;
686 +    static GrDirectContext* getSharedGrDirectContext() { return fGlobalShared ? fGlobalShared->fContext.get() : nullptr; }
688      sk_sp<SkSurface> getBackbufferSurface() override;
689      void swapBuffers() override;
691 -    bool isValid() override { return fDevice != VK_NULL_HANDLE; }
692 +    bool isValid() override { return fSurface != VK_NULL_HANDLE; }
694      void resize(int w, int h) override {
695          this->createSwapchain(w, h, fDisplayParams);
696 @@ -50,9 +54,15 @@ public:
697      VulkanWindowContext(const DisplayParams&, CreateVkSurfaceFn, CanPresentFn,
698                          PFN_vkGetInstanceProcAddr);
700 +    static const VkPhysicalDeviceProperties& getPhysDeviceProperties() {
701 +        assert( fGlobalShared != nullptr );
702 +        return fGlobalShared->physDeviceProperties;
703 +    }
705  private:
706      void initializeContext();
707      void destroyContext();
708 +    static void checkDestroyShared();
710      struct BackbufferInfo {
711          uint32_t        fImageIndex;          // image this is associated with
712 @@ -64,11 +74,6 @@ private:
713      bool createBuffers(VkFormat format, VkImageUsageFlags, SkColorType colorType, VkSharingMode);
714      void destroyBuffers();
716 -    VkInstance fInstance = VK_NULL_HANDLE;
717 -    VkPhysicalDevice fPhysicalDevice = VK_NULL_HANDLE;
718 -    VkDevice fDevice = VK_NULL_HANDLE;
719 -    VkDebugReportCallbackEXT fDebugCallback = VK_NULL_HANDLE;
721      // Create functions
722      CreateVkSurfaceFn fCreateVkSurfaceFn;
723      CanPresentFn      fCanPresentFn;
724 @@ -88,20 +93,46 @@ private:
725      PFN_vkAcquireNextImageKHR fAcquireNextImageKHR = nullptr;
726      PFN_vkQueuePresentKHR fQueuePresentKHR = nullptr;
728 -    PFN_vkDestroyInstance fDestroyInstance = nullptr;
729      PFN_vkDeviceWaitIdle fDeviceWaitIdle = nullptr;
730 -    PFN_vkDestroyDebugReportCallbackEXT fDestroyDebugReportCallbackEXT = nullptr;
731      PFN_vkQueueWaitIdle fQueueWaitIdle = nullptr;
732 -    PFN_vkDestroyDevice fDestroyDevice = nullptr;
733      PFN_vkGetDeviceQueue fGetDeviceQueue = nullptr;
735 +    // We need to use just one GrDirectContext, so share all the relevant data.
736 +    struct Shared : public SkRefCnt
737 +    {
738 +    PFN_vkDestroyInstance fDestroyInstance = nullptr;
739 +    PFN_vkDestroyDevice fDestroyDevice = nullptr;
740 +    PFN_vkDestroyDebugReportCallbackEXT fDestroyDebugReportCallbackEXT = nullptr;
742 +    VkInstance fInstance = VK_NULL_HANDLE;
743 +    VkPhysicalDevice fPhysicalDevice = VK_NULL_HANDLE;
744 +    VkDevice fDevice = VK_NULL_HANDLE;
745 +    VkDebugReportCallbackEXT fDebugCallback = VK_NULL_HANDLE;
747      sk_sp<const skgpu::VulkanInterface> fInterface;
749 -    VkSurfaceKHR      fSurface;
750 -    VkSwapchainKHR    fSwapchain;
751 +    // Original code had this as a function-local variable, but that seems wrong.
752 +    // It should exist as long as the context exists.
753 +    VkPhysicalDeviceFeatures2 features;
755 +    // Store this to make it accessible.
756 +    VkPhysicalDeviceProperties physDeviceProperties;
758 +    GrVkBackendContext backendContext;
760      uint32_t          fGraphicsQueueIndex;
761      VkQueue           fGraphicsQueue;
762      uint32_t          fPresentQueueIndex;
764 +    sk_sp<GrDirectContext> fContext;
765 +    };
767 +    sk_sp<Shared> fShared;
769 +    static sk_sp<Shared> fGlobalShared;
771 +    VkSurfaceKHR      fSurface;
772 +    VkSwapchainKHR    fSwapchain;
773      VkQueue           fPresentQueue;
775      uint32_t               fImageCount;
776 diff --git a/tools/sk_app/WindowContext.h b/tools/sk_app/WindowContext.h
777 index 65ab8b9aa4..2d222385a3 100644
778 --- a/tools/sk_app/WindowContext.h
779 +++ b/tools/sk_app/WindowContext.h
780 @@ -10,9 +10,9 @@
781  #include "include/core/SkRefCnt.h"
782  #include "include/core/SkSurfaceProps.h"
783  #include "include/gpu/GrTypes.h"
784 +#include "include/gpu/GrDirectContext.h"
785  #include "tools/sk_app/DisplayParams.h"
787 -class GrDirectContext;
788  class SkSurface;
789  #ifdef SK_GRAPHITE_ENABLED
790  namespace skgpu::graphite {
791 diff --git a/tools/sk_app/mac/MetalWindowContext_mac.mm b/tools/sk_app/mac/MetalWindowContext_mac.mm
792 index 5bea8578fa..f7df061af0 100644
793 --- a/tools/sk_app/mac/MetalWindowContext_mac.mm
794 +++ b/tools/sk_app/mac/MetalWindowContext_mac.mm
795 @@ -49,10 +49,14 @@ MetalWindowContext_mac::~MetalWindowContext_mac() {
798  bool MetalWindowContext_mac::onInitializeContext() {
799 +    // Allow creating just the shared context, without an associated window.
800 +    if(fMainView == nil)
801 +        return true;
803      SkASSERT(nil != fMainView);
805      fMetalLayer = [CAMetalLayer layer];
806 -    fMetalLayer.device = fDevice.get();
807 +    fMetalLayer.device = fShared->fDevice.get();
808      fMetalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
810      // resize ignores the passed values and uses the fMainView directly.
811 diff --git a/tools/sk_app/unix/VulkanWindowContext_unix.cpp b/tools/sk_app/unix/VulkanWindowContext_unix.cpp
812 index 2b31fedc19..0c05fbfc92 100644
813 --- a/tools/sk_app/unix/VulkanWindowContext_unix.cpp
814 +++ b/tools/sk_app/unix/VulkanWindowContext_unix.cpp
815 @@ -30,7 +30,7 @@ std::unique_ptr<WindowContext> MakeVulkanForXlib(const XlibWindowInfo& info,
816          return nullptr;
817      }
819 -    auto createVkSurface = [&info, instProc](VkInstance instance) -> VkSurfaceKHR {
820 +    VulkanWindowContext::CreateVkSurfaceFn createVkSurface = [&info, instProc](VkInstance instance) -> VkSurfaceKHR {
821          static PFN_vkCreateXcbSurfaceKHR createXcbSurfaceKHR = nullptr;
822          if (!createXcbSurfaceKHR) {
823              createXcbSurfaceKHR =
824 @@ -54,6 +54,9 @@ std::unique_ptr<WindowContext> MakeVulkanForXlib(const XlibWindowInfo& info,
826          return surface;
827      };
828 +    // Allow creating just the shared context, without an associated window.
829 +    if(info.fWindow == None)
830 +        createVkSurface = nullptr;
832      auto canPresent = [&info, instProc](VkInstance instance, VkPhysicalDevice physDev,
833                                uint32_t queueFamilyIndex) {
834 @@ -76,7 +79,7 @@ std::unique_ptr<WindowContext> MakeVulkanForXlib(const XlibWindowInfo& info,
835      };
836      std::unique_ptr<WindowContext> ctx(
837              new VulkanWindowContext(displayParams, createVkSurface, canPresent, instProc));
838 -    if (!ctx->isValid()) {
839 +    if (!ctx->isValid() && createVkSurface != nullptr) {
840          return nullptr;
841      }
842      return ctx;
843 diff --git a/tools/sk_app/win/VulkanWindowContext_win.cpp b/tools/sk_app/win/VulkanWindowContext_win.cpp
844 index 976c42556e..c8f6b162bf 100644
845 --- a/tools/sk_app/win/VulkanWindowContext_win.cpp
846 +++ b/tools/sk_app/win/VulkanWindowContext_win.cpp
847 @@ -29,7 +29,7 @@ std::unique_ptr<WindowContext> MakeVulkanForWin(HWND hwnd, const DisplayParams&
848          return nullptr;
849      }
851 -    auto createVkSurface = [hwnd, instProc] (VkInstance instance) -> VkSurfaceKHR {
852 +    VulkanWindowContext::CreateVkSurfaceFn createVkSurface = [hwnd, instProc] (VkInstance instance) -> VkSurfaceKHR {
853          static PFN_vkCreateWin32SurfaceKHR createWin32SurfaceKHR = nullptr;
854          if (!createWin32SurfaceKHR) {
855              createWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR)
856 @@ -53,6 +53,9 @@ std::unique_ptr<WindowContext> MakeVulkanForWin(HWND hwnd, const DisplayParams&
858          return surface;
859      };
860 +    // Allow creating just the shared context, without an associated window.
861 +    if(hwnd == nullptr)
862 +        createVkSurface = nullptr;
864      auto canPresent = [instProc] (VkInstance instance, VkPhysicalDevice physDev,
865                                    uint32_t queueFamilyIndex) {
866 @@ -70,7 +73,7 @@ std::unique_ptr<WindowContext> MakeVulkanForWin(HWND hwnd, const DisplayParams&
868      std::unique_ptr<WindowContext> ctx(
869              new VulkanWindowContext(params, createVkSurface, canPresent, instProc));
870 -    if (!ctx->isValid()) {
871 +    if (!ctx->isValid() && createVkSurface != nullptr) {
872          return nullptr;
873      }
874      return ctx;