broadcom/vc5: Fix CLIF dumping of lists that aren't capped by a HALT.
[mesa.git] / src / gallium / drivers / vc5 / vc5_job.c
1 /*
2 * Copyright © 2014-2017 Broadcom
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 /** @file vc5_job.c
25 *
26 * Functions for submitting VC5 render jobs to the kernel.
27 */
28
29 #include <xf86drm.h>
30 #include "vc5_context.h"
31 #include "util/hash_table.h"
32 #include "util/ralloc.h"
33 #include "util/set.h"
34 #include "broadcom/clif/clif_dump.h"
35 #include "broadcom/cle/v3d_packet_v33_pack.h"
36
37 static void
38 remove_from_ht(struct hash_table *ht, void *key)
39 {
40 struct hash_entry *entry = _mesa_hash_table_search(ht, key);
41 _mesa_hash_table_remove(ht, entry);
42 }
43
44 static void
45 vc5_job_free(struct vc5_context *vc5, struct vc5_job *job)
46 {
47 struct set_entry *entry;
48
49 set_foreach(job->bos, entry) {
50 struct vc5_bo *bo = (struct vc5_bo *)entry->key;
51 vc5_bo_unreference(&bo);
52 }
53
54 remove_from_ht(vc5->jobs, &job->key);
55
56 for (int i = 0; i < VC5_MAX_DRAW_BUFFERS; i++) {
57 if (job->cbufs[i]) {
58 remove_from_ht(vc5->write_jobs, job->cbufs[i]->texture);
59 pipe_surface_reference(&job->cbufs[i], NULL);
60 }
61 }
62 if (job->zsbuf) {
63 remove_from_ht(vc5->write_jobs, job->zsbuf->texture);
64 pipe_surface_reference(&job->zsbuf, NULL);
65 }
66
67 if (vc5->job == job)
68 vc5->job = NULL;
69
70 vc5_destroy_cl(&job->bcl);
71 vc5_destroy_cl(&job->rcl);
72 vc5_destroy_cl(&job->indirect);
73 vc5_bo_unreference(&job->tile_alloc);
74
75 ralloc_free(job);
76 }
77
78 static struct vc5_job *
79 vc5_job_create(struct vc5_context *vc5)
80 {
81 struct vc5_job *job = rzalloc(vc5, struct vc5_job);
82
83 job->vc5 = vc5;
84
85 vc5_init_cl(job, &job->bcl);
86 vc5_init_cl(job, &job->rcl);
87 vc5_init_cl(job, &job->indirect);
88
89 job->draw_min_x = ~0;
90 job->draw_min_y = ~0;
91 job->draw_max_x = 0;
92 job->draw_max_y = 0;
93
94 job->bos = _mesa_set_create(job,
95 _mesa_hash_pointer,
96 _mesa_key_pointer_equal);
97 return job;
98 }
99
100 void
101 vc5_job_add_bo(struct vc5_job *job, struct vc5_bo *bo)
102 {
103 if (!bo)
104 return;
105
106 if (_mesa_set_search(job->bos, bo))
107 return;
108
109 vc5_bo_reference(bo);
110 _mesa_set_add(job->bos, bo);
111
112 uint32_t *bo_handles = (void *)(uintptr_t)job->submit.bo_handles;
113
114 if (job->submit.bo_handle_count >= job->bo_handles_size) {
115 job->bo_handles_size = MAX2(4, job->bo_handles_size * 2);
116 bo_handles = reralloc(job, bo_handles,
117 uint32_t, job->bo_handles_size);
118 job->submit.bo_handles = (uintptr_t)(void *)bo_handles;
119 }
120 bo_handles[job->submit.bo_handle_count++] = bo->handle;
121 }
122
123 void
124 vc5_flush_jobs_writing_resource(struct vc5_context *vc5,
125 struct pipe_resource *prsc)
126 {
127 struct hash_entry *entry = _mesa_hash_table_search(vc5->write_jobs,
128 prsc);
129 if (entry) {
130 struct vc5_job *job = entry->data;
131 vc5_job_submit(vc5, job);
132 }
133 }
134
135 void
136 vc5_flush_jobs_reading_resource(struct vc5_context *vc5,
137 struct pipe_resource *prsc)
138 {
139 struct vc5_resource *rsc = vc5_resource(prsc);
140
141 vc5_flush_jobs_writing_resource(vc5, prsc);
142
143 struct hash_entry *entry;
144 hash_table_foreach(vc5->jobs, entry) {
145 struct vc5_job *job = entry->data;
146
147 if (_mesa_set_search(job->bos, rsc->bo)) {
148 vc5_job_submit(vc5, job);
149 /* Reminder: vc5->jobs is safe to keep iterating even
150 * after deletion of an entry.
151 */
152 continue;
153 }
154 }
155 }
156
157 static void
158 vc5_job_set_tile_buffer_size(struct vc5_job *job)
159 {
160 static const uint8_t tile_sizes[] = {
161 64, 64,
162 64, 32,
163 32, 32,
164 32, 16,
165 16, 16,
166 };
167 int tile_size_index = 0;
168 if (job->msaa)
169 tile_size_index += 2;
170
171 if (job->cbufs[3])
172 tile_size_index += 2;
173 else if (job->cbufs[2])
174 tile_size_index++;
175
176 int max_bpp = RENDER_TARGET_MAXIMUM_32BPP;
177 for (int i = 0; i < VC5_MAX_DRAW_BUFFERS; i++) {
178 if (job->cbufs[i]) {
179 struct vc5_surface *surf = vc5_surface(job->cbufs[i]);
180 max_bpp = MAX2(max_bpp, surf->internal_bpp);
181 }
182 }
183 job->internal_bpp = max_bpp;
184 STATIC_ASSERT(RENDER_TARGET_MAXIMUM_32BPP == 0);
185 tile_size_index += max_bpp;
186
187 assert(tile_size_index < ARRAY_SIZE(tile_sizes));
188 job->tile_width = tile_sizes[tile_size_index * 2 + 0];
189 job->tile_height = tile_sizes[tile_size_index * 2 + 1];
190 }
191
192 /**
193 * Returns a vc5_job struture for tracking V3D rendering to a particular FBO.
194 *
195 * If we've already started rendering to this FBO, then return old same job,
196 * otherwise make a new one. If we're beginning rendering to an FBO, make
197 * sure that any previous reads of the FBO (or writes to its color/Z surfaces)
198 * have been flushed.
199 */
200 struct vc5_job *
201 vc5_get_job(struct vc5_context *vc5,
202 struct pipe_surface **cbufs, struct pipe_surface *zsbuf)
203 {
204 /* Return the existing job for this FBO if we have one */
205 struct vc5_job_key local_key = {
206 .cbufs = {
207 cbufs[0],
208 cbufs[1],
209 cbufs[2],
210 cbufs[3],
211 },
212 .zsbuf = zsbuf,
213 };
214 struct hash_entry *entry = _mesa_hash_table_search(vc5->jobs,
215 &local_key);
216 if (entry)
217 return entry->data;
218
219 /* Creating a new job. Make sure that any previous jobs reading or
220 * writing these buffers are flushed.
221 */
222 struct vc5_job *job = vc5_job_create(vc5);
223
224 for (int i = 0; i < VC5_MAX_DRAW_BUFFERS; i++) {
225 if (cbufs[i]) {
226 vc5_flush_jobs_reading_resource(vc5, cbufs[i]->texture);
227 pipe_surface_reference(&job->cbufs[i], cbufs[i]);
228
229 if (cbufs[i]->texture->nr_samples > 1)
230 job->msaa = true;
231 }
232 }
233 if (zsbuf) {
234 vc5_flush_jobs_reading_resource(vc5, zsbuf->texture);
235 pipe_surface_reference(&job->zsbuf, zsbuf);
236 if (zsbuf->texture->nr_samples > 1)
237 job->msaa = true;
238 }
239
240 vc5_job_set_tile_buffer_size(job);
241
242 for (int i = 0; i < VC5_MAX_DRAW_BUFFERS; i++) {
243 if (cbufs[i])
244 _mesa_hash_table_insert(vc5->write_jobs,
245 cbufs[i]->texture, job);
246 }
247 if (zsbuf)
248 _mesa_hash_table_insert(vc5->write_jobs, zsbuf->texture, job);
249
250 memcpy(&job->key, &local_key, sizeof(local_key));
251 _mesa_hash_table_insert(vc5->jobs, &job->key, job);
252
253 return job;
254 }
255
256 struct vc5_job *
257 vc5_get_job_for_fbo(struct vc5_context *vc5)
258 {
259 if (vc5->job)
260 return vc5->job;
261
262 struct pipe_surface **cbufs = vc5->framebuffer.cbufs;
263 struct pipe_surface *zsbuf = vc5->framebuffer.zsbuf;
264 struct vc5_job *job = vc5_get_job(vc5, cbufs, zsbuf);
265
266 /* The dirty flags are tracking what's been updated while vc5->job has
267 * been bound, so set them all to ~0 when switching between jobs. We
268 * also need to reset all state at the start of rendering.
269 */
270 vc5->dirty = ~0;
271
272 /* If we're binding to uninitialized buffers, no need to load their
273 * contents before drawing.
274 */
275 for (int i = 0; i < 4; i++) {
276 if (cbufs[i]) {
277 struct vc5_resource *rsc = vc5_resource(cbufs[i]->texture);
278 if (!rsc->writes)
279 job->cleared |= PIPE_CLEAR_COLOR0 << i;
280 }
281 }
282
283 if (zsbuf) {
284 struct vc5_resource *rsc = vc5_resource(zsbuf->texture);
285 if (!rsc->writes)
286 job->cleared |= PIPE_CLEAR_DEPTH | PIPE_CLEAR_STENCIL;
287 }
288
289 job->draw_tiles_x = DIV_ROUND_UP(vc5->framebuffer.width,
290 job->tile_width);
291 job->draw_tiles_y = DIV_ROUND_UP(vc5->framebuffer.height,
292 job->tile_height);
293
294 vc5->job = job;
295
296 return job;
297 }
298
299 static bool
300 vc5_clif_dump_lookup(void *data, uint32_t addr, void **vaddr)
301 {
302 struct vc5_job *job = data;
303 struct set_entry *entry;
304
305 set_foreach(job->bos, entry) {
306 struct vc5_bo *bo = (void *)entry->key;
307
308 if (addr >= bo->offset &&
309 addr < bo->offset + bo->size) {
310 vc5_bo_map(bo);
311 *vaddr = bo->map + addr - bo->offset;
312 return true;
313 }
314 }
315
316 return false;
317 }
318
319 static void
320 vc5_clif_dump(struct vc5_context *vc5, struct vc5_job *job)
321 {
322 if (!(V3D_DEBUG & V3D_DEBUG_CL))
323 return;
324
325 struct clif_dump *clif = clif_dump_init(&vc5->screen->devinfo,
326 stderr, vc5_clif_dump_lookup,
327 job);
328
329 fprintf(stderr, "BCL: 0x%08x..0x%08x\n",
330 job->submit.bcl_start, job->submit.bcl_end);
331
332 clif_dump_add_cl(clif, job->submit.bcl_start, job->submit.bcl_end);
333
334 fprintf(stderr, "RCL: 0x%08x..0x%08x\n",
335 job->submit.rcl_start, job->submit.rcl_end);
336 clif_dump_add_cl(clif, job->submit.rcl_start, job->submit.rcl_end);
337 }
338
339 /**
340 * Submits the job to the kernel and then reinitializes it.
341 */
342 void
343 vc5_job_submit(struct vc5_context *vc5, struct vc5_job *job)
344 {
345 if (!job->needs_flush)
346 goto done;
347
348 /* The RCL setup would choke if the draw bounds cause no drawing, so
349 * just drop the drawing if that's the case.
350 */
351 if (job->draw_max_x <= job->draw_min_x ||
352 job->draw_max_y <= job->draw_min_y) {
353 goto done;
354 }
355
356 vc5_emit_rcl(job);
357
358 if (cl_offset(&job->bcl) > 0) {
359 vc5_cl_ensure_space_with_branch(&job->bcl, 2);
360
361 /* Increment the semaphore indicating that binning is done and
362 * unblocking the render thread. Note that this doesn't act
363 * until the FLUSH completes.
364 */
365 cl_emit(&job->bcl, INCREMENT_SEMAPHORE, incr);
366
367 /* The FLUSH caps all of our bin lists with a
368 * VC5_PACKET_RETURN.
369 */
370 cl_emit(&job->bcl, FLUSH, flush);
371 }
372
373 job->submit.bcl_end = job->bcl.bo->offset + cl_offset(&job->bcl);
374 job->submit.rcl_end = job->rcl.bo->offset + cl_offset(&job->rcl);
375
376 vc5_clif_dump(vc5, job);
377
378 if (!(V3D_DEBUG & V3D_DEBUG_NORAST)) {
379 int ret;
380
381 #ifndef USE_VC5_SIMULATOR
382 ret = drmIoctl(vc5->fd, DRM_IOCTL_VC5_SUBMIT_CL, &job->submit);
383 #else
384 ret = vc5_simulator_flush(vc5, &job->submit, job);
385 #endif
386 static bool warned = false;
387 if (ret && !warned) {
388 fprintf(stderr, "Draw call returned %s. "
389 "Expect corruption.\n", strerror(errno));
390 warned = true;
391 }
392 }
393
394 if (vc5->last_emit_seqno - vc5->screen->finished_seqno > 5) {
395 if (!vc5_wait_seqno(vc5->screen,
396 vc5->last_emit_seqno - 5,
397 PIPE_TIMEOUT_INFINITE,
398 "job throttling")) {
399 fprintf(stderr, "Job throttling failed\n");
400 }
401 }
402
403 done:
404 vc5_job_free(vc5, job);
405 }
406
407 static bool
408 vc5_job_compare(const void *a, const void *b)
409 {
410 return memcmp(a, b, sizeof(struct vc5_job_key)) == 0;
411 }
412
413 static uint32_t
414 vc5_job_hash(const void *key)
415 {
416 return _mesa_hash_data(key, sizeof(struct vc5_job_key));
417 }
418
419 void
420 vc5_job_init(struct vc5_context *vc5)
421 {
422 vc5->jobs = _mesa_hash_table_create(vc5,
423 vc5_job_hash,
424 vc5_job_compare);
425 vc5->write_jobs = _mesa_hash_table_create(vc5,
426 _mesa_hash_pointer,
427 _mesa_key_pointer_equal);
428 }
429