1 /*
2 * Copyright © 2019 Raspberry Pi Ltd
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is 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
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "v3dv_private.h"
25
26 /* We don't expect that the packets we use in this file change across hw
27 * versions, so we just explicitly set the V3D_VERSION and include v3dx_pack
28 * here
29 */
30 #define V3D_VERSION 42
31 #include "broadcom/common/v3d_macros.h"
32 #include "broadcom/cle/v3dx_pack.h"
33
34 void
v3dv_cl_init(struct v3dv_job * job,struct v3dv_cl * cl)35 v3dv_cl_init(struct v3dv_job *job, struct v3dv_cl *cl)
36 {
37 cl->base = NULL;
38 cl->next = cl->base;
39 cl->bo = NULL;
40 cl->size = 0;
41 cl->job = job;
42 list_inithead(&cl->bo_list);
43 }
44
45 void
v3dv_cl_destroy(struct v3dv_cl * cl)46 v3dv_cl_destroy(struct v3dv_cl *cl)
47 {
48 list_for_each_entry_safe(struct v3dv_bo, bo, &cl->bo_list, list_link) {
49 assert(cl->job);
50 list_del(&bo->list_link);
51 v3dv_bo_free(cl->job->device, bo);
52 }
53
54 /* Leave the CL in a reset state to catch use after destroy instances */
55 v3dv_cl_init(NULL, cl);
56 }
57
58 static bool
cl_alloc_bo(struct v3dv_cl * cl,uint32_t space,bool use_branch)59 cl_alloc_bo(struct v3dv_cl *cl, uint32_t space, bool use_branch)
60 {
61 /* If we are growing, double the BO allocation size to reduce the number
62 * of allocations with large command buffers. This has a very significant
63 * impact on the number of draw calls per second reported by vkoverhead.
64 */
65 space = align(space, 4096);
66 if (cl->bo)
67 space = MAX2(cl->bo->size * 2, space);
68
69 struct v3dv_bo *bo = v3dv_bo_alloc(cl->job->device, space, "CL", true);
70 if (!bo) {
71 fprintf(stderr, "failed to allocate memory for command list\n");
72 v3dv_flag_oom(NULL, cl->job);
73 return false;
74 }
75
76 list_addtail(&bo->list_link, &cl->bo_list);
77
78 bool ok = v3dv_bo_map(cl->job->device, bo, bo->size);
79 if (!ok) {
80 fprintf(stderr, "failed to map command list buffer\n");
81 v3dv_flag_oom(NULL, cl->job);
82 return false;
83 }
84
85 /* Chain to the new BO from the old one if requested */
86 if (use_branch && cl->bo) {
87 cl_emit(cl, BRANCH, branch) {
88 branch.address = v3dv_cl_address(bo, 0);
89 }
90 } else {
91 v3dv_job_add_bo_unchecked(cl->job, bo);
92 }
93
94 cl->bo = bo;
95 cl->base = cl->bo->map;
96 cl->size = cl->bo->size;
97 cl->next = cl->base;
98
99 return true;
100 }
101
102 uint32_t
v3dv_cl_ensure_space(struct v3dv_cl * cl,uint32_t space,uint32_t alignment)103 v3dv_cl_ensure_space(struct v3dv_cl *cl, uint32_t space, uint32_t alignment)
104 {
105 uint32_t offset = align(v3dv_cl_offset(cl), alignment);
106
107 if (offset + space <= cl->size) {
108 cl->next = cl->base + offset;
109 return offset;
110 }
111
112 cl_alloc_bo(cl, space, false);
113 return 0;
114 }
115
116 void
v3dv_cl_ensure_space_with_branch(struct v3dv_cl * cl,uint32_t space)117 v3dv_cl_ensure_space_with_branch(struct v3dv_cl *cl, uint32_t space)
118 {
119 /* We do not want to emit branches from secondary command lists, instead,
120 * we will branch to them when we execute them in a primary using
121 * 'branch to sub list' commands, expecting each linked secondary to
122 * end with a 'return from sub list' command.
123 */
124 bool needs_return_from_sub_list = false;
125 if (cl->job->type == V3DV_JOB_TYPE_GPU_CL_SECONDARY && cl->size > 0)
126 needs_return_from_sub_list = true;
127
128 /*
129 * The CLE processor in the simulator tries to read V3D_CL_MAX_INSTR_SIZE
130 * bytes form the CL for each new instruction. If the last instruction in our
131 * CL is smaller than that, and there are not at least V3D_CL_MAX_INSTR_SIZE
132 * bytes until the end of the BO, it will read out of bounds and possibly
133 * cause a GMP violation interrupt to trigger. Ensure we always have at
134 * least that many bytes available to read with the last instruction.
135 */
136 space += V3D_CL_MAX_INSTR_SIZE;
137
138 if (v3dv_cl_offset(cl) + space <= cl->size)
139 return;
140
141 if (needs_return_from_sub_list)
142 cl_emit(cl, RETURN_FROM_SUB_LIST, ret);
143
144 cl_alloc_bo(cl, space, !needs_return_from_sub_list);
145 }
146