2 * Copyright © 2014-2017 Broadcom
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:
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
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
26 * Functions for submitting VC5 render jobs to the kernel.
30 #include "vc5_context.h"
31 #include "util/hash_table.h"
32 #include "util/ralloc.h"
34 #include "broadcom/clif/clif_dump.h"
35 #include "broadcom/cle/v3d_packet_v33_pack.h"
38 remove_from_ht(struct hash_table
*ht
, void *key
)
40 struct hash_entry
*entry
= _mesa_hash_table_search(ht
, key
);
41 _mesa_hash_table_remove(ht
, entry
);
45 vc5_job_free(struct vc5_context
*vc5
, struct vc5_job
*job
)
47 struct set_entry
*entry
;
49 set_foreach(job
->bos
, entry
) {
50 struct vc5_bo
*bo
= (struct vc5_bo
*)entry
->key
;
51 vc5_bo_unreference(&bo
);
54 remove_from_ht(vc5
->jobs
, &job
->key
);
56 if (job
->write_prscs
) {
57 struct set_entry
*entry
;
59 set_foreach(job
->write_prscs
, entry
) {
60 const struct pipe_resource
*prsc
= entry
->key
;
62 remove_from_ht(vc5
->write_jobs
, (void *)prsc
);
66 for (int i
= 0; i
< VC5_MAX_DRAW_BUFFERS
; i
++) {
68 remove_from_ht(vc5
->write_jobs
, job
->cbufs
[i
]->texture
);
69 pipe_surface_reference(&job
->cbufs
[i
], NULL
);
73 remove_from_ht(vc5
->write_jobs
, job
->zsbuf
->texture
);
74 pipe_surface_reference(&job
->zsbuf
, NULL
);
80 vc5_destroy_cl(&job
->bcl
);
81 vc5_destroy_cl(&job
->rcl
);
82 vc5_destroy_cl(&job
->indirect
);
83 vc5_bo_unreference(&job
->tile_alloc
);
88 static struct vc5_job
*
89 vc5_job_create(struct vc5_context
*vc5
)
91 struct vc5_job
*job
= rzalloc(vc5
, struct vc5_job
);
95 vc5_init_cl(job
, &job
->bcl
);
96 vc5_init_cl(job
, &job
->rcl
);
97 vc5_init_cl(job
, &job
->indirect
);
100 job
->draw_min_y
= ~0;
104 job
->bos
= _mesa_set_create(job
,
106 _mesa_key_pointer_equal
);
111 vc5_job_add_bo(struct vc5_job
*job
, struct vc5_bo
*bo
)
116 if (_mesa_set_search(job
->bos
, bo
))
119 vc5_bo_reference(bo
);
120 _mesa_set_add(job
->bos
, bo
);
121 job
->referenced_size
+= bo
->size
;
123 uint32_t *bo_handles
= (void *)(uintptr_t)job
->submit
.bo_handles
;
125 if (job
->submit
.bo_handle_count
>= job
->bo_handles_size
) {
126 job
->bo_handles_size
= MAX2(4, job
->bo_handles_size
* 2);
127 bo_handles
= reralloc(job
, bo_handles
,
128 uint32_t, job
->bo_handles_size
);
129 job
->submit
.bo_handles
= (uintptr_t)(void *)bo_handles
;
131 bo_handles
[job
->submit
.bo_handle_count
++] = bo
->handle
;
135 vc5_job_add_write_resource(struct vc5_job
*job
, struct pipe_resource
*prsc
)
137 struct vc5_context
*vc5
= job
->vc5
;
139 if (!job
->write_prscs
) {
140 job
->write_prscs
= _mesa_set_create(job
,
142 _mesa_key_pointer_equal
);
145 _mesa_set_add(job
->write_prscs
, prsc
);
146 _mesa_hash_table_insert(vc5
->write_jobs
, prsc
, job
);
150 vc5_flush_jobs_writing_resource(struct vc5_context
*vc5
,
151 struct pipe_resource
*prsc
)
153 struct hash_entry
*entry
= _mesa_hash_table_search(vc5
->write_jobs
,
156 struct vc5_job
*job
= entry
->data
;
157 vc5_job_submit(vc5
, job
);
162 vc5_flush_jobs_reading_resource(struct vc5_context
*vc5
,
163 struct pipe_resource
*prsc
)
165 struct vc5_resource
*rsc
= vc5_resource(prsc
);
167 vc5_flush_jobs_writing_resource(vc5
, prsc
);
169 struct hash_entry
*entry
;
170 hash_table_foreach(vc5
->jobs
, entry
) {
171 struct vc5_job
*job
= entry
->data
;
173 if (_mesa_set_search(job
->bos
, rsc
->bo
)) {
174 vc5_job_submit(vc5
, job
);
175 /* Reminder: vc5->jobs is safe to keep iterating even
176 * after deletion of an entry.
184 vc5_job_set_tile_buffer_size(struct vc5_job
*job
)
186 static const uint8_t tile_sizes
[] = {
193 int tile_size_index
= 0;
195 tile_size_index
+= 2;
197 if (job
->cbufs
[3] || job
->cbufs
[2])
198 tile_size_index
+= 2;
199 else if (job
->cbufs
[1])
202 int max_bpp
= RENDER_TARGET_MAXIMUM_32BPP
;
203 for (int i
= 0; i
< VC5_MAX_DRAW_BUFFERS
; i
++) {
205 struct vc5_surface
*surf
= vc5_surface(job
->cbufs
[i
]);
206 max_bpp
= MAX2(max_bpp
, surf
->internal_bpp
);
209 job
->internal_bpp
= max_bpp
;
210 STATIC_ASSERT(RENDER_TARGET_MAXIMUM_32BPP
== 0);
211 tile_size_index
+= max_bpp
;
213 assert(tile_size_index
< ARRAY_SIZE(tile_sizes
));
214 job
->tile_width
= tile_sizes
[tile_size_index
* 2 + 0];
215 job
->tile_height
= tile_sizes
[tile_size_index
* 2 + 1];
219 * Returns a vc5_job struture for tracking V3D rendering to a particular FBO.
221 * If we've already started rendering to this FBO, then return old same job,
222 * otherwise make a new one. If we're beginning rendering to an FBO, make
223 * sure that any previous reads of the FBO (or writes to its color/Z surfaces)
227 vc5_get_job(struct vc5_context
*vc5
,
228 struct pipe_surface
**cbufs
, struct pipe_surface
*zsbuf
)
230 /* Return the existing job for this FBO if we have one */
231 struct vc5_job_key local_key
= {
240 struct hash_entry
*entry
= _mesa_hash_table_search(vc5
->jobs
,
245 /* Creating a new job. Make sure that any previous jobs reading or
246 * writing these buffers are flushed.
248 struct vc5_job
*job
= vc5_job_create(vc5
);
250 for (int i
= 0; i
< VC5_MAX_DRAW_BUFFERS
; i
++) {
252 vc5_flush_jobs_reading_resource(vc5
, cbufs
[i
]->texture
);
253 pipe_surface_reference(&job
->cbufs
[i
], cbufs
[i
]);
255 if (cbufs
[i
]->texture
->nr_samples
> 1)
260 vc5_flush_jobs_reading_resource(vc5
, zsbuf
->texture
);
261 pipe_surface_reference(&job
->zsbuf
, zsbuf
);
262 if (zsbuf
->texture
->nr_samples
> 1)
266 vc5_job_set_tile_buffer_size(job
);
268 for (int i
= 0; i
< VC5_MAX_DRAW_BUFFERS
; i
++) {
270 _mesa_hash_table_insert(vc5
->write_jobs
,
271 cbufs
[i
]->texture
, job
);
274 _mesa_hash_table_insert(vc5
->write_jobs
, zsbuf
->texture
, job
);
276 memcpy(&job
->key
, &local_key
, sizeof(local_key
));
277 _mesa_hash_table_insert(vc5
->jobs
, &job
->key
, job
);
283 vc5_get_job_for_fbo(struct vc5_context
*vc5
)
288 struct pipe_surface
**cbufs
= vc5
->framebuffer
.cbufs
;
289 struct pipe_surface
*zsbuf
= vc5
->framebuffer
.zsbuf
;
290 struct vc5_job
*job
= vc5_get_job(vc5
, cbufs
, zsbuf
);
292 /* The dirty flags are tracking what's been updated while vc5->job has
293 * been bound, so set them all to ~0 when switching between jobs. We
294 * also need to reset all state at the start of rendering.
298 /* If we're binding to uninitialized buffers, no need to load their
299 * contents before drawing.
301 for (int i
= 0; i
< 4; i
++) {
303 struct vc5_resource
*rsc
= vc5_resource(cbufs
[i
]->texture
);
305 job
->cleared
|= PIPE_CLEAR_COLOR0
<< i
;
310 struct vc5_resource
*rsc
= vc5_resource(zsbuf
->texture
);
312 job
->cleared
|= PIPE_CLEAR_DEPTH
| PIPE_CLEAR_STENCIL
;
315 job
->draw_tiles_x
= DIV_ROUND_UP(vc5
->framebuffer
.width
,
317 job
->draw_tiles_y
= DIV_ROUND_UP(vc5
->framebuffer
.height
,
326 vc5_clif_dump_lookup(void *data
, uint32_t addr
, void **vaddr
)
328 struct vc5_job
*job
= data
;
329 struct set_entry
*entry
;
331 set_foreach(job
->bos
, entry
) {
332 struct vc5_bo
*bo
= (void *)entry
->key
;
334 if (addr
>= bo
->offset
&&
335 addr
< bo
->offset
+ bo
->size
) {
337 *vaddr
= bo
->map
+ addr
- bo
->offset
;
346 vc5_clif_dump(struct vc5_context
*vc5
, struct vc5_job
*job
)
348 if (!(V3D_DEBUG
& V3D_DEBUG_CL
))
351 struct clif_dump
*clif
= clif_dump_init(&vc5
->screen
->devinfo
,
352 stderr
, vc5_clif_dump_lookup
,
355 fprintf(stderr
, "BCL: 0x%08x..0x%08x\n",
356 job
->submit
.bcl_start
, job
->submit
.bcl_end
);
358 clif_dump_add_cl(clif
, job
->submit
.bcl_start
, job
->submit
.bcl_end
);
360 fprintf(stderr
, "RCL: 0x%08x..0x%08x\n",
361 job
->submit
.rcl_start
, job
->submit
.rcl_end
);
362 clif_dump_add_cl(clif
, job
->submit
.rcl_start
, job
->submit
.rcl_end
);
366 * Submits the job to the kernel and then reinitializes it.
369 vc5_job_submit(struct vc5_context
*vc5
, struct vc5_job
*job
)
371 if (!job
->needs_flush
)
374 /* The RCL setup would choke if the draw bounds cause no drawing, so
375 * just drop the drawing if that's the case.
377 if (job
->draw_max_x
<= job
->draw_min_x
||
378 job
->draw_max_y
<= job
->draw_min_y
) {
384 if (cl_offset(&job
->bcl
) > 0) {
385 vc5_cl_ensure_space_with_branch(&job
->bcl
,
387 cl_packet_length(OCCLUSION_QUERY_COUNTER
));
389 if (job
->oq_enabled
) {
390 /* Disable the OQ at the end of the CL, so that the
391 * draw calls at the start of the CL don't inherit the
394 cl_emit(&job
->bcl
, OCCLUSION_QUERY_COUNTER
, counter
);
397 /* Increment the semaphore indicating that binning is done and
398 * unblocking the render thread. Note that this doesn't act
399 * until the FLUSH completes.
401 cl_emit(&job
->bcl
, INCREMENT_SEMAPHORE
, incr
);
403 /* The FLUSH_ALL emits any unwritten state changes in each
404 * tile. We can use this to reset any state that needs to be
405 * present at the start of the next tile, as we do with
406 * OCCLUSION_QUERY_COUNTER above.
408 cl_emit(&job
->bcl
, FLUSH_ALL_STATE
, flush
);
411 job
->submit
.bcl_end
= job
->bcl
.bo
->offset
+ cl_offset(&job
->bcl
);
412 job
->submit
.rcl_end
= job
->rcl
.bo
->offset
+ cl_offset(&job
->rcl
);
414 vc5_clif_dump(vc5
, job
);
416 if (!(V3D_DEBUG
& V3D_DEBUG_NORAST
)) {
419 #ifndef USE_VC5_SIMULATOR
420 ret
= drmIoctl(vc5
->fd
, DRM_IOCTL_VC5_SUBMIT_CL
, &job
->submit
);
422 ret
= vc5_simulator_flush(vc5
, &job
->submit
, job
);
424 static bool warned
= false;
425 if (ret
&& !warned
) {
426 fprintf(stderr
, "Draw call returned %s. "
427 "Expect corruption.\n", strerror(errno
));
432 if (vc5
->last_emit_seqno
- vc5
->screen
->finished_seqno
> 5) {
433 if (!vc5_wait_seqno(vc5
->screen
,
434 vc5
->last_emit_seqno
- 5,
435 PIPE_TIMEOUT_INFINITE
,
437 fprintf(stderr
, "Job throttling failed\n");
442 vc5_job_free(vc5
, job
);
446 vc5_job_compare(const void *a
, const void *b
)
448 return memcmp(a
, b
, sizeof(struct vc5_job_key
)) == 0;
452 vc5_job_hash(const void *key
)
454 return _mesa_hash_data(key
, sizeof(struct vc5_job_key
));
458 vc5_job_init(struct vc5_context
*vc5
)
460 vc5
->jobs
= _mesa_hash_table_create(vc5
,
463 vc5
->write_jobs
= _mesa_hash_table_create(vc5
,
465 _mesa_key_pointer_equal
);