[vulkan] Add a mini resource subsystem

The resource subsystem creates buffers, images, buffer views and image
views in a single batch operation, using a single memory object to back
all the buffers and images. I had been doing this by hand for a while,
but got tired of jumping through all those vulkan hoops. While it's
still a little tedious to set up the arrays for QFV_CreateResource (and
they need to be kept around for QFV_DestroyResource), it really eases
calculation of memory object size and sub-resource offsets. And
destroying all the objects is just one call to QFV_DestroyResource.
This commit is contained in:
Bill Currie 2022-05-04 13:59:38 +09:00
parent 9d7cad420d
commit a4f500da3c
3 changed files with 340 additions and 0 deletions

View file

@ -0,0 +1,68 @@
#ifndef __QF_Vulkan_resource_h
#define __QF_Vulkan_resource_h
#ifndef VK_NO_PROTOTYPES
#define VK_NO_PROTOTYPES
#endif
#include <vulkan/vulkan.h>
typedef enum {
qfv_res_buffer = 1,
qfv_res_buffer_view,
qfv_res_image,
qfv_res_image_view,
} qfv_res_type;
typedef struct qfv_resobj_s {
const char *name;
qfv_res_type type;
union {
struct {
VkDeviceSize size;
VkBufferUsageFlags usage;
VkBuffer buffer;
} buffer;
struct {
unsigned buffer;
VkFormat format;
VkDeviceSize offset;
VkDeviceSize size;
VkBufferView view;
} buffer_view;
struct {
int cubemap;
VkImageType type;
VkFormat format;
VkExtent3D extent;
uint32_t num_mipmaps;
uint32_t num_layers;
VkSampleCountFlags samples;
VkImageUsageFlags usage;
VkImage image;
} image;
struct {
unsigned image;
VkImageViewType type;
VkFormat format;
VkImageAspectFlags aspect;
VkImageView view;
} image_view;
};
} qfv_resobj_t;
typedef struct qfv_resource_s {
const char *name;
struct va_ctx_s *va_ctx;
VkMemoryPropertyFlags memory_properties;
unsigned num_objects;
qfv_resobj_t *objects;
VkDeviceMemory memory;
} qfv_resource_t;
struct qfv_device_s;
int QFV_CreateResource (struct qfv_device_s *device, qfv_resource_t *resource);
void QFV_DestroyResource (struct qfv_device_s *device,
qfv_resource_t *resource);
#endif//__QF_Vulkan_resource_h

View file

@ -207,6 +207,7 @@ libs_video_renderer_librender_vulkan_la_SOURCES = \
libs/video/renderer/vulkan/pipeline.c \
libs/video/renderer/vulkan/projection.c \
libs/video/renderer/vulkan/renderpass.c \
libs/video/renderer/vulkan/resource.c \
libs/video/renderer/vulkan/scrap.c \
libs/video/renderer/vulkan/shader.c \
libs/video/renderer/vulkan/staging.c \

View file

@ -0,0 +1,271 @@
/*
resource.c
Vulkan resource functions
Copyright (C) 2022 Bill Currie <bill@taniwha.org>
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:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "QF/va.h"
#include "QF/Vulkan/buffer.h"
#include "QF/Vulkan/debug.h"
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/image.h"
#include "QF/Vulkan/instance.h"
#include "QF/Vulkan/resource.h"
int
QFV_CreateResource (qfv_device_t *device, qfv_resource_t *resource)
{
qfv_devfuncs_t *dfunc = device->funcs;
qfv_physdev_t *physdev = device->physDev;
VkPhysicalDeviceMemoryProperties *memprops = &physdev->memory_properties;
VkMemoryRequirements req;
VkDeviceSize size = 0;
for (unsigned i = 0; i < resource->num_objects; i++) {
__auto_type obj = &resource->objects[i];
switch (obj->type) {
case qfv_res_buffer:
{
__auto_type buffer = &obj->buffer;
buffer->buffer = QFV_CreateBuffer (device,
buffer->size,
buffer->usage);
const char *name = va (resource->va_ctx, "buffer:%s:%s",
resource->name, obj->name);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER,
buffer->buffer, name);
dfunc->vkGetBufferMemoryRequirements (device->dev,
buffer->buffer, &req);
}
break;
case qfv_res_buffer_view:
{
__auto_type buffview = &obj->buffer_view;
__auto_type buffobj = &resource->objects[buffview->buffer];
if (buffview->buffer >= resource->num_objects
|| buffobj->type != qfv_res_buffer) {
Sys_Error ("%s:%s invalid buffer for view",
resource->name, obj->name);
}
}
break;
case qfv_res_image:
{
__auto_type image = &obj->image;
image->image = QFV_CreateImage (device,
image->cubemap,
image->type,
image->format,
image->extent,
image->num_mipmaps,
image->num_layers,
image->samples,
image->usage);
const char *name = va (resource->va_ctx, "image:%s:%s",
resource->name, obj->name);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE,
image->image, name);
dfunc->vkGetImageMemoryRequirements (device->dev,
image->image, &req);
}
break;
case qfv_res_image_view:
{
__auto_type imgview = &obj->image_view;
__auto_type imgobj = &resource->objects[imgview->image];
if (imgview->image >= resource->num_objects
|| imgobj->type != qfv_res_image) {
Sys_Error ("%s:%s invalid image for view",
resource->name, obj->name);
}
}
break;
default:
Sys_Error ("%s:%s invalid resource type %d",
resource->name, obj->name, obj->type);
}
size = QFV_NextOffset (size, &req);
size += req.size;
}
VkMemoryPropertyFlags properties = resource->memory_properties;
for (uint32_t type = 0; type < memprops->memoryTypeCount; type++) {
if ((req.memoryTypeBits & (1 << type))
&& ((memprops->memoryTypes[type].propertyFlags & properties)
== properties)) {
VkMemoryAllocateInfo allocate_info = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = size,
.memoryTypeIndex = type,
};
VkResult res = dfunc->vkAllocateMemory (device->dev, &allocate_info,
0, &resource->memory);
if (res == VK_SUCCESS) {
break;
}
}
}
QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY,
resource->memory, va (resource->va_ctx, "memory:%s",
resource->name));
VkDeviceSize offset = 0;
for (unsigned i = 0; i < resource->num_objects; i++) {
__auto_type obj = &resource->objects[i];
switch (obj->type) {
case qfv_res_buffer:
{
__auto_type buffer = &obj->buffer;
dfunc->vkGetBufferMemoryRequirements (device->dev,
buffer->buffer, &req);
}
break;
case qfv_res_image:
{
__auto_type image = &obj->image;
dfunc->vkGetImageMemoryRequirements (device->dev,
image->image, &req);
}
break;
case qfv_res_buffer_view:
case qfv_res_image_view:
break;
}
offset = QFV_NextOffset (offset, &req);
switch (obj->type) {
case qfv_res_buffer:
{
__auto_type buffer = &obj->buffer;
QFV_BindBufferMemory (device, buffer->buffer,
resource->memory, offset);
}
break;
case qfv_res_image:
{
__auto_type image = &obj->image;
QFV_BindImageMemory (device, image->image,
resource->memory, offset);
}
break;
case qfv_res_buffer_view:
case qfv_res_image_view:
break;
}
offset += req.size;
}
for (unsigned i = 0; i < resource->num_objects; i++) {
__auto_type obj = &resource->objects[i];
switch (obj->type) {
case qfv_res_buffer:
case qfv_res_image:
break;
case qfv_res_buffer_view:
{
__auto_type buffview = &obj->buffer_view;
__auto_type buffobj = &resource->objects[buffview->buffer];
__auto_type buffer = &buffobj->buffer;
buffview->view = QFV_CreateBufferView (device,
buffer->buffer,
buffview->format,
buffview->offset,
buffview->size);
const char *name = va (resource->va_ctx, "bview:%s:%s",
resource->name, obj->name);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER_VIEW,
buffview->view, name);
}
break;
case qfv_res_image_view:
{
__auto_type imgview = &obj->image_view;
__auto_type imgobj = &resource->objects[imgview->image];
__auto_type image = &imgobj->image;
imgview->view = QFV_CreateImageView (device,
image->image,
imgview->type,
imgview->format,
imgview->aspect);
const char *name = va (resource->va_ctx, "iview:%s:%s",
resource->name, obj->name);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW,
imgview->view, name);
}
break;
}
}
return 0;
}
void
QFV_DestroyResource (qfv_device_t *device, qfv_resource_t *resource)
{
qfv_devfuncs_t *dfunc = device->funcs;
for (unsigned i = 0; i < resource->num_objects; i++) {
__auto_type obj = &resource->objects[i];
switch (obj->type) {
case qfv_res_buffer:
case qfv_res_image:
break;
case qfv_res_buffer_view:
{
__auto_type buffview = &obj->buffer_view;
dfunc->vkDestroyBufferView (device->dev, buffview->view, 0);
}
break;
case qfv_res_image_view:
{
__auto_type imgview = &obj->image_view;
dfunc->vkDestroyImageView (device->dev, imgview->view, 0);
}
break;
}
}
for (unsigned i = 0; i < resource->num_objects; i++) {
__auto_type obj = &resource->objects[i];
switch (obj->type) {
case qfv_res_buffer:
{
__auto_type buffer = &obj->buffer;
dfunc->vkDestroyBuffer (device->dev, buffer->buffer, 0);
}
break;
case qfv_res_image:
{
__auto_type image = &obj->image;
dfunc->vkDestroyImage (device->dev, image->image, 0);
}
break;
case qfv_res_buffer_view:
case qfv_res_image_view:
break;
}
}
dfunc->vkFreeMemory (device->dev, resource->memory, 0);
}