• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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