blob: 7e936b0cb04b33da5ae05fb53a69f0ef88196f53 [file] [log] [blame] [edit]
/*
* Copyright 2024 Valve Corporation
* Copyright 2024 Alyssa Rosenzweig
* Copyright 2022-2023 Collabora Ltd. and Red Hat Inc.
* SPDX-License-Identifier: MIT
*/
#include "hk_sampler.h"
#include "hk_device.h"
#include "hk_entrypoints.h"
#include "hk_physical_device.h"
#include "vk_enum_to_str.h"
#include "vk_format.h"
#include "vk_sampler.h"
#include "asahi/genxml/agx_pack.h"
static inline uint32_t
translate_address_mode(VkSamplerAddressMode addr_mode)
{
#define MODE(VK, AGX_) [VK_SAMPLER_ADDRESS_MODE_##VK] = AGX_WRAP_##AGX_
static const uint8_t translate[] = {
MODE(REPEAT, REPEAT),
MODE(MIRRORED_REPEAT, MIRRORED_REPEAT),
MODE(CLAMP_TO_EDGE, CLAMP_TO_EDGE),
MODE(CLAMP_TO_BORDER, CLAMP_TO_BORDER),
MODE(MIRROR_CLAMP_TO_EDGE, MIRRORED_CLAMP_TO_EDGE),
};
#undef MODE
assert(addr_mode < ARRAY_SIZE(translate));
return translate[addr_mode];
}
static uint32_t
translate_texsamp_compare_op(VkCompareOp op)
{
#define OP(VK, AGX_) [VK_COMPARE_OP_##VK] = AGX_COMPARE_FUNC_##AGX_
static const uint8_t translate[] = {
OP(NEVER, NEVER),
OP(LESS, LESS),
OP(EQUAL, EQUAL),
OP(LESS_OR_EQUAL, LEQUAL),
OP(GREATER, GREATER),
OP(NOT_EQUAL, NOT_EQUAL),
OP(GREATER_OR_EQUAL, GEQUAL),
OP(ALWAYS, ALWAYS),
};
#undef OP
assert(op < ARRAY_SIZE(translate));
return translate[op];
}
static enum agx_filter
translate_filter(VkFilter filter)
{
static_assert((enum agx_filter)VK_FILTER_NEAREST == AGX_FILTER_NEAREST);
static_assert((enum agx_filter)VK_FILTER_LINEAR == AGX_FILTER_LINEAR);
return (enum agx_filter)filter;
}
static enum agx_mip_filter
translate_mipfilter(VkSamplerMipmapMode mode)
{
switch (mode) {
case VK_SAMPLER_MIPMAP_MODE_NEAREST:
return AGX_MIP_FILTER_NEAREST;
case VK_SAMPLER_MIPMAP_MODE_LINEAR:
return AGX_MIP_FILTER_LINEAR;
default:
unreachable("Invalid filter");
}
}
static bool
uses_border(const VkSamplerCreateInfo *info)
{
return info->addressModeU == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
info->addressModeV == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
info->addressModeW == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
}
static enum agx_border_colour
is_border_color_custom(VkBorderColor color)
{
/* TODO: for now, opaque black is treated as custom due to rgba4 swizzling
* issues, could be optimized though.
*/
switch (color) {
case VK_BORDER_COLOR_INT_OPAQUE_BLACK:
case VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK:
case VK_BORDER_COLOR_INT_CUSTOM_EXT:
case VK_BORDER_COLOR_FLOAT_CUSTOM_EXT:
return true;
default:
return false;
}
}
/* Translate an American VkBorderColor into a Canadian agx_border_colour */
static enum agx_border_colour
translate_border_color(VkBorderColor color, bool custom_to_1)
{
switch (color) {
case VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK:
case VK_BORDER_COLOR_INT_TRANSPARENT_BLACK:
return AGX_BORDER_COLOUR_TRANSPARENT_BLACK;
case VK_BORDER_COLOR_INT_OPAQUE_WHITE:
case VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE:
return AGX_BORDER_COLOUR_OPAQUE_WHITE;
default:
assert(is_border_color_custom(color));
return custom_to_1 ? AGX_BORDER_COLOUR_OPAQUE_WHITE
: AGX_BORDER_COLOUR_TRANSPARENT_BLACK;
}
}
static void
pack_sampler(const struct hk_physical_device *pdev,
const struct VkSamplerCreateInfo *info, bool custom_to_1,
struct agx_sampler_packed *out)
{
agx_pack(out, SAMPLER, cfg) {
cfg.minimum_lod = info->minLod;
cfg.maximum_lod = info->maxLod;
cfg.magnify = translate_filter(info->magFilter);
cfg.minify = translate_filter(info->minFilter);
cfg.mip_filter = translate_mipfilter(info->mipmapMode);
cfg.wrap_s = translate_address_mode(info->addressModeU);
cfg.wrap_t = translate_address_mode(info->addressModeV);
cfg.wrap_r = translate_address_mode(info->addressModeW);
cfg.pixel_coordinates = info->unnormalizedCoordinates;
cfg.seamful_cube_maps =
info->flags & VK_SAMPLER_CREATE_NON_SEAMLESS_CUBE_MAP_BIT_EXT;
if (info->compareEnable) {
cfg.compare_func = translate_texsamp_compare_op(info->compareOp);
cfg.compare_enable = true;
}
if (info->anisotropyEnable) {
cfg.maximum_anisotropy =
util_next_power_of_two(MAX2(info->maxAnisotropy, 1));
} else {
cfg.maximum_anisotropy = 1;
}
if (uses_border(info)) {
cfg.border_colour =
translate_border_color(info->borderColor, custom_to_1);
}
}
}
VKAPI_ATTR VkResult VKAPI_CALL
hk_CreateSampler(VkDevice device,
const VkSamplerCreateInfo *info /* pCreateInfo */,
const VkAllocationCallbacks *pAllocator, VkSampler *pSampler)
{
VK_FROM_HANDLE(hk_device, dev, device);
struct hk_physical_device *pdev = hk_device_physical(dev);
struct hk_sampler *sampler;
VkResult result;
sampler = vk_sampler_create(&dev->vk, info, pAllocator, sizeof(*sampler));
if (!sampler)
return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
struct agx_sampler_packed samp;
pack_sampler(pdev, info, true, &samp);
/* LOD bias passed in the descriptor set */
sampler->lod_bias_fp16 = _mesa_float_to_half(info->mipLodBias);
result =
hk_sampler_heap_add(dev, samp, &sampler->planes[sampler->plane_count].hw);
if (result != VK_SUCCESS) {
hk_DestroySampler(device, hk_sampler_to_handle(sampler), pAllocator);
return result;
}
sampler->plane_count++;
/* In order to support CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT, we
* need multiple sampler planes: at minimum we will need one for luminance
* (the default), and one for chroma. Each sampler plane needs its own
* sampler table entry. However, sampler table entries are very rare on
* G13, and each plane would burn one of those. So we make sure to allocate
* only the minimum amount that we actually need (i.e., either 1 or 2), and
* then just copy the last sampler plane out as far as we need to fill the
* number of image planes.
*/
if (sampler->vk.ycbcr_conversion) {
assert(!uses_border(info) &&
"consequence of VUID-VkSamplerCreateInfo-addressModeU-01646");
const VkFilter chroma_filter =
sampler->vk.ycbcr_conversion->state.chroma_filter;
if (info->magFilter != chroma_filter ||
info->minFilter != chroma_filter) {
VkSamplerCreateInfo plane2_info = *info;
plane2_info.magFilter = chroma_filter;
plane2_info.minFilter = chroma_filter;
pack_sampler(pdev, &plane2_info, false, &samp);
result = hk_sampler_heap_add(
dev, samp, &sampler->planes[sampler->plane_count].hw);
if (result != VK_SUCCESS) {
hk_DestroySampler(device, hk_sampler_to_handle(sampler),
pAllocator);
return result;
}
sampler->plane_count++;
}
} else if (uses_border(info)) {
/* If the sampler uses custom border colours, we need both clamp-to-1
* and clamp-to-0 variants. We treat these as planes.
*/
pack_sampler(pdev, info, false, &samp);
result = hk_sampler_heap_add(dev, samp,
&sampler->planes[sampler->plane_count].hw);
if (result != VK_SUCCESS) {
hk_DestroySampler(device, hk_sampler_to_handle(sampler), pAllocator);
return result;
}
sampler->plane_count++;
/* We also need to record the border.
*
* If there is a border colour component mapping, we need to swizzle with
* it. Otherwise, we can assume there's nothing to do.
*/
VkClearColorValue bc = sampler->vk.border_color_value;
const VkSamplerBorderColorComponentMappingCreateInfoEXT *swiz_info =
vk_find_struct_const(
info->pNext,
SAMPLER_BORDER_COLOR_COMPONENT_MAPPING_CREATE_INFO_EXT);
if (swiz_info) {
const bool is_int = vk_border_color_is_int(info->borderColor);
bc = vk_swizzle_color_value(bc, swiz_info->components, is_int);
}
sampler->custom_border = bc;
sampler->has_border = true;
}
*pSampler = hk_sampler_to_handle(sampler);
return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL
hk_DestroySampler(VkDevice device, VkSampler _sampler,
const VkAllocationCallbacks *pAllocator)
{
VK_FROM_HANDLE(hk_device, dev, device);
VK_FROM_HANDLE(hk_sampler, sampler, _sampler);
if (!sampler)
return;
for (uint8_t plane = 0; plane < sampler->plane_count; plane++) {
hk_sampler_heap_remove(dev, sampler->planes[plane].hw);
}
vk_sampler_destroy(&dev->vk, pAllocator, &sampler->vk);
}