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 "v3d_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"
38 #include "broadcom/clif/clif_dump.h"
41 remove_from_ht(struct hash_table
*ht
, void *key
)
43 struct hash_entry
*entry
= _mesa_hash_table_search(ht
, key
);
44 _mesa_hash_table_remove(ht
, entry
);
48 v3d_job_free(struct v3d_context
*v3d
, struct v3d_job
*job
)
50 struct set_entry
*entry
;
52 set_foreach(job
->bos
, entry
) {
53 struct v3d_bo
*bo
= (struct v3d_bo
*)entry
->key
;
54 v3d_bo_unreference(&bo
);
57 remove_from_ht(v3d
->jobs
, &job
->key
);
59 if (job
->write_prscs
) {
60 struct set_entry
*entry
;
62 set_foreach(job
->write_prscs
, entry
) {
63 const struct pipe_resource
*prsc
= entry
->key
;
65 remove_from_ht(v3d
->write_jobs
, (void *)prsc
);
69 for (int i
= 0; i
< VC5_MAX_DRAW_BUFFERS
; i
++) {
71 remove_from_ht(v3d
->write_jobs
, job
->cbufs
[i
]->texture
);
72 pipe_surface_reference(&job
->cbufs
[i
], NULL
);
76 struct v3d_resource
*rsc
= v3d_resource(job
->zsbuf
->texture
);
77 if (rsc
->separate_stencil
)
78 remove_from_ht(v3d
->write_jobs
,
79 &rsc
->separate_stencil
->base
);
81 remove_from_ht(v3d
->write_jobs
, job
->zsbuf
->texture
);
82 pipe_surface_reference(&job
->zsbuf
, NULL
);
88 v3d_destroy_cl(&job
->bcl
);
89 v3d_destroy_cl(&job
->rcl
);
90 v3d_destroy_cl(&job
->indirect
);
91 v3d_bo_unreference(&job
->tile_alloc
);
92 v3d_bo_unreference(&job
->tile_state
);
97 static struct v3d_job
*
98 v3d_job_create(struct v3d_context
*v3d
)
100 struct v3d_job
*job
= rzalloc(v3d
, struct v3d_job
);
104 v3d_init_cl(job
, &job
->bcl
);
105 v3d_init_cl(job
, &job
->rcl
);
106 v3d_init_cl(job
, &job
->indirect
);
108 job
->draw_min_x
= ~0;
109 job
->draw_min_y
= ~0;
113 job
->bos
= _mesa_set_create(job
,
115 _mesa_key_pointer_equal
);
120 v3d_job_add_bo(struct v3d_job
*job
, struct v3d_bo
*bo
)
125 if (_mesa_set_search(job
->bos
, bo
))
128 v3d_bo_reference(bo
);
129 _mesa_set_add(job
->bos
, bo
);
130 job
->referenced_size
+= bo
->size
;
132 uint32_t *bo_handles
= (void *)(uintptr_t)job
->submit
.bo_handles
;
134 if (job
->submit
.bo_handle_count
>= job
->bo_handles_size
) {
135 job
->bo_handles_size
= MAX2(4, job
->bo_handles_size
* 2);
136 bo_handles
= reralloc(job
, bo_handles
,
137 uint32_t, job
->bo_handles_size
);
138 job
->submit
.bo_handles
= (uintptr_t)(void *)bo_handles
;
140 bo_handles
[job
->submit
.bo_handle_count
++] = bo
->handle
;
144 v3d_job_add_write_resource(struct v3d_job
*job
, struct pipe_resource
*prsc
)
146 struct v3d_context
*v3d
= job
->v3d
;
148 if (!job
->write_prscs
) {
149 job
->write_prscs
= _mesa_set_create(job
,
151 _mesa_key_pointer_equal
);
154 _mesa_set_add(job
->write_prscs
, prsc
);
155 _mesa_hash_table_insert(v3d
->write_jobs
, prsc
, job
);
159 v3d_flush_jobs_writing_resource(struct v3d_context
*v3d
,
160 struct pipe_resource
*prsc
)
162 struct hash_entry
*entry
= _mesa_hash_table_search(v3d
->write_jobs
,
165 struct v3d_job
*job
= entry
->data
;
166 v3d_job_submit(v3d
, job
);
171 v3d_flush_jobs_reading_resource(struct v3d_context
*v3d
,
172 struct pipe_resource
*prsc
)
174 struct v3d_resource
*rsc
= v3d_resource(prsc
);
176 v3d_flush_jobs_writing_resource(v3d
, prsc
);
178 struct hash_entry
*entry
;
179 hash_table_foreach(v3d
->jobs
, entry
) {
180 struct v3d_job
*job
= entry
->data
;
182 if (_mesa_set_search(job
->bos
, rsc
->bo
)) {
183 v3d_job_submit(v3d
, job
);
184 /* Reminder: v3d->jobs is safe to keep iterating even
185 * after deletion of an entry.
193 v3d_job_set_tile_buffer_size(struct v3d_job
*job
)
195 static const uint8_t tile_sizes
[] = {
202 int tile_size_index
= 0;
204 tile_size_index
+= 2;
206 if (job
->cbufs
[3] || job
->cbufs
[2])
207 tile_size_index
+= 2;
208 else if (job
->cbufs
[1])
211 int max_bpp
= RENDER_TARGET_MAXIMUM_32BPP
;
212 for (int i
= 0; i
< VC5_MAX_DRAW_BUFFERS
; i
++) {
214 struct v3d_surface
*surf
= v3d_surface(job
->cbufs
[i
]);
215 max_bpp
= MAX2(max_bpp
, surf
->internal_bpp
);
218 job
->internal_bpp
= max_bpp
;
219 STATIC_ASSERT(RENDER_TARGET_MAXIMUM_32BPP
== 0);
220 tile_size_index
+= max_bpp
;
222 assert(tile_size_index
< ARRAY_SIZE(tile_sizes
));
223 job
->tile_width
= tile_sizes
[tile_size_index
* 2 + 0];
224 job
->tile_height
= tile_sizes
[tile_size_index
* 2 + 1];
228 * Returns a v3d_job struture for tracking V3D rendering to a particular FBO.
230 * If we've already started rendering to this FBO, then return old same job,
231 * otherwise make a new one. If we're beginning rendering to an FBO, make
232 * sure that any previous reads of the FBO (or writes to its color/Z surfaces)
236 v3d_get_job(struct v3d_context
*v3d
,
237 struct pipe_surface
**cbufs
, struct pipe_surface
*zsbuf
)
239 /* Return the existing job for this FBO if we have one */
240 struct v3d_job_key local_key
= {
249 struct hash_entry
*entry
= _mesa_hash_table_search(v3d
->jobs
,
254 /* Creating a new job. Make sure that any previous jobs reading or
255 * writing these buffers are flushed.
257 struct v3d_job
*job
= v3d_job_create(v3d
);
259 for (int i
= 0; i
< VC5_MAX_DRAW_BUFFERS
; i
++) {
261 v3d_flush_jobs_reading_resource(v3d
, cbufs
[i
]->texture
);
262 pipe_surface_reference(&job
->cbufs
[i
], cbufs
[i
]);
264 if (cbufs
[i
]->texture
->nr_samples
> 1)
269 v3d_flush_jobs_reading_resource(v3d
, zsbuf
->texture
);
270 pipe_surface_reference(&job
->zsbuf
, zsbuf
);
271 if (zsbuf
->texture
->nr_samples
> 1)
275 v3d_job_set_tile_buffer_size(job
);
277 for (int i
= 0; i
< VC5_MAX_DRAW_BUFFERS
; i
++) {
279 _mesa_hash_table_insert(v3d
->write_jobs
,
280 cbufs
[i
]->texture
, job
);
283 _mesa_hash_table_insert(v3d
->write_jobs
, zsbuf
->texture
, job
);
285 struct v3d_resource
*rsc
= v3d_resource(zsbuf
->texture
);
286 if (rsc
->separate_stencil
) {
287 v3d_flush_jobs_reading_resource(v3d
,
288 &rsc
->separate_stencil
->base
);
289 _mesa_hash_table_insert(v3d
->write_jobs
,
290 &rsc
->separate_stencil
->base
,
295 memcpy(&job
->key
, &local_key
, sizeof(local_key
));
296 _mesa_hash_table_insert(v3d
->jobs
, &job
->key
, job
);
302 v3d_get_job_for_fbo(struct v3d_context
*v3d
)
307 struct pipe_surface
**cbufs
= v3d
->framebuffer
.cbufs
;
308 struct pipe_surface
*zsbuf
= v3d
->framebuffer
.zsbuf
;
309 struct v3d_job
*job
= v3d_get_job(v3d
, cbufs
, zsbuf
);
311 /* The dirty flags are tracking what's been updated while v3d->job has
312 * been bound, so set them all to ~0 when switching between jobs. We
313 * also need to reset all state at the start of rendering.
317 /* If we're binding to uninitialized buffers, no need to load their
318 * contents before drawing.
320 for (int i
= 0; i
< 4; i
++) {
322 struct v3d_resource
*rsc
= v3d_resource(cbufs
[i
]->texture
);
324 job
->clear
|= PIPE_CLEAR_COLOR0
<< i
;
329 struct v3d_resource
*rsc
= v3d_resource(zsbuf
->texture
);
331 job
->clear
|= PIPE_CLEAR_DEPTH
| PIPE_CLEAR_STENCIL
;
334 job
->draw_tiles_x
= DIV_ROUND_UP(v3d
->framebuffer
.width
,
336 job
->draw_tiles_y
= DIV_ROUND_UP(v3d
->framebuffer
.height
,
345 v3d_clif_dump_lookup(void *data
, uint32_t addr
, void **vaddr
)
347 struct v3d_job
*job
= data
;
348 struct set_entry
*entry
;
350 set_foreach(job
->bos
, entry
) {
351 struct v3d_bo
*bo
= (void *)entry
->key
;
353 if (addr
>= bo
->offset
&&
354 addr
< bo
->offset
+ bo
->size
) {
356 *vaddr
= bo
->map
+ addr
- bo
->offset
;
365 v3d_clif_dump(struct v3d_context
*v3d
, struct v3d_job
*job
)
367 if (!(V3D_DEBUG
& V3D_DEBUG_CL
))
370 struct clif_dump
*clif
= clif_dump_init(&v3d
->screen
->devinfo
,
371 stderr
, v3d_clif_dump_lookup
,
374 fprintf(stderr
, "BCL: 0x%08x..0x%08x\n",
375 job
->submit
.bcl_start
, job
->submit
.bcl_end
);
377 clif_dump_add_cl(clif
, job
->submit
.bcl_start
, job
->submit
.bcl_end
);
379 fprintf(stderr
, "RCL: 0x%08x..0x%08x\n",
380 job
->submit
.rcl_start
, job
->submit
.rcl_end
);
381 clif_dump_add_cl(clif
, job
->submit
.rcl_start
, job
->submit
.rcl_end
);
385 * Submits the job to the kernel and then reinitializes it.
388 v3d_job_submit(struct v3d_context
*v3d
, struct v3d_job
*job
)
390 MAYBE_UNUSED
struct v3d_screen
*screen
= v3d
->screen
;
392 if (!job
->needs_flush
)
395 if (v3d
->screen
->devinfo
.ver
>= 41)
400 if (cl_offset(&job
->bcl
) > 0) {
401 if (screen
->devinfo
.ver
>= 41)
402 v3d41_bcl_epilogue(v3d
, job
);
404 v3d33_bcl_epilogue(v3d
, job
);
407 job
->submit
.out_sync
= v3d
->out_sync
;
408 job
->submit
.bcl_end
= job
->bcl
.bo
->offset
+ cl_offset(&job
->bcl
);
409 job
->submit
.rcl_end
= job
->rcl
.bo
->offset
+ cl_offset(&job
->rcl
);
411 /* On V3D 4.1, the tile alloc/state setup moved to register writes
412 * instead of binner packets.
414 if (screen
->devinfo
.ver
>= 41) {
415 v3d_job_add_bo(job
, job
->tile_alloc
);
416 job
->submit
.qma
= job
->tile_alloc
->offset
;
417 job
->submit
.qms
= job
->tile_alloc
->size
;
419 v3d_job_add_bo(job
, job
->tile_state
);
420 job
->submit
.qts
= job
->tile_state
->offset
;
423 v3d_clif_dump(v3d
, job
);
425 if (!(V3D_DEBUG
& V3D_DEBUG_NORAST
)) {
428 #ifndef USE_V3D_SIMULATOR
429 ret
= drmIoctl(v3d
->fd
, DRM_IOCTL_V3D_SUBMIT_CL
, &job
->submit
);
431 ret
= v3d_simulator_flush(v3d
, &job
->submit
, job
);
433 static bool warned
= false;
434 if (ret
&& !warned
) {
435 fprintf(stderr
, "Draw call returned %s. "
436 "Expect corruption.\n", strerror(errno
));
442 v3d_job_free(v3d
, job
);
446 v3d_job_compare(const void *a
, const void *b
)
448 return memcmp(a
, b
, sizeof(struct v3d_job_key
)) == 0;
452 v3d_job_hash(const void *key
)
454 return _mesa_hash_data(key
, sizeof(struct v3d_job_key
));
458 v3d_job_init(struct v3d_context
*v3d
)
460 v3d
->jobs
= _mesa_hash_table_create(v3d
,
463 v3d
->write_jobs
= _mesa_hash_table_create(v3d
,
465 _mesa_key_pointer_equal
);