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