The open source OpenXR runtime
1// Copyright 2019-2023, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief Buffer functions.
6 * @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
7 * @author Jakob Bornecrantz <jakob@collabora.com>
8 * @ingroup comp_render
9 */
10
11#include "vk/vk_mini_helpers.h"
12#include "render/render_interface.h"
13
14#include <stdio.h>
15
16
17/*
18 *
19 * Common helpers.
20 *
21 */
22
23XRT_CHECK_RESULT static VkResult
24create_buffer(struct vk_bundle *vk,
25 VkBufferUsageFlags usage_flags,
26 VkMemoryPropertyFlags memory_property_flags,
27 VkDeviceSize size,
28 const void *pNext_for_create,
29 const void *pNext_for_allocate,
30 VkBuffer *out_buffer,
31 VkDeviceMemory *out_memory,
32 VkDeviceSize *out_alignment,
33 VkDeviceSize *out_allocation_size)
34{
35 VkResult ret;
36 bool bret;
37
38 // Create the buffer handle.
39 VkBufferCreateInfo buffer_info = {
40 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
41 .pNext = pNext_for_create,
42 .size = size,
43 .usage = usage_flags,
44 };
45
46 VkBuffer buffer = VK_NULL_HANDLE;
47 ret = vk->vkCreateBuffer(vk->device, //
48 &buffer_info, //
49 NULL, //
50 &buffer); //
51 VK_CHK_AND_RET(ret, "vkCreateBuffer");
52
53 // Create the memory backing up the buffer handle.
54 VkMemoryRequirements mem_reqs;
55 vk->vkGetBufferMemoryRequirements(vk->device, //
56 buffer, //
57 &mem_reqs); //
58
59 // Find a memory type index that fits the properties of the buffer.
60 uint32_t memory_type_index = 0;
61 bret = vk_get_memory_type( //
62 vk, //
63 mem_reqs.memoryTypeBits, //
64 memory_property_flags, //
65 &memory_type_index); //
66 if (!bret) {
67 VK_ERROR(vk, "vk_get_memory_type failed: 'false'\n\tFailed to find a matching memory type.");
68 ret = VK_ERROR_OUT_OF_DEVICE_MEMORY;
69 goto err_buffer;
70 }
71
72 VkMemoryAllocateInfo mem_alloc = {
73 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
74 .allocationSize = mem_reqs.size,
75 .memoryTypeIndex = memory_type_index,
76 .pNext = pNext_for_allocate,
77 };
78
79 VkDeviceMemory memory = VK_NULL_HANDLE;
80 ret = vk->vkAllocateMemory(vk->device, //
81 &mem_alloc, //
82 NULL, //
83 &memory); //
84 VK_CHK_WITH_GOTO(ret, "vkAllocateMemory", err_buffer);
85
86 // Attach the memory to the buffer object
87 ret = vk->vkBindBufferMemory(vk->device, //
88 buffer, // buffer
89 memory, // memory
90 0); // memoryOffset
91 VK_CHK_WITH_GOTO(ret, "vkBindBufferMemory", err_memory);
92
93 *out_memory = memory;
94 *out_buffer = buffer;
95 *out_alignment = mem_reqs.alignment;
96 *out_allocation_size = mem_alloc.allocationSize;
97
98 return VK_SUCCESS;
99
100
101err_memory:
102 DF(Memory, memory);
103
104err_buffer:
105 D(Buffer, buffer);
106
107 return ret;
108}
109
110
111/*
112 *
113 * 'Exported' functions.
114 *
115 */
116
117VkResult
118render_buffer_init(struct vk_bundle *vk,
119 struct render_buffer *buffer,
120 VkBufferUsageFlags usage_flags,
121 VkMemoryPropertyFlags memory_property_flags,
122 VkDeviceSize size)
123{
124 VkResult ret;
125
126 ret = create_buffer(vk, //
127 usage_flags, // usage_flags
128 memory_property_flags, // memory_property_flags
129 size, // size
130 NULL, // pNext for create
131 NULL, // pNext_for_allocate
132 &buffer->buffer, // out_buffer
133 &buffer->memory, // out_memory
134 &buffer->alignment, // out_alignment
135 &buffer->allocation_size); // out_allocation_size
136 if (ret == VK_SUCCESS) {
137 buffer->size = size;
138 }
139
140 return ret;
141}
142
143VkResult
144render_buffer_init_exportable(struct vk_bundle *vk,
145 struct render_buffer *buffer,
146 VkBufferUsageFlags usage_flags,
147 VkMemoryPropertyFlags memory_property_flags,
148 VkDeviceSize size)
149{
150 VkResult ret;
151
152 VkExternalMemoryBufferCreateInfo export_create_info = {
153 .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO,
154 .handleTypes = vk_cb_get_buffer_external_handle_type(vk),
155 };
156
157 VkExportMemoryAllocateInfo export_alloc_info = {
158 .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR,
159 .pNext = NULL,
160 .handleTypes = vk_cb_get_buffer_external_handle_type(vk),
161 };
162
163 ret = create_buffer(vk, //
164 usage_flags, // usage_flags
165 memory_property_flags, // memory_property_flags
166 size, // size
167 &export_create_info, // pNext_for_create
168 &export_alloc_info, // pNext_for_allocate
169 &buffer->buffer, // out_buffer
170 &buffer->memory, // out_memory
171 &buffer->alignment, // out_alignment
172 &buffer->allocation_size); // out_allocation_size
173 if (ret == VK_SUCCESS) {
174 buffer->size = size;
175 }
176
177 return ret;
178}
179
180void
181render_buffer_fini(struct vk_bundle *vk, struct render_buffer *buffer)
182{
183 D(Buffer, buffer->buffer);
184 DF(Memory, buffer->memory);
185 U_ZERO(buffer);
186}
187
188VkResult
189render_buffer_map(struct vk_bundle *vk, struct render_buffer *buffer)
190{
191 return vk->vkMapMemory(vk->device, //
192 buffer->memory, // memory
193 0, // offset
194 VK_WHOLE_SIZE, // size
195 0, // flags
196 &buffer->mapped); // ppData
197}
198
199void
200render_buffer_unmap(struct vk_bundle *vk, struct render_buffer *buffer)
201{
202 if (buffer->mapped != NULL) {
203 vk->vkUnmapMemory(vk->device, buffer->memory);
204 buffer->mapped = NULL;
205 }
206}
207
208VkResult
209render_buffer_map_and_write(struct vk_bundle *vk, struct render_buffer *buffer, void *data, VkDeviceSize size)
210{
211 VkResult ret;
212
213 if (size > buffer->allocation_size) {
214 VK_ERROR(vk, "Trying to write more the buffer size!");
215 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
216 }
217
218 if (buffer->mapped == NULL) {
219 ret = render_buffer_map(vk, buffer);
220 if (ret != VK_SUCCESS) {
221 return ret;
222 }
223 }
224
225 memcpy(buffer->mapped, data, size);
226
227 return VK_SUCCESS;
228}
229
230VkResult
231render_buffer_write(struct vk_bundle *vk, struct render_buffer *buffer, void *data, VkDeviceSize size)
232{
233 if (size > buffer->allocation_size) {
234 VK_ERROR(vk, "Trying to write more the buffer size!");
235 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
236 }
237
238 bool mapped = buffer->mapped != NULL;
239 if (!mapped) {
240 VkResult ret = render_buffer_map(vk, buffer);
241 if (ret != VK_SUCCESS) {
242 return ret;
243 }
244 }
245
246 memcpy(buffer->mapped, data, size);
247
248 // Only unmap if we did the mapping.
249 if (!mapped) {
250 render_buffer_unmap(vk, buffer);
251 }
252
253 return VK_SUCCESS;
254}