| // Copyright 2019 The Dawn & Tint Authors | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are met: | |
| // | |
| // 1. Redistributions of source code must retain the above copyright notice, this | |
| // list of conditions and the following disclaimer. | |
| // | |
| // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| // this list of conditions and the following disclaimer in the documentation | |
| // and/or other materials provided with the distribution. | |
| // | |
| // 3. Neither the name of the copyright holder nor the names of its | |
| // contributors may be used to endorse or promote products derived from | |
| // this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| #include "src/dawn/native/vulkan/UtilsVulkan.h" | |
| #include "src/dawn/native/EnumMaskIterator.h" | |
| #include "src/dawn/native/Format.h" | |
| #include "src/dawn/native/Pipeline.h" | |
| #include "src/dawn/native/ShaderModule.h" | |
| #include "src/dawn/native/vulkan/DeviceVk.h" | |
| #include "src/dawn/native/vulkan/Forward.h" | |
| #include "src/dawn/native/vulkan/PhysicalDeviceVk.h" | |
| #include "src/dawn/native/vulkan/TextureVk.h" | |
| #include "src/dawn/native/vulkan/VulkanError.h" | |
| #include "src/dawn/native/vulkan/VulkanFunctions.h" | |
| #include "src/utils/assert.h" | |
| #include "src/utils/compiler.h" | |
| #include "src/utils/numeric.h" | |
| namespace dawn::native::vulkan { | |
| constexpr char kDeviceDebugPrefix[] = "DawnDbg="; | |
| constexpr char kDeviceDebugSeparator[] = ";"; | |
| #define VK_OBJECT_TYPE_GETTER(object, objectType) \ | |
| template <> \ | |
| VkObjectType GetVkObjectType<object>(object handle) { \ | |
| return objectType; \ | |
| } | |
| VK_OBJECT_TYPE_GETTER(VkBuffer, VK_OBJECT_TYPE_BUFFER) | |
| VK_OBJECT_TYPE_GETTER(VkDescriptorSetLayout, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT) | |
| VK_OBJECT_TYPE_GETTER(VkDescriptorSet, VK_OBJECT_TYPE_DESCRIPTOR_SET) | |
| VK_OBJECT_TYPE_GETTER(VkPipeline, VK_OBJECT_TYPE_PIPELINE) | |
| VK_OBJECT_TYPE_GETTER(VkPipelineLayout, VK_OBJECT_TYPE_PIPELINE_LAYOUT) | |
| VK_OBJECT_TYPE_GETTER(VkQueryPool, VK_OBJECT_TYPE_QUERY_POOL) | |
| VK_OBJECT_TYPE_GETTER(VkSampler, VK_OBJECT_TYPE_SAMPLER) | |
| VK_OBJECT_TYPE_GETTER(VkShaderModule, VK_OBJECT_TYPE_SHADER_MODULE) | |
| VK_OBJECT_TYPE_GETTER(VkImage, VK_OBJECT_TYPE_IMAGE) | |
| VK_OBJECT_TYPE_GETTER(VkImageView, VK_OBJECT_TYPE_IMAGE_VIEW) | |
| VK_OBJECT_TYPE_GETTER(VkBufferView, VK_OBJECT_TYPE_BUFFER_VIEW) | |
| #undef VK_OBJECT_TYPE_GETTER | |
| uint32_t ToPushConstantBytes(const ImmediateMask& immediates) { | |
| return static_cast<uint32_t>(immediates.count()) * kImmediateElementByteSize; | |
| } | |
| uint32_t AttachmentCount(const ColorAttachmentMask& mask) { | |
| return static_cast<uint32_t>(mask.count()); | |
| } | |
| VkCompareOp ToVulkanCompareOp(wgpu::CompareFunction op) { | |
| switch (op) { | |
| case wgpu::CompareFunction::Never: | |
| return VK_COMPARE_OP_NEVER; | |
| case wgpu::CompareFunction::Less: | |
| return VK_COMPARE_OP_LESS; | |
| case wgpu::CompareFunction::LessEqual: | |
| return VK_COMPARE_OP_LESS_OR_EQUAL; | |
| case wgpu::CompareFunction::Greater: | |
| return VK_COMPARE_OP_GREATER; | |
| case wgpu::CompareFunction::GreaterEqual: | |
| return VK_COMPARE_OP_GREATER_OR_EQUAL; | |
| case wgpu::CompareFunction::Equal: | |
| return VK_COMPARE_OP_EQUAL; | |
| case wgpu::CompareFunction::NotEqual: | |
| return VK_COMPARE_OP_NOT_EQUAL; | |
| case wgpu::CompareFunction::Always: | |
| return VK_COMPARE_OP_ALWAYS; | |
| case wgpu::CompareFunction::Undefined: | |
| break; | |
| } | |
| DAWN_UNREACHABLE(); | |
| } | |
| VkFilter ToVulkanSamplerFilter(wgpu::FilterMode filter) { | |
| switch (filter) { | |
| case wgpu::FilterMode::Linear: | |
| return VK_FILTER_LINEAR; | |
| case wgpu::FilterMode::Nearest: | |
| return VK_FILTER_NEAREST; | |
| case wgpu::FilterMode::Undefined: | |
| break; | |
| } | |
| DAWN_UNREACHABLE(); | |
| } | |
| // Convert Dawn texture aspects to Vulkan texture aspect flags | |
| VkImageAspectFlags VulkanAspectMask(const Aspect& aspects) { | |
| VkImageAspectFlags flags = 0; | |
| for (Aspect aspect : IterateEnumMask(aspects)) { | |
| switch (aspect) { | |
| case Aspect::Color: | |
| flags |= VK_IMAGE_ASPECT_COLOR_BIT; | |
| break; | |
| case Aspect::Depth: | |
| flags |= VK_IMAGE_ASPECT_DEPTH_BIT; | |
| break; | |
| case Aspect::Stencil: | |
| flags |= VK_IMAGE_ASPECT_STENCIL_BIT; | |
| break; | |
| case Aspect::CombinedDepthStencil: | |
| flags |= VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; | |
| break; | |
| case Aspect::Plane0: | |
| flags |= VK_IMAGE_ASPECT_PLANE_0_BIT; | |
| break; | |
| case Aspect::Plane1: | |
| flags |= VK_IMAGE_ASPECT_PLANE_1_BIT; | |
| break; | |
| case Aspect::Plane2: | |
| flags |= VK_IMAGE_ASPECT_PLANE_2_BIT; | |
| break; | |
| case Aspect::None: | |
| DAWN_UNREACHABLE(); | |
| } | |
| } | |
| return flags; | |
| } | |
| VkShaderStageFlags VulkanShaderStages(wgpu::ShaderStage stages) { | |
| VkShaderStageFlags flags = 0; | |
| if (stages & wgpu::ShaderStage::Vertex) { | |
| flags |= VK_SHADER_STAGE_VERTEX_BIT; | |
| } | |
| if (stages & wgpu::ShaderStage::Fragment) { | |
| flags |= VK_SHADER_STAGE_FRAGMENT_BIT; | |
| } | |
| if (stages & wgpu::ShaderStage::Compute) { | |
| flags |= VK_SHADER_STAGE_COMPUTE_BIT; | |
| } | |
| return flags; | |
| } | |
| VkShaderStageFlagBits VulkanShaderStage(SingleShaderStage stage) { | |
| return static_cast<VkShaderStageFlagBits>(VulkanShaderStages(StageBit(stage))); | |
| } | |
| VkAttachmentLoadOp VulkanAttachmentLoadOp(wgpu::LoadOp op) { | |
| switch (op) { | |
| case wgpu::LoadOp::Load: | |
| return VK_ATTACHMENT_LOAD_OP_LOAD; | |
| case wgpu::LoadOp::Clear: | |
| return VK_ATTACHMENT_LOAD_OP_CLEAR; | |
| case wgpu::LoadOp::ExpandResolveTexture: | |
| return VK_ATTACHMENT_LOAD_OP_DONT_CARE; | |
| case wgpu::LoadOp::Undefined: | |
| DAWN_UNREACHABLE(); | |
| return VK_ATTACHMENT_LOAD_OP_DONT_CARE; | |
| } | |
| DAWN_UNREACHABLE(); | |
| } | |
| VkAttachmentStoreOp VulkanAttachmentStoreOp(wgpu::StoreOp op) { | |
| // TODO(crbug.com/dawn/485): return STORE_OP_STORE_NONE_QCOM if the device has required | |
| // extension. | |
| switch (op) { | |
| case wgpu::StoreOp::Store: | |
| return VK_ATTACHMENT_STORE_OP_STORE; | |
| case wgpu::StoreOp::Discard: | |
| return VK_ATTACHMENT_STORE_OP_DONT_CARE; | |
| case wgpu::StoreOp::Undefined: | |
| DAWN_UNREACHABLE(); | |
| return VK_ATTACHMENT_STORE_OP_DONT_CARE; | |
| } | |
| DAWN_UNREACHABLE(); | |
| } | |
| // Vulkan SPEC requires the source/destination region specified by each element of | |
| // pRegions must be a region that is contained within srcImage/dstImage. Here the size of | |
| // the image refers to the virtual size, while Dawn validates texture copy extent with the | |
| // physical size, so we need to re-calculate the texture copy extent to ensure it should fit | |
| // in the virtual size of the subresource. | |
| TexelExtent3D ComputeTextureCopyExtent(const TextureCopy& textureCopy, | |
| const TexelExtent3D& copySize) { | |
| TexelExtent3D validTextureCopyExtent = copySize; | |
| const TextureBase* texture = textureCopy.texture.Get(); | |
| TexelExtent3D virtualSizeAtLevel = | |
| texture->GetMipLevelSingleSubresourceVirtualSize(textureCopy.mipLevel, textureCopy.aspect); | |
| DAWN_ASSERT(textureCopy.origin.x <= virtualSizeAtLevel.width); | |
| DAWN_ASSERT(textureCopy.origin.y <= virtualSizeAtLevel.height); | |
| if (copySize.width > virtualSizeAtLevel.width - textureCopy.origin.x) { | |
| DAWN_ASSERT(texture->GetFormat().isCompressed); | |
| validTextureCopyExtent.width = virtualSizeAtLevel.width - textureCopy.origin.x; | |
| } | |
| if (copySize.height > virtualSizeAtLevel.height - textureCopy.origin.y) { | |
| DAWN_ASSERT(texture->GetFormat().isCompressed); | |
| validTextureCopyExtent.height = virtualSizeAtLevel.height - textureCopy.origin.y; | |
| } | |
| return validTextureCopyExtent; | |
| } | |
| VkBufferImageCopy ComputeBufferImageCopyRegion(const TexelCopyBufferLayout& dataLayout, | |
| const TextureCopy& textureCopy, | |
| const BlockExtent3D& copySize) { | |
| const TypedTexelBlockInfo& blockInfo = GetBlockInfo(textureCopy); | |
| BufferCopy bufferCopy; | |
| bufferCopy.offset = dataLayout.offset; | |
| bufferCopy.rowsPerImage = BlockCount{dataLayout.rowsPerImage}; | |
| bufferCopy.blocksPerRow = blockInfo.BytesToBlocks(dataLayout.bytesPerRow); | |
| return ComputeBufferImageCopyRegion(bufferCopy, textureCopy, copySize); | |
| } | |
| VkBufferImageCopy ComputeBufferImageCopyRegion(const BufferCopy& bufferCopy, | |
| const TextureCopy& textureCopy, | |
| const BlockExtent3D& copySize) { | |
| VkBufferImageCopy region; | |
| region.bufferOffset = bufferCopy.offset; | |
| const TypedTexelBlockInfo& blockInfo = GetBlockInfo(textureCopy); | |
| TexelExtent3D copySizeTexels = blockInfo.ToTexel(copySize); | |
| // In Vulkan the row length is in texels while it is in blocks for Dawn | |
| region.bufferRowLength = | |
| dchecked_cast<uint32_t>(blockInfo.ToTexelWidth(bufferCopy.blocksPerRow)); | |
| region.bufferImageHeight = | |
| dchecked_cast<uint32_t>(blockInfo.ToTexelHeight(bufferCopy.rowsPerImage)); | |
| region.imageSubresource.aspectMask = VulkanAspectMask(textureCopy.aspect); | |
| region.imageSubresource.mipLevel = textureCopy.mipLevel; | |
| switch (textureCopy.texture->GetDimension()) { | |
| case wgpu::TextureDimension::Undefined: | |
| DAWN_UNREACHABLE(); | |
| case wgpu::TextureDimension::e1D: | |
| DAWN_ASSERT(textureCopy.origin.z == TexelCount{0u} && | |
| copySizeTexels.depthOrArrayLayers == TexelCount{1u}); | |
| region.imageOffset.x = dchecked_cast<int32_t>(textureCopy.origin.x); | |
| region.imageOffset.y = 0; | |
| region.imageOffset.z = 0; | |
| region.imageSubresource.baseArrayLayer = 0; | |
| region.imageSubresource.layerCount = 1; | |
| DAWN_ASSERT(!textureCopy.texture->GetFormat().isCompressed); | |
| region.imageExtent.width = dchecked_cast<uint32_t>(copySizeTexels.width); | |
| region.imageExtent.height = 1; | |
| region.imageExtent.depth = 1; | |
| break; | |
| case wgpu::TextureDimension::e2D: { | |
| region.imageOffset.x = dchecked_cast<int32_t>(textureCopy.origin.x); | |
| region.imageOffset.y = dchecked_cast<int32_t>(textureCopy.origin.y); | |
| region.imageOffset.z = 0; | |
| region.imageSubresource.baseArrayLayer = dchecked_cast<uint32_t>(textureCopy.origin.z); | |
| region.imageSubresource.layerCount = | |
| dchecked_cast<uint32_t>(copySizeTexels.depthOrArrayLayers); | |
| TexelExtent3D imageExtent = | |
| ComputeTextureCopyExtent(textureCopy, copySizeTexels.ToExtent3D()); | |
| region.imageExtent.width = dchecked_cast<uint32_t>(imageExtent.width); | |
| region.imageExtent.height = dchecked_cast<uint32_t>(imageExtent.height); | |
| region.imageExtent.depth = 1; | |
| break; | |
| } | |
| case wgpu::TextureDimension::e3D: { | |
| region.imageOffset.x = dchecked_cast<int32_t>(textureCopy.origin.x); | |
| region.imageOffset.y = dchecked_cast<int32_t>(textureCopy.origin.y); | |
| region.imageOffset.z = dchecked_cast<int32_t>(textureCopy.origin.z); | |
| region.imageSubresource.baseArrayLayer = 0; | |
| region.imageSubresource.layerCount = 1; | |
| TexelExtent3D imageExtent = | |
| ComputeTextureCopyExtent(textureCopy, copySizeTexels.ToExtent3D()); | |
| region.imageExtent.width = dchecked_cast<uint32_t>(imageExtent.width); | |
| region.imageExtent.height = dchecked_cast<uint32_t>(imageExtent.height); | |
| region.imageExtent.depth = dchecked_cast<uint32_t>(copySizeTexels.depthOrArrayLayers); | |
| break; | |
| } | |
| } | |
| return region; | |
| } | |
| void SetDebugNameInternal(Device* device, | |
| VkObjectType objectType, | |
| uint64_t objectHandle, | |
| const char* prefix, | |
| std::string_view label) { | |
| if (!device->IsToggleEnabled(Toggle::UseUserDefinedLabelsInBackend)) { | |
| return; | |
| } | |
| if (!objectHandle || !device->GetVkDevice()) { | |
| return; | |
| } | |
| if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) { | |
| VkDebugUtilsObjectNameInfoEXT objectNameInfo; | |
| objectNameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; | |
| objectNameInfo.pNext = nullptr; | |
| objectNameInfo.objectType = objectType; | |
| objectNameInfo.objectHandle = objectHandle; | |
| std::ostringstream objectNameStream; | |
| // Prefix with the device's message ID so that if this label appears in a validation | |
| // message it can be parsed out and the message can be associated with the right device. | |
| objectNameStream << device->GetDebugPrefix() << kDeviceDebugSeparator << prefix; | |
| if (!label.empty()) { | |
| objectNameStream << "_" << label; | |
| } | |
| std::string objectName = objectNameStream.str(); | |
| objectNameInfo.pObjectName = objectName.c_str(); | |
| device->fn.SetDebugUtilsObjectNameEXT(device->GetVkDevice(), &objectNameInfo); | |
| } | |
| } | |
| std::string GetNextDeviceDebugPrefix() { | |
| static uint64_t nextDeviceDebugId = 1; | |
| std::ostringstream objectName; | |
| objectName << kDeviceDebugPrefix << nextDeviceDebugId++; | |
| return objectName.str(); | |
| } | |
| std::string GetDeviceDebugPrefixFromDebugName(const char* debugName) { | |
| if (debugName == nullptr) { | |
| return {}; | |
| } | |
| if (DAWN_UNSAFE_TODO(strncmp(debugName, kDeviceDebugPrefix, sizeof(kDeviceDebugPrefix) - 1)) != | |
| 0) { | |
| return {}; | |
| } | |
| const char* separator = | |
| DAWN_UNSAFE_TODO(strstr(debugName + sizeof(kDeviceDebugPrefix), kDeviceDebugSeparator)); | |
| if (separator == nullptr) { | |
| return {}; | |
| } | |
| size_t length = sign_cast(separator - debugName); | |
| return std::string(debugName, length); | |
| } | |
| std::string FormatAPIVersion(uint32_t version) { | |
| std::ostringstream versionString; | |
| versionString << VK_API_VERSION_MAJOR(version) << "." << VK_API_VERSION_MINOR(version) << "." | |
| << VK_API_VERSION_PATCH(version); | |
| return versionString.str(); | |
| } | |
| std::vector<VkDrmFormatModifierPropertiesEXT> GetFormatModifierProps( | |
| const VulkanFunctions& fn, | |
| VkPhysicalDevice vkPhysicalDevice, | |
| VkFormat format) { | |
| VkFormatProperties2 formatProps = {}; | |
| formatProps.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; | |
| PNextChainBuilder formatPropsChain(&formatProps); | |
| // Obtain the list of Linux DRM format modifiers compatible with a VkFormat | |
| VkDrmFormatModifierPropertiesListEXT formatModifierPropsList = {}; | |
| formatModifierPropsList.drmFormatModifierCount = 0; | |
| formatModifierPropsList.pDrmFormatModifierProperties = nullptr; | |
| formatPropsChain.Add(&formatModifierPropsList, | |
| VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT); | |
| fn.GetPhysicalDeviceFormatProperties2(vkPhysicalDevice, format, &formatProps); | |
| const uint32_t modifierCount = formatModifierPropsList.drmFormatModifierCount; | |
| std::vector<VkDrmFormatModifierPropertiesEXT> formatModifierPropsVector; | |
| formatModifierPropsVector.resize(modifierCount); | |
| formatModifierPropsList.pDrmFormatModifierProperties = formatModifierPropsVector.data(); | |
| fn.GetPhysicalDeviceFormatProperties2(vkPhysicalDevice, format, &formatProps); | |
| return formatModifierPropsVector; | |
| } | |
| ResultOrError<VkDrmFormatModifierPropertiesEXT> GetFormatModifierProps( | |
| const VulkanFunctions& fn, | |
| VkPhysicalDevice vkPhysicalDevice, | |
| VkFormat format, | |
| uint64_t modifier) { | |
| std::vector<VkDrmFormatModifierPropertiesEXT> formatModifierPropsVector = | |
| GetFormatModifierProps(fn, vkPhysicalDevice, format); | |
| // Find the modifier props that match the modifier, and return them. | |
| for (const auto& props : formatModifierPropsVector) { | |
| if (props.drmFormatModifier == modifier) { | |
| return VkDrmFormatModifierPropertiesEXT{props}; | |
| } | |
| } | |
| return DAWN_VALIDATION_ERROR("DRM format modifier %u not supported.", modifier); | |
| } | |
| ResultOrError<VkSamplerYcbcrConversion> CreateSamplerYCbCrConversion( | |
| const Device* device, | |
| const YCbCrVkDescriptor& yCbCrDescriptor) { | |
| uint64_t externalFormat = yCbCrDescriptor.externalFormat; | |
| VkFormat vulkanFormat = static_cast<VkFormat>(yCbCrDescriptor.vkFormat); | |
| DAWN_ASSERT(externalFormat != 0 || vulkanFormat != VK_FORMAT_UNDEFINED); | |
| VkComponentMapping vulkanComponent; | |
| vulkanComponent.r = static_cast<VkComponentSwizzle>(yCbCrDescriptor.vkComponentSwizzleRed); | |
| vulkanComponent.g = static_cast<VkComponentSwizzle>(yCbCrDescriptor.vkComponentSwizzleGreen); | |
| vulkanComponent.b = static_cast<VkComponentSwizzle>(yCbCrDescriptor.vkComponentSwizzleBlue); | |
| vulkanComponent.a = static_cast<VkComponentSwizzle>(yCbCrDescriptor.vkComponentSwizzleAlpha); | |
| VkSamplerYcbcrConversionCreateInfo vulkanYCbCrCreateInfo; | |
| vulkanYCbCrCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO; | |
| vulkanYCbCrCreateInfo.pNext = nullptr; | |
| vulkanYCbCrCreateInfo.format = vulkanFormat; | |
| vulkanYCbCrCreateInfo.ycbcrModel = | |
| static_cast<VkSamplerYcbcrModelConversion>(yCbCrDescriptor.vkYCbCrModel); | |
| vulkanYCbCrCreateInfo.ycbcrRange = | |
| static_cast<VkSamplerYcbcrRange>(yCbCrDescriptor.vkYCbCrRange); | |
| vulkanYCbCrCreateInfo.components = vulkanComponent; | |
| vulkanYCbCrCreateInfo.xChromaOffset = | |
| static_cast<VkChromaLocation>(yCbCrDescriptor.vkXChromaOffset); | |
| vulkanYCbCrCreateInfo.yChromaOffset = | |
| static_cast<VkChromaLocation>(yCbCrDescriptor.vkYChromaOffset); | |
| vulkanYCbCrCreateInfo.chromaFilter = ToVulkanSamplerFilter(yCbCrDescriptor.vkChromaFilter); | |
| vulkanYCbCrCreateInfo.forceExplicitReconstruction = | |
| static_cast<VkBool32>(yCbCrDescriptor.forceExplicitReconstruction); | |
| // VUID-VkSamplerYcbcrConversionCreateInfo-chromaFilter-01657 | |
| // Adjust linear filter to nearest if the format doesn't support linear. This is to support | |
| // samplers created with YCrCb conversion info directly, which can't easily validate against the | |
| // driver information at creation time. | |
| if (vulkanFormat != VK_FORMAT_UNDEFINED && | |
| vulkanYCbCrCreateInfo.chromaFilter == VK_FILTER_LINEAR) { | |
| VkPhysicalDevice vkPhysicalDevice = | |
| ToBackend(device->GetPhysicalDevice())->GetVkPhysicalDevice(); | |
| VkFormatProperties formatProperties; | |
| device->fn.GetPhysicalDeviceFormatProperties(vkPhysicalDevice, vulkanFormat, | |
| &formatProperties); | |
| bool supportsLinear = IsSubset( | |
| VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT, | |
| formatProperties.optimalTilingFeatures | formatProperties.linearTilingFeatures); | |
| if (!supportsLinear) { | |
| vulkanYCbCrCreateInfo.chromaFilter = VK_FILTER_NEAREST; | |
| } | |
| } | |
| #if DAWN_PLATFORM_IS(ANDROID) | |
| VkExternalFormatANDROID vulkanExternalFormat; | |
| // Chain VkExternalFormatANDROID only if needed. | |
| if (externalFormat != 0) { | |
| vulkanExternalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID; | |
| vulkanExternalFormat.pNext = nullptr; | |
| vulkanExternalFormat.externalFormat = externalFormat; | |
| vulkanYCbCrCreateInfo.pNext = &vulkanExternalFormat; | |
| } | |
| #endif // DAWN_PLATFORM_IS(ANDROID) | |
| VkSamplerYcbcrConversion samplerYCbCrConversion = VK_NULL_HANDLE; | |
| DAWN_TRY(CheckVkSuccess( | |
| device->fn.CreateSamplerYcbcrConversion(device->GetVkDevice(), &vulkanYCbCrCreateInfo, | |
| nullptr, &*samplerYCbCrConversion), | |
| "CreateSamplerYcbcrConversion")); | |
| return samplerYCbCrConversion; | |
| } | |
| } // namespace dawn::native::vulkan |