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