From bf9c84991fc1475011eb3d5912071436559df0f8 Mon Sep 17 00:00:00 2001 From: Matt Coster Date: Thu, 29 Jun 2023 11:47:53 +0100 Subject: [PATCH] pvr: Add support for custom border colors This includes EXT_custom_border_color and EXT_border_color_swizzle. Test coverage is: - dEQP-VK.pipeline.*.sampler.border_swizzle.* - dEQP-VK.pipeline.*.sampler.view_type.*.format.*.address_modes .*_clamp_to_border_custom_* Co-authored-by: Luigi Santivetti Signed-off-by: Matt Coster Signed-off-by: Luigi Santivetti Acked-by: Erik Faye-Lund Part-of: --- docs/features.txt | 4 +- src/imagination/vulkan/pvr_border.c | 243 ++++++++++++++++++++++++++- src/imagination/vulkan/pvr_border.h | 6 +- src/imagination/vulkan/pvr_common.h | 1 + src/imagination/vulkan/pvr_device.c | 27 ++- src/imagination/vulkan/pvr_formats.c | 25 +++ src/imagination/vulkan/pvr_formats.h | 3 + 7 files changed, 296 insertions(+), 13 deletions(-) diff --git a/docs/features.txt b/docs/features.txt index 51d905144fc..a00e64dc379 100644 --- a/docs/features.txt +++ b/docs/features.txt @@ -591,13 +591,13 @@ Khronos extensions that are not part of any Vulkan version: VK_EXT_attachment_feedback_loop_dynamic_state DONE (anv, lvp, radv, tu, vn) VK_EXT_attachment_feedback_loop_layout DONE (anv, hk, lvp, nvk, radv, tu, v3dv, vn) VK_EXT_blend_operation_advanced DONE (vn) - VK_EXT_border_color_swizzle DONE (anv, hasvk, hk, lvp, nvk, panvk, radv/gfx10+, tu, v3dv, vn) + VK_EXT_border_color_swizzle DONE (anv, hasvk, hk, lvp, nvk, panvk, pvr, radv/gfx10+, tu, v3dv, vn) VK_EXT_buffer_device_address DONE (anv, hasvk, hk, nvk, panvk, radv, vn) VK_EXT_calibrated_timestamps DONE (anv, hasvk, hk, nvk, panvk/v10+, lvp, radv, vn, tu/a750+) VK_EXT_color_write_enable DONE (anv, hasvk, hk, lvp, nvk, pvr, radv, tu, v3dv, vn) VK_EXT_conditional_rendering DONE (anv, hasvk, lvp, nvk, radv, tu, vn) VK_EXT_conservative_rasterization DONE (anv, nvk, radv, vn, tu/a7xx+) - VK_EXT_custom_border_color DONE (anv, hasvk, hk, lvp, nvk, panvk, radv, tu, v3dv, vn) + VK_EXT_custom_border_color DONE (anv, hasvk, hk, lvp, nvk, panvk, pvr, radv, tu, v3dv, vn) VK_EXT_debug_marker DONE (radv) VK_EXT_debug_report DONE (anv, dzn, hk, lvp, nvk, panvk, pvr, radv, tu, v3dv, vn) VK_EXT_debug_utils DONE (anv, dzn, hasvk, hk, lvp, nvk, panvk, pvr, radv, tu, v3dv, vn) diff --git a/src/imagination/vulkan/pvr_border.c b/src/imagination/vulkan/pvr_border.c index 2e1c6b62be2..483a095ed0c 100644 --- a/src/imagination/vulkan/pvr_border.c +++ b/src/imagination/vulkan/pvr_border.c @@ -126,6 +126,8 @@ pvr_border_color_table_alloc_entry(struct pvr_border_color_table *const table) if (!index--) return -1; + assert(index >= PVR_BORDER_COLOR_TABLE_NR_BUILTIN_ENTRIES); + BITSET_CLEAR(table->unused_entries, index); return index; @@ -167,6 +169,117 @@ pvr_border_color_table_fill_entry(struct pvr_border_color_table *const table, } } +/** Attempt to invert a swizzle. + * + * If @param swz contains multiple channels with the same swizzle, this + * operation will fail and return false. The @param dst should be preloaded + * with suitable defaults (@var PIPE_SWIZZLE_0 or @var PIPE_SWIZZLE_1) for + * channels with no source. + * + * For a given swizzle S, this function produces an inverse swizzle S' such + * that for a given input color C: + * + * C * S => C' + * C' * S' => C" + * + * The unswizzled color C" is a subset of the input color C, where channels not + * contained in C' (because they weren't included as outputs in S) are set to + * the defaults specified in S' as described above. + * + * @param swz The swizzle to invert + * @param dst Output + * @return true if the swizzle is invertible and the operation succeeded. + */ +static bool pvr_invert_swizzle(const unsigned char swz[4], unsigned char dst[4]) +{ + bool found[4] = { false }; + unsigned i, c; + + for (i = 0; i < 4; i++) { + c = swz[i]; + + if (c > PIPE_SWIZZLE_W) + continue; + + if (found[c]) + return false; + + dst[c] = i; + found[c] = true; + } + + return true; +} + +static inline void pvr_border_color_swizzle_to_tex_format( + union pipe_color_union *const color, + const enum pipe_format color_format, + const struct pvr_tex_format_description *const pvr_tex_fmt_desc, + bool is_int) +{ + const enum pipe_format tex_pipe_format = + is_int ? pvr_tex_fmt_desc->pipe_format_int + : pvr_tex_fmt_desc->pipe_format_float; + + const struct util_format_description *const color_format_desc = + util_format_description(color_format); + const struct util_format_description *const tex_format_desc = + util_format_description(tex_pipe_format); + + union pipe_color_union swizzled_color; + unsigned char composed_swizzle[4]; + unsigned char color_unswizzle[4] = { + PIPE_SWIZZLE_0, + PIPE_SWIZZLE_0, + PIPE_SWIZZLE_0, + PIPE_SWIZZLE_1, + }; + const unsigned char *tpu_swizzle; + + ASSERTED bool invert_succeeded; + + if (color_format_desc->format == tex_pipe_format) + return; + + /* Some format pairs (e.g. UNORM vs SRGB) fail the above test but still don't + * require a re-swizzle. + */ + if (memcmp(color_format_desc->swizzle, + tex_format_desc->swizzle, + sizeof(color_format_desc->swizzle)) == 0) { + return; + } + + mesa_logd("Mismatched border pipe formats: vk=%s, tex=%s", + color_format_desc->short_name, + tex_format_desc->short_name); + + tpu_swizzle = pvr_get_format_swizzle_for_tpu(color_format_desc); + + /* Any supported format for which this operation is necessary must have an + * invertible swizzle. + */ + invert_succeeded = pvr_invert_swizzle(tpu_swizzle, color_unswizzle); + assert(invert_succeeded); + + util_format_compose_swizzles(color_unswizzle, + tex_format_desc->swizzle, + composed_swizzle); + + mesa_logd("Applying swizzle: %u%u%u%u", + composed_swizzle[0], + composed_swizzle[1], + composed_swizzle[2], + composed_swizzle[3]); + + util_format_apply_color_swizzle(&swizzled_color, + color, + composed_swizzle, + is_int); + + *color = swizzled_color; +} + VkResult pvr_border_color_table_init(struct pvr_border_color_table *const table, struct pvr_device *const device) { @@ -227,9 +340,119 @@ void pvr_border_color_table_finish(struct pvr_border_color_table *const table, pvr_bo_free(device, table->table); } -VkResult pvr_border_color_table_get_or_create_entry( - UNUSED struct pvr_border_color_table *const table, +static inline void pvr_border_color_table_set_custom_entry( + struct pvr_border_color_table *const table, + const uint32_t index, + const VkFormat vk_format, + const union pipe_color_union *const color, + const bool is_int, + const struct pvr_device_info *const dev_info) +{ + struct pvr_border_color_table_entry *const entries = table->table->bo->map; + struct pvr_border_color_table_entry *const entry = &entries[index]; + + const enum pipe_format format = vk_format_to_pipe_format(vk_format); + uint32_t tex_format = pvr_get_tex_format(vk_format); + + assert(tex_format != ROGUE_TEXSTATE_FORMAT_INVALID); + + if (util_format_is_compressed(format)) { + const struct pvr_tex_format_compressed_description *const pvr_tex_fmt_desc = + pvr_get_tex_format_compressed_description(tex_format); + + pvr_border_color_table_pack_single_compressed( + &entry->compressed_values[tex_format], + color, + pvr_tex_fmt_desc, + is_int, + dev_info); + } else { + const struct pvr_tex_format_description *const pvr_tex_fmt_desc = + pvr_get_tex_format_description(tex_format); + union pipe_color_union swizzled_color = *color; + + if (util_format_is_depth_or_stencil(format)) { + VkImageAspectFlags aspect_mask; + + if (is_int) + aspect_mask = VK_IMAGE_ASPECT_STENCIL_BIT; + else + aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; + + /* Write the border color entry at the index of the texture + * format relative to the depth-only or stencil-only compoment + * associated with this Vulkan format. + */ + tex_format = pvr_get_tex_format_aspect(vk_format, aspect_mask); + assert(tex_format != ROGUE_TEXSTATE_FORMAT_INVALID); + } + + pvr_border_color_swizzle_to_tex_format(&swizzled_color, + format, + pvr_tex_fmt_desc, + is_int); + + pvr_border_color_table_pack_single(&entry->values[tex_format], + &swizzled_color, + pvr_tex_fmt_desc, + is_int, + dev_info); + } +} + +static VkResult pvr_border_color_table_create_custom_entry( + struct pvr_device *const device, const struct pvr_sampler *const sampler, + struct pvr_border_color_table *const table, + uint32_t *const index_out) +{ + const bool is_int = vk_border_color_is_int(sampler->vk.border_color); + const VkClearColorValue color = sampler->vk.border_color_value; + const VkFormat vk_format = sampler->vk.format; + const bool map_table = !table->table->bo->map; + VkResult result; + int32_t index; + + assert(vk_format != VK_FORMAT_UNDEFINED); + + index = pvr_border_color_table_alloc_entry(table); + if (index < 0) + goto err_out; + + if (map_table) { + result = pvr_bo_cpu_map_unchanged(device, table->table); + if (result != VK_SUCCESS) + goto err_free_entry; + } + + pvr_border_color_table_set_custom_entry( + table, + index, + vk_format, + (const union pipe_color_union *)&color, + is_int, + &device->pdevice->dev_info); + + if (map_table) + pvr_bo_cpu_unmap(device, table->table); + + *index_out = index; + + return VK_SUCCESS; + +err_free_entry: + pvr_border_color_table_free_entry(table, index); + +err_out: + return vk_errorf(sampler, + VK_ERROR_OUT_OF_DEVICE_MEMORY, + "Failed to allocate border color table entry"); +} + +VkResult pvr_border_color_table_get_or_create_entry( + struct pvr_device *const device, + const struct pvr_sampler *const sampler, + struct pvr_border_color_table *const table, uint32_t *const index_out) { const VkBorderColor vk_type = sampler->vk.border_color; @@ -239,6 +462,18 @@ VkResult pvr_border_color_table_get_or_create_entry( return VK_SUCCESS; } - pvr_finishme("VK_EXT_custom_border_color is currently unsupported."); - return vk_error(sampler, VK_ERROR_EXTENSION_NOT_PRESENT); + return pvr_border_color_table_create_custom_entry(device, + sampler, + table, + index_out); +} + +void pvr_border_color_table_release_entry( + struct pvr_border_color_table *const table, + const uint32_t index) +{ + if (index < PVR_BORDER_COLOR_TABLE_NR_BUILTIN_ENTRIES) + return; + + pvr_border_color_table_free_entry(table, index); } diff --git a/src/imagination/vulkan/pvr_border.h b/src/imagination/vulkan/pvr_border.h index a5bd3ee0a88..0cd82dd2f27 100644 --- a/src/imagination/vulkan/pvr_border.h +++ b/src/imagination/vulkan/pvr_border.h @@ -65,10 +65,14 @@ void pvr_border_color_table_finish(struct pvr_border_color_table *table, struct pvr_device *device); VkResult -pvr_border_color_table_get_or_create_entry(struct pvr_border_color_table *table, +pvr_border_color_table_get_or_create_entry(struct pvr_device *device, const struct pvr_sampler *sampler, + struct pvr_border_color_table *table, uint32_t *index_out); +void pvr_border_color_table_release_entry(struct pvr_border_color_table *table, + uint32_t index); + static inline bool pvr_border_color_table_is_index_valid( const struct pvr_border_color_table *const table, const uint32_t index) diff --git a/src/imagination/vulkan/pvr_common.h b/src/imagination/vulkan/pvr_common.h index bd4911af33d..9113b3c88a7 100644 --- a/src/imagination/vulkan/pvr_common.h +++ b/src/imagination/vulkan/pvr_common.h @@ -232,6 +232,7 @@ static_assert( struct pvr_sampler { struct vk_sampler vk; struct pvr_sampler_descriptor descriptor; + uint32_t border_color_table_index; }; struct pvr_descriptor_set_layout_binding { diff --git a/src/imagination/vulkan/pvr_device.c b/src/imagination/vulkan/pvr_device.c index f008d31815d..c1e61306da2 100644 --- a/src/imagination/vulkan/pvr_device.c +++ b/src/imagination/vulkan/pvr_device.c @@ -192,7 +192,9 @@ static void pvr_physical_device_get_supported_extensions( .KHR_uniform_buffer_standard_layout = true, .KHR_vertex_attribute_divisor = true, .KHR_zero_initialize_workgroup_memory = false, + .EXT_border_color_swizzle = true, .EXT_color_write_enable = true, + .EXT_custom_border_color = true, .EXT_depth_clamp_zero_one = true, .EXT_extended_dynamic_state = true, .EXT_external_memory_dma_buf = true, @@ -324,6 +326,13 @@ static void pvr_physical_device_get_supported_features( /* Vulkan 1.3 / VK_KHR_zero_initialize_workgroup_memory */ .shaderZeroInitializeWorkgroupMemory = false, + + /* VK_EXT_border_color_swizzle */ + .borderColorSwizzle = true, + .borderColorSwizzleFromImage = true, + + /* VK_EXT_custom_border_color */ + .customBorderColors = true, }; } @@ -562,6 +571,9 @@ static bool pvr_physical_device_get_properties( /* Vulkan 1.4 / VK_EXT_vertex_attribute_divisor / VK_KHR_vertex_attribute_divisor */ .maxVertexAttribDivisor = UINT32_MAX, .supportsNonZeroFirstInstance = true, + + /* VK_EXT_custom_border_color */ + .maxCustomBorderColorSamplers = PVR_BORDER_COLOR_TABLE_NR_CUSTOM_ENTRIES, }; if (PVR_HAS_FEATURE(dev_info, gpu_multicore_support)) { @@ -3139,7 +3151,6 @@ VkResult pvr_CreateSampler(VkDevice _device, VkSampler *pSampler) { PVR_FROM_HANDLE(pvr_device, device, _device); - uint32_t border_color_table_index; struct pvr_sampler *sampler; float lod_rounding_bias; VkFilter min_filter; @@ -3158,10 +3169,11 @@ VkResult pvr_CreateSampler(VkDevice _device, mag_filter = pCreateInfo->magFilter; min_filter = pCreateInfo->minFilter; - result = - pvr_border_color_table_get_or_create_entry(&device->border_color_table, - sampler, - &border_color_table_index); + result = pvr_border_color_table_get_or_create_entry( + device, + sampler, + &device->border_color_table, + &sampler->border_color_table_index); if (result != VK_SUCCESS) goto err_free_sampler; @@ -3256,7 +3268,7 @@ VkResult pvr_CreateSampler(VkDevice _device, word.maxlod = util_unsigned_fixed(CLAMP(max_lod, 0.0f, lod_clamp_max), ROGUE_TEXSTATE_CLAMP_FRACTIONAL_BITS); - word.bordercolor_index = border_color_table_index; + word.bordercolor_index = sampler->border_color_table_index; if (pCreateInfo->unnormalizedCoordinates) word.non_normalized_coords = true; @@ -3300,6 +3312,9 @@ void pvr_DestroySampler(VkDevice _device, if (!sampler) return; + pvr_border_color_table_release_entry(&device->border_color_table, + sampler->border_color_table_index); + vk_sampler_destroy(&device->vk, pAllocator, &sampler->vk); } diff --git a/src/imagination/vulkan/pvr_formats.c b/src/imagination/vulkan/pvr_formats.c index 91919c3f1bb..b1002c2edab 100644 --- a/src/imagination/vulkan/pvr_formats.c +++ b/src/imagination/vulkan/pvr_formats.c @@ -717,6 +717,31 @@ const uint8_t *pvr_get_format_swizzle(VkFormat vk_format) return vf->swizzle; } +/* For DS formats, hardware can only access either depth or stencil at once. + * It expects to find whichever one it requires in the given context in the + * first channel, whereas pipe formats swizzle depth into the first channel and + * stencil into the second. + */ +const uint8_t * +pvr_get_format_swizzle_for_tpu(const struct util_format_description *desc) +{ + const bool has_stencil = util_format_has_stencil(desc); + const bool has_depth = util_format_has_depth(desc); + + if (has_depth || has_stencil) { + static const uint8_t pvr_swizzle[4] = { + PIPE_SWIZZLE_X, + PIPE_SWIZZLE_NONE, + PIPE_SWIZZLE_NONE, + PIPE_SWIZZLE_NONE, + }; + + return pvr_swizzle; + } + + return desc->swizzle; +} + static VkFormatFeatureFlags2 pvr_get_buffer_format_features2(const struct pvr_format *pvr_format) { diff --git a/src/imagination/vulkan/pvr_formats.h b/src/imagination/vulkan/pvr_formats.h index a16ba3dff67..d8aec43b1a2 100644 --- a/src/imagination/vulkan/pvr_formats.h +++ b/src/imagination/vulkan/pvr_formats.h @@ -263,6 +263,9 @@ pvr_get_tex_format_compressed_description(uint32_t tex_format); desc; \ desc = NULL) +struct util_format_description; +const uint8_t * +pvr_get_format_swizzle_for_tpu(const struct util_format_description *desc); const uint8_t *pvr_get_format_swizzle(VkFormat vk_format); uint32_t pvr_get_tex_format(VkFormat vk_format); uint32_t pvr_get_tex_format_aspect(VkFormat vk_format,