blob: 7ebf10b8af2ba00450ece1ab32d2d82fb99238fb [file] [log] [blame]
/*
* Copyright © 2019 Raspberry Pi
*
* 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 <fcntl.h>
#include <stdbool.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/sysinfo.h>
#include <unistd.h>
#include <xf86drm.h>
#include "v3dv_private.h"
#include "common/v3d_debug.h"
#include "broadcom/cle/v3dx_pack.h"
#include "compiler/v3d_compiler.h"
#include "compiler/glsl_types.h"
#include "drm-uapi/v3d_drm.h"
#include "format/u_format.h"
#include "u_atomic.h"
#include "vk_util.h"
#include "util/build_id.h"
#ifdef VK_USE_PLATFORM_XCB_KHR
#include <xcb/xcb.h>
#include <xcb/dri3.h>
#endif
#ifdef USE_V3D_SIMULATOR
#include "drm-uapi/i915_drm.h"
#endif
static void *
default_alloc_func(void *pUserData, size_t size, size_t align,
VkSystemAllocationScope allocationScope)
{
return malloc(size);
}
static void *
default_realloc_func(void *pUserData, void *pOriginal, size_t size,
size_t align, VkSystemAllocationScope allocationScope)
{
return realloc(pOriginal, size);
}
static void
default_free_func(void *pUserData, void *pMemory)
{
free(pMemory);
}
static const VkAllocationCallbacks default_alloc = {
.pUserData = NULL,
.pfnAllocation = default_alloc_func,
.pfnReallocation = default_realloc_func,
.pfnFree = default_free_func,
};
VkResult
v3dv_EnumerateInstanceExtensionProperties(const char *pLayerName,
uint32_t *pPropertyCount,
VkExtensionProperties *pProperties)
{
/* We don't support any layers */
if (pLayerName)
return vk_error(NULL, VK_ERROR_LAYER_NOT_PRESENT);
VK_OUTARRAY_MAKE(out, pProperties, pPropertyCount);
for (int i = 0; i < V3DV_INSTANCE_EXTENSION_COUNT; i++) {
if (v3dv_instance_extensions_supported.extensions[i]) {
vk_outarray_append(&out, prop) {
*prop = v3dv_instance_extensions[i];
}
}
}
return vk_outarray_status(&out);
}
VkResult
v3dv_CreateInstance(const VkInstanceCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkInstance *pInstance)
{
struct v3dv_instance *instance;
VkResult result;
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO);
struct v3dv_instance_extension_table enabled_extensions = {};
for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
int idx;
for (idx = 0; idx < V3DV_INSTANCE_EXTENSION_COUNT; idx++) {
if (strcmp(pCreateInfo->ppEnabledExtensionNames[i],
v3dv_instance_extensions[idx].extensionName) == 0)
break;
}
if (idx >= V3DV_INSTANCE_EXTENSION_COUNT)
return vk_error(NULL, VK_ERROR_EXTENSION_NOT_PRESENT);
if (!v3dv_instance_extensions_supported.extensions[idx])
return vk_error(NULL, VK_ERROR_EXTENSION_NOT_PRESENT);
enabled_extensions.extensions[idx] = true;
}
instance = vk_alloc2(&default_alloc, pAllocator, sizeof(*instance), 8,
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
if (!instance)
return vk_error(NULL, VK_ERROR_OUT_OF_HOST_MEMORY);
instance->_loader_data.loaderMagic = ICD_LOADER_MAGIC;
if (pAllocator)
instance->alloc = *pAllocator;
else
instance->alloc = default_alloc;
v3d_process_debug_variable();
instance->app_info = (struct v3dv_app_info) { .api_version = 0 };
if (pCreateInfo->pApplicationInfo) {
const VkApplicationInfo *app = pCreateInfo->pApplicationInfo;
instance->app_info.app_name =
vk_strdup(&instance->alloc, app->pApplicationName,
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
instance->app_info.app_version = app->applicationVersion;
instance->app_info.engine_name =
vk_strdup(&instance->alloc, app->pEngineName,
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
instance->app_info.engine_version = app->engineVersion;
instance->app_info.api_version = app->apiVersion;
}
if (instance->app_info.api_version == 0)
instance->app_info.api_version = VK_API_VERSION_1_0;
instance->enabled_extensions = enabled_extensions;
for (unsigned i = 0; i < ARRAY_SIZE(instance->dispatch.entrypoints); i++) {
/* Vulkan requires that entrypoints for extensions which have not been
* enabled must not be advertised.
*/
if (!v3dv_instance_entrypoint_is_enabled(i,
instance->app_info.api_version,
&instance->enabled_extensions)) {
instance->dispatch.entrypoints[i] = NULL;
} else {
instance->dispatch.entrypoints[i] =
v3dv_instance_dispatch_table.entrypoints[i];
}
}
struct v3dv_physical_device *pdevice = &instance->physicalDevice;
for (unsigned i = 0; i < ARRAY_SIZE(pdevice->dispatch.entrypoints); i++) {
/* Vulkan requires that entrypoints for extensions which have not been
* enabled must not be advertised.
*/
if (!v3dv_physical_device_entrypoint_is_enabled(i,
instance->app_info.api_version,
&instance->enabled_extensions)) {
pdevice->dispatch.entrypoints[i] = NULL;
} else {
pdevice->dispatch.entrypoints[i] =
v3dv_physical_device_dispatch_table.entrypoints[i];
}
}
for (unsigned i = 0; i < ARRAY_SIZE(instance->device_dispatch.entrypoints); i++) {
/* Vulkan requires that entrypoints for extensions which have not been
* enabled must not be advertised.
*/
if (!v3dv_device_entrypoint_is_enabled(i,
instance->app_info.api_version,
&instance->enabled_extensions,
NULL)) {
instance->device_dispatch.entrypoints[i] = NULL;
} else {
instance->device_dispatch.entrypoints[i] =
v3dv_device_dispatch_table.entrypoints[i];
}
}
instance->physicalDeviceCount = -1;
result = vk_debug_report_instance_init(&instance->debug_report_callbacks);
if (result != VK_SUCCESS) {
vk_free2(&default_alloc, pAllocator, instance);
return vk_error(NULL, result);
}
glsl_type_singleton_init_or_ref();
VG(VALGRIND_CREATE_MEMPOOL(instance, 0, false));
*pInstance = v3dv_instance_to_handle(instance);
return VK_SUCCESS;
}
static void
physical_device_finish(struct v3dv_physical_device *device)
{
v3dv_wsi_finish(device);
v3d_compiler_free(device->compiler);
close(device->render_fd);
if (device->display_fd >= 0)
close(device->display_fd);
free(device->name);
#if using_v3d_simulator
v3d_simulator_destroy(device->sim_file);
#endif
}
void
v3dv_DestroyInstance(VkInstance _instance,
const VkAllocationCallbacks *pAllocator)
{
V3DV_FROM_HANDLE(v3dv_instance, instance, _instance);
if (!instance)
return;
if (instance->physicalDeviceCount > 0) {
/* We support at most one physical device. */
assert(instance->physicalDeviceCount == 1);
physical_device_finish(&instance->physicalDevice);
}
vk_free(&instance->alloc, (char *)instance->app_info.app_name);
vk_free(&instance->alloc, (char *)instance->app_info.engine_name);
VG(VALGRIND_DESTROY_MEMPOOL(instance));
vk_debug_report_instance_destroy(&instance->debug_report_callbacks);
glsl_type_singleton_decref();
vk_free(&instance->alloc, instance);
}
static uint64_t
compute_heap_size()
{
/* Query the total ram from the system */
struct sysinfo info;
sysinfo(&info);
uint64_t total_ram = (uint64_t)info.totalram * (uint64_t)info.mem_unit;
/* We don't want to burn too much ram with the GPU. If the user has 4GiB
* or less, we use at most half. If they have more than 4GiB, we use 3/4.
*/
uint64_t available_ram;
if (total_ram <= 4ull * 1024ull * 1024ull * 1024ull)
available_ram = total_ram / 2;
else
available_ram = total_ram * 3 / 4;
return available_ram;
}
/* When running on the simulator we do everything on a single render node so
* we don't need to get an authenticated display fd from the display server.
*/
#if !using_v3d_simulator
#ifdef VK_USE_PLATFORM_XCB_KHR
static int
create_display_fd_xcb()
{
xcb_connection_t *conn = xcb_connect(NULL, NULL);
const xcb_setup_t *setup = xcb_get_setup(conn);
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
xcb_screen_t *screen = iter.data;
xcb_dri3_open_cookie_t cookie;
xcb_dri3_open_reply_t *reply;
cookie = xcb_dri3_open(conn, screen->root, None);
reply = xcb_dri3_open_reply(conn, cookie, NULL);
if (!reply)
return -1;
if (reply->nfd != 1) {
free(reply);
return -1;
}
int fd = xcb_dri3_open_reply_fds(conn, reply)[0];
free(reply);
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
return fd;
}
#endif
#endif
static bool
v3d_has_feature(struct v3dv_physical_device *device, enum drm_v3d_param feature)
{
struct drm_v3d_get_param p = {
.param = feature,
};
if (v3dv_ioctl(device->render_fd, DRM_IOCTL_V3D_GET_PARAM, &p) != 0)
return false;
return p.value;
}
static bool
device_has_expected_features(struct v3dv_physical_device *device)
{
return v3d_has_feature(device, DRM_V3D_PARAM_SUPPORTS_TFU) &&
v3d_has_feature(device, DRM_V3D_PARAM_SUPPORTS_CSD) &&
v3d_has_feature(device, DRM_V3D_PARAM_SUPPORTS_CACHE_FLUSH);
}
static VkResult
init_uuids(struct v3dv_physical_device *device)
{
const struct build_id_note *note =
build_id_find_nhdr_for_addr(init_uuids);
if (!note) {
return vk_errorf(device->instance,
VK_ERROR_INITIALIZATION_FAILED,
"Failed to find build-id");
}
unsigned build_id_len = build_id_length(note);
if (build_id_len < 20) {
return vk_errorf(device->instance,
VK_ERROR_INITIALIZATION_FAILED,
"build-id too short. It needs to be a SHA");
}
struct mesa_sha1 sha1_ctx;
uint8_t sha1[20];
STATIC_ASSERT(VK_UUID_SIZE <= sizeof(sha1));
uint32_t device_id = v3dv_physical_device_device_id(device);
/* The pipeline cache UUID is used for determining when a pipeline cache is
* invalid. It needs both a driver build and the PCI ID of the device.
*/
_mesa_sha1_init(&sha1_ctx);
_mesa_sha1_update(&sha1_ctx, build_id_data(note), build_id_len);
_mesa_sha1_update(&sha1_ctx, &device_id, sizeof(device_id));
_mesa_sha1_final(&sha1_ctx, sha1);
memcpy(device->pipeline_cache_uuid, sha1, VK_UUID_SIZE);
return VK_SUCCESS;
}
static VkResult
physical_device_init(struct v3dv_physical_device *device,
struct v3dv_instance *instance,
drmDevicePtr drm_device)
{
VkResult result = VK_SUCCESS;
int32_t display_fd = -1;
device->_loader_data.loaderMagic = ICD_LOADER_MAGIC;
device->instance = instance;
const char *path = drm_device->nodes[DRM_NODE_RENDER];
int32_t render_fd = open(path, O_RDWR | O_CLOEXEC);
if (render_fd < 0)
return vk_error(instance, VK_ERROR_INCOMPATIBLE_DRIVER);
/* If we are running on real hardware we need to open the vc4 display
* device so we can allocate winsys BOs for the v3d core to render into.
*/
#if !using_v3d_simulator
#ifdef VK_USE_PLATFORM_XCB_KHR
display_fd = create_display_fd_xcb();
#endif
if (display_fd == -1) {
result = VK_ERROR_INCOMPATIBLE_DRIVER;
goto fail;
}
#endif
device->render_fd = render_fd; /* The v3d render node */
device->display_fd = display_fd; /* The vc4 primary node */
result = init_uuids(device);
if (result != VK_SUCCESS)
goto fail;
#if using_v3d_simulator
device->sim_file = v3d_simulator_init(device->render_fd);
#endif
if (!v3d_get_device_info(device->render_fd, &device->devinfo, &v3dv_ioctl)) {
result = VK_ERROR_INCOMPATIBLE_DRIVER;
goto fail;
}
if (device->devinfo.ver < 42) {
result = VK_ERROR_INCOMPATIBLE_DRIVER;
goto fail;
}
if (!device_has_expected_features(device)) {
result = VK_ERROR_INCOMPATIBLE_DRIVER;
goto fail;
}
device->compiler = v3d_compiler_init(&device->devinfo);
device->next_program_id = 0;
asprintf(&device->name, "V3D %d.%d",
device->devinfo.ver / 10, device->devinfo.ver % 10);
/* Setup available memory heaps and types */
VkPhysicalDeviceMemoryProperties *mem = &device->memory;
mem->memoryHeapCount = 1;
mem->memoryHeaps[0].size = compute_heap_size();
mem->memoryHeaps[0].flags = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT;
mem->memoryTypeCount = 2;
/* This is the only combination required by the spec */
mem->memoryTypes[0].propertyFlags =
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
mem->memoryTypes[0].heapIndex = 0;
mem->memoryTypes[1].propertyFlags =
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
mem->memoryTypes[1].heapIndex = 0;
device->options.merge_jobs = getenv("V3DV_NO_MERGE_JOBS") == NULL;
result = v3dv_wsi_init(device);
if (result != VK_SUCCESS) {
vk_error(instance, result);
goto fail;
}
v3dv_physical_device_get_supported_extensions(device,
&device->supported_extensions);
fprintf(stderr, "WARNING: v3dv is neither a complete nor a conformant "
"Vulkan implementation. Testing use only.\n");
return VK_SUCCESS;
fail:
if (render_fd >= 0)
close(render_fd);
if (display_fd >= 0)
close(display_fd);
return result;
}
static VkResult
enumerate_devices(struct v3dv_instance *instance)
{
/* TODO: Check for more devices? */
drmDevicePtr devices[8];
VkResult result = VK_ERROR_INCOMPATIBLE_DRIVER;
int max_devices;
instance->physicalDeviceCount = 0;
max_devices = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
if (max_devices < 1)
return VK_ERROR_INCOMPATIBLE_DRIVER;
#if !using_v3d_simulator
int32_t v3d_idx = -1;
int32_t vc4_idx = -1;
#endif
for (unsigned i = 0; i < (unsigned)max_devices; i++) {
#if using_v3d_simulator
/* In the simulator, we look for an Intel render node */
if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
devices[i]->bustype == DRM_BUS_PCI &&
devices[i]->deviceinfo.pci->vendor_id == 0x8086) {
result = physical_device_init(&instance->physicalDevice, instance,
devices[i]);
if (result != VK_ERROR_INCOMPATIBLE_DRIVER)
break;
}
#else
/* On actual hardware, we should have a render node (v3d)
* and a primary node (vc4). We will need to use the primary
* to allocate WSI buffers and share them with the render node
* via prime, but that is a privileged operation so we need the
* primary node to be authenticated, and for that we need the
* display server to provide the device fd (with DRI3), so we
* here we only check that the device is present but we don't
* try to open it.
*/
if (devices[i]->bustype != DRM_BUS_PLATFORM)
continue;
if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER) {
char **compat = devices[i]->deviceinfo.platform->compatible;
while (*compat) {
if (strncmp(*compat, "brcm,2711-v3d", 13) == 0) {
v3d_idx = i;
break;
}
compat++;
}
} else if (devices[i]->available_nodes & 1 << DRM_NODE_PRIMARY) {
char **compat = devices[i]->deviceinfo.platform->compatible;
while (*compat) {
if (strncmp(*compat, "brcm,bcm2711-vc5", 16) == 0 ||
strncmp(*compat, "brcm,bcm2835-vc4", 16) == 0 ) {
vc4_idx = i;
break;
}
compat++;
}
}
#endif
}
#if !using_v3d_simulator
if (v3d_idx == -1 || vc4_idx == -1)
result = VK_ERROR_INCOMPATIBLE_DRIVER;
else
result = physical_device_init(&instance->physicalDevice, instance,
devices[v3d_idx]);
#endif
drmFreeDevices(devices, max_devices);
if (result == VK_SUCCESS)
instance->physicalDeviceCount = 1;
return result;
}
static VkResult
instance_ensure_physical_device(struct v3dv_instance *instance)
{
if (instance->physicalDeviceCount < 0) {
VkResult result = enumerate_devices(instance);
if (result != VK_SUCCESS &&
result != VK_ERROR_INCOMPATIBLE_DRIVER)
return result;
}
return VK_SUCCESS;
}
VkResult
v3dv_EnumeratePhysicalDevices(VkInstance _instance,
uint32_t *pPhysicalDeviceCount,
VkPhysicalDevice *pPhysicalDevices)
{
V3DV_FROM_HANDLE(v3dv_instance, instance, _instance);
VK_OUTARRAY_MAKE(out, pPhysicalDevices, pPhysicalDeviceCount);
VkResult result = instance_ensure_physical_device(instance);
if (result != VK_SUCCESS)
return result;
if (instance->physicalDeviceCount == 0)
return VK_SUCCESS;
assert(instance->physicalDeviceCount == 1);
vk_outarray_append(&out, i) {
*i = v3dv_physical_device_to_handle(&instance->physicalDevice);
}
return vk_outarray_status(&out);
}
void
v3dv_GetPhysicalDeviceFeatures(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceFeatures *pFeatures)
{
memset(pFeatures, 0, sizeof(*pFeatures));
*pFeatures = (VkPhysicalDeviceFeatures) {
.robustBufferAccess = true, /* This feature is mandatory */
.fullDrawIndexUint32 = false,
.imageCubeArray = true,
.independentBlend = false,
.geometryShader = false,
.tessellationShader = false,
.sampleRateShading = false,
.dualSrcBlend = false,
.logicOp = false,
.multiDrawIndirect = false,
.drawIndirectFirstInstance = false,
.depthClamp = false,
.depthBiasClamp = false,
.fillModeNonSolid = true,
.depthBounds = false, /* Only available since V3D 4.3.16.2 */
.wideLines = true,
.largePoints = false,
.alphaToOne = false,
.multiViewport = false,
.samplerAnisotropy = true,
.textureCompressionETC2 = true,
.textureCompressionASTC_LDR = false,
.textureCompressionBC = false,
.occlusionQueryPrecise = true,
.pipelineStatisticsQuery = false,
.vertexPipelineStoresAndAtomics = true,
.fragmentStoresAndAtomics = true,
.shaderTessellationAndGeometryPointSize = false,
.shaderImageGatherExtended = false,
.shaderStorageImageExtendedFormats = false,
.shaderStorageImageMultisample = false,
.shaderStorageImageReadWithoutFormat = false,
.shaderStorageImageWriteWithoutFormat = false,
.shaderUniformBufferArrayDynamicIndexing = false,
.shaderSampledImageArrayDynamicIndexing = false,
.shaderStorageBufferArrayDynamicIndexing = false,
.shaderStorageImageArrayDynamicIndexing = false,
.shaderClipDistance = true,
.shaderCullDistance = false,
.shaderFloat64 = false,
.shaderInt64 = false,
.shaderInt16 = false,
.shaderResourceResidency = false,
.shaderResourceMinLod = false,
.sparseBinding = false,
.sparseResidencyBuffer = false,
.sparseResidencyImage2D = false,
.sparseResidencyImage3D = false,
.sparseResidency2Samples = false,
.sparseResidency4Samples = false,
.sparseResidency8Samples = false,
.sparseResidency16Samples = false,
.sparseResidencyAliased = false,
.variableMultisampleRate = false,
.inheritedQueries = true,
};
}
void
v3dv_GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceFeatures2 *pFeatures)
{
v3dv_GetPhysicalDeviceFeatures(physicalDevice, &pFeatures->features);
vk_foreach_struct(ext, pFeatures->pNext) {
switch (ext->sType) {
default:
v3dv_debug_ignored_stype(ext->sType);
break;
}
}
}
uint32_t
v3dv_physical_device_vendor_id(struct v3dv_physical_device *dev)
{
return 0x14E4;
}
#if using_v3d_simulator
static bool
get_i915_param(int fd, uint32_t param, int *value)
{
int tmp;
struct drm_i915_getparam gp = {
.param = param,
.value = &tmp,
};
int ret = drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
if (ret != 0)
return false;
*value = tmp;
return true;
}
#endif
/* FIXME:
* Getting deviceID and UUID will probably require to use the kernel pci
* interface. See this:
* https://www.kernel.org/doc/html/latest/PCI/pci.html#how-to-find-pci-devices-manually
* And check the getparam ioctl in the i915 kernel with CHIPSET_ID for
* example.
*/
uint32_t
v3dv_physical_device_device_id(struct v3dv_physical_device *dev)
{
#if using_v3d_simulator
int devid = 0;
if (!get_i915_param(dev->render_fd, I915_PARAM_CHIPSET_ID, &devid))
fprintf(stderr, "Error getting for device_id\n");
return devid;
#else
/* FIXME */
return 0;
#endif
}
void
v3dv_GetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceProperties *pProperties)
{
V3DV_FROM_HANDLE(v3dv_physical_device, pdevice, physicalDevice);
const uint32_t page_size = 4096;
const uint32_t mem_size = compute_heap_size();
/* Per-stage limits */
const uint32_t max_samplers = 16;
const uint32_t max_uniform_buffers = 12;
const uint32_t max_storage_buffers = 12;
const uint32_t max_dynamic_storage_buffers = 6;
const uint32_t max_sampled_images = 16;
const uint32_t max_storage_images = 4;
const uint32_t max_varying_components = 16 * 4;
const uint32_t max_render_targets = 4;
const uint32_t v3d_coord_shift = 6;
const uint32_t max_fb_size = 4096;
const VkSampleCountFlags supported_sample_counts =
VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT;
/* FIXME: this will probably require an in-depth review */
VkPhysicalDeviceLimits limits = {
.maxImageDimension1D = 4096,
.maxImageDimension2D = 4096,
.maxImageDimension3D = 4096,
.maxImageDimensionCube = 4096,
.maxImageArrayLayers = 2048,
.maxTexelBufferElements = (1ul << 28),
.maxUniformBufferRange = (1ul << 27),
.maxStorageBufferRange = (1ul << 27),
.maxPushConstantsSize = MAX_PUSH_CONSTANTS_SIZE,
.maxMemoryAllocationCount = mem_size / page_size,
.maxSamplerAllocationCount = 64 * 1024,
.bufferImageGranularity = 256, /* A cache line */
.sparseAddressSpaceSize = 0,
.maxBoundDescriptorSets = MAX_SETS,
.maxPerStageDescriptorSamplers = max_samplers,
.maxPerStageDescriptorUniformBuffers = max_uniform_buffers,
.maxPerStageDescriptorStorageBuffers = max_storage_buffers,
.maxPerStageDescriptorSampledImages = max_sampled_images,
.maxPerStageDescriptorStorageImages = max_storage_images,
.maxPerStageDescriptorInputAttachments = 4,
.maxPerStageResources = 128,
/* We multiply some limits by 6 to account for all shader stages */
.maxDescriptorSetSamplers = 6 * max_samplers,
.maxDescriptorSetUniformBuffers = 6 * max_uniform_buffers,
.maxDescriptorSetUniformBuffersDynamic = 8,
.maxDescriptorSetStorageBuffers = 6 * max_storage_buffers,
.maxDescriptorSetStorageBuffersDynamic = 6 * max_dynamic_storage_buffers,
.maxDescriptorSetSampledImages = 6 * max_sampled_images,
.maxDescriptorSetStorageImages = 6 * max_storage_images,
.maxDescriptorSetInputAttachments = 4,
/* Vertex limits */
.maxVertexInputAttributes = MAX_VERTEX_ATTRIBS,
.maxVertexInputBindings = MAX_VBS,
.maxVertexInputAttributeOffset = 0xffffffff,
.maxVertexInputBindingStride = 0xffffffff,
.maxVertexOutputComponents = max_varying_components,
/* Tessellation limits */
.maxTessellationGenerationLevel = 0,
.maxTessellationPatchSize = 0,
.maxTessellationControlPerVertexInputComponents = 0,
.maxTessellationControlPerVertexOutputComponents = 0,
.maxTessellationControlPerPatchOutputComponents = 0,
.maxTessellationControlTotalOutputComponents = 0,
.maxTessellationEvaluationInputComponents = 0,
.maxTessellationEvaluationOutputComponents = 0,
/* Geometry limits */
.maxGeometryShaderInvocations = 0,
.maxGeometryInputComponents = 0,
.maxGeometryOutputComponents = 0,
.maxGeometryOutputVertices = 0,
.maxGeometryTotalOutputComponents = 0,
/* Fragment limits */
.maxFragmentInputComponents = max_varying_components,
.maxFragmentOutputAttachments = 4,
.maxFragmentDualSrcAttachments = 0,
.maxFragmentCombinedOutputResources = max_render_targets +
max_storage_buffers +
max_storage_images,
/* Compute limits */
.maxComputeSharedMemorySize = 16384,
.maxComputeWorkGroupCount = { 65535, 65535, 65535 },
.maxComputeWorkGroupInvocations = 256,
.maxComputeWorkGroupSize = { 256, 256, 256 },
.subPixelPrecisionBits = v3d_coord_shift,
.subTexelPrecisionBits = 8,
.mipmapPrecisionBits = 8,
.maxDrawIndexedIndexValue = 0x00ffffff,
.maxDrawIndirectCount = 0x7fffffff,
.maxSamplerLodBias = 14.0f,
.maxSamplerAnisotropy = 16.0f,
.maxViewports = MAX_VIEWPORTS,
.maxViewportDimensions = { max_fb_size, max_fb_size },
.viewportBoundsRange = { -2.0 * max_fb_size,
2.0 * max_fb_size - 1 },
.viewportSubPixelBits = 0,
.minMemoryMapAlignment = page_size,
.minTexelBufferOffsetAlignment = 16,
.minUniformBufferOffsetAlignment = 32,
.minStorageBufferOffsetAlignment = 32,
.minTexelOffset = -8,
.maxTexelOffset = 7,
.minTexelGatherOffset = -8,
.maxTexelGatherOffset = 7,
.minInterpolationOffset = -0.5,
.maxInterpolationOffset = 0.5,
.subPixelInterpolationOffsetBits = v3d_coord_shift,
.maxFramebufferWidth = max_fb_size,
.maxFramebufferHeight = max_fb_size,
.maxFramebufferLayers = 256,
.framebufferColorSampleCounts = supported_sample_counts,
.framebufferDepthSampleCounts = supported_sample_counts,
.framebufferStencilSampleCounts = supported_sample_counts,
.framebufferNoAttachmentsSampleCounts = supported_sample_counts,
.maxColorAttachments = max_render_targets,
.sampledImageColorSampleCounts = supported_sample_counts,
.sampledImageIntegerSampleCounts = supported_sample_counts,
.sampledImageDepthSampleCounts = supported_sample_counts,
.sampledImageStencilSampleCounts = supported_sample_counts,
.storageImageSampleCounts = VK_SAMPLE_COUNT_1_BIT,
.maxSampleMaskWords = 1,
.timestampComputeAndGraphics = false,
.timestampPeriod = 0.0f,
.maxClipDistances = 8,
.maxCullDistances = 0,
.maxCombinedClipAndCullDistances = 8,
.discreteQueuePriorities = 2,
.pointSizeRange = { 1.0f, 1.0f },
.lineWidthRange = { 1.0f, 32.0f },
.pointSizeGranularity = 0.0f,
.lineWidthGranularity = 2.0f / (1 << v3d_coord_shift),
.strictLines = true,
.standardSampleLocations = false,
.optimalBufferCopyOffsetAlignment = 32,
.optimalBufferCopyRowPitchAlignment = 32,
.nonCoherentAtomSize = 256,
};
*pProperties = (VkPhysicalDeviceProperties) {
.apiVersion = v3dv_physical_device_api_version(pdevice),
.driverVersion = vk_get_driver_version(),
.vendorID = v3dv_physical_device_vendor_id(pdevice),
.deviceID = v3dv_physical_device_device_id(pdevice),
.deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU,
.limits = limits,
.sparseProperties = { 0 },
};
snprintf(pProperties->deviceName, sizeof(pProperties->deviceName),
"%s", pdevice->name);
memcpy(pProperties->pipelineCacheUUID,
pdevice->pipeline_cache_uuid, VK_UUID_SIZE);
}
void
v3dv_GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceProperties2 *pProperties)
{
v3dv_GetPhysicalDeviceProperties(physicalDevice, &pProperties->properties);
vk_foreach_struct(ext, pProperties->pNext) {
switch (ext->sType) {
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES: {
VkPhysicalDeviceIDProperties *id_props =
(VkPhysicalDeviceIDProperties *)ext;
/* FIXME */
memset(id_props->deviceUUID, 0, VK_UUID_SIZE);
memset(id_props->driverUUID, 0, VK_UUID_SIZE);
/* The LUID is for Windows. */
id_props->deviceLUIDValid = false;
break;
}
default:
v3dv_debug_ignored_stype(ext->sType);
break;
}
}
}
/* We support exactly one queue family. */
static const VkQueueFamilyProperties
v3dv_queue_family_properties = {
.queueFlags = VK_QUEUE_GRAPHICS_BIT |
VK_QUEUE_COMPUTE_BIT |
VK_QUEUE_TRANSFER_BIT,
.queueCount = 1,
.timestampValidBits = 0, /* FIXME */
.minImageTransferGranularity = { 1, 1, 1 },
};
void
v3dv_GetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice,
uint32_t *pCount,
VkQueueFamilyProperties *pQueueFamilyProperties)
{
VK_OUTARRAY_MAKE(out, pQueueFamilyProperties, pCount);
vk_outarray_append(&out, p) {
*p = v3dv_queue_family_properties;
}
}
void
v3dv_GetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice,
uint32_t *pQueueFamilyPropertyCount,
VkQueueFamilyProperties2 *pQueueFamilyProperties)
{
VK_OUTARRAY_MAKE(out, pQueueFamilyProperties, pQueueFamilyPropertyCount);
vk_outarray_append(&out, p) {
p->queueFamilyProperties = v3dv_queue_family_properties;
vk_foreach_struct(s, p->pNext) {
v3dv_debug_ignored_stype(s->sType);
}
}
}
void
v3dv_GetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceMemoryProperties *pMemoryProperties)
{
V3DV_FROM_HANDLE(v3dv_physical_device, device, physicalDevice);
*pMemoryProperties = device->memory;
}
void
v3dv_GetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceMemoryProperties2 *pMemoryProperties)
{
v3dv_GetPhysicalDeviceMemoryProperties(physicalDevice,
&pMemoryProperties->memoryProperties);
vk_foreach_struct(ext, pMemoryProperties->pNext) {
switch (ext->sType) {
default:
v3dv_debug_ignored_stype(ext->sType);
break;
}
}
}
PFN_vkVoidFunction
v3dv_GetInstanceProcAddr(VkInstance _instance,
const char *pName)
{
V3DV_FROM_HANDLE(v3dv_instance, instance, _instance);
/* The Vulkan 1.0 spec for vkGetInstanceProcAddr has a table of exactly
* when we have to return valid function pointers, NULL, or it's left
* undefined. See the table for exact details.
*/
if (pName == NULL)
return NULL;
#define LOOKUP_V3DV_ENTRYPOINT(entrypoint) \
if (strcmp(pName, "vk" #entrypoint) == 0) \
return (PFN_vkVoidFunction)v3dv_##entrypoint
LOOKUP_V3DV_ENTRYPOINT(EnumerateInstanceExtensionProperties);
LOOKUP_V3DV_ENTRYPOINT(CreateInstance);
#undef LOOKUP_V3DV_ENTRYPOINT
if (instance == NULL)
return NULL;
int idx = v3dv_get_instance_entrypoint_index(pName);
if (idx >= 0)
return instance->dispatch.entrypoints[idx];
idx = v3dv_get_physical_device_entrypoint_index(pName);
if (idx >= 0)
return instance->physicalDevice.dispatch.entrypoints[idx];
idx = v3dv_get_device_entrypoint_index(pName);
if (idx >= 0)
return instance->device_dispatch.entrypoints[idx];
return NULL;
}
/* With version 1+ of the loader interface the ICD should expose
* vk_icdGetInstanceProcAddr to work around certain LD_PRELOAD issues seen in apps.
*/
PUBLIC
VKAPI_ATTR PFN_vkVoidFunction
VKAPI_CALL vk_icdGetInstanceProcAddr(VkInstance instance,
const char *pName);
PUBLIC
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
vk_icdGetInstanceProcAddr(VkInstance instance,
const char* pName)
{
return v3dv_GetInstanceProcAddr(instance, pName);
}
PFN_vkVoidFunction
v3dv_GetDeviceProcAddr(VkDevice _device,
const char *pName)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
if (!device || !pName)
return NULL;
int idx = v3dv_get_device_entrypoint_index(pName);
if (idx < 0)
return NULL;
return device->dispatch.entrypoints[idx];
}
/* With version 4+ of the loader interface the ICD should expose
* vk_icdGetPhysicalDeviceProcAddr()
*/
PUBLIC
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
vk_icdGetPhysicalDeviceProcAddr(VkInstance _instance,
const char* pName);
PFN_vkVoidFunction
vk_icdGetPhysicalDeviceProcAddr(VkInstance _instance,
const char* pName)
{
V3DV_FROM_HANDLE(v3dv_instance, instance, _instance);
if (!pName || !instance)
return NULL;
int idx = v3dv_get_physical_device_entrypoint_index(pName);
if (idx < 0)
return NULL;
return instance->physicalDevice.dispatch.entrypoints[idx];
}
VkResult
v3dv_EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice,
const char *pLayerName,
uint32_t *pPropertyCount,
VkExtensionProperties *pProperties)
{
/* We don't support any layers */
if (pLayerName)
return vk_error(NULL, VK_ERROR_LAYER_NOT_PRESENT);
V3DV_FROM_HANDLE(v3dv_physical_device, device, physicalDevice);
VK_OUTARRAY_MAKE(out, pProperties, pPropertyCount);
for (int i = 0; i < V3DV_DEVICE_EXTENSION_COUNT; i++) {
if (device->supported_extensions.extensions[i]) {
vk_outarray_append(&out, prop) {
*prop = v3dv_device_extensions[i];
}
}
}
return vk_outarray_status(&out);
}
VkResult
v3dv_EnumerateInstanceLayerProperties(uint32_t *pPropertyCount,
VkLayerProperties *pProperties)
{
if (pProperties == NULL) {
*pPropertyCount = 0;
return VK_SUCCESS;
}
return vk_error(NULL, VK_ERROR_LAYER_NOT_PRESENT);
}
VkResult
v3dv_EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice,
uint32_t *pPropertyCount,
VkLayerProperties *pProperties)
{
V3DV_FROM_HANDLE(v3dv_physical_device, physical_device, physicalDevice);
if (pProperties == NULL) {
*pPropertyCount = 0;
return VK_SUCCESS;
}
return vk_error(physical_device->instance, VK_ERROR_LAYER_NOT_PRESENT);
}
static VkResult
queue_init(struct v3dv_device *device, struct v3dv_queue *queue)
{
queue->_loader_data.loaderMagic = ICD_LOADER_MAGIC;
queue->device = device;
queue->flags = 0;
list_inithead(&queue->submit_wait_list);
pthread_mutex_init(&queue->mutex, NULL);
return VK_SUCCESS;
}
static void
queue_finish(struct v3dv_queue *queue)
{
assert(list_is_empty(&queue->submit_wait_list));
pthread_mutex_destroy(&queue->mutex);
}
static void
init_device_dispatch(struct v3dv_device *device)
{
for (unsigned i = 0; i < ARRAY_SIZE(device->dispatch.entrypoints); i++) {
/* Vulkan requires that entrypoints for extensions which have not been
* enabled must not be advertised.
*/
if (!v3dv_device_entrypoint_is_enabled(i, device->instance->app_info.api_version,
&device->instance->enabled_extensions,
&device->enabled_extensions)) {
device->dispatch.entrypoints[i] = NULL;
} else {
device->dispatch.entrypoints[i] =
v3dv_device_dispatch_table.entrypoints[i];
}
}
}
static uint32_t
u64_hash(const void *key)
{
return _mesa_hash_data(key, sizeof(uint64_t));
}
static bool
u64_compare(const void *key1, const void *key2)
{
return memcmp(key1, key2, sizeof(uint64_t)) == 0;
}
static void
init_meta_clear_resources(struct v3dv_device *device)
{
device->meta.color_clear.cache =
_mesa_hash_table_create(NULL, u64_hash, u64_compare);
device->meta.depth_clear.cache =
_mesa_hash_table_create(NULL, u64_hash, u64_compare);
}
static uint32_t
meta_blit_key_hash(const void *key)
{
return _mesa_hash_data(key, V3DV_META_BLIT_CACHE_KEY_SIZE);
}
static bool
meta_blit_key_compare(const void *key1, const void *key2)
{
return memcmp(key1, key2, V3DV_META_BLIT_CACHE_KEY_SIZE) == 0;
}
static void
init_meta_blit_resources(struct v3dv_device *device)
{
for (uint32_t i = 0; i < 3; i++) {
device->meta.blit.cache[i] =
_mesa_hash_table_create(NULL,
meta_blit_key_hash,
meta_blit_key_compare);
}
}
static void
init_device_meta(struct v3dv_device *device)
{
mtx_init(&device->meta.mtx, mtx_plain);
init_meta_clear_resources(device);
init_meta_blit_resources(device);
}
static void
destroy_device_meta(struct v3dv_device *device)
{
VkDevice _device = v3dv_device_to_handle(device);
mtx_destroy(&device->meta.mtx);
hash_table_foreach(device->meta.color_clear.cache, entry) {
struct v3dv_meta_color_clear_pipeline *item = entry->data;
v3dv_DestroyPipeline(_device, item->pipeline, &device->alloc);
if (item->free_render_pass)
v3dv_DestroyRenderPass(_device, item->pass, &device->alloc);
vk_free(&device->alloc, item);
}
_mesa_hash_table_destroy(device->meta.color_clear.cache, NULL);
if (device->meta.color_clear.playout) {
v3dv_DestroyPipelineLayout(_device, device->meta.color_clear.playout,
&device->alloc);
}
hash_table_foreach(device->meta.depth_clear.cache, entry) {
struct v3dv_meta_depth_clear_pipeline *item = entry->data;
v3dv_DestroyPipeline(_device, item->pipeline, &device->alloc);
vk_free(&device->alloc, item);
}
_mesa_hash_table_destroy(device->meta.depth_clear.cache, NULL);
if (device->meta.depth_clear.playout) {
v3dv_DestroyPipelineLayout(_device, device->meta.depth_clear.playout,
&device->alloc);
}
for (uint32_t i = 0; i < 3; i++) {
hash_table_foreach(device->meta.blit.cache[i], entry) {
struct v3dv_meta_blit_pipeline *item = entry->data;
v3dv_DestroyPipeline(_device, item->pipeline, &device->alloc);
v3dv_DestroyRenderPass(_device, item->pass, &device->alloc);
vk_free(&device->alloc, item);
}
_mesa_hash_table_destroy(device->meta.blit.cache[i], NULL);
}
if (device->meta.blit.playout) {
v3dv_DestroyPipelineLayout(_device, device->meta.blit.playout,
&device->alloc);
}
if (device->meta.blit.dslayout) {
v3dv_DestroyDescriptorSetLayout(_device, device->meta.blit.dslayout,
&device->alloc);
}
}
VkResult
v3dv_CreateDevice(VkPhysicalDevice physicalDevice,
const VkDeviceCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkDevice *pDevice)
{
V3DV_FROM_HANDLE(v3dv_physical_device, physical_device, physicalDevice);
struct v3dv_instance *instance = physical_device->instance;
VkResult result;
struct v3dv_device *device;
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO);
/* Check enabled extensions */
struct v3dv_device_extension_table enabled_extensions = { };
for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
int idx;
for (idx = 0; idx < V3DV_DEVICE_EXTENSION_COUNT; idx++) {
if (strcmp(pCreateInfo->ppEnabledExtensionNames[i],
v3dv_device_extensions[idx].extensionName) == 0)
break;
}
if (idx >= V3DV_DEVICE_EXTENSION_COUNT)
return vk_error(instance, VK_ERROR_EXTENSION_NOT_PRESENT);
if (!physical_device->supported_extensions.extensions[idx])
return vk_error(instance, VK_ERROR_EXTENSION_NOT_PRESENT);
enabled_extensions.extensions[idx] = true;
}
/* Check enabled features */
if (pCreateInfo->pEnabledFeatures) {
VkPhysicalDeviceFeatures supported_features;
v3dv_GetPhysicalDeviceFeatures(physicalDevice, &supported_features);
VkBool32 *supported_feature = (VkBool32 *)&supported_features;
VkBool32 *enabled_feature = (VkBool32 *)pCreateInfo->pEnabledFeatures;
unsigned num_features = sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32);
for (uint32_t i = 0; i < num_features; i++) {
if (enabled_feature[i] && !supported_feature[i])
return vk_error(instance, VK_ERROR_FEATURE_NOT_PRESENT);
}
}
/* Check requested queues (we only expose one queue ) */
assert(pCreateInfo->queueCreateInfoCount == 1);
for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++) {
assert(pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex == 0);
assert(pCreateInfo->pQueueCreateInfos[i].queueCount == 1);
if (pCreateInfo->pQueueCreateInfos[i].flags != 0)
return vk_error(instance, VK_ERROR_INITIALIZATION_FAILED);
}
device = vk_zalloc2(&physical_device->instance->alloc, pAllocator,
sizeof(*device), 8,
VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
if (!device)
return vk_error(instance, VK_ERROR_OUT_OF_HOST_MEMORY);
device->_loader_data.loaderMagic = ICD_LOADER_MAGIC;
device->instance = instance;
if (pAllocator)
device->alloc = *pAllocator;
else
device->alloc = physical_device->instance->alloc;
device->render_fd = physical_device->render_fd;
if (device->render_fd == -1) {
result = VK_ERROR_INITIALIZATION_FAILED;
goto fail;
}
if (physical_device->display_fd != -1) {
device->display_fd = physical_device->display_fd;
if (device->display_fd == -1) {
result = VK_ERROR_INITIALIZATION_FAILED;
goto fail;
}
} else {
device->display_fd = -1;
}
pthread_mutex_init(&device->mutex, NULL);
result = queue_init(device, &device->queue);
if (result != VK_SUCCESS)
goto fail;
device->devinfo = physical_device->devinfo;
device->enabled_extensions = enabled_extensions;
int ret = drmSyncobjCreate(device->render_fd,
DRM_SYNCOBJ_CREATE_SIGNALED,
&device->last_job_sync);
if (ret) {
result = VK_ERROR_INITIALIZATION_FAILED;
goto fail;
}
init_device_dispatch(device);
init_device_meta(device);
v3dv_bo_cache_init(device);
*pDevice = v3dv_device_to_handle(device);
return VK_SUCCESS;
fail:
vk_free(&device->alloc, device);
return result;
}
void
v3dv_DestroyDevice(VkDevice _device,
const VkAllocationCallbacks *pAllocator)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
v3dv_DeviceWaitIdle(_device);
queue_finish(&device->queue);
pthread_mutex_destroy(&device->mutex);
drmSyncobjDestroy(device->render_fd, device->last_job_sync);
destroy_device_meta(device);
v3dv_bo_cache_destroy(device);
vk_free2(&default_alloc, pAllocator, device);
}
void
v3dv_GetDeviceQueue(VkDevice _device,
uint32_t queueFamilyIndex,
uint32_t queueIndex,
VkQueue *pQueue)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
assert(queueIndex == 0);
assert(queueFamilyIndex == 0);
*pQueue = v3dv_queue_to_handle(&device->queue);
}
VkResult
v3dv_DeviceWaitIdle(VkDevice _device)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
return v3dv_QueueWaitIdle(v3dv_queue_to_handle(&device->queue));
}
VkResult
v3dv_CreateDebugReportCallbackEXT(VkInstance _instance,
const VkDebugReportCallbackCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkDebugReportCallbackEXT* pCallback)
{
V3DV_FROM_HANDLE(v3dv_instance, instance, _instance);
return vk_create_debug_report_callback(&instance->debug_report_callbacks,
pCreateInfo, pAllocator, &instance->alloc,
pCallback);
}
void
v3dv_DestroyDebugReportCallbackEXT(VkInstance _instance,
VkDebugReportCallbackEXT _callback,
const VkAllocationCallbacks* pAllocator)
{
V3DV_FROM_HANDLE(v3dv_instance, instance, _instance);
vk_destroy_debug_report_callback(&instance->debug_report_callbacks,
_callback, pAllocator, &instance->alloc);
}
static VkResult
device_alloc(struct v3dv_device *device,
struct v3dv_device_memory *mem,
VkDeviceSize size)
{
/* Our kernel interface is 32-bit */
if (size > UINT32_MAX)
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
mem->bo = v3dv_bo_alloc(device, size, "device_alloc", false);
if (!mem->bo)
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
return VK_SUCCESS;
}
static void
device_free(struct v3dv_device *device, struct v3dv_device_memory *mem)
{
if (mem->has_bo_ownership)
v3dv_bo_free(device, mem->bo);
else if (mem->bo)
vk_free(&device->alloc, mem->bo);
}
static void
device_unmap(struct v3dv_device *device, struct v3dv_device_memory *mem)
{
assert(mem && mem->bo->map && mem->bo->map_size > 0);
v3dv_bo_unmap(device, mem->bo);
}
static VkResult
device_map(struct v3dv_device *device, struct v3dv_device_memory *mem)
{
assert(mem && mem->bo);
/* From the spec:
*
* "After a successful call to vkMapMemory the memory object memory is
* considered to be currently host mapped. It is an application error to
* call vkMapMemory on a memory object that is already host mapped."
*
* We are not concerned with this ourselves (validation layers should
* catch these errors and warn users), however, the driver may internally
* map things (for example for debug CLIF dumps or some CPU-side operations)
* so by the time the user calls here the buffer might already been mapped
* internally by the driver.
*/
if (mem->bo->map) {
assert(mem->bo->map_size == mem->bo->size);
return VK_SUCCESS;
}
bool ok = v3dv_bo_map(device, mem->bo, mem->bo->size);
if (!ok)
return VK_ERROR_MEMORY_MAP_FAILED;
return VK_SUCCESS;
}
static VkResult
device_import_bo(struct v3dv_device *device,
const VkAllocationCallbacks *pAllocator,
int fd, uint64_t size,
struct v3dv_bo **bo)
{
VkResult result;
*bo = vk_alloc2(&device->alloc, pAllocator, sizeof(struct v3dv_bo), 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (*bo == NULL) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail;
}
off_t real_size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
if (real_size < 0 || (uint64_t) real_size < size) {
result = VK_ERROR_INVALID_EXTERNAL_HANDLE;
goto fail;
}
int ret;
uint32_t handle;
ret = drmPrimeFDToHandle(device->render_fd, fd, &handle);
if (ret) {
result = VK_ERROR_INVALID_EXTERNAL_HANDLE;
goto fail;
}
struct drm_v3d_get_bo_offset get_offset = {
.handle = handle,
};
ret = v3dv_ioctl(device->render_fd, DRM_IOCTL_V3D_GET_BO_OFFSET, &get_offset);
if (ret) {
result = VK_ERROR_INVALID_EXTERNAL_HANDLE;
goto fail;
}
assert(get_offset.offset != 0);
(*bo)->handle = handle;
(*bo)->size = size;
(*bo)->offset = get_offset.offset;
(*bo)->map = NULL;
(*bo)->map_size = 0;
(*bo)->private = false;
return VK_SUCCESS;
fail:
if (*bo) {
vk_free2(&device->alloc, pAllocator, *bo);
*bo = NULL;
}
return result;
}
static VkResult
device_alloc_for_wsi(struct v3dv_device *device,
const VkAllocationCallbacks *pAllocator,
struct v3dv_device_memory *mem,
VkDeviceSize size)
{
/* In the simulator we can get away with a regular allocation since both
* allocation and rendering happen in the same DRM render node. On actual
* hardware we need to allocate our winsys BOs on the vc4 display device
* and import them into v3d.
*/
#if using_v3d_simulator
return device_alloc(device, mem, size);
#else
assert(device->display_fd != -1);
int display_fd = device->instance->physicalDevice.display_fd;
struct drm_mode_create_dumb create_dumb = {
.width = 1024, /* one page */
.height = align(size, 4096) / 4096,
.bpp = util_format_get_blocksizebits(PIPE_FORMAT_RGBA8888_UNORM),
};
int err;
err = v3dv_ioctl(display_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
if (err < 0)
goto fail_create;
int fd;
err =
drmPrimeHandleToFD(display_fd, create_dumb.handle, O_CLOEXEC, &fd);
if (err < 0)
goto fail_export;
VkResult result = device_import_bo(device, pAllocator, fd, size, &mem->bo);
close(fd);
if (result != VK_SUCCESS)
goto fail_import;
return VK_SUCCESS;
fail_import:
fail_export: {
struct drm_mode_destroy_dumb destroy_dumb = {
.handle = create_dumb.handle,
};
v3dv_ioctl(display_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb);
}
fail_create:
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
#endif
}
VkResult
v3dv_AllocateMemory(VkDevice _device,
const VkMemoryAllocateInfo *pAllocateInfo,
const VkAllocationCallbacks *pAllocator,
VkDeviceMemory *pMem)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
struct v3dv_device_memory *mem;
struct v3dv_physical_device *pdevice = &device->instance->physicalDevice;
assert(pAllocateInfo->sType == VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO);
/* The Vulkan 1.0.33 spec says "allocationSize must be greater than 0". */
assert(pAllocateInfo->allocationSize > 0);
mem = vk_alloc2(&device->alloc, pAllocator, sizeof(*mem), 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (mem == NULL)
return vk_error(NULL, VK_ERROR_OUT_OF_HOST_MEMORY);
assert(pAllocateInfo->memoryTypeIndex < pdevice->memory.memoryTypeCount);
mem->type = &pdevice->memory.memoryTypes[pAllocateInfo->memoryTypeIndex];
mem->has_bo_ownership = true;
const struct wsi_memory_allocate_info *wsi_info = NULL;
const VkImportMemoryFdInfoKHR *fd_info = NULL;
vk_foreach_struct_const(ext, pAllocateInfo->pNext) {
switch ((unsigned)ext->sType) {
case VK_STRUCTURE_TYPE_WSI_MEMORY_ALLOCATE_INFO_MESA:
wsi_info = (void *)ext;
break;
case VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR:
fd_info = (void *)ext;
break;
default:
v3dv_debug_ignored_stype(ext->sType);
break;
}
}
VkResult result = VK_SUCCESS;
if (wsi_info) {
result = device_alloc_for_wsi(device, pAllocator, mem,
pAllocateInfo->allocationSize);
} else if (fd_info && fd_info->handleType) {
assert(fd_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT ||
fd_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT);
result = device_import_bo(device, pAllocator,
fd_info->fd, pAllocateInfo->allocationSize,
&mem->bo);
mem->has_bo_ownership = false;
if (result == VK_SUCCESS)
close(fd_info->fd);
} else {
result = device_alloc(device, mem, pAllocateInfo->allocationSize);
}
if (result != VK_SUCCESS) {
vk_free2(&device->alloc, pAllocator, mem);
return vk_error(device->instance, result);
}
*pMem = v3dv_device_memory_to_handle(mem);
return result;
}
void
v3dv_FreeMemory(VkDevice _device,
VkDeviceMemory _mem,
const VkAllocationCallbacks *pAllocator)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
V3DV_FROM_HANDLE(v3dv_device_memory, mem, _mem);
if (mem == NULL)
return;
if (mem->bo->map)
v3dv_UnmapMemory(_device, _mem);
device_free(device, mem);
vk_free2(&device->alloc, pAllocator, mem);
}
VkResult
v3dv_MapMemory(VkDevice _device,
VkDeviceMemory _memory,
VkDeviceSize offset,
VkDeviceSize size,
VkMemoryMapFlags flags,
void **ppData)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
V3DV_FROM_HANDLE(v3dv_device_memory, mem, _memory);
if (mem == NULL) {
*ppData = NULL;
return VK_SUCCESS;
}
assert(offset < mem->bo->size);
/* Since the driver can map BOs internally as well and the mapped range
* required by the user or the driver might not be the same, we always map
* the entire BO and then add the requested offset to the start address
* of the mapped region.
*/
VkResult result = device_map(device, mem);
if (result != VK_SUCCESS)
return vk_error(device->instance, result);
*ppData = ((uint8_t *) mem->bo->map) + offset;
return VK_SUCCESS;
}
void
v3dv_UnmapMemory(VkDevice _device,
VkDeviceMemory _memory)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
V3DV_FROM_HANDLE(v3dv_device_memory, mem, _memory);
if (mem == NULL)
return;
device_unmap(device, mem);
}
VkResult
v3dv_FlushMappedMemoryRanges(VkDevice _device,
uint32_t memoryRangeCount,
const VkMappedMemoryRange *pMemoryRanges)
{
/* FIXME: stub (although note that both radv and tu just returns success
* here. Pending further research)
*/
return VK_SUCCESS;
}
VkResult
v3dv_InvalidateMappedMemoryRanges(VkDevice _device,
uint32_t memoryRangeCount,
const VkMappedMemoryRange *pMemoryRanges)
{
/* FIXME: stub (although note that both radv and tu just returns success
* here. Pending further research)
*/
return VK_SUCCESS;
}
void
v3dv_GetImageMemoryRequirements(VkDevice _device,
VkImage _image,
VkMemoryRequirements *pMemoryRequirements)
{
V3DV_FROM_HANDLE(v3dv_image, image, _image);
assert(image->size > 0);
pMemoryRequirements->size = image->size;
pMemoryRequirements->alignment = image->alignment;
pMemoryRequirements->memoryTypeBits = 0x3; /* Both memory types */
}
VkResult
v3dv_BindImageMemory(VkDevice _device,
VkImage _image,
VkDeviceMemory _memory,
VkDeviceSize memoryOffset)
{
V3DV_FROM_HANDLE(v3dv_device_memory, mem, _memory);
V3DV_FROM_HANDLE(v3dv_image, image, _image);
/* Valid usage:
*
* "memoryOffset must be an integer multiple of the alignment member of
* the VkMemoryRequirements structure returned from a call to
* vkGetImageMemoryRequirements with image"
*/
assert(memoryOffset % image->alignment == 0);
assert(memoryOffset < mem->bo->size);
image->mem = mem;
image->mem_offset = memoryOffset;
return VK_SUCCESS;
}
void
v3dv_GetBufferMemoryRequirements(VkDevice _device,
VkBuffer _buffer,
VkMemoryRequirements* pMemoryRequirements)
{
V3DV_FROM_HANDLE(v3dv_buffer, buffer, _buffer);
pMemoryRequirements->memoryTypeBits = 0x3; /* Both memory types */
pMemoryRequirements->alignment = buffer->alignment;
pMemoryRequirements->size =
align64(buffer->size, pMemoryRequirements->alignment);
}
VkResult
v3dv_BindBufferMemory(VkDevice _device,
VkBuffer _buffer,
VkDeviceMemory _memory,
VkDeviceSize memoryOffset)
{
V3DV_FROM_HANDLE(v3dv_device_memory, mem, _memory);
V3DV_FROM_HANDLE(v3dv_buffer, buffer, _buffer);
/* Valid usage:
*
* "memoryOffset must be an integer multiple of the alignment member of
* the VkMemoryRequirements structure returned from a call to
* vkGetBufferMemoryRequirements with buffer"
*/
assert(memoryOffset % buffer->alignment == 0);
assert(memoryOffset < mem->bo->size);
buffer->mem = mem;
buffer->mem_offset = memoryOffset;
return VK_SUCCESS;
}
VkResult
v3dv_CreateBuffer(VkDevice _device,
const VkBufferCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkBuffer *pBuffer)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
struct v3dv_buffer *buffer;
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO);
assert(pCreateInfo->usage != 0);
/* We don't support any flags for now */
assert(pCreateInfo->flags == 0);
buffer = vk_alloc2(&device->alloc, pAllocator, sizeof(*buffer), 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (buffer == NULL)
return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
buffer->size = pCreateInfo->size;
buffer->usage = pCreateInfo->usage;
buffer->alignment = 256; /* nonCoherentAtomSize */
/* Limit allocations to 32-bit */
const VkDeviceSize aligned_size = align64(buffer->size, buffer->alignment);
if (aligned_size > UINT32_MAX || aligned_size < buffer->size)
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
*pBuffer = v3dv_buffer_to_handle(buffer);
return VK_SUCCESS;
}
void
v3dv_DestroyBuffer(VkDevice _device,
VkBuffer _buffer,
const VkAllocationCallbacks *pAllocator)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
V3DV_FROM_HANDLE(v3dv_buffer, buffer, _buffer);
if (!buffer)
return;
vk_free2(&device->alloc, pAllocator, buffer);
}
/**
* This computes the maximum bpp used by any of the render targets used by
* a particular subpass. If we don't have a subpass (when we are not inside a
* render pass), then we assume that all framebuffer attachments are used.
*/
uint8_t
v3dv_framebuffer_compute_internal_bpp(const struct v3dv_framebuffer *framebuffer,
const struct v3dv_subpass *subpass)
{
STATIC_ASSERT(RENDER_TARGET_MAXIMUM_32BPP == 0);
uint8_t max_bpp = RENDER_TARGET_MAXIMUM_32BPP;
if (subpass) {
for (uint32_t i = 0; i < subpass->color_count; i++) {
uint32_t att_idx = subpass->color_attachments[i].attachment;
if (att_idx == VK_ATTACHMENT_UNUSED)
continue;
const struct v3dv_image_view *att = framebuffer->attachments[att_idx];
assert(att);
if (att->aspects & VK_IMAGE_ASPECT_COLOR_BIT)
max_bpp = MAX2(max_bpp, att->internal_bpp);
}
return max_bpp;
}
assert(framebuffer->attachment_count <= 4);
for (uint32_t i = 0; i < framebuffer->attachment_count; i++) {
const struct v3dv_image_view *att = framebuffer->attachments[i];
assert(att);
if (att->aspects & VK_IMAGE_ASPECT_COLOR_BIT)
max_bpp = MAX2(max_bpp, att->internal_bpp);
}
return max_bpp;
}
VkResult
v3dv_CreateFramebuffer(VkDevice _device,
const VkFramebufferCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkFramebuffer *pFramebuffer)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
struct v3dv_framebuffer *framebuffer;
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO);
size_t size = sizeof(*framebuffer) +
sizeof(struct v3dv_image_view *) * pCreateInfo->attachmentCount;
framebuffer = vk_alloc2(&device->alloc, pAllocator, size, 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (framebuffer == NULL)
return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
framebuffer->width = pCreateInfo->width;
framebuffer->height = pCreateInfo->height;
framebuffer->layers = pCreateInfo->layers;
framebuffer->attachment_count = pCreateInfo->attachmentCount;
framebuffer->color_attachment_count = 0;
for (uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) {
framebuffer->attachments[i] =
v3dv_image_view_from_handle(pCreateInfo->pAttachments[i]);
if (framebuffer->attachments[i]->aspects & VK_IMAGE_ASPECT_COLOR_BIT)
framebuffer->color_attachment_count++;
}
*pFramebuffer = v3dv_framebuffer_to_handle(framebuffer);
return VK_SUCCESS;
}
void
v3dv_DestroyFramebuffer(VkDevice _device,
VkFramebuffer _fb,
const VkAllocationCallbacks *pAllocator)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
V3DV_FROM_HANDLE(v3dv_framebuffer, fb, _fb);
if (!fb)
return;
vk_free2(&device->alloc, pAllocator, fb);
}
VkResult
v3dv_GetMemoryFdPropertiesKHR(VkDevice _device,
VkExternalMemoryHandleTypeFlagBits handleType,
int fd,
VkMemoryFdPropertiesKHR *pMemoryFdProperties)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
struct v3dv_physical_device *pdevice = &device->instance->physicalDevice;
switch (handleType) {
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT:
pMemoryFdProperties->memoryTypeBits =
(1 << pdevice->memory.memoryTypeCount) - 1;
return VK_SUCCESS;
default:
return vk_error(device->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
}
}
VkResult
v3dv_GetMemoryFdKHR(VkDevice _device,
const VkMemoryGetFdInfoKHR *pGetFdInfo,
int *pFd)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
V3DV_FROM_HANDLE(v3dv_device_memory, mem, pGetFdInfo->memory);
assert(pGetFdInfo->sType == VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR);
assert(pGetFdInfo->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT ||
pGetFdInfo->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT);
int fd, ret;
ret =
drmPrimeHandleToFD(device->render_fd, mem->bo->handle, DRM_CLOEXEC, &fd);
if (ret)
return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
*pFd = fd;
return VK_SUCCESS;
}
VkResult
v3dv_CreateEvent(VkDevice _device,
const VkEventCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkEvent *pEvent)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
struct v3dv_event *event =
vk_alloc2(&device->alloc, pAllocator, sizeof(*event), 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (!event)
return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
/* Events are created in the unsignaled state */
event->state = false;
*pEvent = v3dv_event_to_handle(event);
return VK_SUCCESS;
}
void
v3dv_DestroyEvent(VkDevice _device,
VkEvent _event,
const VkAllocationCallbacks *pAllocator)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
V3DV_FROM_HANDLE(v3dv_event, event, _event);
if (!event)
return;
vk_free2(&device->alloc, pAllocator, event);
}
VkResult
v3dv_GetEventStatus(VkDevice _device, VkEvent _event)
{
V3DV_FROM_HANDLE(v3dv_event, event, _event);
return p_atomic_read(&event->state) ? VK_EVENT_SET : VK_EVENT_RESET;
}
VkResult
v3dv_SetEvent(VkDevice _device, VkEvent _event)
{
V3DV_FROM_HANDLE(v3dv_event, event, _event);
p_atomic_set(&event->state, 1);
return VK_SUCCESS;
}
VkResult
v3dv_ResetEvent(VkDevice _device, VkEvent _event)
{
V3DV_FROM_HANDLE(v3dv_event, event, _event);
p_atomic_set(&event->state, 0);
return VK_SUCCESS;
}
static const enum V3DX(Wrap_Mode) vk_to_v3d_wrap_mode[] = {
[VK_SAMPLER_ADDRESS_MODE_REPEAT] = V3D_WRAP_MODE_REPEAT,
[VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT] = V3D_WRAP_MODE_MIRROR,
[VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE] = V3D_WRAP_MODE_CLAMP,
[VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE] = V3D_WRAP_MODE_MIRROR_ONCE,
[VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER] = V3D_WRAP_MODE_BORDER,
};
static const enum V3DX(Compare_Function)
vk_to_v3d_compare_func[] = {
[VK_COMPARE_OP_NEVER] = V3D_COMPARE_FUNC_NEVER,
[VK_COMPARE_OP_LESS] = V3D_COMPARE_FUNC_LESS,
[VK_COMPARE_OP_EQUAL] = V3D_COMPARE_FUNC_EQUAL,
[VK_COMPARE_OP_LESS_OR_EQUAL] = V3D_COMPARE_FUNC_LEQUAL,
[VK_COMPARE_OP_GREATER] = V3D_COMPARE_FUNC_GREATER,
[VK_COMPARE_OP_NOT_EQUAL] = V3D_COMPARE_FUNC_NOTEQUAL,
[VK_COMPARE_OP_GREATER_OR_EQUAL] = V3D_COMPARE_FUNC_GEQUAL,
[VK_COMPARE_OP_ALWAYS] = V3D_COMPARE_FUNC_ALWAYS,
};
static void
pack_sampler_state(struct v3dv_sampler *sampler,
const VkSamplerCreateInfo *pCreateInfo)
{
enum V3DX(Border_Color_Mode) border_color_mode;
/* For now we only support the preset Vulkan border color modes. If we
* want to implement VK_EXT_custom_border_color in the future we would have
* to use V3D_BORDER_COLOR_FOLLOWS, and fill up border_color_word_[0/1/2/3]
* SAMPLER_STATE.
*/
switch (pCreateInfo->borderColor) {
case VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK:
case VK_BORDER_COLOR_INT_TRANSPARENT_BLACK:
border_color_mode = V3D_BORDER_COLOR_0000;
break;
case VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK:
case VK_BORDER_COLOR_INT_OPAQUE_BLACK:
border_color_mode = V3D_BORDER_COLOR_0001;
break;
case VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE:
case VK_BORDER_COLOR_INT_OPAQUE_WHITE:
border_color_mode = V3D_BORDER_COLOR_1111;
break;
default:
unreachable("Unknown border color");
break;
}
/* For some texture formats, when clamping to transparent black border the
* CTS expects alpha to be set to 1 instead of 0, but the border color mode
* will take priority over the texture state swizzle, so the only way to
* fix that is to apply a swizzle in the shader. Here we keep track of
* whether we are activating that mode and we will decide if we need to
* activate the texture swizzle lowering in the shader key at compile time
* depending on the actual texture format.
*/
if ((pCreateInfo->addressModeU == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
pCreateInfo->addressModeV == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
pCreateInfo->addressModeW == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER) &&
border_color_mode == V3D_BORDER_COLOR_0000) {
sampler->clamp_to_transparent_black_border = true;
}
v3dv_pack(sampler->sampler_state, SAMPLER_STATE, s) {
if (pCreateInfo->anisotropyEnable) {
s.anisotropy_enable = true;
if (pCreateInfo->maxAnisotropy > 8)
s.maximum_anisotropy = 3;
else if (pCreateInfo->maxAnisotropy > 4)
s.maximum_anisotropy = 2;
else if (pCreateInfo->maxAnisotropy > 2)
s.maximum_anisotropy = 1;
}
s.border_color_mode = border_color_mode;
s.wrap_i_border = false; /* Also hardcoded on v3d */
s.wrap_s = vk_to_v3d_wrap_mode[pCreateInfo->addressModeU];
s.wrap_t = vk_to_v3d_wrap_mode[pCreateInfo->addressModeV];
s.wrap_r = vk_to_v3d_wrap_mode[pCreateInfo->addressModeW];
s.fixed_bias = pCreateInfo->mipLodBias;
s.max_level_of_detail = MIN2(MAX2(0, pCreateInfo->maxLod), 15);
s.min_level_of_detail = MIN2(MAX2(0, pCreateInfo->minLod), 15);
s.srgb_disable = 0; /* Not even set by v3d */
s.depth_compare_function =
vk_to_v3d_compare_func[pCreateInfo->compareEnable ?
pCreateInfo->compareOp : VK_COMPARE_OP_NEVER];
s.mip_filter_nearest = pCreateInfo->mipmapMode == VK_SAMPLER_MIPMAP_MODE_NEAREST;
s.min_filter_nearest = pCreateInfo->minFilter == VK_FILTER_NEAREST;
s.mag_filter_nearest = pCreateInfo->magFilter == VK_FILTER_NEAREST;
}
}
VkResult
v3dv_CreateSampler(VkDevice _device,
const VkSamplerCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkSampler *pSampler)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
struct v3dv_sampler *sampler;
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO);
sampler = vk_zalloc2(&device->alloc, pAllocator, sizeof(*sampler), 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (!sampler)
return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
sampler->compare_enable = pCreateInfo->compareEnable;
sampler->unnormalized_coordinates = pCreateInfo->unnormalizedCoordinates;
pack_sampler_state(sampler, pCreateInfo);
*pSampler = v3dv_sampler_to_handle(sampler);
return VK_SUCCESS;
}
void
v3dv_DestroySampler(VkDevice _device,
VkSampler _sampler,
const VkAllocationCallbacks *pAllocator)
{
V3DV_FROM_HANDLE(v3dv_device, device, _device);
V3DV_FROM_HANDLE(v3dv_sampler, sampler, _sampler);
if (!sampler)
return;
vk_free2(&device->alloc, pAllocator, sampler);
}
void
v3dv_GetDeviceMemoryCommitment(VkDevice device,
VkDeviceMemory memory,
VkDeviceSize *pCommittedMemoryInBytes)
{
*pCommittedMemoryInBytes = 0;
}
void
v3dv_GetImageSparseMemoryRequirements(
VkDevice device,
VkImage image,
uint32_t *pSparseMemoryRequirementCount,
VkSparseImageMemoryRequirements *pSparseMemoryRequirements)
{
*pSparseMemoryRequirementCount = 0;
}
void
v3dv_GetImageSparseMemoryRequirements2(
VkDevice device,
const VkImageSparseMemoryRequirementsInfo2 *pInfo,
uint32_t *pSparseMemoryRequirementCount,
VkSparseImageMemoryRequirements2 *pSparseMemoryRequirements)
{
*pSparseMemoryRequirementCount = 0;
}