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