quakeforge/libs/video/renderer/vulkan/resource.c
Bill Currie faef61aab6 [vulkan] Ensure host-visible buffers are big enough to flush
Flushing memory requires nonCoherentAtomSize alignment, but this can
cause the flush range to go out of bounds of an improperly sized buffer.
However, only host-visible (and probably really only cached, but all
three covered) needs flushing, so no rounding up is done for
device-local memory.
2023-03-09 10:19:24 +09:00

350 lines
9.9 KiB
C

/*
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/mathlib.h"
#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"
static void
create_image (qfv_device_t *device, qfv_resobj_t *image_obj)
{
qfv_devfuncs_t *dfunc = device->funcs;
__auto_type image = &image_obj->image;
if (image->image) {
// the image was created externally and is being
return;
}
VkImageCreateInfo createInfo = {
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 0,
.flags = image->flags,
.imageType = image->type,
.format = image->format,
.extent = image->extent,
.mipLevels = image->num_mipmaps,
.arrayLayers = image->num_layers,
.samples = image->samples,
.tiling = image->tiling,
.usage = image->usage,
.sharingMode = image->sharing,
.queueFamilyIndexCount = image->num_queue_inds,
.pQueueFamilyIndices = image->queue_inds,
.initialLayout = image->initialLayout,
};
dfunc->vkCreateImage (device->dev, &createInfo, 0, &image->image);
}
static void
create_image_view (qfv_device_t *device, qfv_resobj_t *imgview_obj,
qfv_resobj_t *imgobj)
{
qfv_devfuncs_t *dfunc = device->funcs;
__auto_type view = &imgview_obj->image_view;
VkImage image = view->external_image ? view->external_image
: imgobj->image.image;
VkImageViewCreateInfo createInfo = {
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 0,
.flags = view->flags,
.image = image,
.viewType = view->type,
.format = view->format,
.components = view->components,
.subresourceRange = view->subresourceRange,
};
dfunc->vkCreateImageView (device->dev, &createInfo, 0, &view->view);
}
int
QFV_CreateResource (qfv_device_t *device, qfv_resource_t *resource)
{
qfv_devfuncs_t *dfunc = device->funcs;
qfv_physdev_t *physdev = device->physDev;
size_t atom = physdev->properties->limits.nonCoherentAtomSize;
VkPhysicalDeviceMemoryProperties *memprops = &physdev->memory_properties;
VkMemoryRequirements req;
VkDeviceSize size = 0;
if (!(resource->memory_properties
& (VK_MEMORY_PROPERTY_HOST_CACHED_BIT
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))) {
// if the memory isn't host visible then there's no need to worry about
// alignment with nonCoherentAtomSize
atom = 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);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER,
buffer->buffer,
va (resource->va_ctx, "buffer:%s:%s",
resource->name, obj->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:
{
create_image (device, obj);
__auto_type image = &obj->image;
QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE,
image->image,
va (resource->va_ctx, "image:%s:%s",
resource->name, obj->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->external_image
&& (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);
}
req.alignment = max (req.alignment, atom);
size = QFV_NextOffset (size, &req);
size += QFV_NextOffset (req.size, &req);
}
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;
}
}
}
resource->size = size;
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;
}
req.alignment = max (req.alignment, atom);
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);
buffer->offset = offset;
}
break;
case qfv_res_image:
{
__auto_type image = &obj->image;
QFV_BindImageMemory (device, image->image,
resource->memory, offset);
image->offset = offset;
}
break;
case qfv_res_buffer_view:
case qfv_res_image_view:
break;
}
req.alignment = max (req.alignment, atom);
offset = QFV_NextOffset (offset, &req);
offset += QFV_NextOffset (req.size, &req);
}
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);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER_VIEW,
buffview->view,
va (resource->va_ctx, "bview:%s:%s",
resource->name, obj->name));
}
break;
case qfv_res_image_view:
{
__auto_type imgview = &obj->image_view;
__auto_type imgobj = &resource->objects[imgview->image];
create_image_view (device, obj, imgobj);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW,
imgview->view,
va (resource->va_ctx, "iview:%s:%s",
resource->name, obj->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);
}
void
QFV_ResourceInitTexImage (qfv_resobj_t *image, const char *name,
int mips, const tex_t *tex)
{
*image = (qfv_resobj_t) {
.name = name,
.type = qfv_res_image,
.image = {
.type = VK_IMAGE_TYPE_2D,
.format = QFV_ImageFormat (tex->format, 0),
.extent = {
.width = tex->width,
.height = tex->height,
.depth = 1,
},
.num_mipmaps = mips ? QFV_MipLevels (tex->width, tex->height) : 1,
.num_layers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT
| VK_IMAGE_USAGE_SAMPLED_BIT,
},
};
}