| /* |
| * Copyright 2023 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/gpu/graphite/vk/VulkanYcbcrConversion.h" |
| |
| #include "include/gpu/graphite/vk/VulkanGraphiteTypes.h" |
| #include "src/gpu/graphite/vk/VulkanCaps.h" |
| #include "src/gpu/graphite/vk/VulkanGraphiteUtilsPriv.h" |
| #include "src/gpu/graphite/vk/VulkanSharedContext.h" |
| |
| namespace skgpu::graphite { |
| |
| sk_sp<VulkanYcbcrConversion> VulkanYcbcrConversion::Make( |
| const VulkanSharedContext* context, const VulkanYcbcrConversionInfo& conversionInfo) { |
| if (!context->vulkanCaps().supportsYcbcrConversion()) { |
| return nullptr; |
| } |
| |
| VkSamplerYcbcrConversionCreateInfo ycbcrCreateInfo; |
| skgpu::SetupSamplerYcbcrConversionInfo(&ycbcrCreateInfo, conversionInfo); |
| |
| #ifdef SK_BUILD_FOR_ANDROID |
| VkExternalFormatANDROID externalFormat; |
| if (conversionInfo.fExternalFormat) { |
| // Format must not be specified for external images. |
| SkASSERT(conversionInfo.fFormat == VK_FORMAT_UNDEFINED); |
| externalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID; |
| externalFormat.pNext = nullptr; |
| externalFormat.externalFormat = conversionInfo.fExternalFormat; |
| SkASSERT(ycbcrCreateInfo.pNext == nullptr); |
| ycbcrCreateInfo.pNext = &externalFormat; |
| } |
| #else |
| // External images are supported only on Android. |
| SkASSERT(!conversionInfo.fExternalFormat); |
| #endif |
| |
| if (!conversionInfo.fExternalFormat) { |
| SkASSERT(conversionInfo.fFormat != VK_FORMAT_UNDEFINED); |
| } |
| |
| VkSamplerYcbcrConversion conversion; |
| VkResult result; |
| VULKAN_CALL_RESULT(context, |
| result, |
| CreateSamplerYcbcrConversion( |
| context->device(), &ycbcrCreateInfo, nullptr, &conversion)); |
| if (result != VK_SUCCESS) { |
| return nullptr; |
| } |
| return sk_sp<VulkanYcbcrConversion>(new VulkanYcbcrConversion(context, conversion)); |
| } |
| |
| sk_sp<VulkanYcbcrConversion> VulkanYcbcrConversion::Make(const VulkanSharedContext* context, |
| uint32_t nonFormatInfo, |
| uint64_t format) { |
| VkSamplerYcbcrConversionCreateInfo ycbcrCreateInfo; |
| |
| bool useExternalFormat = static_cast<bool>( |
| (nonFormatInfo & ycbcrPackaging::kUseExternalFormatMask) >> |
| ycbcrPackaging::kUsesExternalFormatShift); |
| |
| ycbcrCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO; |
| ycbcrCreateInfo.pNext = nullptr; |
| ycbcrCreateInfo.format = useExternalFormat ? VK_FORMAT_UNDEFINED |
| : static_cast<VkFormat>(format); |
| |
| ycbcrCreateInfo.ycbcrModel = static_cast<VkSamplerYcbcrModelConversion>( |
| (nonFormatInfo & ycbcrPackaging::kYcbcrModelMask) >> |
| ycbcrPackaging::kYcbcrModelShift); |
| ycbcrCreateInfo.ycbcrRange = static_cast<VkSamplerYcbcrRange>( |
| (nonFormatInfo & ycbcrPackaging::kYcbcrRangeMask) >> |
| ycbcrPackaging::kYcbcrRangeShift); |
| ycbcrCreateInfo.components = { |
| static_cast<VkComponentSwizzle>( |
| (nonFormatInfo & ycbcrPackaging::kComponentRMask) >> |
| ycbcrPackaging::kComponentRShift), |
| static_cast<VkComponentSwizzle>( |
| (nonFormatInfo & ycbcrPackaging::kComponentGMask) >> |
| ycbcrPackaging::kComponentGShift), |
| static_cast<VkComponentSwizzle>( |
| (nonFormatInfo & ycbcrPackaging::kComponentBMask) >> |
| ycbcrPackaging::kComponentBShift), |
| static_cast<VkComponentSwizzle>( |
| (nonFormatInfo & ycbcrPackaging::kComponentAMask) >> |
| ycbcrPackaging::kComponentAShift)}; |
| ycbcrCreateInfo.xChromaOffset = static_cast<VkChromaLocation>( |
| (nonFormatInfo & ycbcrPackaging::kXChromaOffsetMask) >> |
| ycbcrPackaging::kXChromaOffsetShift); |
| ycbcrCreateInfo.yChromaOffset = static_cast<VkChromaLocation>( |
| (nonFormatInfo & ycbcrPackaging::kYChromaOffsetMask) >> |
| ycbcrPackaging::kYChromaOffsetShift); |
| ycbcrCreateInfo.chromaFilter = static_cast<VkFilter>( |
| (nonFormatInfo & ycbcrPackaging::kChromaFilterMask) >> |
| ycbcrPackaging::kChromaFilterShift); |
| ycbcrCreateInfo.forceExplicitReconstruction = static_cast<VkBool32>( |
| (nonFormatInfo & ycbcrPackaging::kForceExplicitReconMask) >> |
| ycbcrPackaging::kForceExplicitReconShift); |
| |
| #ifdef SK_BUILD_FOR_ANDROID |
| VkExternalFormatANDROID externalFormat; |
| if (useExternalFormat) { |
| externalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID; |
| externalFormat.pNext = nullptr; |
| externalFormat.externalFormat = format; |
| SkASSERT(ycbcrCreateInfo.pNext == nullptr); |
| ycbcrCreateInfo.pNext = &externalFormat; |
| } |
| #endif |
| |
| VkSamplerYcbcrConversion conversion; |
| VkResult result; |
| VULKAN_CALL_RESULT(context, |
| result, |
| CreateSamplerYcbcrConversion( |
| context->device(), &ycbcrCreateInfo, nullptr, &conversion)); |
| if (result != VK_SUCCESS) { |
| return nullptr; |
| } |
| return sk_sp<VulkanYcbcrConversion>(new VulkanYcbcrConversion(context, conversion)); |
| } |
| |
| namespace { |
| // Define this anonymous helper to get a static resource type for YCbCr conversions regardless of |
| // which method is used to create it (from VulkanYcbcrConversionInfo or from SamplerDesc) |
| ResourceType conversion_rsrc_type() { |
| static const ResourceType conversionType = GraphiteResourceKey::GenerateResourceType(); |
| return conversionType; |
| } |
| } |
| GraphiteResourceKey VulkanYcbcrConversion::MakeYcbcrConversionKey( |
| const VulkanSharedContext* context, const VulkanYcbcrConversionInfo& info) { |
| bool useExternalFormat = info.fFormat == VK_FORMAT_UNDEFINED; |
| GraphiteResourceKey key; |
| GraphiteResourceKey::Builder builder(&key, |
| conversion_rsrc_type(), |
| ycbcrPackaging::numInt32sNeeded(info), |
| Shareable::kYes); |
| int i = 0; |
| builder[i++] = ycbcrPackaging::nonFormatInfoAsUInt32(info); |
| if (useExternalFormat) { |
| builder[i++] = (uint32_t)info.fExternalFormat; |
| builder[i++] = (uint32_t)(info.fExternalFormat >> 32); |
| } else { |
| builder[i++] = (uint32_t)info.fFormat; |
| } |
| SkASSERT(i == ycbcrPackaging::numInt32sNeeded(info)); |
| |
| builder.finish(); |
| return key; |
| } |
| |
| GraphiteResourceKey VulkanYcbcrConversion::GetKeyFromSamplerDesc(const SamplerDesc& samplerDesc) { |
| GraphiteResourceKey key; |
| const SkSpan<const uint32_t>& samplerData = samplerDesc.asSpan(); |
| GraphiteResourceKey::Builder builder(&key, conversion_rsrc_type(), samplerData.size(), |
| Shareable::kYes); |
| |
| // The first index of sampler data (sampler desc value) includes non-ycbcr information |
| // that needs to be shifted past in order to isolate the ycbcr information for this key. |
| builder[0] = samplerData[0] >> SamplerDesc::kImmutableSamplerInfoShift; |
| for (size_t i = 1; i < samplerData.size(); i++) { |
| builder[i] = samplerData[i]; |
| } |
| |
| builder.finish(); |
| return key; |
| } |
| |
| VulkanYcbcrConversion::VulkanYcbcrConversion(const VulkanSharedContext* context, |
| VkSamplerYcbcrConversion ycbcrConversion) |
| : Resource(context, |
| Ownership::kOwned, |
| skgpu::Budgeted::kYes, // Shareable, so must be budgeted |
| /*gpuMemorySize=*/0) |
| , fYcbcrConversion (ycbcrConversion) {} |
| |
| void VulkanYcbcrConversion::freeGpuData() { |
| auto sharedContext = static_cast<const VulkanSharedContext*>(this->sharedContext()); |
| SkASSERT(fYcbcrConversion != VK_NULL_HANDLE); |
| VULKAN_CALL(sharedContext->interface(), |
| DestroySamplerYcbcrConversion(sharedContext->device(), fYcbcrConversion, nullptr)); |
| } |
| |
| } // namespace skgpu::graphite |
| |