braodcom/vc5: Flush the job when it grows over 1GB.
[mesa.git] / src / gallium / drivers / vc5 / vc5_job.c
1 /*
2 * Copyright © 2014-2017 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 vc5_job.c
25 *
26 * Functions for submitting VC5 render jobs to the kernel.
27 */
28
29 #include <xf86drm.h>
30 #include "vc5_context.h"
31 #include "util/hash_table.h"
32 #include "util/ralloc.h"
33 #include "util/set.h"
34 #include "broadcom/clif/clif_dump.h"
35 #include "broadcom/cle/v3d_packet_v33_pack.h"
36
37 static void
38 remove_from_ht(struct hash_table *ht, void *key)
39 {
40 struct hash_entry *entry = _mesa_hash_table_search(ht, key);
41 _mesa_hash_table_remove(ht, entry);
42 }
43
44 static void
45 vc5_job_free(struct vc5_context *vc5, struct vc5_job *job)
46 {
47 struct set_entry *entry;
48
49 set_foreach(job->bos, entry) {
50 struct vc5_bo *bo = (struct vc5_bo *)entry->key;
51 vc5_bo_unreference(&bo);
52 }
53
54 remove_from_ht(vc5->jobs, &job->key);
55
56 if (job->write_prscs) {
57 struct set_entry *entry;
58
59 set_foreach(job->write_prscs, entry) {
60 const struct pipe_resource *prsc = entry->key;
61
62 remove_from_ht(vc5->write_jobs, (void *)prsc);
63 }
64 }
65
66 for (int i = 0; i < VC5_MAX_DRAW_BUFFERS; i++) {
67 if (job->cbufs[i]) {
68 remove_from_ht(vc5->write_jobs, job->cbufs[i]->texture);
69 pipe_surface_reference(&job->cbufs[i], NULL);
70 }
71 }
72 if (job->zsbuf) {
73 remove_from_ht(vc5->write_jobs, job->zsbuf->texture);
74 pipe_surface_reference(&job->zsbuf, NULL);
75 }
76
77 if (vc5->job == job)
78 vc5->job = NULL;
79
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);
84
85 ralloc_free(job);
86 }
87
88 static struct vc5_job *
89 vc5_job_create(struct vc5_context *vc5)
90 {
91 struct vc5_job *job = rzalloc(vc5, struct vc5_job);
92
93 job->vc5 = vc5;
94
95 vc5_init_cl(job, &job->bcl);
96 vc5_init_cl(job, &job->rcl);
97 vc5_init_cl(job, &job->indirect);
98
99 job->draw_min_x = ~0;
100 job->draw_min_y = ~0;
101 job->draw_max_x = 0;
102 job->draw_max_y = 0;
103
104 job->bos = _mesa_set_create(job,
105 _mesa_hash_pointer,
106 _mesa_key_pointer_equal);
107 return job;
108 }
109
110 void
111 vc5_job_add_bo(struct vc5_job *job, struct vc5_bo *bo)
112 {
113 if (!bo)
114 return;
115
116 if (_mesa_set_search(job->bos, bo))
117 return;
118
119 vc5_bo_reference(bo);
120 _mesa_set_add(job->bos, bo);
121 job->referenced_size += bo->size;
122
123 uint32_t *bo_handles = (void *)(uintptr_t)job->submit.bo_handles;
124
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;
130 }
131 bo_handles[job->submit.bo_handle_count++] = bo->handle;
132 }
133
134 void
135 vc5_job_add_write_resource(struct vc5_job *job, struct pipe_resource *prsc)
136 {
137 struct vc5_context *vc5 = job->vc5;
138
139 if (!job->write_prscs) {
140 job->write_prscs = _mesa_set_create(job,
141 _mesa_hash_pointer,
142 _mesa_key_pointer_equal);
143 }
144
145 _mesa_set_add(job->write_prscs, prsc);
146 _mesa_hash_table_insert(vc5->write_jobs, prsc, job);
147 }
148
149 void
150 vc5_flush_jobs_writing_resource(struct vc5_context *vc5,
151 struct pipe_resource *prsc)
152 {
153 struct hash_entry *entry = _mesa_hash_table_search(vc5->write_jobs,
154 prsc);
155 if (entry) {
156 struct vc5_job *job = entry->data;
157 vc5_job_submit(vc5, job);
158 }
159 }
160
161 void
162 vc5_flush_jobs_reading_resource(struct vc5_context *vc5,
163 struct pipe_resource *prsc)
164 {
165 struct vc5_resource *rsc = vc5_resource(prsc);
166
167 vc5_flush_jobs_writing_resource(vc5, prsc);
168
169 struct hash_entry *entry;
170 hash_table_foreach(vc5->jobs, entry) {
171 struct vc5_job *job = entry->data;
172
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.
177 */
178 continue;
179 }
180 }
181 }
182
183 static void
184 vc5_job_set_tile_buffer_size(struct vc5_job *job)
185 {
186 static const uint8_t tile_sizes[] = {
187 64, 64,
188 64, 32,
189 32, 32,
190 32, 16,
191 16, 16,
192 };
193 int tile_size_index = 0;
194 if (job->msaa)
195 tile_size_index += 2;
196
197 if (job->cbufs[3] || job->cbufs[2])
198 tile_size_index += 2;
199 else if (job->cbufs[1])
200 tile_size_index++;
201
202 int max_bpp = RENDER_TARGET_MAXIMUM_32BPP;
203 for (int i = 0; i < VC5_MAX_DRAW_BUFFERS; i++) {
204 if (job->cbufs[i]) {
205 struct vc5_surface *surf = vc5_surface(job->cbufs[i]);
206 max_bpp = MAX2(max_bpp, surf->internal_bpp);
207 }
208 }
209 job->internal_bpp = max_bpp;
210 STATIC_ASSERT(RENDER_TARGET_MAXIMUM_32BPP == 0);
211 tile_size_index += max_bpp;
212
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];
216 }
217
218 /**
219 * Returns a vc5_job struture for tracking V3D rendering to a particular FBO.
220 *
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)
224 * have been flushed.
225 */
226 struct vc5_job *
227 vc5_get_job(struct vc5_context *vc5,
228 struct pipe_surface **cbufs, struct pipe_surface *zsbuf)
229 {
230 /* Return the existing job for this FBO if we have one */
231 struct vc5_job_key local_key = {
232 .cbufs = {
233 cbufs[0],
234 cbufs[1],
235 cbufs[2],
236 cbufs[3],
237 },
238 .zsbuf = zsbuf,
239 };
240 struct hash_entry *entry = _mesa_hash_table_search(vc5->jobs,
241 &local_key);
242 if (entry)
243 return entry->data;
244
245 /* Creating a new job. Make sure that any previous jobs reading or
246 * writing these buffers are flushed.
247 */
248 struct vc5_job *job = vc5_job_create(vc5);
249
250 for (int i = 0; i < VC5_MAX_DRAW_BUFFERS; i++) {
251 if (cbufs[i]) {
252 vc5_flush_jobs_reading_resource(vc5, cbufs[i]->texture);
253 pipe_surface_reference(&job->cbufs[i], cbufs[i]);
254
255 if (cbufs[i]->texture->nr_samples > 1)
256 job->msaa = true;
257 }
258 }
259 if (zsbuf) {
260 vc5_flush_jobs_reading_resource(vc5, zsbuf->texture);
261 pipe_surface_reference(&job->zsbuf, zsbuf);
262 if (zsbuf->texture->nr_samples > 1)
263 job->msaa = true;
264 }
265
266 vc5_job_set_tile_buffer_size(job);
267
268 for (int i = 0; i < VC5_MAX_DRAW_BUFFERS; i++) {
269 if (cbufs[i])
270 _mesa_hash_table_insert(vc5->write_jobs,
271 cbufs[i]->texture, job);
272 }
273 if (zsbuf)
274 _mesa_hash_table_insert(vc5->write_jobs, zsbuf->texture, job);
275
276 memcpy(&job->key, &local_key, sizeof(local_key));
277 _mesa_hash_table_insert(vc5->jobs, &job->key, job);
278
279 return job;
280 }
281
282 struct vc5_job *
283 vc5_get_job_for_fbo(struct vc5_context *vc5)
284 {
285 if (vc5->job)
286 return vc5->job;
287
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);
291
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.
295 */
296 vc5->dirty = ~0;
297
298 /* If we're binding to uninitialized buffers, no need to load their
299 * contents before drawing.
300 */
301 for (int i = 0; i < 4; i++) {
302 if (cbufs[i]) {
303 struct vc5_resource *rsc = vc5_resource(cbufs[i]->texture);
304 if (!rsc->writes)
305 job->cleared |= PIPE_CLEAR_COLOR0 << i;
306 }
307 }
308
309 if (zsbuf) {
310 struct vc5_resource *rsc = vc5_resource(zsbuf->texture);
311 if (!rsc->writes)
312 job->cleared |= PIPE_CLEAR_DEPTH | PIPE_CLEAR_STENCIL;
313 }
314
315 job->draw_tiles_x = DIV_ROUND_UP(vc5->framebuffer.width,
316 job->tile_width);
317 job->draw_tiles_y = DIV_ROUND_UP(vc5->framebuffer.height,
318 job->tile_height);
319
320 vc5->job = job;
321
322 return job;
323 }
324
325 static bool
326 vc5_clif_dump_lookup(void *data, uint32_t addr, void **vaddr)
327 {
328 struct vc5_job *job = data;
329 struct set_entry *entry;
330
331 set_foreach(job->bos, entry) {
332 struct vc5_bo *bo = (void *)entry->key;
333
334 if (addr >= bo->offset &&
335 addr < bo->offset + bo->size) {
336 vc5_bo_map(bo);
337 *vaddr = bo->map + addr - bo->offset;
338 return true;
339 }
340 }
341
342 return false;
343 }
344
345 static void
346 vc5_clif_dump(struct vc5_context *vc5, struct vc5_job *job)
347 {
348 if (!(V3D_DEBUG & V3D_DEBUG_CL))
349 return;
350
351 struct clif_dump *clif = clif_dump_init(&vc5->screen->devinfo,
352 stderr, vc5_clif_dump_lookup,
353 job);
354
355 fprintf(stderr, "BCL: 0x%08x..0x%08x\n",
356 job->submit.bcl_start, job->submit.bcl_end);
357
358 clif_dump_add_cl(clif, job->submit.bcl_start, job->submit.bcl_end);
359
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);
363 }
364
365 /**
366 * Submits the job to the kernel and then reinitializes it.
367 */
368 void
369 vc5_job_submit(struct vc5_context *vc5, struct vc5_job *job)
370 {
371 if (!job->needs_flush)
372 goto done;
373
374 /* The RCL setup would choke if the draw bounds cause no drawing, so
375 * just drop the drawing if that's the case.
376 */
377 if (job->draw_max_x <= job->draw_min_x ||
378 job->draw_max_y <= job->draw_min_y) {
379 goto done;
380 }
381
382 vc5_emit_rcl(job);
383
384 if (cl_offset(&job->bcl) > 0) {
385 vc5_cl_ensure_space_with_branch(&job->bcl,
386 7 +
387 cl_packet_length(OCCLUSION_QUERY_COUNTER));
388
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
392 * OQ counter.
393 */
394 cl_emit(&job->bcl, OCCLUSION_QUERY_COUNTER, counter);
395 }
396
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.
400 */
401 cl_emit(&job->bcl, INCREMENT_SEMAPHORE, incr);
402
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.
407 */
408 cl_emit(&job->bcl, FLUSH_ALL_STATE, flush);
409 }
410
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);
413
414 vc5_clif_dump(vc5, job);
415
416 if (!(V3D_DEBUG & V3D_DEBUG_NORAST)) {
417 int ret;
418
419 #ifndef USE_VC5_SIMULATOR
420 ret = drmIoctl(vc5->fd, DRM_IOCTL_VC5_SUBMIT_CL, &job->submit);
421 #else
422 ret = vc5_simulator_flush(vc5, &job->submit, job);
423 #endif
424 static bool warned = false;
425 if (ret && !warned) {
426 fprintf(stderr, "Draw call returned %s. "
427 "Expect corruption.\n", strerror(errno));
428 warned = true;
429 }
430 }
431
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,
436 "job throttling")) {
437 fprintf(stderr, "Job throttling failed\n");
438 }
439 }
440
441 done:
442 vc5_job_free(vc5, job);
443 }
444
445 static bool
446 vc5_job_compare(const void *a, const void *b)
447 {
448 return memcmp(a, b, sizeof(struct vc5_job_key)) == 0;
449 }
450
451 static uint32_t
452 vc5_job_hash(const void *key)
453 {
454 return _mesa_hash_data(key, sizeof(struct vc5_job_key));
455 }
456
457 void
458 vc5_job_init(struct vc5_context *vc5)
459 {
460 vc5->jobs = _mesa_hash_table_create(vc5,
461 vc5_job_hash,
462 vc5_job_compare);
463 vc5->write_jobs = _mesa_hash_table_create(vc5,
464 _mesa_hash_pointer,
465 _mesa_key_pointer_equal);
466 }
467