/* * Copyright © 2023 Imagination Technologies Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #include "hwdef/rogue_hw_utils.h" #include "pvr_bo.h" #include "pvr_border.h" #include "pvr_device.h" #include "pvr_device_info.h" #include "pvr_formats.h" #include "pvr_macros.h" #include "pvr_physical_device.h" #include "pvr_sampler.h" #include "util/bitset.h" #include "util/format/u_format.h" #include "util/format/u_formats.h" #include "util/log.h" #include "util/macros.h" #include "vk_format.h" #include "vk_log.h" #include "vk_sampler.h" struct pvr_border_color_table_value { uint8_t value[16]; } PACKED; static_assert(sizeof(struct pvr_border_color_table_value) == 4 * sizeof(uint32_t), "struct pvr_border_color_table_value must be 4 x u32"); struct pvr_border_color_table_entry { struct pvr_border_color_table_value values[PVR_TEX_FORMAT_COUNT]; struct pvr_border_color_table_value compressed_values[PVR_TEX_FORMAT_COUNT]; } PACKED; /* FIXME: Replace all instances of uint32_t with ROGUE_TEXSTATE_FORMAT or * ROGUE_TEXSTATE_FORMAT_COMPRESSED after the pvr_common cleanup is complete. */ struct pvr_tex_format_description { enum pipe_format pipe_format_int; enum pipe_format pipe_format_float; }; struct pvr_tex_format_compressed_description { uint32_t tex_format; enum pipe_format pipe_format; uint32_t tex_format_simple; }; #define FORMAT(tex_fmt, pipe_fmt_int, pipe_fmt_float) \ [ROGUE_TEXSTATE_FORMAT_##tex_fmt] = { \ .desc = { \ .pipe_format_int = PIPE_FORMAT_##pipe_fmt_int, \ .pipe_format_float = PIPE_FORMAT_##pipe_fmt_float, \ }, \ .present = true, \ } static const struct pvr_tex_format_table_entry { struct pvr_tex_format_description desc; bool present; } pvr_tex_format_table[PVR_TEX_FORMAT_COUNT] = { /* 0 */ FORMAT(U8, R8_UINT, R8_UNORM), /* 1 */ FORMAT(S8, R8_SINT, R8_SNORM), /* 2 */ FORMAT(A4R4G4B4, A4R4G4B4_UINT, A4R4G4B4_UNORM), /* 4 */ FORMAT(A1R5G5B5, A1R5G5B5_UINT, B5G5R5A1_UNORM), /* 5 */ FORMAT(R5G6B5, R5G6B5_UINT, B5G6R5_UNORM), /* 7 */ FORMAT(U8U8, R8G8_UINT, R8G8_UNORM), /* 8 */ FORMAT(S8S8, R8G8_SINT, R8G8_SNORM), /* 9 */ FORMAT(U16, R16_UINT, R16_UNORM), /* 10 */ FORMAT(S16, R16_SINT, R16_SNORM), /* 11 */ FORMAT(F16, NONE, R16_FLOAT), /* 12 */ FORMAT(U8U8U8U8, R8G8B8A8_UINT, R8G8B8A8_UNORM), /* 13 */ FORMAT(S8S8S8S8, R8G8B8A8_SINT, R8G8B8A8_SNORM), /* 14 */ FORMAT(A2R10B10G10, R10G10B10A2_UINT, R10G10B10A2_UNORM), /* 15 */ FORMAT(U16U16, R16G16_UINT, R16G16_UNORM), /* 16 */ FORMAT(S16S16, R16G16_SINT, R16G16_SNORM), /* 17 */ FORMAT(F16F16, NONE, R16G16_FLOAT), /* 18 */ FORMAT(F32, NONE, R32_FLOAT), /* 21 */ FORMAT(X8U24, NONE, Z24X8_UNORM), /* 22 */ FORMAT(ST8U24, Z24_UNORM_S8_UINT, Z24_UNORM_S8_UINT), /* 23 */ FORMAT(U8X24, X24S8_UINT, NONE), /* 24 */ FORMAT(U32, R32_UINT, R32_UNORM), /* 25 */ FORMAT(S32, R32_SINT, R32_SNORM), /* 26 */ FORMAT(SE9995, NONE, R9G9B9E5_FLOAT), /* 28 */ FORMAT(F16F16F16F16, NONE, R16G16B16A16_FLOAT), /* 29 */ FORMAT(U16U16U16U16, R16G16B16A16_UINT, R16G16B16A16_UNORM), /* 30 */ FORMAT(S16S16S16S16, R16G16B16A16_SINT, R16G16B16A16_SNORM), /* 32 */ FORMAT(U16U16U16, R16G16B16_UINT, R16G16B16_UNORM), /* 33 */ FORMAT(S16S16S16, R16G16B16_SINT, R16G16B16_SNORM), /* 34 */ FORMAT(F32F32, NONE, R32G32_FLOAT), /* 35 */ FORMAT(U32U32, R32G32_UINT, R32G32_UNORM), /* 36 */ FORMAT(S32S32, R32G32_SINT, R32G32_SNORM), /* 37 */ FORMAT(X24U8F32, Z32_FLOAT_S8X24_UINT, Z32_FLOAT_S8X24_UINT), /* 38 */ FORMAT(X24X8F32, NONE, Z32_FLOAT_S8X24_UINT), /* 39 */ FORMAT(X24G8X32, X32_S8X24_UINT, NONE), /* 58 */ FORMAT(U8U8U8, R8G8B8_UINT, R8G8B8_UNORM), /* 61 */ FORMAT(F32F32F32F32, NONE, R32G32B32A32_FLOAT), /* 62 */ FORMAT(U32U32U32U32, R32G32B32A32_UINT, R32G32B32A32_UNORM), /* 63 */ FORMAT(S32S32S32S32, R32G32B32A32_SINT, R32G32B32A32_SNORM), /* 64 */ FORMAT(F32F32F32, NONE, R32G32B32_FLOAT), /* 65 */ FORMAT(U32U32U32, R32G32B32_UINT, R32G32B32_UNORM), /* 66 */ FORMAT(S32S32S32, R32G32B32_SINT, R32G32B32_SNORM), /* 88 */ FORMAT(F10F11F11, NONE, R11G11B10_FLOAT), }; #undef FORMAT #define FORMAT(tex_fmt, pipe_fmt, tex_fmt_simple) \ [ROGUE_TEXSTATE_FORMAT_COMPRESSED_##tex_fmt] = { \ .desc = { \ .tex_format = ROGUE_TEXSTATE_FORMAT_COMPRESSED_##tex_fmt, \ .pipe_format = PIPE_FORMAT_##pipe_fmt, \ .tex_format_simple = ROGUE_TEXSTATE_FORMAT_##tex_fmt_simple, \ }, \ .present = true, \ } static const struct pvr_tex_format_compressed_table_entry { struct pvr_tex_format_compressed_description desc; bool present; } pvr_tex_format_compressed_table[PVR_TEX_FORMAT_COUNT] = { /* 68 */ FORMAT(ETC2_RGB, ETC2_RGB8, U8U8U8U8), /* 69 */ FORMAT(ETC2A_RGBA, ETC2_RGBA8, U8U8U8U8), /* 70 */ FORMAT(ETC2_PUNCHTHROUGHA, ETC2_RGB8A1, U8U8U8U8), /* 71 */ FORMAT(EAC_R11_UNSIGNED, ETC2_R11_UNORM, U16U16U16U16), /* 72 */ FORMAT(EAC_R11_SIGNED, ETC2_R11_SNORM, S16S16S16S16), /* 73 */ FORMAT(EAC_RG11_UNSIGNED, ETC2_RG11_UNORM, U16U16U16U16), /* 74 */ FORMAT(EAC_RG11_SIGNED, ETC2_RG11_SNORM, S16S16S16S16), }; #undef FORMAT static inline bool tex_format_is_supported(const uint32_t tex_format) { return tex_format < ARRAY_SIZE(pvr_tex_format_table) && pvr_tex_format_table[tex_format].present; } static inline const struct pvr_tex_format_description * get_tex_format_description(const uint32_t tex_format) { assert(tex_format_is_supported(tex_format)); return &pvr_tex_format_table[tex_format].desc; } static inline bool tex_format_compressed_is_supported(const uint32_t tex_format) { return tex_format < ARRAY_SIZE(pvr_tex_format_compressed_table) && pvr_tex_format_compressed_table[tex_format].present; } static inline const struct pvr_tex_format_compressed_description * get_tex_format_compressed_description(const uint32_t tex_format) { assert(tex_format_compressed_is_supported(tex_format)); return &pvr_tex_format_compressed_table[tex_format].desc; } static inline void pvr_border_color_table_pack_single( struct pvr_border_color_table_value *const dst, const union pipe_color_union *const color, const struct pvr_tex_format_description *const pvr_tex_fmt_desc, const bool is_int, const struct pvr_device_info *const dev_info) { enum pipe_format pipe_format = is_int ? pvr_tex_fmt_desc->pipe_format_int : pvr_tex_fmt_desc->pipe_format_float; if (pipe_format == PIPE_FORMAT_NONE) return; memset(dst->value, 0, sizeof(dst->value)); if (util_format_is_depth_or_stencil(pipe_format)) { if (is_int) { const uint8_t s_color[4] = { color->ui[0], color->ui[1], color->ui[2], color->ui[3], }; util_format_pack_s_8uint(pipe_format, dst->value, s_color, 1); } else { util_format_pack_z_float(pipe_format, dst->value, color->f, 1); } } else { if (PVR_HAS_FEATURE(dev_info, tpu_border_colour_enhanced)) { if (pipe_format == PIPE_FORMAT_R9G9B9E5_FLOAT) pipe_format = PIPE_FORMAT_R16G16B16A16_FLOAT; } util_format_pack_rgba(pipe_format, dst->value, color, 1); } } static inline void pvr_border_color_table_pack_single_compressed( struct pvr_border_color_table_value *const dst, const union pipe_color_union *const color, const struct pvr_tex_format_compressed_description *const pvr_tex_fmt_desc, const bool is_int, const struct pvr_device_info *const dev_info) { if (PVR_HAS_FEATURE(dev_info, tpu_border_colour_enhanced)) { const struct pvr_tex_format_description *pvr_tex_fmt_desc_simple = get_tex_format_description(pvr_tex_fmt_desc->tex_format_simple); pvr_border_color_table_pack_single(dst, color, pvr_tex_fmt_desc_simple, is_int, dev_info); return; } memset(dst->value, 0, sizeof(dst->value)); pvr_finishme("Devices without tpu_border_colour_enhanced require entries " "for compressed formats to be stored in the table " "pre-compressed."); } static int32_t pvr_border_color_table_alloc_entry(struct pvr_border_color_table *const table) { /* BITSET_FFS() returns a 1-indexed position or 0 if no bits are set. */ int32_t index = BITSET_FFS(table->unused_entries); if (!index--) return -1; assert(index >= PVR_BORDER_COLOR_TABLE_NR_BUILTIN_ENTRIES); BITSET_CLEAR(table->unused_entries, index); return index; } static void pvr_border_color_table_free_entry(struct pvr_border_color_table *const table, const uint32_t index) { assert(pvr_border_color_table_is_index_valid(table, index)); BITSET_SET(table->unused_entries, index); } static void pvr_border_color_table_fill_entry(struct pvr_border_color_table *const table, const uint32_t index, 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]; for (enum ROGUE_TEXSTATE_FORMAT tex_format = 0; tex_format < PVR_TEX_FORMAT_COUNT; tex_format++) { if (tex_format_is_supported(tex_format)) pvr_border_color_table_pack_single( &entry->values[tex_format], color, get_tex_format_description(tex_format), is_int, dev_info); } for (enum ROGUE_TEXSTATE_FORMAT_COMPRESSED tex_format = 0; tex_format < PVR_TEX_FORMAT_COUNT; tex_format++) { if (tex_format_compressed_is_supported(tex_format)) pvr_border_color_table_pack_single_compressed( &entry->compressed_values[tex_format], color, get_tex_format_compressed_description(tex_format), is_int, dev_info); } } /** 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_arch_border_color_table_init(struct pvr_device *const device) { struct pvr_border_color_table *table = device->border_color_table = vk_zalloc(&device->vk.alloc, sizeof(struct pvr_border_color_table), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); if (!table) return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); const struct pvr_device_info *const dev_info = &device->pdevice->dev_info; const uint32_t cache_line_size = pvr_get_slc_cache_line_size(dev_info); const uint32_t table_size = sizeof(struct pvr_border_color_table_entry) * PVR_BORDER_COLOR_TABLE_NR_ENTRIES; VkResult result; /* Initialize to ones so ffs can be used to find unused entries. */ BITSET_ONES(table->unused_entries); result = pvr_bo_alloc(device, device->heaps.general_heap, table_size, cache_line_size, PVR_BO_ALLOC_FLAG_CPU_MAPPED, &table->table); if (result != VK_SUCCESS) goto err_out; BITSET_CLEAR_RANGE_INSIDE_WORD(table->unused_entries, 0, PVR_BORDER_COLOR_TABLE_NR_BUILTIN_ENTRIES - 1); for (uint32_t i = 0; i < PVR_BORDER_COLOR_TABLE_NR_BUILTIN_ENTRIES; i++) { const VkClearColorValue color = vk_border_color_value(i); const bool is_int = vk_border_color_is_int(i); pvr_border_color_table_fill_entry(table, i, (const union pipe_color_union *)&color, is_int, dev_info); } pvr_bo_cpu_unmap(device, table->table); return VK_SUCCESS; err_out: vk_free(&device->vk.alloc, table); return result; } void pvr_arch_border_color_table_finish(struct pvr_device *const device) { #if MESA_DEBUG BITSET_SET_RANGE_INSIDE_WORD(device->border_color_table->unused_entries, 0, PVR_BORDER_COLOR_TABLE_NR_BUILTIN_ENTRIES - 1); BITSET_NOT(device->border_color_table->unused_entries); assert(BITSET_IS_EMPTY(device->border_color_table->unused_entries)); #endif pvr_bo_free(device, device->border_color_table->table); vk_free(&device->vk.alloc, device->border_color_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_arch_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 = 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 = 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_arch_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_arch_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; if (vk_type <= PVR_BORDER_COLOR_TABLE_NR_BUILTIN_ENTRIES) { *index_out = vk_type; return VK_SUCCESS; } return pvr_border_color_table_create_custom_entry(device, sampler, table, index_out); } void pvr_arch_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); }