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,bool avoidBufferAlloc)122 std::unique_ptr<ResourceProvider> VulkanSharedContext::makeResourceProvider(
123 SingleOwner* singleOwner,
124 uint32_t recorderID,
125 size_t resourceBudget,
126 bool avoidBufferAlloc) {
127
128 sk_sp<Buffer> intrinsicConstantBuffer;
129
130 if (!avoidBufferAlloc) {
131 // Establish a uniform buffer that can be updated across multiple render passes and
132 // cmd buffers
133 size_t alignedIntrinsicConstantSize =
134 std::max(VulkanResourceProvider::kIntrinsicConstantSize,
135 this->vulkanCaps().requiredUniformBufferAlignment());
136 intrinsicConstantBuffer = VulkanBuffer::Make(
137 this, alignedIntrinsicConstantSize, BufferType::kUniform, AccessPattern::kGpuOnly);
138 if (!intrinsicConstantBuffer) {
139 SKGPU_LOG_E("Failed to create intrinsic constant uniform buffer");
140 return nullptr;
141 }
142 SkASSERT(static_cast<VulkanBuffer*>(intrinsicConstantBuffer.get())->bufferUsageFlags()
143 & VK_BUFFER_USAGE_TRANSFER_DST_BIT);
144 intrinsicConstantBuffer->setLabel("IntrinsicConstantBuffer");
145 }
146
147 return std::unique_ptr<ResourceProvider>(
148 new VulkanResourceProvider(this,
149 singleOwner,
150 recorderID,
151 resourceBudget,
152 std::move(intrinsicConstantBuffer)));
153 }
154
checkVkResult(VkResult result) const155 bool VulkanSharedContext::checkVkResult(VkResult result) const {
156 switch (result) {
157 case VK_SUCCESS:
158 return true;
159 case VK_ERROR_DEVICE_LOST:
160 {
161 SkAutoMutexExclusive lock(fDeviceIsLostMutex);
162 if (fDeviceIsLost) {
163 return false;
164 }
165 fDeviceIsLost = true;
166 // Fall through to InvokeDeviceLostCallback (on first VK_ERROR_DEVICE_LOST) only afer
167 // releasing fDeviceIsLostMutex, otherwise clients might cause deadlock by checking
168 // isDeviceLost() from the callback.
169 }
170 skgpu::InvokeDeviceLostCallback(interface(),
171 device(),
172 fDeviceLostContext,
173 fDeviceLostProc,
174 vulkanCaps().supportsDeviceFaultInfo());
175 return false;
176 case VK_ERROR_OUT_OF_DEVICE_MEMORY:
177 case VK_ERROR_OUT_OF_HOST_MEMORY:
178 // TODO: determine how we'll track this in a thread-safe manner
179 //this->setOOMed();
180 return false;
181 default:
182 return false;
183 }
184 }
185 } // namespace skgpu::graphite
186