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; }
25 void destroyContext();
26 virtual void onDestroyContext() = 0;
28 + static void checkDestroyShared();
30 void onSwapBuffers() override;
34 + // We need to use just one GrDirectContext, so share all the relevant data.
35 + struct Shared : public SkRefCnt
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));
46 + sk_sp<GrDirectContext> fContext;
49 + sk_sp<Shared> fShared;
51 + static sk_sp<Shared> fGlobalShared;
53 + CAMetalLayer* fMetalLayer;
54 + GrMTLHandle fDrawableHandle;
58 +// Access function when header is used from C++ code that wouldn't handle ObjC++ headers.
59 +extern "C" SK_API GrDirectContext* getMetalSharedGrDirectContext();
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() {
70 void MetalWindowContext::initializeContext() {
71 + fShared = fGlobalShared;
74 + // TODO do we need a mutex?
76 + fGlobalShared = sk_make_sp<Shared>();
77 + Shared* d = fGlobalShared.get(); // shorter variable name
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();
94 + fGlobalShared.reset();
98 - fSampleCount = fDisplayParams.fMSAASampleCount;
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
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);
121 @@ -77,46 +83,75 @@ void MetalWindowContext::initializeContext() {
124 if (@available(macOS 11.0, iOS 14.0, *)) {
125 - fPipelineArchive = nil;
126 + d->fPipelineArchive = nil;
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);
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();
152 + fShared = fGlobalShared;
153 + } // if( !fShared )
155 + fContext = fShared->fContext;
157 + fSampleCount = fDisplayParams.fMSAASampleCount;
160 + fValid = this->onInitializeContext();
163 void MetalWindowContext::destroyContext() {
165 - // in case we have outstanding refs to this (lua?)
166 - fContext->abandonContext();
170 this->onDestroyContext();
178 + checkDestroyShared();
181 +void MetalWindowContext::checkDestroyShared()
183 + if(!fGlobalShared || !fGlobalShared->unique()) // TODO mutex?
185 +#ifndef SK_TRACE_VK_RESOURCES
186 + if(!fGlobalShared->fContext->unique())
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();
197 #if SKGPU_GRAPHITE_METAL_SDK_VERSION >= 230
198 if (@available(macOS 11.0, iOS 14.0, *)) {
199 - [fPipelineArchive release];
200 + [fGlobalShared->fPipelineArchive release];
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) {
223 #if SKGPU_GRAPHITE_METAL_SDK_VERSION >= 230
224 if (@available(macOS 11.0, iOS 14.0, *)) {
225 - if (fPipelineArchive) {
226 + if (fShared->fPipelineArchive) {
228 - [fPipelineArchive serializeToURL:CacheURL() error:&error];
229 + [fShared->fPipelineArchive serializeToURL:CacheURL() error:&error];
231 SkDebugf("Error storing MTLBinaryArchive:\n%s\n",
232 error.debugDescription.UTF8String);
233 @@ -188,4 +223,11 @@ void MetalWindowContext::activate(bool isActive) {
237 +SK_API sk_sp<MetalWindowContext::Shared> MetalWindowContext::fGlobalShared;
239 +GrDirectContext* getMetalSharedGrDirectContext()
241 + return MetalWindowContext::getSharedGrDirectContext();
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
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)
265 @@ -49,31 +53,39 @@ VulkanWindowContext::VulkanWindowContext(const DisplayParams& params,
268 void VulkanWindowContext::initializeContext() {
269 + fShared = fGlobalShared;
272 + // TODO do we need a mutex?
274 + fGlobalShared = sk_make_sp<Shared>();
275 + Shared* d = fGlobalShared.get(); // shorter variable name
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,
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();
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();
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,
320 if (!localGetPhysicalDeviceProperties) {
321 - sk_gpu_test::FreeVulkanFeaturesStructs(&features);
322 + sk_gpu_test::FreeVulkanFeaturesStructs(&d->features);
323 + fGlobalShared.reset();
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,
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);
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)
373 - fSurface = fCreateVkSurfaceFn(fInstance);
374 + fSurface = fCreateVkSurfaceFn(fShared->fInstance);
375 if (VK_NULL_HANDLE == fSurface) {
376 this->destroyContext();
377 - sk_gpu_test::FreeVulkanFeaturesStructs(&features);
381 + // create presentQueue
382 + fGetDeviceQueue(fShared->fDevice, fShared->fPresentQueueIndex, 0, &fPresentQueue);
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);
394 if (!this->createSwapchain(-1, -1, fDisplayParams)) {
395 this->destroyContext();
396 - sk_gpu_test::FreeVulkanFeaturesStructs(&features);
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) {
415 uint32_t surfaceFormatCount;
416 - res = fGetPhysicalDeviceSurfaceFormatsKHR(fPhysicalDevice, fSurface, &surfaceFormatCount,
417 + res = fGetPhysicalDeviceSurfaceFormatsKHR(fShared->fPhysicalDevice, fSurface, &surfaceFormatCount,
419 if (VK_SUCCESS != res) {
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,
428 if (VK_SUCCESS != res) {
432 uint32_t presentModeCount;
433 - res = fGetPhysicalDeviceSurfacePresentModesKHR(fPhysicalDevice, fSurface, &presentModeCount,
434 + res = fGetPhysicalDeviceSurfacePresentModesKHR(fShared->fPhysicalDevice, fSurface, &presentModeCount,
436 if (VK_SUCCESS != res) {
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,
445 if (VK_SUCCESS != res) {
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) {
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);
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);
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);
524 @@ -401,8 +419,8 @@ void VulkanWindowContext::destroyBuffers() {
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,
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;
550 if (VK_NULL_HANDLE != fSurface) {
551 - fDestroySurfaceKHR(fInstance, fSurface, nullptr);
552 + fDestroySurfaceKHR(fShared->fInstance, fSurface, nullptr);
553 fSurface = VK_NULL_HANDLE;
557 - SkASSERT(fContext->unique());
559 - fInterface.reset();
562 + checkDestroyShared();
565 +void VulkanWindowContext::checkDestroyShared()
567 + if(!fGlobalShared || !fGlobalShared->unique()) // TODO mutex?
569 +#ifndef SK_TRACE_VK_RESOURCES
570 + if(!fGlobalShared->fContext->unique())
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;
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);
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;
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);
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));
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));
637 backbuffer = this->getAvailableBackbuffer();
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));
651 @@ -547,7 +582,7 @@ void VulkanWindowContext::swapBuffers() {
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);
660 @@ -562,4 +597,6 @@ void VulkanWindowContext::swapBuffers() {
661 fQueuePresentKHR(fPresentQueue, &presentInfo);
664 +SK_API sk_sp<VulkanWindowContext::Shared> VulkanWindowContext::fGlobalShared;
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
672 #include "tools/gpu/vk/VkTestUtils.h"
673 #include "tools/sk_app/WindowContext.h"
677 class GrRenderTarget;
679 namespace skgpu { struct VulkanInterface; }
683 -class VulkanWindowContext : public WindowContext {
684 +class SK_API VulkanWindowContext : public WindowContext {
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;
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;
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
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;
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
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;
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)
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,
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,
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,
837 std::unique_ptr<WindowContext> ctx(
838 new VulkanWindowContext(displayParams, createVkSurface, canPresent, instProc));
839 - if (!ctx->isValid()) {
840 + if (!ctx->isValid() && createVkSurface != nullptr) {
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&
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&
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) {