vkQuake2 doxygen  1.0 dev
vk_buffer.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 2018-2019 Krzysztof Kondrak
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 
19 */
20 
21 #include "vk_local.h"
22 
23 // internal helper
24 static void copyBuffer(const VkBuffer *src, VkBuffer *dst, VkDeviceSize size)
25 {
26  VkCommandBuffer commandBuffer = QVk_CreateCommandBuffer(&vk_transferCommandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
27  QVk_BeginCommand(&commandBuffer);
28 
29  VkBufferCopy copyRegion = {
30  .srcOffset = 0,
31  .dstOffset = 0,
32  .size = size
33  };
34  vkCmdCopyBuffer(commandBuffer, *src, *dst, 1, &copyRegion);
35 
36  QVk_SubmitCommand(&commandBuffer, &vk_device.transferQueue);
37  vkFreeCommandBuffers(vk_device.logical, vk_transferCommandPool, 1, &commandBuffer);
38 }
39 
40 // internal helper
41 static void createStagedBuffer(const void *data, VkDeviceSize size, qvkbuffer_t *dstBuffer, qvkbufferopts_t bufferOpts, qvkbuffer_t *stagingBuffer)
42 {
43  qvkbuffer_t *stgBuffer = stagingBuffer;
44  // create/release internal staging buffer if NULL has been passed
45  if (!stagingBuffer)
46  {
47  stgBuffer = (qvkbuffer_t *)malloc(sizeof(qvkbuffer_t));
48  VK_VERIFY(QVk_CreateStagingBuffer(size, stgBuffer, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VK_MEMORY_PROPERTY_HOST_CACHED_BIT));
49  }
50 
51  if (data)
52  {
53  void *dst;
54  // staging buffers in vkQuake2 are required to be host coherent, so no flushing/invalidation is involved
55  VK_VERIFY(vmaMapMemory(vk_malloc, stgBuffer->allocation, &dst));
56  memcpy(dst, data, (size_t)size);
57  vmaUnmapMemory(vk_malloc, stgBuffer->allocation);
58  }
59 
60  VK_VERIFY(QVk_CreateBuffer(size, dstBuffer, bufferOpts));
61  copyBuffer(&stgBuffer->buffer, &dstBuffer->buffer, size);
62 
63  if (!stagingBuffer)
64  {
65  QVk_FreeBuffer(stgBuffer);
66  free(stgBuffer);
67  }
68 }
69 
70 VkResult QVk_CreateBuffer(VkDeviceSize size, qvkbuffer_t *dstBuffer, const qvkbufferopts_t options)
71 {
72  VkBufferCreateInfo bcInfo = {
73  .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
74  .pNext = NULL,
75  .flags = 0,
76  .size = size,
77  .usage = options.usage,
78  .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
79  .queueFamilyIndexCount = 0,
80  .pQueueFamilyIndices = NULL,
81  };
82 
83  // separate transfer queue makes sense only if the buffer is targetted for being transfered to GPU, so ignore it if it's CPU-only
84  uint32_t queueFamilies[] = { (uint32_t)vk_device.gfxFamilyIndex, (uint32_t)vk_device.transferFamilyIndex };
86  {
87  bcInfo.sharingMode = VK_SHARING_MODE_CONCURRENT;
88  bcInfo.queueFamilyIndexCount = 2;
89  bcInfo.pQueueFamilyIndices = queueFamilies;
90  }
91 
92  VmaAllocationCreateInfo vmallocInfo = {
93  .flags = options.vmaFlags,
94  .usage = options.vmaUsage,
95  .requiredFlags = options.reqMemFlags,
96  .preferredFlags = options.prefMemFlags,
97  .memoryTypeBits = 0,
98  .pool = VK_NULL_HANDLE,
99  .pUserData = NULL
100  };
101 
102  dstBuffer->currentOffset = 0;
103  return vmaCreateBuffer(vk_malloc, &bcInfo, &vmallocInfo, &dstBuffer->buffer, &dstBuffer->allocation, &dstBuffer->allocInfo);
104 }
105 
107 {
108  vmaDestroyBuffer(vk_malloc, buffer->buffer, buffer->allocation);
109  buffer->buffer = VK_NULL_HANDLE;
110  buffer->allocation = VK_NULL_HANDLE;
111  buffer->currentOffset = 0;
112 }
113 
114 VkResult QVk_CreateStagingBuffer(VkDeviceSize size, qvkbuffer_t *dstBuffer, VkMemoryPropertyFlags reqMemFlags, VkMemoryPropertyFlags prefMemFlags)
115 {
116  qvkbufferopts_t stagingOpts = {
117  .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
118  .reqMemFlags = reqMemFlags,
119  .prefMemFlags = prefMemFlags,
120  .vmaUsage = VMA_MEMORY_USAGE_CPU_ONLY,
121  .vmaFlags = 0
122  };
123 
124  return QVk_CreateBuffer(size, dstBuffer, stagingOpts);
125 }
126 
127 VkResult QVk_CreateUniformBuffer(VkDeviceSize size, qvkbuffer_t *dstBuffer, VkMemoryPropertyFlags reqMemFlags, VkMemoryPropertyFlags prefMemFlags)
128 {
129  qvkbufferopts_t dstOpts = {
130  .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
131  .reqMemFlags = reqMemFlags,
132  .prefMemFlags = prefMemFlags,
133  .vmaUsage = VMA_MEMORY_USAGE_CPU_TO_GPU,
134  // When resizing dynamic uniform buffers on Intel, the Linux driver may throw a warning:
135  // "Mapping an image with layout VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used."
136  // Minor annoyance but we don't want any validation warnings, so we create dedicated allocation for uniform buffer.
137  // more details: https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/issues/34
138  // Note that this is a false positive which in other cases could be ignored: https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/general_considerations.html#general_considerations_validation_layer_warnings
140  };
141 
142  return QVk_CreateBuffer(size, dstBuffer, dstOpts);
143 }
144 
145 void QVk_CreateVertexBuffer(const void *data, VkDeviceSize size, qvkbuffer_t *dstBuffer, qvkbuffer_t *stagingBuffer, VkMemoryPropertyFlags reqMemFlags, VkMemoryPropertyFlags prefMemFlags)
146 {
147  qvkbufferopts_t dstOpts = {
148  .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
149  .reqMemFlags = reqMemFlags,
150  .prefMemFlags = prefMemFlags,
151  .vmaUsage = VMA_MEMORY_USAGE_GPU_ONLY,
152  .vmaFlags = 0
153  };
154 
155  createStagedBuffer(data, size, dstBuffer, dstOpts, stagingBuffer);
156 }
157 
158 void QVk_CreateIndexBuffer(const void *data, VkDeviceSize size, qvkbuffer_t *dstBuffer, qvkbuffer_t *stagingBuffer, VkMemoryPropertyFlags reqMemFlags, VkMemoryPropertyFlags prefMemFlags)
159 {
160  qvkbufferopts_t dstOpts = {
161  .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
162  .reqMemFlags = reqMemFlags,
163  .prefMemFlags = prefMemFlags,
164  .vmaUsage = VMA_MEMORY_USAGE_GPU_ONLY,
165  .vmaFlags = 0
166  };
167 
168  createStagedBuffer(data, size, dstBuffer, dstOpts, stagingBuffer);
169 }
QVk_FreeBuffer
void QVk_FreeBuffer(qvkbuffer_t *buffer)
Definition: vk_buffer.c:106
createStagedBuffer
static void createStagedBuffer(const void *data, VkDeviceSize size, qvkbuffer_t *dstBuffer, qvkbufferopts_t bufferOpts, qvkbuffer_t *stagingBuffer)
Definition: vk_buffer.c:41
qvkdevice_t::logical
VkDevice logical
Definition: qvk.h:40
qvkbuffer_t::buffer
VkBuffer buffer
Definition: qvk.h:126
vk_local.h
QVk_CreateBuffer
VkResult QVk_CreateBuffer(VkDeviceSize size, qvkbuffer_t *dstBuffer, const qvkbufferopts_t options)
Definition: vk_buffer.c:70
QVk_CreateUniformBuffer
VkResult QVk_CreateUniformBuffer(VkDeviceSize size, qvkbuffer_t *dstBuffer, VkMemoryPropertyFlags reqMemFlags, VkMemoryPropertyFlags prefMemFlags)
Definition: vk_buffer.c:127
vmaUnmapMemory
void vmaUnmapMemory(VmaAllocator allocator, VmaAllocation allocation)
Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
buffer
GLenum GLfloat * buffer
Definition: qgl_win.c:151
qvkdevice_t::gfxFamilyIndex
int gfxFamilyIndex
Definition: qvk.h:46
vk_device
qvkdevice_t vk_device
Definition: vk_common.c:51
qvkbufferopts_t::prefMemFlags
VkMemoryPropertyFlags prefMemFlags
Definition: qvk.h:146
vk_transferCommandPool
VkCommandPool vk_transferCommandPool
Definition: vk_common.c:96
VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
@ VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
Set this flag if the allocation should have its own memory block.
Definition: vk_mem_alloc.h:2114
VMA_MEMORY_USAGE_CPU_ONLY
@ VMA_MEMORY_USAGE_CPU_ONLY
Memory will be mappable on host.
Definition: vk_mem_alloc.h:2086
qvkbufferopts_t
Definition: qvk.h:142
qvkbuffer_t
Definition: qvk.h:124
VMA_MEMORY_USAGE_GPU_ONLY
@ VMA_MEMORY_USAGE_GPU_ONLY
Memory will be used on device only, so fast access from the device is preferred.
Definition: vk_mem_alloc.h:2076
qvkbuffer_t::currentOffset
VkDeviceSize currentOffset
Definition: qvk.h:129
QVk_SubmitCommand
void QVk_SubmitCommand(const VkCommandBuffer *commandBuffer, const VkQueue *queue)
Definition: vk_cmd.c:35
NULL
#define NULL
Definition: q_shared.h:67
qvkbuffer_t::allocation
VmaAllocation allocation
Definition: qvk.h:127
QVk_BeginCommand
VkResult QVk_BeginCommand(const VkCommandBuffer *commandBuffer)
Definition: vk_cmd.c:23
qvkbuffer_t::allocInfo
VmaAllocationInfo allocInfo
Definition: qvk.h:128
QVk_CreateCommandBuffer
VkCommandBuffer QVk_CreateCommandBuffer(const VkCommandPool *commandPool, VkCommandBufferLevel level)
Definition: vk_cmd.c:78
VK_VERIFY
#define VK_VERIFY(x)
Definition: vk_local.h:59
QVk_CreateStagingBuffer
VkResult QVk_CreateStagingBuffer(VkDeviceSize size, qvkbuffer_t *dstBuffer, VkMemoryPropertyFlags reqMemFlags, VkMemoryPropertyFlags prefMemFlags)
Definition: vk_buffer.c:114
vmaMapMemory
VkResult vmaMapMemory(VmaAllocator allocator, VmaAllocation allocation, void **ppData)
Maps memory represented by given allocation and returns pointer to it.
copyBuffer
static void copyBuffer(const VkBuffer *src, VkBuffer *dst, VkDeviceSize size)
Definition: vk_buffer.c:24
vk_malloc
VmaAllocator vk_malloc
Definition: vk_common.c:48
VMA_MEMORY_USAGE_CPU_TO_GPU
@ VMA_MEMORY_USAGE_CPU_TO_GPU
Memory that is both mappable on host (guarantees to be HOST_VISIBLE) and preferably fast to access by...
Definition: vk_mem_alloc.h:2093
qvkbufferopts_t::usage
VkBufferUsageFlags usage
Definition: qvk.h:144
vmaCreateBuffer
VkResult vmaCreateBuffer(VmaAllocator allocator, const VkBufferCreateInfo *pBufferCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, VkBuffer *pBuffer, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
QVk_CreateVertexBuffer
void QVk_CreateVertexBuffer(const void *data, VkDeviceSize size, qvkbuffer_t *dstBuffer, qvkbuffer_t *stagingBuffer, VkMemoryPropertyFlags reqMemFlags, VkMemoryPropertyFlags prefMemFlags)
Definition: vk_buffer.c:145
qvkbufferopts_t::vmaUsage
VmaMemoryUsage vmaUsage
Definition: qvk.h:147
qvkdevice_t::transferQueue
VkQueue transferQueue
Definition: qvk.h:45
vmaDestroyBuffer
void vmaDestroyBuffer(VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation)
Destroys Vulkan buffer and frees allocated memory.
qvkdevice_t::transferFamilyIndex
int transferFamilyIndex
Definition: qvk.h:48
qvkbufferopts_t::vmaFlags
VmaAllocationCreateFlags vmaFlags
Definition: qvk.h:148
qvkbufferopts_t::reqMemFlags
VkMemoryPropertyFlags reqMemFlags
Definition: qvk.h:145
VmaAllocationCreateInfo::flags
VmaAllocationCreateFlags flags
Use VmaAllocationCreateFlagBits enum.
Definition: vk_mem_alloc.h:2217
VmaAllocationCreateInfo
Definition: vk_mem_alloc.h:2214
QVk_CreateIndexBuffer
void QVk_CreateIndexBuffer(const void *data, VkDeviceSize size, qvkbuffer_t *dstBuffer, qvkbuffer_t *stagingBuffer, VkMemoryPropertyFlags reqMemFlags, VkMemoryPropertyFlags prefMemFlags)
Definition: vk_buffer.c:158