| /* |
| * Copyright 2023 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/gpu/vk/VulkanUtilsPriv.h" |
| |
| #include "include/private/base/SkDebug.h" |
| #include "src/gpu/vk/VulkanInterface.h" |
| |
| #include <vector> |
| |
| namespace skgpu { |
| |
| /** |
| * Define a macro that both ganesh and graphite can use to make simple calls into Vulkan so we can |
| * share more code between them. |
| */ |
| #define SHARED_GR_VULKAN_CALL(IFACE, X) (IFACE)->fFunctions.f##X |
| |
| /** |
| * Returns a populated VkSamplerYcbcrConversionCreateInfo object based on VulkanYcbcrConversionInfo |
| */ |
| void SetupSamplerYcbcrConversionInfo(VkSamplerYcbcrConversionCreateInfo* outInfo, |
| const VulkanYcbcrConversionInfo& conversionInfo) { |
| #ifdef SK_DEBUG |
| const VkFormatFeatureFlags& featureFlags = conversionInfo.fFormatFeatures; |
| if (conversionInfo.fXChromaOffset == VK_CHROMA_LOCATION_MIDPOINT || |
| conversionInfo.fYChromaOffset == VK_CHROMA_LOCATION_MIDPOINT) { |
| SkASSERT(featureFlags & VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT); |
| } |
| if (conversionInfo.fXChromaOffset == VK_CHROMA_LOCATION_COSITED_EVEN || |
| conversionInfo.fYChromaOffset == VK_CHROMA_LOCATION_COSITED_EVEN) { |
| SkASSERT(featureFlags & VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT); |
| } |
| if (conversionInfo.fChromaFilter == VK_FILTER_LINEAR) { |
| SkASSERT(featureFlags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT); |
| } |
| if (conversionInfo.fForceExplicitReconstruction) { |
| SkASSERT(featureFlags & |
| VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT); |
| } |
| #endif |
| |
| VkFilter chromaFilter = conversionInfo.fChromaFilter; |
| if (!(conversionInfo.fFormatFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) { |
| if (!(conversionInfo.fFormatFeatures & |
| VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT)) { |
| // Because we don't have have separate reconstruction filter, the min, mag and |
| // chroma filter must all match. However, we also don't support linear sampling so |
| // the min/mag filter have to be nearest. Therefore, we force the chrome filter to |
| // be nearest regardless of support for the feature |
| // VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT. |
| chromaFilter = VK_FILTER_NEAREST; |
| } |
| } |
| |
| outInfo->sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO; |
| outInfo->pNext = nullptr; |
| outInfo->format = conversionInfo.fFormat; |
| outInfo->ycbcrModel = conversionInfo.fYcbcrModel; |
| outInfo->ycbcrRange = conversionInfo.fYcbcrRange; |
| outInfo->components = conversionInfo.fComponents; |
| outInfo->xChromaOffset = conversionInfo.fXChromaOffset; |
| outInfo->yChromaOffset = conversionInfo.fYChromaOffset; |
| outInfo->chromaFilter = chromaFilter; |
| outInfo->forceExplicitReconstruction = conversionInfo.fForceExplicitReconstruction; |
| } |
| |
| #ifdef SK_BUILD_FOR_ANDROID |
| |
| /** |
| * Shared Vulkan AHardwareBuffer utility functions between graphite and ganesh |
| */ |
| void GetYcbcrConversionInfoFromFormatProps( |
| VulkanYcbcrConversionInfo* outConversionInfo, |
| const VkAndroidHardwareBufferFormatPropertiesANDROID& formatProps) { |
| outConversionInfo->fYcbcrModel = formatProps.suggestedYcbcrModel; |
| outConversionInfo->fYcbcrRange = formatProps.suggestedYcbcrRange; |
| outConversionInfo->fComponents = formatProps.samplerYcbcrConversionComponents; |
| outConversionInfo->fXChromaOffset = formatProps.suggestedXChromaOffset; |
| outConversionInfo->fYChromaOffset = formatProps.suggestedYChromaOffset; |
| outConversionInfo->fForceExplicitReconstruction = VK_FALSE; |
| outConversionInfo->fExternalFormat = formatProps.externalFormat; |
| outConversionInfo->fFormatFeatures = formatProps.formatFeatures; |
| if (VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT & |
| formatProps.formatFeatures) { |
| outConversionInfo->fChromaFilter = VK_FILTER_LINEAR; |
| } else { |
| outConversionInfo->fChromaFilter = VK_FILTER_NEAREST; |
| } |
| } |
| |
| bool GetAHardwareBufferProperties( |
| VkAndroidHardwareBufferFormatPropertiesANDROID* outHwbFormatProps, |
| VkAndroidHardwareBufferPropertiesANDROID* outHwbProps, |
| const skgpu::VulkanInterface* interface, |
| const AHardwareBuffer* hwBuffer, |
| VkDevice device) { |
| outHwbFormatProps->sType = |
| VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID; |
| outHwbFormatProps->pNext = nullptr; |
| |
| outHwbProps->sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID; |
| outHwbProps->pNext = outHwbFormatProps; |
| |
| VkResult result = |
| SHARED_GR_VULKAN_CALL(interface, |
| GetAndroidHardwareBufferProperties(device, |
| hwBuffer, |
| outHwbProps)); |
| if (result != VK_SUCCESS) { |
| SkDebugf("Failed to get AndroidHardwareBufferProperties\n"); |
| return false; |
| } |
| return true; |
| } |
| |
| bool AllocateAndBindImageMemory(skgpu::VulkanAlloc* outVulkanAlloc, |
| VkImage image, |
| const VkPhysicalDeviceMemoryProperties2& phyDevMemProps, |
| const VkAndroidHardwareBufferPropertiesANDROID& hwbProps, |
| AHardwareBuffer* hardwareBuffer, |
| const skgpu::VulkanInterface* interface, |
| VkDevice device) { |
| VkResult result; |
| uint32_t typeIndex = 0; |
| bool foundHeap = false; |
| uint32_t memTypeCnt = phyDevMemProps.memoryProperties.memoryTypeCount; |
| for (uint32_t i = 0; i < memTypeCnt && !foundHeap; ++i) { |
| if (hwbProps.memoryTypeBits & (1 << i)) { |
| const VkPhysicalDeviceMemoryProperties& pdmp = phyDevMemProps.memoryProperties; |
| uint32_t supportedFlags = pdmp.memoryTypes[i].propertyFlags & |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
| if (supportedFlags == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { |
| typeIndex = i; |
| foundHeap = true; |
| } |
| } |
| } |
| |
| /** |
| * Fallback to use any available memory type for AHB. |
| * |
| * For external memory import, compatible memory types are decided by the Vulkan driver since |
| * the memory has been allocated externally. There are usually special requirements against |
| * external memory. e.g. AHB allocated with CPU R/W often usage bits is only importable for |
| * non-device-local heap on some AMD systems. |
| */ |
| if (!foundHeap && hwbProps.memoryTypeBits) { |
| typeIndex = ffs(hwbProps.memoryTypeBits) - 1; |
| foundHeap = true; |
| } |
| if (!foundHeap) { |
| return false; |
| } |
| |
| VkImportAndroidHardwareBufferInfoANDROID hwbImportInfo; |
| hwbImportInfo.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID; |
| hwbImportInfo.pNext = nullptr; |
| hwbImportInfo.buffer = hardwareBuffer; |
| |
| VkMemoryDedicatedAllocateInfo dedicatedAllocInfo; |
| dedicatedAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; |
| dedicatedAllocInfo.pNext = &hwbImportInfo; |
| dedicatedAllocInfo.image = image; |
| dedicatedAllocInfo.buffer = VK_NULL_HANDLE; |
| |
| VkMemoryAllocateInfo allocInfo = { |
| VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // sType |
| &dedicatedAllocInfo, // pNext |
| hwbProps.allocationSize, // allocationSize |
| typeIndex, // memoryTypeIndex |
| }; |
| |
| VkDeviceMemory memory; |
| result = SHARED_GR_VULKAN_CALL(interface, |
| AllocateMemory(device, &allocInfo, nullptr, &memory)); |
| if (result != VK_SUCCESS) { |
| return false; |
| } |
| |
| VkBindImageMemoryInfo bindImageInfo; |
| bindImageInfo.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO; |
| bindImageInfo.pNext = nullptr; |
| bindImageInfo.image = image; |
| bindImageInfo.memory = memory; |
| bindImageInfo.memoryOffset = 0; |
| |
| result = SHARED_GR_VULKAN_CALL(interface, BindImageMemory2(device, 1, &bindImageInfo)); |
| if (result != VK_SUCCESS) { |
| SHARED_GR_VULKAN_CALL(interface, FreeMemory(device, memory, nullptr)); |
| return false; |
| } |
| |
| outVulkanAlloc->fMemory = memory; |
| outVulkanAlloc->fOffset = 0; |
| outVulkanAlloc->fSize = hwbProps.allocationSize; |
| outVulkanAlloc->fFlags = 0; |
| outVulkanAlloc->fBackendMemory = 0; |
| return true; |
| } |
| |
| #endif // SK_BUILD_FOR_ANDROID |
| |
| // Note: since this is called from Vulkan result-checking functions, any Vk calls this function |
| // makes must NOT be checked with those same functions to avoid infinite recursion. |
| void InvokeDeviceLostCallback(const skgpu::VulkanInterface* vulkanInterface, |
| VkDevice vkDevice, |
| skgpu::VulkanDeviceLostContext deviceLostContext, |
| skgpu::VulkanDeviceLostProc deviceLostProc, |
| bool supportsDeviceFaultInfoExtension) { |
| if (!deviceLostProc) { |
| return; |
| } |
| |
| std::vector<VkDeviceFaultAddressInfoEXT> addressInfos = {}; |
| std::vector<VkDeviceFaultVendorInfoEXT> vendorInfos = {}; |
| std::vector<std::byte> vendorBinaryData = {}; |
| |
| if (!supportsDeviceFaultInfoExtension) { |
| deviceLostProc(deviceLostContext, |
| "No details: VK_EXT_device_fault not available/enabled.", |
| addressInfos, |
| vendorInfos, |
| vendorBinaryData); |
| return; |
| } |
| |
| // Query counts |
| VkDeviceFaultCountsEXT faultCounts = {}; |
| faultCounts.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_COUNTS_EXT; |
| VkResult result = SHARED_GR_VULKAN_CALL(vulkanInterface, |
| GetDeviceFaultInfo(vkDevice, &faultCounts, NULL)); |
| if (result != VK_SUCCESS) { |
| deviceLostProc( |
| deviceLostContext, |
| "No details: VK_EXT_device_fault error counting failed: " + std::to_string(result), |
| addressInfos, |
| vendorInfos, |
| vendorBinaryData); |
| return; |
| } |
| |
| // Prepare storage |
| addressInfos.resize(faultCounts.addressInfoCount); |
| vendorInfos.resize(faultCounts.vendorInfoCount); |
| vendorBinaryData.resize(faultCounts.vendorBinarySize); |
| |
| // Query fault info |
| VkDeviceFaultInfoEXT faultInfo = {}; |
| faultInfo.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_INFO_EXT; |
| faultInfo.pAddressInfos = addressInfos.data(); |
| faultInfo.pVendorInfos = vendorInfos.data(); |
| faultInfo.pVendorBinaryData = |
| faultCounts.vendorBinarySize > 0 ? vendorBinaryData.data() : nullptr; |
| result = SHARED_GR_VULKAN_CALL(vulkanInterface, |
| GetDeviceFaultInfo(vkDevice, &faultCounts, &faultInfo)); |
| if (result != VK_SUCCESS) { |
| deviceLostProc( |
| deviceLostContext, |
| "No details: VK_EXT_device_fault info dumping failed: " + std::to_string(result), |
| addressInfos, |
| vendorInfos, |
| vendorBinaryData); |
| return; |
| } |
| |
| deviceLostProc(deviceLostContext, |
| std::string(faultInfo.description), |
| addressInfos, |
| vendorInfos, |
| vendorBinaryData); |
| } |
| |
| } // namespace skgpu |