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