diff --git a/docs/features.txt b/docs/features.txt index 0e17ae25f6e..f262b238a8b 100644 --- a/docs/features.txt +++ b/docs/features.txt @@ -450,7 +450,7 @@ Vulkan 1.1 -- all DONE: anv, lvp, nvk, panvk/v10+, radv, tu, vn VK_KHR_maintenance3 DONE (anv, dzn, hasvk, lvp, nvk, panvk, radv, tu, v3dv, vn) VK_KHR_multiview DONE (anv, dzn, hasvk, lvp, nvk, panvk/v10+, radv, tu, v3dv, vn) VK_KHR_relaxed_block_layout DONE (anv, dzn, hasvk, lvp, nvk, panvk, radv, tu, v3dv, vn) - VK_KHR_sampler_ycbcr_conversion DONE (anv, hasvk, lvp, nvk, radv, tu, v3dv, vn) + VK_KHR_sampler_ycbcr_conversion DONE (anv, hasvk, lvp, nvk, panvk/v10+, radv, tu, v3dv, vn) VK_KHR_shader_draw_parameters DONE (anv, dzn, hasvk, lvp, nvk, radv, tu, vn, panvk) VK_KHR_storage_buffer_storage_class DONE (anv, dzn, hasvk, lvp, nvk, panvk, radv, tu, v3dv, vn) VK_KHR_variable_pointers DONE (anv, hasvk, lvp, nvk, panvk, radv, tu, v3dv, vn) @@ -507,7 +507,7 @@ Vulkan 1.3 -- all DONE: anv, lvp, nvk, radv, tu, vn, v3dv VK_EXT_texel_buffer_alignment DONE (anv, hasvk, lvp, nvk, pvr, radv, tu, v3dv, vn) VK_EXT_texture_compression_astc_hdr DONE (vn) VK_EXT_tooling_info DONE (anv, hasvk, nvk, panvk, pvr, radv, tu, v3dv, vn) - VK_EXT_ycbcr_2plane_444_formats DONE (anv, lvp, nvk, vn) + VK_EXT_ycbcr_2plane_444_formats DONE (anv, lvp, nvk, panvk/v10+, vn) Vulkan 1.4 -- all DONE: anv, lvp, nvk, radv/gfx8+, tu/a7xx+ @@ -655,7 +655,7 @@ Khronos extensions that are not part of any Vulkan version: VK_EXT_transform_feedback DONE (anv, hasvk, lvp, nvk, radv, tu, vn) VK_EXT_vertex_attribute_divisor DONE (anv, dzn, hasvk, lvp, nvk, radv, tu, v3dv, vn) VK_EXT_vertex_input_dynamic_state DONE (anv, lvp, nvk, radv, tu, vn) - VK_EXT_ycbcr_image_arrays DONE (anv, hasvk, lvp, nvk, radv) + VK_EXT_ycbcr_image_arrays DONE (anv, hasvk, lvp, nvk, panvk/v10+, radv) VK_ANDROID_external_memory_android_hardware_buffer DONE (anv, radv, tu, vn) VK_ANDROID_native_buffer DONE (anv, radv, tu, v3dv, vn) VK_GOOGLE_decorate_string DONE (anv, hasvk, lvp, nvk, panvk, radv, tu) diff --git a/docs/relnotes/new_features.txt b/docs/relnotes/new_features.txt index ee7c5a0798f..c787892830a 100644 --- a/docs/relnotes/new_features.txt +++ b/docs/relnotes/new_features.txt @@ -4,3 +4,6 @@ storageInputOutput16 on panvk VK_KHR_depth_stencil_resolve on panvk VK_KHR_separate_depth_stencil_layouts on panvk VK_EXT_separate_stencil_usage on panvk +samplerYcbcrConversion on panvk/v10+ +ycbcr2plane444Formats on panvk/v10+ +ycbcrImageArrays on panvk/v10+ diff --git a/src/panfrost/vulkan/panvk_image.c b/src/panfrost/vulkan/panvk_image.c index 6845399d1ad..749d67ff968 100644 --- a/src/panfrost/vulkan/panvk_image.c +++ b/src/panfrost/vulkan/panvk_image.c @@ -45,6 +45,32 @@ #include "vk_object.h" #include "vk_util.h" +static bool +can_be_aliased_to_yuv_plane(const struct panvk_image *image) +{ + if (!(image->vk.create_flags & VK_IMAGE_CREATE_ALIAS_BIT)) + return false; + + VkFormat format = image->vk.format; + + if (vk_format_is_depth_or_stencil(format) || + vk_format_is_alpha(format) || + vk_format_get_blockwidth(format) != 1 || + vk_format_get_blockheight(format) != 1) + return false; + + unsigned block_size = vk_format_get_blocksize(format); + + switch (block_size) { + case 1: + case 2: + case 4: + return true; + } + + return false; +} + static bool panvk_image_can_use_mod(struct panvk_image *image, uint64_t mod) { @@ -102,6 +128,15 @@ panvk_image_can_use_mod(struct panvk_image *image, uint64_t mod) } if (mod == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED) { + /* Multiplanar YUV with U-interleaving isn't supported by the HW. We + * also need to make sure images that can be aliased to planes of + * multi-planar images remain compatible with the aliased images, so + * don't allow U-interleaving for those either. + */ + if (vk_format_get_plane_count(image->vk.format) > 1 || + can_be_aliased_to_yuv_plane(image)) + return false; + /* If we're dealing with a compressed format that requires non-compressed * views we can't use U_INTERLEAVED tiling because the tiling is different * between compressed and non-compressed formats. If we wanted to support @@ -218,10 +253,12 @@ panvk_image_init_layouts(struct panvk_image *image, image->plane_count = 2; for (uint8_t plane = 0; plane < image->plane_count; plane++) { - VkFormat format = - (image->vk.format == VK_FORMAT_D32_SFLOAT_S8_UINT) ? - ((plane == 0) ? VK_FORMAT_D32_SFLOAT : VK_FORMAT_S8_UINT) : - image->vk.format; + VkFormat format; + + if (image->vk.format == VK_FORMAT_D32_SFLOAT_S8_UINT) + format = plane == 0 ? VK_FORMAT_D32_SFLOAT : VK_FORMAT_S8_UINT; + else + format = vk_format_get_plane_format(image->vk.format, plane); struct pan_image_explicit_layout plane_layout; if (explicit_info) @@ -233,8 +270,10 @@ panvk_image_init_layouts(struct panvk_image *image, image->planes[plane].layout = (struct pan_image_layout){ .format = vk_format_to_pipe_format(format), .dim = panvk_image_type_to_mali_tex_dim(image->vk.image_type), - .width = image->vk.extent.width, - .height = image->vk.extent.height, + .width = vk_format_get_plane_width(image->vk.format, plane, + image->vk.extent.width), + .height = vk_format_get_plane_height(image->vk.format, plane, + image->vk.extent.height), .depth = image->vk.extent.depth, .array_size = image->vk.array_layers, .nr_samples = image->vk.samples, diff --git a/src/panfrost/vulkan/panvk_meta.h b/src/panfrost/vulkan/panvk_meta.h index 6022cfbb275..4c3ff6936c1 100644 --- a/src/panfrost/vulkan/panvk_meta.h +++ b/src/panfrost/vulkan/panvk_meta.h @@ -50,12 +50,16 @@ panvk_meta_copy_get_image_properties(struct panvk_image *img) { uint64_t mod = img->vk.drm_format_mod; enum pipe_format pfmt = vk_format_to_pipe_format(img->vk.format); - unsigned blk_sz = util_format_get_blocksize(pfmt); struct vk_meta_copy_image_properties props; memset(&props, 0, sizeof(props)); + const struct vk_format_ycbcr_info *ycbcr_info = + vk_format_get_ycbcr_info(img->vk.format); if (drm_is_afbc(mod)) { - if (!vk_format_is_depth_or_stencil(img->vk.format)) { + if (ycbcr_info) { + for (uint32_t p = 0; p < ycbcr_info->n_planes; p++) + props.plane[p].view_format = ycbcr_info->planes[p].format; + } else if (!vk_format_is_depth_or_stencil(img->vk.format)) { props.color.view_format = img->vk.format; } else { switch (img->vk.format) { @@ -112,7 +116,17 @@ panvk_meta_copy_get_image_properties(struct panvk_image *img) assert(!"Invalid ZS format"); break; } + } else if (ycbcr_info) { + for (uint32_t p = 0; p < ycbcr_info->n_planes; p++) { + unsigned blk_sz = + vk_format_get_blocksize(ycbcr_info->planes[p].format); + + props.plane[p].view_format = + panvk_meta_get_uint_format_for_blk_size(blk_sz); + } } else { + unsigned blk_sz = util_format_get_blocksize(pfmt); + props.color.view_format = panvk_meta_get_uint_format_for_blk_size(blk_sz); } diff --git a/src/panfrost/vulkan/panvk_physical_device.c b/src/panfrost/vulkan/panvk_physical_device.c index e9abc9323ba..c68fc2beddd 100644 --- a/src/panfrost/vulkan/panvk_physical_device.c +++ b/src/panfrost/vulkan/panvk_physical_device.c @@ -209,6 +209,7 @@ get_device_extensions(const struct panvk_physical_device *device, .KHR_push_descriptor = true, .KHR_relaxed_block_layout = true, .KHR_sampler_mirror_clamp_to_edge = true, + .KHR_sampler_ycbcr_conversion = arch >= 10, .KHR_separate_depth_stencil_layouts = true, .KHR_shader_draw_parameters = true, .KHR_shader_expect_assume = true, @@ -250,6 +251,8 @@ get_device_extensions(const struct panvk_physical_device *device, .EXT_shader_module_identifier = true, .EXT_subgroup_size_control = arch >= 10, /* requires vk1.1 */ .EXT_tooling_info = true, + .EXT_ycbcr_2plane_444_formats = arch >= 10, + .EXT_ycbcr_image_arrays = arch >= 10, .GOOGLE_decorate_string = true, .GOOGLE_hlsl_functionality1 = true, .GOOGLE_user_type = true, @@ -298,7 +301,7 @@ get_features(const struct panvk_physical_device *device, .variablePointersStorageBuffer = true, .variablePointers = true, .protectedMemory = false, - .samplerYcbcrConversion = false, + .samplerYcbcrConversion = arch >= 10, .shaderDrawParameters = true, /* Vulkan 1.2 */ @@ -421,6 +424,12 @@ get_features(const struct panvk_physical_device *device, /* VK_EXT_shader_module_identifier */ .shaderModuleIdentifier = true, + + /* VK_EXT_ycbcr_2plane_444_formats */ + .ycbcr2plane444Formats = arch >= 10, + + /* VK_EXT_ycbcr_image_arrays */ + .ycbcrImageArrays = arch >= 10, }; } @@ -1179,8 +1188,8 @@ format_is_supported(struct panvk_physical_device *physical_device, } static VkFormatFeatureFlags -get_image_format_features(struct panvk_physical_device *physical_device, - VkFormat format) +get_image_plane_format_features(struct panvk_physical_device *physical_device, + VkFormat format) { VkFormatFeatureFlags features = 0; enum pipe_format pfmt = vk_format_to_pipe_format(format); @@ -1193,9 +1202,7 @@ get_image_format_features(struct panvk_physical_device *physical_device, if (fmt.bind & PAN_BIND_SAMPLER_VIEW) { features |= VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT | - VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | - VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT | - VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT; + VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT; if (arch >= 10) features |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT; @@ -1231,6 +1238,74 @@ get_image_format_features(struct panvk_physical_device *physical_device, return features; } +static VkFormatFeatureFlags +get_image_format_features(struct panvk_physical_device *physical_device, + VkFormat format) +{ + const struct vk_format_ycbcr_info *ycbcr_info = + vk_format_get_ycbcr_info(format); + const unsigned arch = pan_arch(physical_device->kmod.props.gpu_prod_id); + + /* TODO: Bifrost YCbCr support */ + if (ycbcr_info && arch <= 7) + return 0; + + if (ycbcr_info == NULL) + return get_image_plane_format_features(physical_device, format); + + if (unsupported_yuv_format(vk_format_to_pipe_format(format))) + return 0; + + /* For multi-plane, we get the feature flags of each plane separately, + * then take their intersection as the overall format feature flags + */ + VkFormatFeatureFlags features = ~0u; + bool cosited_chroma = false; + for (uint8_t plane = 0; plane < ycbcr_info->n_planes; plane++) { + const struct vk_format_ycbcr_plane *plane_info = + &ycbcr_info->planes[plane]; + features &= + get_image_plane_format_features(physical_device, plane_info->format); + if (plane_info->denominator_scales[0] > 1 || + plane_info->denominator_scales[1] > 1) + cosited_chroma = true; + } + if (features == 0) + return 0; + + /* Uh... We really should be able to sample from YCbCr */ + assert(features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); + assert(features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT); + + /* Siting is handled in the YCbCr lowering pass. */ + features |= VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT; + if (cosited_chroma) + features |= VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT; + + /* These aren't allowed for YCbCr formats */ + features &= ~(VK_FORMAT_FEATURE_BLIT_SRC_BIT | + VK_FORMAT_FEATURE_BLIT_DST_BIT | + VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | + VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | + VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT); + + /* This is supported on all YCbCr formats */ + features |= + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT; + + if (ycbcr_info->n_planes > 1) { + /* DISJOINT_BIT implies that each plane has its own separate binding, + * while SEPARATE_RECONSTRUCTION_FILTER_BIT implies that luma and chroma + * each have their own, separate filters, so these two bits make sense + * for multi-planar formats only. + */ + features |= VK_FORMAT_FEATURE_DISJOINT_BIT | + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT; + } + + return features; +} + static VkFormatFeatureFlags get_buffer_format_features(struct panvk_physical_device *physical_device, VkFormat format) @@ -1316,15 +1391,12 @@ get_image_format_properties(struct panvk_physical_device *physical_device, VkImageUsageFlags stencil_usage = stencil_usage_info ? stencil_usage_info->stencilUsage : info->usage; VkImageUsageFlags all_usage = info->usage | stencil_usage; + const struct vk_format_ycbcr_info *ycbcr_info = + vk_format_get_ycbcr_info(info->format); switch (info->tiling) { case VK_IMAGE_TILING_LINEAR: - format_feature_flags = - get_image_format_features(physical_device, info->format); - break; case VK_IMAGE_TILING_OPTIMAL: - format_feature_flags = - get_image_format_features(physical_device, info->format); break; case VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT: { const VkPhysicalDeviceImageDrmFormatModifierInfoEXT *mod_info = @@ -1340,18 +1412,35 @@ get_image_format_properties(struct panvk_physical_device *physical_device, */ if (util_format_is_depth_or_stencil(format)) goto unsupported; - - format_feature_flags = - get_image_format_features(physical_device, info->format); break; } default: unreachable("bad VkPhysicalDeviceImageFormatInfo2"); } + /* For the purposes of these checks, we don't care about all the extra + * YCbCr features and we just want the intersection of features available + * to all planes of the given format. + */ + if (ycbcr_info == NULL) { + format_feature_flags = + get_image_format_features(physical_device, info->format); + } else { + format_feature_flags = ~0u; + assert(ycbcr_info->n_planes > 0); + for (uint8_t plane = 0; plane < ycbcr_info->n_planes; plane++) { + const VkFormat plane_format = ycbcr_info->planes[plane].format; + format_feature_flags &= + get_image_format_features(physical_device, plane_format); + } + } + if (format_feature_flags == 0) goto unsupported; + if (ycbcr_info && info->type != VK_IMAGE_TYPE_2D) + goto unsupported; + switch (info->type) { default: unreachable("bad vkimage type"); @@ -1378,8 +1467,11 @@ get_image_format_properties(struct panvk_physical_device *physical_device, break; } + if (ycbcr_info) + maxMipLevels = 1; + if (info->tiling == VK_IMAGE_TILING_OPTIMAL && - info->type == VK_IMAGE_TYPE_2D && + info->type == VK_IMAGE_TYPE_2D && ycbcr_info == NULL && (format_feature_flags & (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) && @@ -1612,6 +1704,43 @@ panvk_GetPhysicalDeviceImageFormatProperties2( } } + const struct vk_format_ycbcr_info *ycbcr_info = + vk_format_get_ycbcr_info(base_info->format); + const unsigned plane_count = + vk_format_get_plane_count(base_info->format); + + /* From the Vulkan 1.3.259 spec, VkImageCreateInfo: + * + * VUID-VkImageCreateInfo-imageCreateFormatFeatures-02260 + * + * "If format is a multi-planar format, and if imageCreateFormatFeatures + * (as defined in Image Creation Limits) does not contain + * VK_FORMAT_FEATURE_DISJOINT_BIT, then flags must not contain + * VK_IMAGE_CREATE_DISJOINT_BIT" + * + * This is satisfied trivially because we support DISJOINT on all + * multi-plane formats. Also, + * + * VUID-VkImageCreateInfo-format-01577 + * + * "If format is not a multi-planar format, and flags does not include + * VK_IMAGE_CREATE_ALIAS_BIT, flags must not contain + * VK_IMAGE_CREATE_DISJOINT_BIT" + */ + if (plane_count == 1 && + !(base_info->flags & VK_IMAGE_CREATE_ALIAS_BIT) && + (base_info->flags & VK_IMAGE_CREATE_DISJOINT_BIT)) + goto fail; + + if (ycbcr_info && + ((base_info->flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) || + (base_info->flags & VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT))) + goto fail; + + if ((base_info->flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) && + (base_info->usage & VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT)) + goto fail; + if (ycbcr_props) ycbcr_props->combinedImageSamplerDescriptorCount = 1; diff --git a/src/panfrost/vulkan/panvk_vX_image_view.c b/src/panfrost/vulkan/panvk_vX_image_view.c index 23553d714cb..2cb72a1f66c 100644 --- a/src/panfrost/vulkan/panvk_vX_image_view.c +++ b/src/panfrost/vulkan/panvk_vX_image_view.c @@ -279,7 +279,10 @@ panvk_per_arch(CreateImageView)(VkDevice _device, .dim = panvk_view_type_to_mali_tex_dim(view->vk.view_type), .nr_samples = image->vk.samples, .first_level = view->vk.base_mip_level, - .last_level = view->vk.base_mip_level + view->vk.level_count - 1, + /* MIPmapping in YUV formats is not supported by the HW. */ + .last_level = vk_format_get_ycbcr_info(view->vk.format) + ? view->vk.base_mip_level + : view->vk.base_mip_level + view->vk.level_count - 1, .first_layer = view->vk.base_array_layer, .last_layer = view->vk.base_array_layer + view->vk.layer_count - 1, };