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
7 #include "tools/sk_app/WindowContext.h"
10 #import <Metal/Metal.h>
11 #import <QuartzCore/CAMetalLayer.h>
17 class MetalWindowContext : public WindowContext {
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();
32 + // We need to use just one GrDirectContext, so share all the relevant data.
33 + struct Shared : public SkRefCnt
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));
44 + sk_sp<GrDirectContext> fContext;
47 + sk_sp<Shared> fShared;
49 + static sk_sp<Shared> fGlobalShared;
51 + CAMetalLayer* fMetalLayer;
52 + GrMTLHandle fDrawableHandle;
56 +// Access function when header is used from C++ code that wouldn't handle ObjC++ headers.
57 +extern "C" SK_API GrDirectContext* getMetalSharedGrDirectContext();
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() {
68 void MetalWindowContext::initializeContext() {
69 + fShared = fGlobalShared;
72 + // TODO do we need a mutex?
74 + fGlobalShared = sk_make_sp<Shared>();
75 + Shared* d = fGlobalShared.get(); // shorter variable name
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();
92 + fGlobalShared.reset();
96 - fSampleCount = fDisplayParams.fMSAASampleCount;
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
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);
119 @@ -74,46 +80,75 @@ void MetalWindowContext::initializeContext() {
122 if (@available(macOS 11.0, iOS 14.0, *)) {
123 - fPipelineArchive = nil;
124 + d->fPipelineArchive = nil;
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);
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();
150 + fShared = fGlobalShared;
151 + } // if( !fShared )
153 + fContext = fShared->fContext;
155 + fSampleCount = fDisplayParams.fMSAASampleCount;
158 + fValid = this->onInitializeContext();
161 void MetalWindowContext::destroyContext() {
163 - // in case we have outstanding refs to this (lua?)
164 - fContext->abandonContext();
168 this->onDestroyContext();
176 + checkDestroyShared();
179 +void MetalWindowContext::checkDestroyShared()
181 + if(!fGlobalShared || !fGlobalShared->unique()) // TODO mutex?
183 +#ifndef SK_TRACE_VK_RESOURCES
184 + if(!fGlobalShared->fContext->unique())
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();
195 #if GR_METAL_SDK_VERSION >= 230
196 if (@available(macOS 11.0, iOS 14.0, *)) {
197 - [fPipelineArchive release];
198 + [fGlobalShared->fPipelineArchive release];
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) {
221 #if GR_METAL_SDK_VERSION >= 230
222 if (@available(macOS 11.0, iOS 14.0, *)) {
223 - if (fPipelineArchive) {
224 + if (fShared->fPipelineArchive) {
226 - [fPipelineArchive serializeToURL:CacheURL() error:&error];
227 + [fShared->fPipelineArchive serializeToURL:CacheURL() error:&error];
229 SkDebugf("Error storing MTLBinaryArchive:\n%s\n",
230 error.debugDescription.UTF8String);
231 @@ -188,4 +223,11 @@ void MetalWindowContext::activate(bool isActive) {
235 +SK_API sk_sp<MetalWindowContext::Shared> MetalWindowContext::fGlobalShared;
237 +GrDirectContext* getMetalSharedGrDirectContext()
239 + return MetalWindowContext::getSharedGrDirectContext();
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
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)
263 @@ -49,31 +53,39 @@ VulkanWindowContext::VulkanWindowContext(const DisplayParams& params,
266 void VulkanWindowContext::initializeContext() {
267 + fShared = fGlobalShared;
270 + // TODO do we need a mutex?
272 + fGlobalShared = sk_make_sp<Shared>();
273 + Shared* d = fGlobalShared.get(); // shorter variable name
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,
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();
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();
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,
318 if (!localGetPhysicalDeviceProperties) {
319 - sk_gpu_test::FreeVulkanFeaturesStructs(&features);
320 + sk_gpu_test::FreeVulkanFeaturesStructs(&d->features);
321 + fGlobalShared.reset();
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,
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);
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)
371 - fSurface = fCreateVkSurfaceFn(fInstance);
372 + fSurface = fCreateVkSurfaceFn(fShared->fInstance);
373 if (VK_NULL_HANDLE == fSurface) {
374 this->destroyContext();
375 - sk_gpu_test::FreeVulkanFeaturesStructs(&features);
379 + // create presentQueue
380 + fGetDeviceQueue(fShared->fDevice, fShared->fPresentQueueIndex, 0, &fPresentQueue);
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);
392 if (!this->createSwapchain(-1, -1, fDisplayParams)) {
393 this->destroyContext();
394 - sk_gpu_test::FreeVulkanFeaturesStructs(&features);
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) {
413 uint32_t surfaceFormatCount;
414 - res = fGetPhysicalDeviceSurfaceFormatsKHR(fPhysicalDevice, fSurface, &surfaceFormatCount,
415 + res = fGetPhysicalDeviceSurfaceFormatsKHR(fShared->fPhysicalDevice, fSurface, &surfaceFormatCount,
417 if (VK_SUCCESS != res) {
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,
426 if (VK_SUCCESS != res) {
430 uint32_t presentModeCount;
431 - res = fGetPhysicalDeviceSurfacePresentModesKHR(fPhysicalDevice, fSurface, &presentModeCount,
432 + res = fGetPhysicalDeviceSurfacePresentModesKHR(fShared->fPhysicalDevice, fSurface, &presentModeCount,
434 if (VK_SUCCESS != res) {
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,
443 if (VK_SUCCESS != res) {
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) {
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);
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);
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);
522 @@ -401,8 +419,8 @@ void VulkanWindowContext::destroyBuffers() {
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,
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;
548 if (VK_NULL_HANDLE != fSurface) {
549 - fDestroySurfaceKHR(fInstance, fSurface, nullptr);
550 + fDestroySurfaceKHR(fShared->fInstance, fSurface, nullptr);
551 fSurface = VK_NULL_HANDLE;
555 - SkASSERT(fContext->unique());
557 - fInterface.reset();
560 + checkDestroyShared();
563 +void VulkanWindowContext::checkDestroyShared()
565 + if(!fGlobalShared || !fGlobalShared->unique()) // TODO mutex?
567 +#ifndef SK_TRACE_VK_RESOURCES
568 + if(!fGlobalShared->fContext->unique())
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;
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);
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;
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);
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));
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));
635 backbuffer = this->getAvailableBackbuffer();
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));
649 @@ -542,7 +577,7 @@ void VulkanWindowContext::swapBuffers() {
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;
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
670 #include "tools/gpu/vk/VkTestUtils.h"
671 #include "tools/sk_app/WindowContext.h"
675 class GrRenderTarget;
677 namespace skgpu { struct VulkanInterface; }
681 -class VulkanWindowContext : public WindowContext {
682 +class SK_API VulkanWindowContext : public WindowContext {
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;
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;
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
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;
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
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;
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)
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,
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,
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,
836 std::unique_ptr<WindowContext> ctx(
837 new VulkanWindowContext(displayParams, createVkSurface, canPresent, instProc));
838 - if (!ctx->isValid()) {
839 + if (!ctx->isValid() && createVkSurface != nullptr) {
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&
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&
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) {