1 /*
2 * Copyright 2022 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/gpu/graphite/vk/VulkanSharedContext.h"
9
10 #include "include/gpu/GpuTypes.h"
11 #include "include/gpu/graphite/ContextOptions.h"
12 #include "include/gpu/vk/VulkanBackendContext.h"
13 #include "include/gpu/vk/VulkanExtensions.h"
14 #include "include/private/base/SkMutex.h"
15 #include "src/gpu/GpuTypesPriv.h"
16 #include "src/gpu/graphite/Log.h"
17 #include "src/gpu/graphite/ResourceTypes.h"
18 #include "src/gpu/graphite/vk/VulkanBuffer.h"
19 #include "src/gpu/graphite/vk/VulkanCaps.h"
20 #include "src/gpu/graphite/vk/VulkanResourceProvider.h"
21 #include "src/gpu/vk/VulkanInterface.h"
22 #include "src/gpu/vk/VulkanUtilsPriv.h"
23
24 #if defined(SK_USE_VMA)
25 #include "src/gpu/vk/vulkanmemoryallocator/VulkanMemoryAllocatorPriv.h"
26 #endif
27
28 namespace skgpu::graphite {
29
Make(const VulkanBackendContext & context,const ContextOptions & options)30 sk_sp<SharedContext> VulkanSharedContext::Make(const VulkanBackendContext& context,
31 const ContextOptions& options) {
32 if (context.fInstance == VK_NULL_HANDLE ||
33 context.fPhysicalDevice == VK_NULL_HANDLE ||
34 context.fDevice == VK_NULL_HANDLE ||
35 context.fQueue == VK_NULL_HANDLE) {
36 SKGPU_LOG_E("Failed to create VulkanSharedContext because either fInstance,"
37 "fPhysicalDevice, fDevice, or fQueue in the VulkanBackendContext is"
38 "VK_NULL_HANDLE.");
39 return nullptr;
40 }
41 if (!context.fGetProc) {
42 SKGPU_LOG_E("Failed to create VulkanSharedContext because there is no valid VulkanGetProc"
43 "on the VulkanBackendContext");
44 return nullptr;
45 }
46 // If no extensions are provided, make sure we don't have a null dereference downstream.
47 skgpu::VulkanExtensions noExtensions;
48 const skgpu::VulkanExtensions* extensions = &noExtensions;
49 if (context.fVkExtensions) {
50 extensions = context.fVkExtensions;
51 }
52
53 uint32_t physDevVersion = 0;
54 sk_sp<const skgpu::VulkanInterface> interface =
55 skgpu::MakeInterface(context, extensions, &physDevVersion, nullptr);
56 if (!interface) {
57 SKGPU_LOG_E("Failed to create VulkanInterface.");
58 return nullptr;
59 }
60
61 VkPhysicalDeviceFeatures2 features;
62 const VkPhysicalDeviceFeatures2* featuresPtr;
63 // If fDeviceFeatures2 is not null, then we ignore fDeviceFeatures. If both are null, we assume
64 // no features are enabled.
65 if (!context.fDeviceFeatures2 && context.fDeviceFeatures) {
66 features.pNext = nullptr;
67 features.features = *context.fDeviceFeatures;
68 featuresPtr = &features;
69 } else {
70 featuresPtr = context.fDeviceFeatures2;
71 }
72
73 std::unique_ptr<const VulkanCaps> caps(new VulkanCaps(options,
74 interface.get(),
75 context.fPhysicalDevice,
76 physDevVersion,
77 featuresPtr,
78 extensions,
79 context.fProtectedContext));
80
81 sk_sp<skgpu::VulkanMemoryAllocator> memoryAllocator = context.fMemoryAllocator;
82 #if defined(SK_USE_VMA)
83 if (!memoryAllocator) {
84 // We were not given a memory allocator at creation
85 skgpu::ThreadSafe threadSafe = options.fClientWillExternallySynchronizeAllThreads
86 ? skgpu::ThreadSafe::kNo
87 : skgpu::ThreadSafe::kYes;
88 memoryAllocator = skgpu::VulkanMemoryAllocators::Make(context,
89 threadSafe,
90 options.fVulkanVMALargeHeapBlockSize);
91 }
92 #endif
93 if (!memoryAllocator) {
94 SKGPU_LOG_E("No supplied vulkan memory allocator and unable to create one internally.");
95 return nullptr;
96 }
97
98 return sk_sp<SharedContext>(new VulkanSharedContext(context,
99 std::move(interface),
100 std::move(memoryAllocator),
101 std::move(caps)));
102 }
103
VulkanSharedContext(const VulkanBackendContext & backendContext,sk_sp<const skgpu::VulkanInterface> interface,sk_sp<skgpu::VulkanMemoryAllocator> memoryAllocator,std::unique_ptr<const VulkanCaps> caps)104 VulkanSharedContext::VulkanSharedContext(const VulkanBackendContext& backendContext,
105 sk_sp<const skgpu::VulkanInterface> interface,
106 sk_sp<skgpu::VulkanMemoryAllocator> memoryAllocator,
107 std::unique_ptr<const VulkanCaps> caps)
108 : skgpu::graphite::SharedContext(std::move(caps), BackendApi::kVulkan)
109 , fInterface(std::move(interface))
110 , fMemoryAllocator(std::move(memoryAllocator))
111 , fPhysDevice(backendContext.fPhysicalDevice)
112 , fDevice(backendContext.fDevice)
113 , fQueueIndex(backendContext.fGraphicsQueueIndex)
114 , fDeviceLostContext(backendContext.fDeviceLostContext)
115 , fDeviceLostProc(backendContext.fDeviceLostProc) {}
116
~VulkanSharedContext()117 VulkanSharedContext::~VulkanSharedContext() {
118 // need to clear out resources before the allocator is removed
119 this->globalCache()->deleteResources();
120 }
121
makeResourceProvider(SingleOwner * singleOwner,uint32_t recorderID,size_t resourceBudget)122 std::unique_ptr<ResourceProvider> VulkanSharedContext::makeResourceProvider(
123 SingleOwner* singleOwner,
124 uint32_t recorderID,
125 size_t resourceBudget) {
126 return std::unique_ptr<ResourceProvider>(
127 new VulkanResourceProvider(this,
128 singleOwner,
129 recorderID,
130 resourceBudget));
131 }
132
checkVkResult(VkResult result) const133 bool VulkanSharedContext::checkVkResult(VkResult result) const {
134 switch (result) {
135 case VK_SUCCESS:
136 return true;
137 case VK_ERROR_DEVICE_LOST:
138 {
139 SkAutoMutexExclusive lock(fDeviceIsLostMutex);
140 if (fDeviceIsLost) {
141 return false;
142 }
143 fDeviceIsLost = true;
144 // Fall through to InvokeDeviceLostCallback (on first VK_ERROR_DEVICE_LOST) only afer
145 // releasing fDeviceIsLostMutex, otherwise clients might cause deadlock by checking
146 // isDeviceLost() from the callback.
147 }
148 skgpu::InvokeDeviceLostCallback(interface(),
149 device(),
150 fDeviceLostContext,
151 fDeviceLostProc,
152 vulkanCaps().supportsDeviceFaultInfo());
153 return false;
154 case VK_ERROR_OUT_OF_DEVICE_MEMORY:
155 case VK_ERROR_OUT_OF_HOST_MEMORY:
156 // TODO: determine how we'll track this in a thread-safe manner
157 //this->setOOMed();
158 return false;
159 default:
160 return false;
161 }
162 }
163 } // namespace skgpu::graphite
164