| /* |
| * 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 <assert.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <vulkan/vulkan_core.h> |
| |
| #include "hwdef/rogue_hw_utils.h" |
| #include "pvr_bo.h" |
| #include "pvr_border.h" |
| #include "pvr_device_info.h" |
| #include "pvr_formats.h" |
| #include "pvr_private.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 * 2]; |
| } PACKED; |
| |
| 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 format, |
| const bool is_int) |
| { |
| const enum pipe_format pipe_format = is_int ? format->pipe_format_int |
| : format->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 { |
| 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 format, |
| const struct pvr_device_info *const dev_info) |
| { |
| if (PVR_HAS_FEATURE(dev_info, tpu_border_colour_enhanced)) { |
| const struct pvr_tex_format_description *format_simple = |
| pvr_get_tex_format_description(format->tex_format_simple); |
| |
| pvr_border_color_table_pack_single(dst, color, format_simple, false); |
| 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; |
| |
| 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]; |
| uint32_t tex_format = 0; |
| |
| for (; tex_format < PVR_TEX_FORMAT_COUNT; tex_format++) { |
| if (!pvr_tex_format_is_supported(tex_format)) |
| continue; |
| |
| pvr_border_color_table_pack_single( |
| &entry->values[tex_format], |
| color, |
| pvr_get_tex_format_description(tex_format), |
| is_int); |
| } |
| |
| for (; tex_format < PVR_TEX_FORMAT_COUNT * 2; tex_format++) { |
| if (!pvr_tex_format_compressed_is_supported(tex_format)) |
| continue; |
| |
| pvr_border_color_table_pack_single_compressed( |
| &entry->values[tex_format], |
| color, |
| pvr_get_tex_format_compressed_description(tex_format), |
| dev_info); |
| } |
| } |
| |
| VkResult pvr_border_color_table_init(struct pvr_border_color_table *const table, |
| struct pvr_device *const device) |
| { |
| const struct pvr_device_info *const dev_info = &device->pdevice->dev_info; |
| const uint32_t cache_line_size = rogue_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: |
| return result; |
| } |
| |
| void pvr_border_color_table_finish(struct pvr_border_color_table *const table, |
| struct pvr_device *const device) |
| { |
| #if MESA_DEBUG |
| BITSET_SET_RANGE_INSIDE_WORD(table->unused_entries, |
| 0, |
| PVR_BORDER_COLOR_TABLE_NR_BUILTIN_ENTRIES - 1); |
| BITSET_NOT(table->unused_entries); |
| assert(BITSET_IS_EMPTY(table->unused_entries)); |
| #endif |
| |
| pvr_bo_free(device, table->table); |
| } |
| |
| VkResult pvr_border_color_table_get_or_create_entry( |
| UNUSED struct pvr_border_color_table *const table, |
| const struct pvr_sampler *const sampler, |
| 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; |
| } |
| |
| pvr_finishme("VK_EXT_custom_border_color is currently unsupported."); |
| return vk_error(sampler, VK_ERROR_EXTENSION_NOT_PRESENT); |
| } |