/* * 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::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(new VulkanYcbcrConversion(context, conversion)); } sk_sp VulkanYcbcrConversion::Make(const VulkanSharedContext* context, uint32_t nonFormatInfo, uint64_t format) { VkSamplerYcbcrConversionCreateInfo ycbcrCreateInfo; bool useExternalFormat = static_cast( (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(format); ycbcrCreateInfo.ycbcrModel = static_cast( (nonFormatInfo & ycbcrPackaging::kYcbcrModelMask) >> ycbcrPackaging::kYcbcrModelShift); ycbcrCreateInfo.ycbcrRange = static_cast( (nonFormatInfo & ycbcrPackaging::kYcbcrRangeMask) >> ycbcrPackaging::kYcbcrRangeShift); ycbcrCreateInfo.components = { static_cast( (nonFormatInfo & ycbcrPackaging::kComponentRMask) >> ycbcrPackaging::kComponentRShift), static_cast( (nonFormatInfo & ycbcrPackaging::kComponentGMask) >> ycbcrPackaging::kComponentGShift), static_cast( (nonFormatInfo & ycbcrPackaging::kComponentBMask) >> ycbcrPackaging::kComponentBShift), static_cast( (nonFormatInfo & ycbcrPackaging::kComponentAMask) >> ycbcrPackaging::kComponentAShift)}; ycbcrCreateInfo.xChromaOffset = static_cast( (nonFormatInfo & ycbcrPackaging::kXChromaOffsetMask) >> ycbcrPackaging::kXChromaOffsetShift); ycbcrCreateInfo.yChromaOffset = static_cast( (nonFormatInfo & ycbcrPackaging::kYChromaOffsetMask) >> ycbcrPackaging::kYChromaOffsetShift); ycbcrCreateInfo.chromaFilter = static_cast( (nonFormatInfo & ycbcrPackaging::kChromaFilterMask) >> ycbcrPackaging::kChromaFilterShift); ycbcrCreateInfo.forceExplicitReconstruction = static_cast( (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(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& 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(this->sharedContext()); SkASSERT(fYcbcrConversion != VK_NULL_HANDLE); VULKAN_CALL(sharedContext->interface(), DestroySamplerYcbcrConversion(sharedContext->device(), fYcbcrConversion, nullptr)); } } // namespace skgpu::graphite