xref: /aosp_15_r20/external/mesa3d/src/imagination/vulkan/pvr_csb.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © 2022 Imagination Technologies Ltd.
3  *
4  * based in part on anv driver which is:
5  * Copyright © 2015 Intel Corporation
6  *
7  * based in part on v3dv_cl.c which is:
8  * Copyright © 2019 Raspberry Pi
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to deal
12  * in the Software without restriction, including without limitation the rights
13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice (including the next
18  * paragraph) shall be included in all copies or substantial portions of the
19  * Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE.
28  */
29 
30 #include <assert.h>
31 #include <stdbool.h>
32 #include <stdint.h>
33 #include <string.h>
34 #include <vulkan/vulkan.h>
35 
36 #include "hwdef/rogue_hw_utils.h"
37 #include "pvr_bo.h"
38 #include "pvr_csb.h"
39 #include "pvr_debug.h"
40 #include "pvr_device_info.h"
41 #include "pvr_private.h"
42 #include "pvr_types.h"
43 #include "util/list.h"
44 #include "util/u_dynarray.h"
45 #include "vk_log.h"
46 
47 /**
48  * \file pvr_csb.c
49  *
50  * \brief Contains functions to manage Control Stream Builder (csb) object.
51  *
52  * A csb object can be used to create a primary/main control stream, referred
53  * as control stream hereafter, or a secondary control stream, also referred as
54  * a sub control stream. The main difference between these is that, the control
55  * stream is the one directly submitted to the GPU and is terminated using
56  * STREAM_TERMINATE. Whereas, the secondary control stream can be thought of as
57  * an independent set of commands that can be referenced by a primary control
58  * stream to avoid duplication and is instead terminated using STREAM_RETURN,
59  * which means the control stream parser should return to the main stream it
60  * came from.
61  *
62  * Note: Sub control stream is only supported for PVR_CMD_STREAM_TYPE_GRAPHICS
63  * type control streams.
64  */
65 
66 /**
67  * \brief Initializes the csb object.
68  *
69  * \param[in] device Logical device pointer.
70  * \param[in] csb    Control Stream Builder object to initialize.
71  *
72  * \sa #pvr_csb_finish()
73  */
pvr_csb_init(struct pvr_device * device,enum pvr_cmd_stream_type stream_type,struct pvr_csb * csb)74 void pvr_csb_init(struct pvr_device *device,
75                   enum pvr_cmd_stream_type stream_type,
76                   struct pvr_csb *csb)
77 {
78    csb->start = NULL;
79    csb->next = NULL;
80    csb->pvr_bo = NULL;
81    csb->end = NULL;
82    csb->relocation_mark = NULL;
83 
84 #if MESA_DEBUG
85    csb->relocation_mark_status = PVR_CSB_RELOCATION_MARK_UNINITIALIZED;
86 #endif
87 
88    csb->device = device;
89    csb->stream_type = stream_type;
90    csb->status = VK_SUCCESS;
91 
92    if (stream_type == PVR_CMD_STREAM_TYPE_GRAPHICS_DEFERRED)
93       util_dynarray_init(&csb->deferred_cs_mem, NULL);
94    else
95       list_inithead(&csb->pvr_bo_list);
96 }
97 
98 /**
99  * \brief Frees the resources associated with the csb object.
100  *
101  * \param[in] csb Control Stream Builder object to free.
102  *
103  * \sa #pvr_csb_init()
104  */
pvr_csb_finish(struct pvr_csb * csb)105 void pvr_csb_finish(struct pvr_csb *csb)
106 {
107 #if MESA_DEBUG
108    assert(csb->relocation_mark_status ==
109              PVR_CSB_RELOCATION_MARK_UNINITIALIZED ||
110           csb->relocation_mark_status == PVR_CSB_RELOCATION_MARK_CLEARED);
111 #endif
112 
113    if (csb->stream_type == PVR_CMD_STREAM_TYPE_GRAPHICS_DEFERRED) {
114       util_dynarray_fini(&csb->deferred_cs_mem);
115    } else {
116       list_for_each_entry_safe (struct pvr_bo, pvr_bo, &csb->pvr_bo_list, link) {
117          list_del(&pvr_bo->link);
118          pvr_bo_free(csb->device, pvr_bo);
119       }
120    }
121 
122    /* Leave the csb in a reset state to catch use after destroy instances */
123    pvr_csb_init(NULL, PVR_CMD_STREAM_TYPE_INVALID, csb);
124 }
125 
126 /**
127  * \brief Discard information only required while building and return the BOs.
128  *
129  * \param[in] csb Control Stream Builder object to bake.
130  * \param[out] bo_list_out A list of \c pvr_bo containing the control stream.
131  *
132  * \return The last status value of \c csb.
133  *
134  * The value of \c bo_list_out is only defined iff this function returns
135  * \c VK_SUCCESS. It is not allowed to call this function on a \c pvr_csb for
136  * a deferred control stream type.
137  *
138  * The state of \c csb after calling this function (iff it returns
139  * \c VK_SUCCESS) is identical to that after calling #pvr_csb_finish().
140  * Unlike #pvr_csb_finish(), however, the caller must free every entry in
141  * \c bo_list_out itself.
142  */
pvr_csb_bake(struct pvr_csb * const csb,struct list_head * const bo_list_out)143 VkResult pvr_csb_bake(struct pvr_csb *const csb,
144                       struct list_head *const bo_list_out)
145 {
146    assert(csb->stream_type != PVR_CMD_STREAM_TYPE_GRAPHICS_DEFERRED);
147 
148    if (csb->status != VK_SUCCESS)
149       return csb->status;
150 
151    list_replace(&csb->pvr_bo_list, bo_list_out);
152 
153    /* Same as pvr_csb_finish(). */
154    pvr_csb_init(NULL, PVR_CMD_STREAM_TYPE_INVALID, csb);
155 
156    return VK_SUCCESS;
157 }
158 
159 /**
160  * \brief Adds VDMCTRL_STREAM_LINK/CDMCTRL_STREAM_LINK dwords into the control
161  * stream pointed by csb object without setting a relocation mark.
162  *
163  * \warning This does not set the relocation mark.
164  *
165  * \param[in] csb  Control Stream Builder object to add LINK dwords to.
166  * \param[in] addr Device virtual address of the sub control stream to link to.
167  * \param[in] ret  Selects whether the sub control stream will return or
168  *                 terminate.
169  */
170 static void
pvr_csb_emit_link_unmarked(struct pvr_csb * csb,pvr_dev_addr_t addr,bool ret)171 pvr_csb_emit_link_unmarked(struct pvr_csb *csb, pvr_dev_addr_t addr, bool ret)
172 {
173    /* Not supported for deferred control stream. */
174    assert(csb->stream_type != PVR_CMD_STREAM_TYPE_GRAPHICS_DEFERRED);
175 
176    /* Stream return is only supported for graphics control stream. */
177    assert(!ret || csb->stream_type == PVR_CMD_STREAM_TYPE_GRAPHICS);
178 
179    switch (csb->stream_type) {
180    case PVR_CMD_STREAM_TYPE_GRAPHICS:
181       pvr_csb_emit (csb, VDMCTRL_STREAM_LINK0, link) {
182          link.link_addrmsb = addr;
183          link.with_return = ret;
184       }
185 
186       pvr_csb_emit (csb, VDMCTRL_STREAM_LINK1, link) {
187          link.link_addrlsb = addr;
188       }
189 
190       break;
191 
192    case PVR_CMD_STREAM_TYPE_COMPUTE:
193       pvr_csb_emit (csb, CDMCTRL_STREAM_LINK0, link) {
194          link.link_addrmsb = addr;
195       }
196 
197       pvr_csb_emit (csb, CDMCTRL_STREAM_LINK1, link) {
198          link.link_addrlsb = addr;
199       }
200 
201       break;
202 
203    default:
204       unreachable("Unknown stream type");
205       break;
206    }
207 }
208 
209 /**
210  * \brief Helper function to extend csb memory.
211  *
212  * Allocates a new buffer object and links it with the previous buffer object
213  * using STREAM_LINK dwords and updates csb object to use the new buffer.
214  *
215  * To make sure that we have enough space to emit STREAM_LINK dwords in the
216  * current buffer, a few bytes including guard padding size are reserved at the
217  * end, every time a buffer is created. Every time we allocate a new buffer we
218  * fix the current buffer in use to emit the stream link dwords. This makes sure
219  * that when #pvr_csb_alloc_dwords() is called from #pvr_csb_emit() to add
220  * STREAM_LINK0 and STREAM_LINK1, it succeeds without trying to allocate new
221  * pages.
222  *
223  * \param[in] csb Control Stream Builder object to extend.
224  * \return true on success and false otherwise.
225  */
pvr_csb_buffer_extend(struct pvr_csb * csb)226 static bool pvr_csb_buffer_extend(struct pvr_csb *csb)
227 {
228    const uint8_t stream_link_space =
229       PVR_DW_TO_BYTES(pvr_cmd_length(VDMCTRL_STREAM_LINK0) +
230                       pvr_cmd_length(VDMCTRL_STREAM_LINK1));
231    const uint8_t stream_reserved_space =
232       stream_link_space + PVRX(VDMCTRL_GUARD_SIZE_DEFAULT);
233    const uint32_t cache_line_size =
234       rogue_get_slc_cache_line_size(&csb->device->pdevice->dev_info);
235    size_t current_state_update_size = 0;
236    struct pvr_bo *pvr_bo;
237    VkResult result;
238 
239    /* Make sure extra space allocated for stream links is sufficient for both
240     * stream types.
241     */
242    STATIC_ASSERT((pvr_cmd_length(VDMCTRL_STREAM_LINK0) +
243                   pvr_cmd_length(VDMCTRL_STREAM_LINK1)) ==
244                  (pvr_cmd_length(CDMCTRL_STREAM_LINK0) +
245                   pvr_cmd_length(CDMCTRL_STREAM_LINK1)));
246 
247    STATIC_ASSERT(PVRX(VDMCTRL_GUARD_SIZE_DEFAULT) ==
248                  PVRX(CDMCTRL_GUARD_SIZE_DEFAULT));
249 
250    result = pvr_bo_alloc(csb->device,
251                          csb->device->heaps.general_heap,
252                          PVR_CMD_BUFFER_CSB_BO_SIZE,
253                          cache_line_size,
254                          PVR_BO_ALLOC_FLAG_CPU_MAPPED,
255                          &pvr_bo);
256    if (result != VK_SUCCESS) {
257       vk_error(csb->device, result);
258       csb->status = result;
259       return false;
260    }
261 
262    /* if this is not the first BO in csb */
263    if (csb->pvr_bo) {
264       bool zero_after_move = PVR_IS_DEBUG_SET(DUMP_CONTROL_STREAM);
265       void *new_buffer = pvr_bo->bo->map;
266 
267       current_state_update_size =
268          (uint8_t *)csb->next - (uint8_t *)csb->relocation_mark;
269 
270       assert(csb->relocation_mark != NULL);
271       assert(csb->next >= csb->relocation_mark);
272 
273       memcpy(new_buffer, csb->relocation_mark, current_state_update_size);
274 
275 #if MESA_DEBUG
276       assert(csb->relocation_mark_status == PVR_CSB_RELOCATION_MARK_SET);
277       csb->relocation_mark_status = PVR_CSB_RELOCATION_MARK_SET_AND_CONSUMED;
278       zero_after_move = true;
279 #endif
280 
281       if (zero_after_move)
282          memset(csb->relocation_mark, 0, current_state_update_size);
283 
284       csb->next = csb->relocation_mark;
285 
286       csb->end = (uint8_t *)csb->end + stream_link_space;
287       assert((uint8_t *)csb->next + stream_link_space <= (uint8_t *)csb->end);
288 
289       pvr_csb_emit_link_unmarked(csb, pvr_bo->vma->dev_addr, false);
290    }
291 
292    csb->pvr_bo = pvr_bo;
293    csb->start = pvr_bo->bo->map;
294 
295    /* Reserve space at the end, including the default guard padding, to make
296     * sure we don't run out of space when a stream link is required.
297     */
298    csb->end = (uint8_t *)csb->start + pvr_bo->bo->size - stream_reserved_space;
299    csb->next = (uint8_t *)csb->start + current_state_update_size;
300 
301    list_addtail(&pvr_bo->link, &csb->pvr_bo_list);
302 
303    return true;
304 }
305 
306 /**
307  * \brief Provides a chunk of memory from the current csb buffer. In cases where
308  * the buffer is not able to fulfill the required amount of memory,
309  * #pvr_csb_buffer_extend() is called to allocate a new buffer. Maximum size
310  * allocable in bytes is #PVR_CMD_BUFFER_CSB_BO_SIZE - size of STREAM_LINK0
311  * and STREAM_LINK1 dwords.
312  *
313  * \param[in] csb        Control Stream Builder object to allocate from.
314  * \param[in] num_dwords Number of dwords to allocate.
315  * \return Valid host virtual address or NULL otherwise.
316  */
pvr_csb_alloc_dwords(struct pvr_csb * csb,uint32_t num_dwords)317 void *pvr_csb_alloc_dwords(struct pvr_csb *csb, uint32_t num_dwords)
318 {
319    const uint32_t required_space = PVR_DW_TO_BYTES(num_dwords);
320    void *p;
321 
322    if (csb->status != VK_SUCCESS)
323       return NULL;
324 
325    if (csb->stream_type == PVR_CMD_STREAM_TYPE_GRAPHICS_DEFERRED) {
326       p = util_dynarray_grow_bytes(&csb->deferred_cs_mem, 1, required_space);
327       if (!p)
328          csb->status = vk_error(csb->device, VK_ERROR_OUT_OF_HOST_MEMORY);
329 
330       return p;
331    }
332 
333 #if MESA_DEBUG
334    if (csb->relocation_mark_status == PVR_CSB_RELOCATION_MARK_CLEARED)
335       mesa_logd_once("CS memory without relocation mark detected.");
336 #endif
337 
338    if ((uint8_t *)csb->next + required_space > (uint8_t *)csb->end) {
339       bool ret = pvr_csb_buffer_extend(csb);
340       if (!ret)
341          return NULL;
342    }
343 
344    p = csb->next;
345 
346    csb->next = (uint8_t *)csb->next + required_space;
347    assert(csb->next <= csb->end);
348 
349    return p;
350 }
351 
352 /**
353  * \brief Copies control stream words from src csb into dst csb.
354  *
355  * The intended use is to copy PVR_CMD_STREAM_TYPE_GRAPHICS_DEFERRED type
356  * control stream into PVR_CMD_STREAM_TYPE_GRAPHICS type device accessible
357  * control stream for processing.
358  *
359  * This is mainly for secondary command buffers created with
360  * VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT flag. In that case we need to
361  * copy secondary control stream into the primary control stream for processing.
362  * This is done as part of vkCmdExecuteCommands.
363  *
364  * We create deferred control stream which is basically the same control stream
365  * but based in host side memory to avoid reserving device side resource.
366  *
367  * \param[in,out] csb_dst Destination control Stream Builder object.
368  * \param[in]     csb_src Source Control Stream Builder object.
369  */
pvr_csb_copy(struct pvr_csb * csb_dst,struct pvr_csb * csb_src)370 VkResult pvr_csb_copy(struct pvr_csb *csb_dst, struct pvr_csb *csb_src)
371 {
372    const uint8_t stream_reserved_space =
373       PVR_DW_TO_BYTES(pvr_cmd_length(VDMCTRL_STREAM_LINK0) +
374                       pvr_cmd_length(VDMCTRL_STREAM_LINK1)) +
375       PVRX(VDMCTRL_GUARD_SIZE_DEFAULT);
376    const uint32_t size =
377       util_dynarray_num_elements(&csb_src->deferred_cs_mem, char);
378    const uint8_t *start = util_dynarray_begin(&csb_src->deferred_cs_mem);
379    void *destination;
380 
381    /* Only deferred control stream supported as src. */
382    assert(csb_src->stream_type == PVR_CMD_STREAM_TYPE_GRAPHICS_DEFERRED);
383 
384    /* Only graphics control stream supported as dst. */
385    assert(csb_dst->stream_type == PVR_CMD_STREAM_TYPE_GRAPHICS);
386 
387    if (size >= (PVR_CMD_BUFFER_CSB_BO_SIZE - stream_reserved_space)) {
388       /* TODO: For now we don't support deferred streams bigger than one csb
389        * buffer object size.
390        *
391        * While adding support for this make sure to not break the words/dwords
392        * over two csb buffers.
393        */
394       pvr_finishme("Add support to copy streams bigger than one csb buffer");
395       assert(!"CSB source buffer too large to do a full copy");
396    }
397 
398    destination = pvr_csb_alloc_dwords(csb_dst, size);
399    if (!destination) {
400       assert(csb_dst->status != VK_SUCCESS);
401       return csb_dst->status;
402    }
403 
404    memcpy(destination, start, size);
405 
406    return VK_SUCCESS;
407 }
408 
409 /**
410  * \brief Adds VDMCTRL_STREAM_LINK/CDMCTRL_STREAM_LINK dwords into the control
411  * stream pointed by csb object.
412  *
413  * \param[in] csb  Control Stream Builder object to add LINK dwords to.
414  * \param[in] addr Device virtual address of the sub control stream to link to.
415  * \param[in] ret  Selects whether the sub control stream will return or
416  *                 terminate.
417  */
pvr_csb_emit_link(struct pvr_csb * csb,pvr_dev_addr_t addr,bool ret)418 void pvr_csb_emit_link(struct pvr_csb *csb, pvr_dev_addr_t addr, bool ret)
419 {
420    pvr_csb_set_relocation_mark(csb);
421    pvr_csb_emit_link_unmarked(csb, addr, ret);
422    pvr_csb_clear_relocation_mark(csb);
423 }
424 
425 /**
426  * \brief Adds VDMCTRL_STREAM_RETURN dword into the control stream pointed by
427  * csb object. Given a VDMCTRL_STREAM_RETURN marks the end of the sub control
428  * stream, we return the status of the control stream as well.
429  *
430  * \param[in] csb Control Stream Builder object to add VDMCTRL_STREAM_RETURN to.
431  * \return VK_SUCCESS on success, or error code otherwise.
432  */
pvr_csb_emit_return(struct pvr_csb * csb)433 VkResult pvr_csb_emit_return(struct pvr_csb *csb)
434 {
435    /* STREAM_RETURN is only supported by graphics control stream. */
436    assert(csb->stream_type == PVR_CMD_STREAM_TYPE_GRAPHICS ||
437           csb->stream_type == PVR_CMD_STREAM_TYPE_GRAPHICS_DEFERRED);
438 
439    pvr_csb_set_relocation_mark(csb);
440    /* clang-format off */
441    pvr_csb_emit(csb, VDMCTRL_STREAM_RETURN, ret);
442    /* clang-format on */
443    pvr_csb_clear_relocation_mark(csb);
444 
445    return csb->status;
446 }
447 
448 /**
449  * \brief Adds STREAM_TERMINATE dword into the control stream pointed by csb
450  * object. Given a STREAM_TERMINATE marks the end of the control stream, we
451  * return the status of the control stream as well.
452  *
453  * \param[in] csb Control Stream Builder object to terminate.
454  * \return VK_SUCCESS on success, or error code otherwise.
455  */
pvr_csb_emit_terminate(struct pvr_csb * csb)456 VkResult pvr_csb_emit_terminate(struct pvr_csb *csb)
457 {
458    pvr_csb_set_relocation_mark(csb);
459 
460    switch (csb->stream_type) {
461    case PVR_CMD_STREAM_TYPE_GRAPHICS:
462       /* clang-format off */
463       pvr_csb_emit(csb, VDMCTRL_STREAM_TERMINATE, terminate);
464       /* clang-format on */
465       break;
466 
467    case PVR_CMD_STREAM_TYPE_COMPUTE:
468       /* clang-format off */
469       pvr_csb_emit(csb, CDMCTRL_STREAM_TERMINATE, terminate);
470       /* clang-format on */
471       break;
472 
473    default:
474       unreachable("Unknown stream type");
475       break;
476    }
477 
478    pvr_csb_clear_relocation_mark(csb);
479 
480    return csb->status;
481 }
482