mirror of
https://github.com/morgan9e/grd
synced 2026-04-13 16:04:13 +09:00
576 lines
17 KiB
C
576 lines
17 KiB
C
/*
|
|
* Copyright (C) 2022 Pascal Nowack
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "grd-vk-device.h"
|
|
|
|
#include <gio/gio.h>
|
|
|
|
#include "grd-hwaccel-vulkan.h"
|
|
#include "grd-vk-physical-device.h"
|
|
#include "grd-vk-queue.h"
|
|
#include "grd-vk-utils.h"
|
|
|
|
#define MAX_DEVICE_QUEUES 6
|
|
|
|
typedef struct
|
|
{
|
|
GrdVkQueue *queue;
|
|
uint64_t ref_count;
|
|
} QueueInfo;
|
|
|
|
struct _GrdVkDevice
|
|
{
|
|
GObject parent;
|
|
|
|
GrdVkPhysicalDevice *physical_device;
|
|
|
|
VkDevice vk_device;
|
|
VkPipelineCache vk_pipeline_cache;
|
|
|
|
uint32_t queue_family_idx;
|
|
uint32_t max_queues;
|
|
|
|
GMutex queue_pool_mutex;
|
|
GHashTable *queue_table;
|
|
|
|
float timestamp_period;
|
|
VkDeviceSize non_coherent_atom_size;
|
|
int64_t drm_render_node;
|
|
VkDriverId driver_id;
|
|
|
|
GrdVkDeviceFuncs device_funcs;
|
|
GrdVkShaderModules shader_modules;
|
|
};
|
|
|
|
G_DEFINE_TYPE (GrdVkDevice, grd_vk_device, G_TYPE_OBJECT)
|
|
|
|
GrdVkPhysicalDevice *
|
|
grd_vk_device_get_physical_device (GrdVkDevice *device)
|
|
{
|
|
return device->physical_device;
|
|
}
|
|
|
|
VkDevice
|
|
grd_vk_device_get_device (GrdVkDevice *device)
|
|
{
|
|
return device->vk_device;
|
|
}
|
|
|
|
VkPipelineCache
|
|
grd_vk_device_get_pipeline_cache (GrdVkDevice *device)
|
|
{
|
|
return device->vk_pipeline_cache;
|
|
}
|
|
|
|
float
|
|
grd_vk_device_get_timestamp_period (GrdVkDevice *device)
|
|
{
|
|
return device->timestamp_period;
|
|
}
|
|
|
|
int64_t
|
|
grd_vk_device_get_drm_render_node (GrdVkDevice *device)
|
|
{
|
|
return device->drm_render_node;
|
|
}
|
|
|
|
VkDriverId
|
|
grd_vk_device_get_driver_id (GrdVkDevice *device)
|
|
{
|
|
return device->driver_id;
|
|
}
|
|
|
|
GrdVkDeviceFuncs *
|
|
grd_vk_device_get_device_funcs (GrdVkDevice *device)
|
|
{
|
|
return &device->device_funcs;
|
|
}
|
|
|
|
const GrdVkShaderModules *
|
|
grd_vk_device_get_shader_modules (GrdVkDevice *device)
|
|
{
|
|
return &device->shader_modules;
|
|
}
|
|
|
|
VkDeviceSize
|
|
grd_vk_device_get_aligned_size (GrdVkDevice *device,
|
|
VkDeviceSize size)
|
|
{
|
|
VkDeviceSize alignment = device->non_coherent_atom_size;
|
|
|
|
return size + (size % alignment ? alignment - size % alignment : 0);
|
|
}
|
|
|
|
GrdVkQueue *
|
|
grd_vk_device_acquire_queue (GrdVkDevice *device)
|
|
{
|
|
/* Least-used-queue queue info */
|
|
QueueInfo *luq_queue_info = NULL;
|
|
QueueInfo *queue_info;
|
|
GHashTableIter iter;
|
|
|
|
g_assert (device->max_queues > 0);
|
|
|
|
g_mutex_lock (&device->queue_pool_mutex);
|
|
g_hash_table_iter_init (&iter, device->queue_table);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &queue_info))
|
|
{
|
|
if (!luq_queue_info || queue_info->ref_count < luq_queue_info->ref_count)
|
|
luq_queue_info = queue_info;
|
|
}
|
|
g_assert (luq_queue_info);
|
|
|
|
g_assert (luq_queue_info->ref_count < UINT64_MAX);
|
|
++luq_queue_info->ref_count;
|
|
g_mutex_unlock (&device->queue_pool_mutex);
|
|
|
|
return luq_queue_info->queue;
|
|
}
|
|
|
|
void
|
|
grd_vk_device_release_queue (GrdVkDevice *device,
|
|
GrdVkQueue *queue)
|
|
{
|
|
uint32_t queue_idx = grd_vk_queue_get_queue_idx (queue);
|
|
QueueInfo *queue_info = NULL;
|
|
|
|
g_mutex_lock (&device->queue_pool_mutex);
|
|
if (!g_hash_table_lookup_extended (device->queue_table,
|
|
GUINT_TO_POINTER (queue_idx),
|
|
NULL, (gpointer *) &queue_info))
|
|
g_assert_not_reached ();
|
|
|
|
g_assert (queue_info->ref_count > 0);
|
|
--queue_info->ref_count;
|
|
g_mutex_unlock (&device->queue_pool_mutex);
|
|
}
|
|
|
|
static void
|
|
get_queue_family_properties (VkPhysicalDevice vk_physical_device,
|
|
VkQueueFamilyProperties2 **properties_2,
|
|
uint32_t *n_properties_2)
|
|
{
|
|
uint32_t i;
|
|
|
|
*properties_2 = NULL;
|
|
*n_properties_2 = 0;
|
|
|
|
vkGetPhysicalDeviceQueueFamilyProperties2 (vk_physical_device,
|
|
n_properties_2, NULL);
|
|
g_assert (*n_properties_2 > 0);
|
|
|
|
*properties_2 = g_new0 (VkQueueFamilyProperties2, *n_properties_2);
|
|
for (i = 0; i < *n_properties_2; ++i)
|
|
(*properties_2)[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2;
|
|
|
|
vkGetPhysicalDeviceQueueFamilyProperties2 (vk_physical_device,
|
|
n_properties_2, *properties_2);
|
|
}
|
|
|
|
static void
|
|
find_queue_family_with_most_queues (GrdVkDevice *device)
|
|
{
|
|
VkPhysicalDevice vk_physical_device =
|
|
grd_vk_physical_device_get_physical_device (device->physical_device);
|
|
g_autofree VkQueueFamilyProperties2 *properties_2 = NULL;
|
|
uint32_t n_properties_2 = 0;
|
|
VkQueueFlags bitmask;
|
|
uint32_t i;
|
|
|
|
g_assert (device->max_queues == 0);
|
|
|
|
get_queue_family_properties (vk_physical_device,
|
|
&properties_2, &n_properties_2);
|
|
|
|
bitmask = VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT;
|
|
|
|
for (i = 0; i < n_properties_2; ++i)
|
|
{
|
|
VkQueueFamilyProperties *properties =
|
|
&properties_2[i].queueFamilyProperties;
|
|
|
|
if (properties->queueFlags & bitmask &&
|
|
properties->queueCount > device->max_queues)
|
|
{
|
|
device->queue_family_idx = i;
|
|
device->max_queues = properties->queueCount;
|
|
}
|
|
}
|
|
g_assert (device->max_queues > 0);
|
|
}
|
|
|
|
static gboolean
|
|
create_pipeline_cache (GrdVkDevice *device,
|
|
GError **error)
|
|
{
|
|
VkDevice vk_device = device->vk_device;
|
|
VkPipelineCacheCreateInfo cache_create_info = {};
|
|
VkResult vk_result;
|
|
|
|
cache_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
|
|
|
|
vk_result = vkCreatePipelineCache (vk_device, &cache_create_info, NULL,
|
|
&device->vk_pipeline_cache);
|
|
if (vk_result != VK_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to create pipeline cache: %i", vk_result);
|
|
return FALSE;
|
|
}
|
|
g_assert (device->vk_pipeline_cache != VK_NULL_HANDLE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
load_device_funcs (GrdVkDevice *device,
|
|
GError **error)
|
|
{
|
|
GrdVkDeviceFuncs *device_funcs = &device->device_funcs;
|
|
VkDevice vk_device = device->vk_device;
|
|
|
|
/* VK_KHR_external_memory_fd */
|
|
device_funcs->vkGetMemoryFdPropertiesKHR = (PFN_vkGetMemoryFdPropertiesKHR)
|
|
vkGetDeviceProcAddr (vk_device, "vkGetMemoryFdPropertiesKHR");
|
|
if (!device_funcs->vkGetMemoryFdPropertiesKHR)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to get device function address for function "
|
|
"\"vkGetMemoryFdPropertiesKHR\"");
|
|
return FALSE;
|
|
}
|
|
|
|
/* VK_KHR_synchronization2 */
|
|
device_funcs->vkCmdPipelineBarrier2KHR = (PFN_vkCmdPipelineBarrier2KHR)
|
|
vkGetDeviceProcAddr (vk_device, "vkCmdPipelineBarrier2KHR");
|
|
if (!device_funcs->vkCmdPipelineBarrier2KHR)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to get device function address for function "
|
|
"\"vkCmdPipelineBarrier2KHR\"");
|
|
return FALSE;
|
|
}
|
|
|
|
device_funcs->vkCmdWriteTimestamp2KHR = (PFN_vkCmdWriteTimestamp2KHR)
|
|
vkGetDeviceProcAddr (vk_device, "vkCmdWriteTimestamp2KHR");
|
|
if (!device_funcs->vkCmdWriteTimestamp2KHR)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to get device function address for function "
|
|
"\"vkCmdWriteTimestamp2KHR\"");
|
|
return FALSE;
|
|
}
|
|
|
|
device_funcs->vkQueueSubmit2KHR = (PFN_vkQueueSubmit2KHR)
|
|
vkGetDeviceProcAddr (vk_device, "vkQueueSubmit2KHR");
|
|
if (!device_funcs->vkQueueSubmit2KHR)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to get device function address for function "
|
|
"\"vkQueueSubmit2KHR\"");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
create_shader_module (GrdVkDevice *device,
|
|
const GrdVkSPIRVSource *spirv_source,
|
|
VkShaderModule *shader_module,
|
|
GError **error)
|
|
{
|
|
VkDevice vk_device = device->vk_device;
|
|
VkShaderModuleCreateInfo shader_module_create_info = {};
|
|
VkResult vk_result;
|
|
|
|
g_assert (shader_module);
|
|
g_assert (spirv_source->size % 4 == 0);
|
|
|
|
shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
|
shader_module_create_info.codeSize = spirv_source->size;
|
|
shader_module_create_info.pCode = (const uint32_t *) spirv_source->data;
|
|
|
|
vk_result = vkCreateShaderModule (vk_device, &shader_module_create_info, NULL,
|
|
shader_module);
|
|
if (vk_result != VK_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to create shader module: %i", vk_result);
|
|
return FALSE;
|
|
}
|
|
g_assert (*shader_module != VK_NULL_HANDLE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
create_shader_modules (GrdVkDevice *device,
|
|
const GrdVkSPIRVSources *spirv_sources,
|
|
GError **error)
|
|
{
|
|
GrdVkShaderModules *shader_modules = &device->shader_modules;
|
|
|
|
if (!create_shader_module (device, spirv_sources->avc_dual_view,
|
|
&shader_modules->create_avc_dual_view, error))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
prepare_queue_pool (GrdVkDevice *device)
|
|
{
|
|
uint32_t i;
|
|
|
|
g_assert (device->max_queues > 0);
|
|
|
|
for (i = 0; i < device->max_queues; ++i)
|
|
{
|
|
QueueInfo *queue_info;
|
|
|
|
queue_info = g_new0 (QueueInfo, 1);
|
|
queue_info->queue =
|
|
grd_vk_queue_new (device, device->queue_family_idx, i);
|
|
|
|
g_hash_table_insert (device->queue_table,
|
|
GUINT_TO_POINTER (i), queue_info);
|
|
}
|
|
g_assert (g_hash_table_size (device->queue_table) == device->max_queues);
|
|
}
|
|
|
|
static void
|
|
fetch_device_properties (GrdVkDevice *device)
|
|
{
|
|
VkPhysicalDevice vk_physical_device =
|
|
grd_vk_physical_device_get_physical_device (device->physical_device);
|
|
VkPhysicalDeviceProperties properties = {};
|
|
VkPhysicalDeviceProperties2 properties_2 = {};
|
|
VkPhysicalDeviceDrmPropertiesEXT drm_properties = {};
|
|
VkPhysicalDeviceVulkan12Properties vk12_properties = {};
|
|
|
|
vkGetPhysicalDeviceProperties (vk_physical_device, &properties);
|
|
device->timestamp_period = properties.limits.timestampPeriod;
|
|
device->non_coherent_atom_size = properties.limits.nonCoherentAtomSize;
|
|
|
|
g_assert (device->timestamp_period > 0);
|
|
|
|
properties_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
|
drm_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT;
|
|
vk12_properties.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES;
|
|
|
|
grd_vk_append_to_chain (&properties_2, &drm_properties);
|
|
grd_vk_append_to_chain (&properties_2, &vk12_properties);
|
|
|
|
vkGetPhysicalDeviceProperties2 (vk_physical_device, &properties_2);
|
|
device->drm_render_node = drm_properties.renderMinor;
|
|
device->driver_id = vk12_properties.driverID;
|
|
}
|
|
|
|
GrdVkDevice *
|
|
grd_vk_device_new (GrdVkPhysicalDevice *physical_device,
|
|
const GrdVkSPIRVSources *spirv_sources,
|
|
GError **error)
|
|
{
|
|
VkPhysicalDevice vk_physical_device =
|
|
grd_vk_physical_device_get_physical_device (physical_device);
|
|
GrdVkDeviceFeatures device_features =
|
|
grd_vk_physical_device_get_device_features (physical_device);
|
|
g_autoptr (GrdVkDevice) device = NULL;
|
|
VkDeviceCreateInfo device_create_info = {};
|
|
VkPhysicalDeviceVulkan12Features vulkan12_features = {};
|
|
VkPhysicalDeviceSynchronization2Features synchronization2_features = {};
|
|
VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures zero_init_features = {};
|
|
VkDeviceQueueCreateInfo device_queue_create_info = {};
|
|
float queue_priorities[MAX_DEVICE_QUEUES] = {};
|
|
const char *extensions[7] = {};
|
|
uint32_t n_extensions = 0;
|
|
VkResult vk_result;
|
|
uint32_t i;
|
|
|
|
device = g_object_new (GRD_TYPE_VK_DEVICE, NULL);
|
|
device->physical_device = g_object_ref (physical_device);
|
|
|
|
find_queue_family_with_most_queues (device);
|
|
device->max_queues = MIN (device->max_queues, MAX_DEVICE_QUEUES);
|
|
|
|
for (i = 0; i < device->max_queues; ++i)
|
|
queue_priorities[i] = 1.0f;
|
|
|
|
device_queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
device_queue_create_info.queueFamilyIndex = device->queue_family_idx;
|
|
device_queue_create_info.queueCount = device->max_queues;
|
|
device_queue_create_info.pQueuePriorities = queue_priorities;
|
|
|
|
extensions[n_extensions++] = VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME;
|
|
extensions[n_extensions++] = VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME;
|
|
extensions[n_extensions++] = VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME;
|
|
extensions[n_extensions++] = VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME;
|
|
extensions[n_extensions++] = VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME;
|
|
extensions[n_extensions++] = VK_KHR_ZERO_INITIALIZE_WORKGROUP_MEMORY_EXTENSION_NAME;
|
|
extensions[n_extensions++] = VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME;
|
|
|
|
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
|
device_create_info.queueCreateInfoCount = 1;
|
|
device_create_info.pQueueCreateInfos = &device_queue_create_info;
|
|
device_create_info.enabledExtensionCount = n_extensions;
|
|
device_create_info.ppEnabledExtensionNames = extensions;
|
|
|
|
vulkan12_features.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
|
|
|
|
if (device_features & GRD_VK_DEVICE_FEATURE_UPDATE_AFTER_BIND_SAMPLED_IMAGE)
|
|
vulkan12_features.descriptorBindingSampledImageUpdateAfterBind = VK_TRUE;
|
|
if (device_features & GRD_VK_DEVICE_FEATURE_UPDATE_AFTER_BIND_STORAGE_IMAGE)
|
|
vulkan12_features.descriptorBindingStorageImageUpdateAfterBind = VK_TRUE;
|
|
|
|
synchronization2_features.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES;
|
|
synchronization2_features.synchronization2 = VK_TRUE;
|
|
|
|
zero_init_features.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES;
|
|
zero_init_features.shaderZeroInitializeWorkgroupMemory = VK_TRUE;
|
|
|
|
grd_vk_append_to_chain (&device_create_info, &vulkan12_features);
|
|
grd_vk_append_to_chain (&device_create_info, &synchronization2_features);
|
|
grd_vk_append_to_chain (&device_create_info, &zero_init_features);
|
|
|
|
vk_result = vkCreateDevice (vk_physical_device, &device_create_info, NULL,
|
|
&device->vk_device);
|
|
if (vk_result != VK_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to create device: %i", vk_result);
|
|
return NULL;
|
|
}
|
|
g_assert (device->vk_device != VK_NULL_HANDLE);
|
|
|
|
if (!create_pipeline_cache (device, error))
|
|
return NULL;
|
|
if (!load_device_funcs (device, error))
|
|
return NULL;
|
|
if (!create_shader_modules (device, spirv_sources, error))
|
|
return NULL;
|
|
|
|
prepare_queue_pool (device);
|
|
fetch_device_properties (device);
|
|
|
|
g_debug ("[HWAccel.Vulkan] Using device features: 0x%08X", device_features);
|
|
|
|
return g_steal_pointer (&device);
|
|
}
|
|
|
|
static void
|
|
clear_shader_module (GrdVkDevice *device,
|
|
VkShaderModule *vk_shader_module)
|
|
{
|
|
VkDevice vk_device = device->vk_device;
|
|
|
|
g_assert (vk_device != VK_NULL_HANDLE);
|
|
|
|
if (*vk_shader_module == VK_NULL_HANDLE)
|
|
return;
|
|
|
|
vkDestroyShaderModule (vk_device, *vk_shader_module, NULL);
|
|
vk_shader_module = VK_NULL_HANDLE;
|
|
}
|
|
|
|
static void
|
|
destroy_shader_modules (GrdVkDevice *device)
|
|
{
|
|
GrdVkShaderModules *shader_modules = &device->shader_modules;
|
|
|
|
clear_shader_module (device, &shader_modules->create_avc_dual_view);
|
|
}
|
|
|
|
static void
|
|
grd_vk_device_dispose (GObject *object)
|
|
{
|
|
GrdVkDevice *device = GRD_VK_DEVICE (object);
|
|
|
|
g_hash_table_remove_all (device->queue_table);
|
|
|
|
if (device->vk_device != VK_NULL_HANDLE)
|
|
destroy_shader_modules (device);
|
|
|
|
if (device->vk_pipeline_cache != VK_NULL_HANDLE)
|
|
{
|
|
vkDestroyPipelineCache (device->vk_device, device->vk_pipeline_cache,
|
|
NULL);
|
|
device->vk_pipeline_cache = VK_NULL_HANDLE;
|
|
}
|
|
if (device->vk_device != VK_NULL_HANDLE)
|
|
{
|
|
vkDestroyDevice (device->vk_device, NULL);
|
|
device->vk_device = VK_NULL_HANDLE;
|
|
}
|
|
|
|
G_OBJECT_CLASS (grd_vk_device_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
grd_vk_device_finalize (GObject *object)
|
|
{
|
|
GrdVkDevice *device = GRD_VK_DEVICE (object);
|
|
|
|
g_mutex_clear (&device->queue_pool_mutex);
|
|
|
|
g_assert (g_hash_table_size (device->queue_table) == 0);
|
|
g_clear_pointer (&device->queue_table, g_hash_table_unref);
|
|
|
|
g_clear_object (&device->physical_device);
|
|
|
|
G_OBJECT_CLASS (grd_vk_device_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
queue_info_free (gpointer data)
|
|
{
|
|
QueueInfo *queue_info = data;
|
|
|
|
g_assert (queue_info->ref_count == 0);
|
|
g_clear_object (&queue_info->queue);
|
|
|
|
g_free (queue_info);
|
|
}
|
|
|
|
static void
|
|
grd_vk_device_init (GrdVkDevice *device)
|
|
{
|
|
g_assert (MAX_DEVICE_QUEUES > 0);
|
|
|
|
device->queue_table = g_hash_table_new_full (NULL, NULL,
|
|
NULL, queue_info_free);
|
|
|
|
g_mutex_init (&device->queue_pool_mutex);
|
|
}
|
|
|
|
static void
|
|
grd_vk_device_class_init (GrdVkDeviceClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->dispose = grd_vk_device_dispose;
|
|
object_class->finalize = grd_vk_device_finalize;
|
|
}
|