1 /*
2 * Copyright 2023 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/vk/VulkanUtilsPriv.h"
9
10 #include "include/private/base/SkDebug.h"
11 #include "src/gpu/vk/VulkanInterface.h"
12
13 #include <vector>
14
15 namespace skgpu {
16
17 /**
18 * Define a macro that both ganesh and graphite can use to make simple calls into Vulkan so we can
19 * share more code between them.
20 */
21 #define SHARED_GR_VULKAN_CALL(IFACE, X) (IFACE)->fFunctions.f##X
22
23 /**
24 * Returns a populated VkSamplerYcbcrConversionCreateInfo object based on VulkanYcbcrConversionInfo
25 */
SetupSamplerYcbcrConversionInfo(VkSamplerYcbcrConversionCreateInfo * outInfo,const VulkanYcbcrConversionInfo & conversionInfo)26 void SetupSamplerYcbcrConversionInfo(VkSamplerYcbcrConversionCreateInfo* outInfo,
27 const VulkanYcbcrConversionInfo& conversionInfo) {
28 #ifdef SK_DEBUG
29 const VkFormatFeatureFlags& featureFlags = conversionInfo.fFormatFeatures;
30 if (conversionInfo.fXChromaOffset == VK_CHROMA_LOCATION_MIDPOINT ||
31 conversionInfo.fYChromaOffset == VK_CHROMA_LOCATION_MIDPOINT) {
32 SkASSERT(featureFlags & VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT);
33 }
34 if (conversionInfo.fXChromaOffset == VK_CHROMA_LOCATION_COSITED_EVEN ||
35 conversionInfo.fYChromaOffset == VK_CHROMA_LOCATION_COSITED_EVEN) {
36 SkASSERT(featureFlags & VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT);
37 }
38 if (conversionInfo.fChromaFilter == VK_FILTER_LINEAR) {
39 SkASSERT(featureFlags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT);
40 }
41 if (conversionInfo.fForceExplicitReconstruction) {
42 SkASSERT(featureFlags &
43 VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT);
44 }
45 #endif
46
47 VkFilter chromaFilter = conversionInfo.fChromaFilter;
48 if (!(conversionInfo.fFormatFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
49 if (!(conversionInfo.fFormatFeatures &
50 VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT)) {
51 // Because we don't have have separate reconstruction filter, the min, mag and
52 // chroma filter must all match. However, we also don't support linear sampling so
53 // the min/mag filter have to be nearest. Therefore, we force the chrome filter to
54 // be nearest regardless of support for the feature
55 // VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT.
56 chromaFilter = VK_FILTER_NEAREST;
57 }
58 }
59
60 outInfo->sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
61 outInfo->pNext = nullptr;
62 outInfo->format = conversionInfo.fFormat;
63 outInfo->ycbcrModel = conversionInfo.fYcbcrModel;
64 outInfo->ycbcrRange = conversionInfo.fYcbcrRange;
65 outInfo->components = conversionInfo.fComponents;
66 outInfo->xChromaOffset = conversionInfo.fXChromaOffset;
67 outInfo->yChromaOffset = conversionInfo.fYChromaOffset;
68 outInfo->chromaFilter = chromaFilter;
69 outInfo->forceExplicitReconstruction = conversionInfo.fForceExplicitReconstruction;
70 }
71
72 #ifdef SK_BUILD_FOR_ANDROID
73
74 /**
75 * Shared Vulkan AHardwareBuffer utility functions between graphite and ganesh
76 */
GetYcbcrConversionInfoFromFormatProps(VulkanYcbcrConversionInfo * outConversionInfo,const VkAndroidHardwareBufferFormatPropertiesANDROID & formatProps)77 void GetYcbcrConversionInfoFromFormatProps(
78 VulkanYcbcrConversionInfo* outConversionInfo,
79 const VkAndroidHardwareBufferFormatPropertiesANDROID& formatProps) {
80 outConversionInfo->fYcbcrModel = formatProps.suggestedYcbcrModel;
81 outConversionInfo->fYcbcrRange = formatProps.suggestedYcbcrRange;
82 outConversionInfo->fComponents = formatProps.samplerYcbcrConversionComponents;
83 outConversionInfo->fXChromaOffset = formatProps.suggestedXChromaOffset;
84 outConversionInfo->fYChromaOffset = formatProps.suggestedYChromaOffset;
85 outConversionInfo->fForceExplicitReconstruction = VK_FALSE;
86 outConversionInfo->fExternalFormat = formatProps.externalFormat;
87 outConversionInfo->fFormatFeatures = formatProps.formatFeatures;
88 if (VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT &
89 formatProps.formatFeatures) {
90 outConversionInfo->fChromaFilter = VK_FILTER_LINEAR;
91 } else {
92 outConversionInfo->fChromaFilter = VK_FILTER_NEAREST;
93 }
94 }
95
GetAHardwareBufferProperties(VkAndroidHardwareBufferFormatPropertiesANDROID * outHwbFormatProps,VkAndroidHardwareBufferPropertiesANDROID * outHwbProps,const skgpu::VulkanInterface * interface,const AHardwareBuffer * hwBuffer,VkDevice device)96 bool GetAHardwareBufferProperties(
97 VkAndroidHardwareBufferFormatPropertiesANDROID* outHwbFormatProps,
98 VkAndroidHardwareBufferPropertiesANDROID* outHwbProps,
99 const skgpu::VulkanInterface* interface,
100 const AHardwareBuffer* hwBuffer,
101 VkDevice device) {
102 outHwbFormatProps->sType =
103 VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID;
104 outHwbFormatProps->pNext = nullptr;
105
106 outHwbProps->sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID;
107 outHwbProps->pNext = outHwbFormatProps;
108
109 VkResult result =
110 SHARED_GR_VULKAN_CALL(interface,
111 GetAndroidHardwareBufferProperties(device,
112 hwBuffer,
113 outHwbProps));
114 if (result != VK_SUCCESS) {
115 SkDebugf("Failed to get AndroidHardwareBufferProperties\n");
116 return false;
117 }
118 return true;
119 }
120
AllocateAndBindImageMemory(skgpu::VulkanAlloc * outVulkanAlloc,VkImage image,const VkPhysicalDeviceMemoryProperties2 & phyDevMemProps,const VkAndroidHardwareBufferPropertiesANDROID & hwbProps,AHardwareBuffer * hardwareBuffer,const skgpu::VulkanInterface * interface,VkDevice device)121 bool AllocateAndBindImageMemory(skgpu::VulkanAlloc* outVulkanAlloc,
122 VkImage image,
123 const VkPhysicalDeviceMemoryProperties2& phyDevMemProps,
124 const VkAndroidHardwareBufferPropertiesANDROID& hwbProps,
125 AHardwareBuffer* hardwareBuffer,
126 const skgpu::VulkanInterface* interface,
127 VkDevice device) {
128 VkResult result;
129 uint32_t typeIndex = 0;
130 bool foundHeap = false;
131 uint32_t memTypeCnt = phyDevMemProps.memoryProperties.memoryTypeCount;
132 for (uint32_t i = 0; i < memTypeCnt && !foundHeap; ++i) {
133 if (hwbProps.memoryTypeBits & (1 << i)) {
134 const VkPhysicalDeviceMemoryProperties& pdmp = phyDevMemProps.memoryProperties;
135 uint32_t supportedFlags = pdmp.memoryTypes[i].propertyFlags &
136 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
137 if (supportedFlags == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
138 typeIndex = i;
139 foundHeap = true;
140 }
141 }
142 }
143
144 /**
145 * Fallback to use any available memory type for AHB.
146 *
147 * For external memory import, compatible memory types are decided by the Vulkan driver since
148 * the memory has been allocated externally. There are usually special requirements against
149 * external memory. e.g. AHB allocated with CPU R/W often usage bits is only importable for
150 * non-device-local heap on some AMD systems.
151 */
152 if (!foundHeap && hwbProps.memoryTypeBits) {
153 typeIndex = ffs(hwbProps.memoryTypeBits) - 1;
154 foundHeap = true;
155 }
156 if (!foundHeap) {
157 return false;
158 }
159
160 VkImportAndroidHardwareBufferInfoANDROID hwbImportInfo;
161 hwbImportInfo.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID;
162 hwbImportInfo.pNext = nullptr;
163 hwbImportInfo.buffer = hardwareBuffer;
164
165 VkMemoryDedicatedAllocateInfo dedicatedAllocInfo;
166 dedicatedAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;
167 dedicatedAllocInfo.pNext = &hwbImportInfo;
168 dedicatedAllocInfo.image = image;
169 dedicatedAllocInfo.buffer = VK_NULL_HANDLE;
170
171 VkMemoryAllocateInfo allocInfo = {
172 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // sType
173 &dedicatedAllocInfo, // pNext
174 hwbProps.allocationSize, // allocationSize
175 typeIndex, // memoryTypeIndex
176 };
177
178 VkDeviceMemory memory;
179 result = SHARED_GR_VULKAN_CALL(interface,
180 AllocateMemory(device, &allocInfo, nullptr, &memory));
181 if (result != VK_SUCCESS) {
182 return false;
183 }
184
185 VkBindImageMemoryInfo bindImageInfo;
186 bindImageInfo.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
187 bindImageInfo.pNext = nullptr;
188 bindImageInfo.image = image;
189 bindImageInfo.memory = memory;
190 bindImageInfo.memoryOffset = 0;
191
192 result = SHARED_GR_VULKAN_CALL(interface, BindImageMemory2(device, 1, &bindImageInfo));
193 if (result != VK_SUCCESS) {
194 SHARED_GR_VULKAN_CALL(interface, FreeMemory(device, memory, nullptr));
195 return false;
196 }
197
198 outVulkanAlloc->fMemory = memory;
199 outVulkanAlloc->fOffset = 0;
200 outVulkanAlloc->fSize = hwbProps.allocationSize;
201 outVulkanAlloc->fFlags = 0;
202 outVulkanAlloc->fBackendMemory = 0;
203 return true;
204 }
205
206 #endif // SK_BUILD_FOR_ANDROID
207
208 // Note: since this is called from Vulkan result-checking functions, any Vk calls this function
209 // makes must NOT be checked with those same functions to avoid infinite recursion.
InvokeDeviceLostCallback(const skgpu::VulkanInterface * vulkanInterface,VkDevice vkDevice,skgpu::VulkanDeviceLostContext deviceLostContext,skgpu::VulkanDeviceLostProc deviceLostProc,bool supportsDeviceFaultInfoExtension)210 void InvokeDeviceLostCallback(const skgpu::VulkanInterface* vulkanInterface,
211 VkDevice vkDevice,
212 skgpu::VulkanDeviceLostContext deviceLostContext,
213 skgpu::VulkanDeviceLostProc deviceLostProc,
214 bool supportsDeviceFaultInfoExtension) {
215 if (!deviceLostProc) {
216 return;
217 }
218
219 std::vector<VkDeviceFaultAddressInfoEXT> addressInfos = {};
220 std::vector<VkDeviceFaultVendorInfoEXT> vendorInfos = {};
221 std::vector<std::byte> vendorBinaryData = {};
222
223 if (!supportsDeviceFaultInfoExtension) {
224 deviceLostProc(deviceLostContext,
225 "No details: VK_EXT_device_fault not available/enabled.",
226 addressInfos,
227 vendorInfos,
228 vendorBinaryData);
229 return;
230 }
231
232 // Query counts
233 VkDeviceFaultCountsEXT faultCounts = {};
234 faultCounts.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_COUNTS_EXT;
235 VkResult result = SHARED_GR_VULKAN_CALL(vulkanInterface,
236 GetDeviceFaultInfo(vkDevice, &faultCounts, NULL));
237 if (result != VK_SUCCESS) {
238 deviceLostProc(
239 deviceLostContext,
240 "No details: VK_EXT_device_fault error counting failed: " + std::to_string(result),
241 addressInfos,
242 vendorInfos,
243 vendorBinaryData);
244 return;
245 }
246
247 // Prepare storage
248 addressInfos.resize(faultCounts.addressInfoCount);
249 vendorInfos.resize(faultCounts.vendorInfoCount);
250 vendorBinaryData.resize(faultCounts.vendorBinarySize);
251
252 // Query fault info
253 VkDeviceFaultInfoEXT faultInfo = {};
254 faultInfo.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_INFO_EXT;
255 faultInfo.pAddressInfos = addressInfos.data();
256 faultInfo.pVendorInfos = vendorInfos.data();
257 faultInfo.pVendorBinaryData =
258 faultCounts.vendorBinarySize > 0 ? vendorBinaryData.data() : nullptr;
259 result = SHARED_GR_VULKAN_CALL(vulkanInterface,
260 GetDeviceFaultInfo(vkDevice, &faultCounts, &faultInfo));
261 if (result != VK_SUCCESS) {
262 deviceLostProc(
263 deviceLostContext,
264 "No details: VK_EXT_device_fault info dumping failed: " + std::to_string(result),
265 addressInfos,
266 vendorInfos,
267 vendorBinaryData);
268 return;
269 }
270
271 deviceLostProc(deviceLostContext,
272 std::string(faultInfo.description),
273 addressInfos,
274 vendorInfos,
275 vendorBinaryData);
276 }
277
278 } // namespace skgpu
279