1 /*
2 * Copyright © 2014-2015 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 vc4_job.c
25 *
26 * Functions for submitting VC4 render jobs to the kernel.
27 */
28
29 #include <xf86drm.h>
30 #include "vc4_cl_dump.h"
31 #include "vc4_context.h"
32 #include "util/hash_table.h"
33
34 static void
remove_from_ht(struct hash_table * ht,void * key)35 remove_from_ht(struct hash_table *ht, void *key)
36 {
37 struct hash_entry *entry = _mesa_hash_table_search(ht, key);
38 _mesa_hash_table_remove(ht, entry);
39 }
40
41 static void
vc4_job_free(struct vc4_context * vc4,struct vc4_job * job)42 vc4_job_free(struct vc4_context *vc4, struct vc4_job *job)
43 {
44 struct vc4_bo **referenced_bos = job->bo_pointers.base;
45 for (int i = 0; i < cl_offset(&job->bo_handles) / 4; i++) {
46 vc4_bo_unreference(&referenced_bos[i]);
47 }
48
49 remove_from_ht(vc4->jobs, &job->key);
50
51 if (job->color_write) {
52 remove_from_ht(vc4->write_jobs, job->color_write->texture);
53 pipe_surface_reference(&job->color_write, NULL);
54 }
55 if (job->msaa_color_write) {
56 remove_from_ht(vc4->write_jobs, job->msaa_color_write->texture);
57 pipe_surface_reference(&job->msaa_color_write, NULL);
58 }
59 if (job->zs_write) {
60 remove_from_ht(vc4->write_jobs, job->zs_write->texture);
61 pipe_surface_reference(&job->zs_write, NULL);
62 }
63 if (job->msaa_zs_write) {
64 remove_from_ht(vc4->write_jobs, job->msaa_zs_write->texture);
65 pipe_surface_reference(&job->msaa_zs_write, NULL);
66 }
67
68 pipe_surface_reference(&job->color_read, NULL);
69 pipe_surface_reference(&job->zs_read, NULL);
70
71 if (vc4->job == job)
72 vc4->job = NULL;
73
74 ralloc_free(job);
75 }
76
77 static struct vc4_job *
vc4_job_create(struct vc4_context * vc4)78 vc4_job_create(struct vc4_context *vc4)
79 {
80 struct vc4_job *job = rzalloc(vc4, struct vc4_job);
81
82 vc4_init_cl(job, &job->bcl);
83 vc4_init_cl(job, &job->shader_rec);
84 vc4_init_cl(job, &job->uniforms);
85 vc4_init_cl(job, &job->bo_handles);
86 vc4_init_cl(job, &job->bo_pointers);
87
88 job->draw_min_x = ~0;
89 job->draw_min_y = ~0;
90 job->draw_max_x = 0;
91 job->draw_max_y = 0;
92
93 job->last_gem_handle_hindex = ~0;
94
95 return job;
96 }
97
98 void
vc4_flush_jobs_writing_resource(struct vc4_context * vc4,struct pipe_resource * prsc)99 vc4_flush_jobs_writing_resource(struct vc4_context *vc4,
100 struct pipe_resource *prsc)
101 {
102 struct hash_entry *entry = _mesa_hash_table_search(vc4->write_jobs,
103 prsc);
104 if (entry) {
105 struct vc4_job *job = entry->data;
106 vc4_job_submit(vc4, job);
107 }
108 }
109
110 void
vc4_flush_jobs_reading_resource(struct vc4_context * vc4,struct pipe_resource * prsc)111 vc4_flush_jobs_reading_resource(struct vc4_context *vc4,
112 struct pipe_resource *prsc)
113 {
114 struct vc4_resource *rsc = vc4_resource(prsc);
115
116 vc4_flush_jobs_writing_resource(vc4, prsc);
117
118 struct hash_entry *entry;
119 hash_table_foreach(vc4->jobs, entry) {
120 struct vc4_job *job = entry->data;
121
122 struct vc4_bo **referenced_bos = job->bo_pointers.base;
123 bool found = false;
124 for (int i = 0; i < cl_offset(&job->bo_handles) / 4; i++) {
125 if (referenced_bos[i] == rsc->bo) {
126 found = true;
127 break;
128 }
129 }
130 if (found) {
131 vc4_job_submit(vc4, job);
132 continue;
133 }
134
135 /* Also check for the Z/color buffers, since the references to
136 * those are only added immediately before submit.
137 */
138 if (job->color_read && !(job->cleared & PIPE_CLEAR_COLOR)) {
139 struct vc4_resource *ctex =
140 vc4_resource(job->color_read->texture);
141 if (ctex->bo == rsc->bo) {
142 vc4_job_submit(vc4, job);
143 continue;
144 }
145 }
146
147 if (job->zs_read && !(job->cleared &
148 (PIPE_CLEAR_DEPTH | PIPE_CLEAR_STENCIL))) {
149 struct vc4_resource *ztex =
150 vc4_resource(job->zs_read->texture);
151 if (ztex->bo == rsc->bo) {
152 vc4_job_submit(vc4, job);
153 continue;
154 }
155 }
156 }
157 }
158
159 /**
160 * Returns a vc4_job struture for tracking V3D rendering to a particular FBO.
161 *
162 * If we've already started rendering to this FBO, then return old same job,
163 * otherwise make a new one. If we're beginning rendering to an FBO, make
164 * sure that any previous reads of the FBO (or writes to its color/Z surfaces)
165 * have been flushed.
166 */
167 struct vc4_job *
vc4_get_job(struct vc4_context * vc4,struct pipe_surface * cbuf,struct pipe_surface * zsbuf)168 vc4_get_job(struct vc4_context *vc4,
169 struct pipe_surface *cbuf, struct pipe_surface *zsbuf)
170 {
171 /* Return the existing job for this FBO if we have one */
172 struct vc4_job_key local_key = {.cbuf = cbuf, .zsbuf = zsbuf};
173 struct hash_entry *entry = _mesa_hash_table_search(vc4->jobs,
174 &local_key);
175 if (entry)
176 return entry->data;
177
178 /* Creating a new job. Make sure that any previous jobs reading or
179 * writing these buffers are flushed.
180 */
181 if (cbuf)
182 vc4_flush_jobs_reading_resource(vc4, cbuf->texture);
183 if (zsbuf)
184 vc4_flush_jobs_reading_resource(vc4, zsbuf->texture);
185
186 struct vc4_job *job = vc4_job_create(vc4);
187
188 if (cbuf) {
189 if (cbuf->texture->nr_samples > 1) {
190 job->msaa = true;
191 pipe_surface_reference(&job->msaa_color_write, cbuf);
192 } else {
193 pipe_surface_reference(&job->color_write, cbuf);
194 }
195 }
196
197 if (zsbuf) {
198 if (zsbuf->texture->nr_samples > 1) {
199 job->msaa = true;
200 pipe_surface_reference(&job->msaa_zs_write, zsbuf);
201 } else {
202 pipe_surface_reference(&job->zs_write, zsbuf);
203 }
204 }
205
206 if (job->msaa) {
207 job->tile_width = 32;
208 job->tile_height = 32;
209 } else {
210 job->tile_width = 64;
211 job->tile_height = 64;
212 }
213
214 if (cbuf)
215 _mesa_hash_table_insert(vc4->write_jobs, cbuf->texture, job);
216 if (zsbuf)
217 _mesa_hash_table_insert(vc4->write_jobs, zsbuf->texture, job);
218
219 job->key.cbuf = cbuf;
220 job->key.zsbuf = zsbuf;
221 _mesa_hash_table_insert(vc4->jobs, &job->key, job);
222
223 return job;
224 }
225
226 struct vc4_job *
vc4_get_job_for_fbo(struct vc4_context * vc4)227 vc4_get_job_for_fbo(struct vc4_context *vc4)
228 {
229 if (vc4->job)
230 return vc4->job;
231
232 struct pipe_surface *cbuf = vc4->framebuffer.cbufs[0];
233 struct pipe_surface *zsbuf = vc4->framebuffer.zsbuf;
234 struct vc4_job *job = vc4_get_job(vc4, cbuf, zsbuf);
235
236 /* The dirty flags are tracking what's been updated while vc4->job has
237 * been bound, so set them all to ~0 when switching between jobs. We
238 * also need to reset all state at the start of rendering.
239 */
240 vc4->dirty = ~0;
241
242 /* Set up the read surfaces in the job. If they aren't actually
243 * getting read (due to a clear starting the frame), job->cleared will
244 * mask out the read.
245 */
246 pipe_surface_reference(&job->color_read, cbuf);
247 pipe_surface_reference(&job->zs_read, zsbuf);
248
249 /* If we're binding to uninitialized buffers, no need to load their
250 * contents before drawing.
251 */
252 if (cbuf) {
253 struct vc4_resource *rsc = vc4_resource(cbuf->texture);
254 if (!rsc->writes)
255 job->cleared |= PIPE_CLEAR_COLOR0;
256 }
257
258 if (zsbuf) {
259 struct vc4_resource *rsc = vc4_resource(zsbuf->texture);
260 if (!rsc->writes)
261 job->cleared |= PIPE_CLEAR_DEPTH | PIPE_CLEAR_STENCIL;
262 }
263
264 job->draw_tiles_x = DIV_ROUND_UP(vc4->framebuffer.width,
265 job->tile_width);
266 job->draw_tiles_y = DIV_ROUND_UP(vc4->framebuffer.height,
267 job->tile_height);
268
269 /* Initialize the job with the raster order flags -- each draw will
270 * check that we haven't changed the flags, since that requires a
271 * flush.
272 */
273 if (vc4->rasterizer)
274 job->flags = vc4->rasterizer->tile_raster_order_flags;
275
276 vc4->job = job;
277
278 return job;
279 }
280
281 static void
vc4_submit_setup_rcl_surface(struct vc4_job * job,struct drm_vc4_submit_rcl_surface * submit_surf,struct pipe_surface * psurf,bool is_depth,bool is_write)282 vc4_submit_setup_rcl_surface(struct vc4_job *job,
283 struct drm_vc4_submit_rcl_surface *submit_surf,
284 struct pipe_surface *psurf,
285 bool is_depth, bool is_write)
286 {
287 struct vc4_surface *surf = vc4_surface(psurf);
288
289 if (!surf)
290 return;
291
292 struct vc4_resource *rsc = vc4_resource(psurf->texture);
293 submit_surf->hindex = vc4_gem_hindex(job, rsc->bo);
294 submit_surf->offset = surf->offset;
295
296 if (psurf->texture->nr_samples <= 1) {
297 if (is_depth) {
298 submit_surf->bits =
299 VC4_SET_FIELD(VC4_LOADSTORE_TILE_BUFFER_ZS,
300 VC4_LOADSTORE_TILE_BUFFER_BUFFER);
301
302 } else {
303 submit_surf->bits =
304 VC4_SET_FIELD(VC4_LOADSTORE_TILE_BUFFER_COLOR,
305 VC4_LOADSTORE_TILE_BUFFER_BUFFER) |
306 VC4_SET_FIELD(vc4_rt_format_is_565(psurf->format) ?
307 VC4_LOADSTORE_TILE_BUFFER_BGR565 :
308 VC4_LOADSTORE_TILE_BUFFER_RGBA8888,
309 VC4_LOADSTORE_TILE_BUFFER_FORMAT);
310 }
311 submit_surf->bits |=
312 VC4_SET_FIELD(surf->tiling,
313 VC4_LOADSTORE_TILE_BUFFER_TILING);
314 } else {
315 assert(!is_write);
316 submit_surf->flags |= VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES;
317 }
318
319 if (is_write)
320 rsc->writes++;
321 }
322
323 static void
vc4_submit_setup_rcl_render_config_surface(struct vc4_job * job,struct drm_vc4_submit_rcl_surface * submit_surf,struct pipe_surface * psurf)324 vc4_submit_setup_rcl_render_config_surface(struct vc4_job *job,
325 struct drm_vc4_submit_rcl_surface *submit_surf,
326 struct pipe_surface *psurf)
327 {
328 struct vc4_surface *surf = vc4_surface(psurf);
329
330 if (!surf)
331 return;
332
333 struct vc4_resource *rsc = vc4_resource(psurf->texture);
334 submit_surf->hindex = vc4_gem_hindex(job, rsc->bo);
335 submit_surf->offset = surf->offset;
336
337 if (psurf->texture->nr_samples <= 1) {
338 submit_surf->bits =
339 VC4_SET_FIELD(vc4_rt_format_is_565(surf->base.format) ?
340 VC4_RENDER_CONFIG_FORMAT_BGR565 :
341 VC4_RENDER_CONFIG_FORMAT_RGBA8888,
342 VC4_RENDER_CONFIG_FORMAT) |
343 VC4_SET_FIELD(surf->tiling,
344 VC4_RENDER_CONFIG_MEMORY_FORMAT);
345 }
346
347 rsc->writes++;
348 }
349
350 static void
vc4_submit_setup_rcl_msaa_surface(struct vc4_job * job,struct drm_vc4_submit_rcl_surface * submit_surf,struct pipe_surface * psurf)351 vc4_submit_setup_rcl_msaa_surface(struct vc4_job *job,
352 struct drm_vc4_submit_rcl_surface *submit_surf,
353 struct pipe_surface *psurf)
354 {
355 struct vc4_surface *surf = vc4_surface(psurf);
356
357 if (!surf)
358 return;
359
360 struct vc4_resource *rsc = vc4_resource(psurf->texture);
361 submit_surf->hindex = vc4_gem_hindex(job, rsc->bo);
362 submit_surf->offset = surf->offset;
363 submit_surf->bits = 0;
364 rsc->writes++;
365 }
366
367 /**
368 * Submits the job to the kernel and then reinitializes it.
369 */
370 void
vc4_job_submit(struct vc4_context * vc4,struct vc4_job * job)371 vc4_job_submit(struct vc4_context *vc4, struct vc4_job *job)
372 {
373 if (!job->needs_flush)
374 goto done;
375
376 /* The RCL setup would choke if the draw bounds cause no drawing, so
377 * just drop the drawing if that's the case.
378 */
379 if (job->draw_max_x <= job->draw_min_x ||
380 job->draw_max_y <= job->draw_min_y) {
381 goto done;
382 }
383
384 if (vc4_debug & VC4_DEBUG_CL) {
385 fprintf(stderr, "BCL:\n");
386 vc4_dump_cl(job->bcl.base, cl_offset(&job->bcl), false);
387 }
388
389 if (cl_offset(&job->bcl) > 0) {
390 /* Increment the semaphore indicating that binning is done and
391 * unblocking the render thread. Note that this doesn't act
392 * until the FLUSH completes.
393 */
394 cl_ensure_space(&job->bcl, 8);
395 cl_emit(&job->bcl, INCREMENT_SEMAPHORE, incr);
396 /* The FLUSH caps all of our bin lists with a
397 * VC4_PACKET_RETURN.
398 */
399 cl_emit(&job->bcl, FLUSH, flush);
400 }
401 struct drm_vc4_submit_cl submit = {
402 .color_read.hindex = ~0,
403 .zs_read.hindex = ~0,
404 .color_write.hindex = ~0,
405 .msaa_color_write.hindex = ~0,
406 .zs_write.hindex = ~0,
407 .msaa_zs_write.hindex = ~0,
408 };
409
410 cl_ensure_space(&job->bo_handles, 6 * sizeof(uint32_t));
411 cl_ensure_space(&job->bo_pointers, 6 * sizeof(struct vc4_bo *));
412
413 if (job->resolve & PIPE_CLEAR_COLOR) {
414 if (!(job->cleared & PIPE_CLEAR_COLOR)) {
415 vc4_submit_setup_rcl_surface(job, &submit.color_read,
416 job->color_read,
417 false, false);
418 }
419 vc4_submit_setup_rcl_render_config_surface(job,
420 &submit.color_write,
421 job->color_write);
422 vc4_submit_setup_rcl_msaa_surface(job,
423 &submit.msaa_color_write,
424 job->msaa_color_write);
425 }
426 if (job->resolve & (PIPE_CLEAR_DEPTH | PIPE_CLEAR_STENCIL)) {
427 if (!(job->cleared & (PIPE_CLEAR_DEPTH | PIPE_CLEAR_STENCIL))) {
428 vc4_submit_setup_rcl_surface(job, &submit.zs_read,
429 job->zs_read, true, false);
430 }
431 vc4_submit_setup_rcl_surface(job, &submit.zs_write,
432 job->zs_write, true, true);
433 vc4_submit_setup_rcl_msaa_surface(job, &submit.msaa_zs_write,
434 job->msaa_zs_write);
435 }
436
437 if (job->msaa) {
438 /* This bit controls how many pixels the general
439 * (i.e. subsampled) loads/stores are iterating over
440 * (multisample loads replicate out to the other samples).
441 */
442 submit.color_write.bits |= VC4_RENDER_CONFIG_MS_MODE_4X;
443 /* Controls whether color_write's
444 * VC4_PACKET_STORE_MS_TILE_BUFFER does 4x decimation
445 */
446 submit.color_write.bits |= VC4_RENDER_CONFIG_DECIMATE_MODE_4X;
447 }
448
449 submit.bo_handles = (uintptr_t)job->bo_handles.base;
450 submit.bo_handle_count = cl_offset(&job->bo_handles) / 4;
451 submit.bin_cl = (uintptr_t)job->bcl.base;
452 submit.bin_cl_size = cl_offset(&job->bcl);
453 submit.shader_rec = (uintptr_t)job->shader_rec.base;
454 submit.shader_rec_size = cl_offset(&job->shader_rec);
455 submit.shader_rec_count = job->shader_rec_count;
456 submit.uniforms = (uintptr_t)job->uniforms.base;
457 submit.uniforms_size = cl_offset(&job->uniforms);
458
459 assert(job->draw_min_x != ~0 && job->draw_min_y != ~0);
460 submit.min_x_tile = job->draw_min_x / job->tile_width;
461 submit.min_y_tile = job->draw_min_y / job->tile_height;
462 submit.max_x_tile = (job->draw_max_x - 1) / job->tile_width;
463 submit.max_y_tile = (job->draw_max_y - 1) / job->tile_height;
464 submit.width = job->draw_width;
465 submit.height = job->draw_height;
466 if (job->cleared) {
467 submit.flags |= VC4_SUBMIT_CL_USE_CLEAR_COLOR;
468 submit.clear_color[0] = job->clear_color[0];
469 submit.clear_color[1] = job->clear_color[1];
470 submit.clear_z = job->clear_depth;
471 submit.clear_s = job->clear_stencil;
472 }
473 submit.flags |= job->flags;
474
475 if (!(vc4_debug & VC4_DEBUG_NORAST)) {
476 int ret;
477
478 #ifndef USE_VC4_SIMULATOR
479 ret = drmIoctl(vc4->fd, DRM_IOCTL_VC4_SUBMIT_CL, &submit);
480 #else
481 ret = vc4_simulator_flush(vc4, &submit, job);
482 #endif
483 static bool warned = false;
484 if (ret && !warned) {
485 fprintf(stderr, "Draw call returned %s. "
486 "Expect corruption.\n", strerror(errno));
487 warned = true;
488 } else if (!ret) {
489 vc4->last_emit_seqno = submit.seqno;
490 }
491 }
492
493 if (vc4->last_emit_seqno - vc4->screen->finished_seqno > 5) {
494 if (!vc4_wait_seqno(vc4->screen,
495 vc4->last_emit_seqno - 5,
496 PIPE_TIMEOUT_INFINITE,
497 "job throttling")) {
498 fprintf(stderr, "Job throttling failed\n");
499 }
500 }
501
502 if (vc4_debug & VC4_DEBUG_ALWAYS_SYNC) {
503 if (!vc4_wait_seqno(vc4->screen, vc4->last_emit_seqno,
504 PIPE_TIMEOUT_INFINITE, "sync")) {
505 fprintf(stderr, "Wait failed.\n");
506 abort();
507 }
508 }
509
510 done:
511 vc4_job_free(vc4, job);
512 }
513
514 static bool
vc4_job_compare(const void * a,const void * b)515 vc4_job_compare(const void *a, const void *b)
516 {
517 return memcmp(a, b, sizeof(struct vc4_job_key)) == 0;
518 }
519
520 static uint32_t
vc4_job_hash(const void * key)521 vc4_job_hash(const void *key)
522 {
523 return _mesa_hash_data(key, sizeof(struct vc4_job_key));
524 }
525
526 void
vc4_job_init(struct vc4_context * vc4)527 vc4_job_init(struct vc4_context *vc4)
528 {
529 vc4->jobs = _mesa_hash_table_create(vc4,
530 vc4_job_hash,
531 vc4_job_compare);
532 vc4->write_jobs = _mesa_hash_table_create(vc4,
533 _mesa_hash_pointer,
534 _mesa_key_pointer_equal);
535 }
536
537