1 /*
2 * Copyright © 2022 Imagination Technologies Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24 #include <assert.h>
25 #include <stddef.h>
26 #include <stdint.h>
27 #include <vulkan/vulkan.h>
28
29 #include "pvr_bo.h"
30 #include "pvr_private.h"
31 #include "pvr_types.h"
32 #include "pvr_winsys.h"
33 #include "vk_alloc.h"
34 #include "vk_log.h"
35
pvr_bo_alloc_to_winsys_flags(uint64_t flags)36 static uint32_t pvr_bo_alloc_to_winsys_flags(uint64_t flags)
37 {
38 uint32_t ws_flags = 0;
39
40 if (flags & PVR_BO_ALLOC_FLAG_CPU_ACCESS)
41 ws_flags |= PVR_WINSYS_BO_FLAG_CPU_ACCESS;
42
43 if (flags & PVR_BO_ALLOC_FLAG_GPU_UNCACHED)
44 ws_flags |= PVR_WINSYS_BO_FLAG_GPU_UNCACHED;
45
46 if (flags & PVR_BO_ALLOC_FLAG_PM_FW_PROTECT)
47 ws_flags |= PVR_WINSYS_BO_FLAG_PM_FW_PROTECT;
48
49 if (flags & PVR_BO_ALLOC_FLAG_ZERO_ON_ALLOC)
50 ws_flags |= PVR_WINSYS_BO_FLAG_ZERO_ON_ALLOC;
51
52 return ws_flags;
53 }
54
55 /**
56 * \brief Helper interface to allocate a GPU buffer and map it to both host and
57 * device virtual memory. Host mapping is conditional and is controlled by
58 * flags.
59 *
60 * \param[in] device Logical device pointer.
61 * \param[in] heap Heap to allocate device virtual address from.
62 * \param[in] size Size of buffer to allocate.
63 * \param[in] alignment Required alignment of the allocation. Must be a power
64 * of two.
65 * \param[in] flags Controls allocation, CPU and GPU mapping behavior
66 * using PVR_BO_ALLOC_FLAG_*.
67 * \param[out] pvr_bo_out On success output buffer is returned in this pointer.
68 * \return VK_SUCCESS on success, or error code otherwise.
69 *
70 * \sa #pvr_bo_free()
71 */
pvr_bo_alloc(struct pvr_device * device,struct pvr_winsys_heap * heap,uint64_t size,uint64_t alignment,uint64_t flags,struct pvr_bo ** const pvr_bo_out)72 VkResult pvr_bo_alloc(struct pvr_device *device,
73 struct pvr_winsys_heap *heap,
74 uint64_t size,
75 uint64_t alignment,
76 uint64_t flags,
77 struct pvr_bo **const pvr_bo_out)
78 {
79 const uint32_t ws_flags = pvr_bo_alloc_to_winsys_flags(flags);
80 struct pvr_bo *pvr_bo;
81 pvr_dev_addr_t addr;
82 VkResult result;
83
84 pvr_bo = vk_alloc(&device->vk.alloc,
85 sizeof(*pvr_bo),
86 8,
87 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
88 if (!pvr_bo)
89 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
90
91 result = device->ws->ops->buffer_create(device->ws,
92 size,
93 alignment,
94 PVR_WINSYS_BO_TYPE_GPU,
95 ws_flags,
96 &pvr_bo->bo);
97 if (result != VK_SUCCESS)
98 goto err_vk_free;
99
100 if (flags & PVR_BO_ALLOC_FLAG_CPU_MAPPED) {
101 void *map = device->ws->ops->buffer_map(pvr_bo->bo);
102 if (!map) {
103 result = VK_ERROR_MEMORY_MAP_FAILED;
104 goto err_buffer_destroy;
105 }
106 }
107
108 pvr_bo->vma = device->ws->ops->heap_alloc(heap, size, alignment);
109 if (!pvr_bo->vma) {
110 result = VK_ERROR_OUT_OF_DEVICE_MEMORY;
111 goto err_buffer_unmap;
112 }
113
114 addr = device->ws->ops->vma_map(pvr_bo->vma, pvr_bo->bo, 0, size);
115 if (!addr.addr) {
116 result = VK_ERROR_MEMORY_MAP_FAILED;
117 goto err_heap_free;
118 }
119
120 *pvr_bo_out = pvr_bo;
121
122 return VK_SUCCESS;
123
124 err_heap_free:
125 device->ws->ops->heap_free(pvr_bo->vma);
126
127 err_buffer_unmap:
128 if (flags & PVR_BO_ALLOC_FLAG_CPU_MAPPED)
129 device->ws->ops->buffer_unmap(pvr_bo->bo);
130
131 err_buffer_destroy:
132 device->ws->ops->buffer_destroy(pvr_bo->bo);
133
134 err_vk_free:
135 vk_free(&device->vk.alloc, pvr_bo);
136
137 return result;
138 }
139
140 /**
141 * \brief Interface to map the buffer into host virtual address space.
142 *
143 * Buffer should have been created with the #PVR_BO_ALLOC_FLAG_CPU_ACCESS
144 * flag. It should also not already be mapped or it should have been unmapped
145 * using #pvr_bo_cpu_unmap() before mapping again.
146 *
147 * \param[in] device Logical device pointer.
148 * \param[in] pvr_bo Buffer to map.
149 * \return Valid host virtual address on success, or NULL otherwise.
150 *
151 * \sa #pvr_bo_alloc(), #PVR_BO_ALLOC_FLAG_CPU_MAPPED
152 */
pvr_bo_cpu_map(struct pvr_device * device,struct pvr_bo * pvr_bo)153 void *pvr_bo_cpu_map(struct pvr_device *device, struct pvr_bo *pvr_bo)
154 {
155 assert(!pvr_bo->bo->map);
156
157 return device->ws->ops->buffer_map(pvr_bo->bo);
158 }
159
160 /**
161 * \brief Interface to unmap the buffer from host virtual address space.
162 *
163 * Buffer should have a valid mapping, created either using #pvr_bo_cpu_map() or
164 * by passing #PVR_BO_ALLOC_FLAG_CPU_MAPPED flag to #pvr_bo_alloc() at
165 * allocation time.
166 *
167 * Buffer can be remapped using #pvr_bo_cpu_map().
168 *
169 * \param[in] device Logical device pointer.
170 * \param[in] pvr_bo Buffer to unmap.
171 */
pvr_bo_cpu_unmap(struct pvr_device * device,struct pvr_bo * pvr_bo)172 void pvr_bo_cpu_unmap(struct pvr_device *device, struct pvr_bo *pvr_bo)
173 {
174 assert(pvr_bo->bo->map);
175 device->ws->ops->buffer_unmap(pvr_bo->bo);
176 }
177
178 /**
179 * \brief Interface to free the buffer object.
180 *
181 * \param[in] device Logical device pointer.
182 * \param[in] pvr_bo Buffer to free.
183 *
184 * \sa #pvr_bo_alloc()
185 */
pvr_bo_free(struct pvr_device * device,struct pvr_bo * pvr_bo)186 void pvr_bo_free(struct pvr_device *device, struct pvr_bo *pvr_bo)
187 {
188 if (!pvr_bo)
189 return;
190
191 device->ws->ops->vma_unmap(pvr_bo->vma);
192 device->ws->ops->heap_free(pvr_bo->vma);
193
194 if (pvr_bo->bo->map)
195 device->ws->ops->buffer_unmap(pvr_bo->bo);
196
197 device->ws->ops->buffer_destroy(pvr_bo->bo);
198
199 vk_free(&device->vk.alloc, pvr_bo);
200 }
201