freedreno: allocate ctx's batch on demand
[mesa.git] / src / gallium / drivers / freedreno / freedreno_batch.c
1 /*
2 * Copyright (C) 2016 Rob Clark <robclark@freedesktop.org>
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 FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors:
24 * Rob Clark <robclark@freedesktop.org>
25 */
26
27 #include "util/list.h"
28 #include "util/set.h"
29 #include "util/hash_table.h"
30 #include "util/u_string.h"
31
32 #include "freedreno_batch.h"
33 #include "freedreno_context.h"
34 #include "freedreno_fence.h"
35 #include "freedreno_resource.h"
36 #include "freedreno_query_hw.h"
37
38 static void
39 batch_init(struct fd_batch *batch)
40 {
41 struct fd_context *ctx = batch->ctx;
42 unsigned size = 0;
43
44 if (ctx->screen->reorder)
45 util_queue_fence_init(&batch->flush_fence);
46
47 /* if kernel is too old to support unlimited # of cmd buffers, we
48 * have no option but to allocate large worst-case sizes so that
49 * we don't need to grow the ringbuffer. Performance is likely to
50 * suffer, but there is no good alternative.
51 */
52 if ((fd_device_version(ctx->screen->dev) < FD_VERSION_UNLIMITED_CMDS) ||
53 (fd_mesa_debug & FD_DBG_NOGROW)){
54 size = 0x100000;
55 }
56
57 batch->draw = fd_ringbuffer_new(ctx->pipe, size);
58 if (!batch->nondraw) {
59 batch->binning = fd_ringbuffer_new(ctx->pipe, size);
60 batch->gmem = fd_ringbuffer_new(ctx->pipe, size);
61
62 fd_ringbuffer_set_parent(batch->gmem, NULL);
63 fd_ringbuffer_set_parent(batch->draw, batch->gmem);
64 fd_ringbuffer_set_parent(batch->binning, batch->gmem);
65 } else {
66 fd_ringbuffer_set_parent(batch->draw, NULL);
67 }
68
69 batch->in_fence_fd = -1;
70 batch->fence = fd_fence_create(batch);
71
72 batch->cleared = batch->partial_cleared = 0;
73 batch->restore = batch->resolve = 0;
74 batch->needs_flush = false;
75 batch->flushed = false;
76 batch->gmem_reason = 0;
77 batch->num_draws = 0;
78 batch->stage = FD_STAGE_NULL;
79
80 fd_reset_wfi(batch);
81
82 /* reset maximal bounds: */
83 batch->max_scissor.minx = batch->max_scissor.miny = ~0;
84 batch->max_scissor.maxx = batch->max_scissor.maxy = 0;
85
86 util_dynarray_init(&batch->draw_patches, NULL);
87
88 if (is_a3xx(ctx->screen))
89 util_dynarray_init(&batch->rbrc_patches, NULL);
90
91 util_dynarray_init(&batch->gmem_patches, NULL);
92
93 assert(batch->resources->entries == 0);
94
95 util_dynarray_init(&batch->samples, NULL);
96 }
97
98 struct fd_batch *
99 fd_batch_create(struct fd_context *ctx, bool nondraw)
100 {
101 struct fd_batch *batch = CALLOC_STRUCT(fd_batch);
102
103 if (!batch)
104 return NULL;
105
106 DBG("%p", batch);
107
108 pipe_reference_init(&batch->reference, 1);
109 batch->ctx = ctx;
110 batch->nondraw = nondraw;
111
112 batch->resources = _mesa_set_create(NULL, _mesa_hash_pointer,
113 _mesa_key_pointer_equal);
114
115 batch_init(batch);
116
117 return batch;
118 }
119
120 static void
121 batch_fini(struct fd_batch *batch)
122 {
123 DBG("%p", batch);
124
125 pipe_resource_reference(&batch->query_buf, NULL);
126
127 if (batch->in_fence_fd != -1)
128 close(batch->in_fence_fd);
129
130 /* in case batch wasn't flushed but fence was created: */
131 fd_fence_populate(batch->fence, 0, -1);
132
133 fd_fence_ref(NULL, &batch->fence, NULL);
134
135 fd_ringbuffer_del(batch->draw);
136 if (!batch->nondraw) {
137 fd_ringbuffer_del(batch->binning);
138 fd_ringbuffer_del(batch->gmem);
139 } else {
140 debug_assert(!batch->binning);
141 debug_assert(!batch->gmem);
142 }
143 if (batch->lrz_clear) {
144 fd_ringbuffer_del(batch->lrz_clear);
145 batch->lrz_clear = NULL;
146 }
147
148 util_dynarray_fini(&batch->draw_patches);
149
150 if (is_a3xx(batch->ctx->screen))
151 util_dynarray_fini(&batch->rbrc_patches);
152
153 util_dynarray_fini(&batch->gmem_patches);
154
155 while (batch->samples.size > 0) {
156 struct fd_hw_sample *samp =
157 util_dynarray_pop(&batch->samples, struct fd_hw_sample *);
158 fd_hw_sample_reference(batch->ctx, &samp, NULL);
159 }
160 util_dynarray_fini(&batch->samples);
161
162 if (batch->ctx->screen->reorder)
163 util_queue_fence_destroy(&batch->flush_fence);
164 }
165
166 static void
167 batch_flush_reset_dependencies(struct fd_batch *batch, bool flush)
168 {
169 struct fd_batch_cache *cache = &batch->ctx->screen->batch_cache;
170 struct fd_batch *dep;
171
172 foreach_batch(dep, cache, batch->dependents_mask) {
173 if (flush)
174 fd_batch_flush(dep, false, false);
175 fd_batch_reference(&dep, NULL);
176 }
177
178 batch->dependents_mask = 0;
179 }
180
181 static void
182 batch_reset_resources_locked(struct fd_batch *batch)
183 {
184 struct set_entry *entry;
185
186 pipe_mutex_assert_locked(batch->ctx->screen->lock);
187
188 set_foreach(batch->resources, entry) {
189 struct fd_resource *rsc = (struct fd_resource *)entry->key;
190 _mesa_set_remove(batch->resources, entry);
191 debug_assert(rsc->batch_mask & (1 << batch->idx));
192 rsc->batch_mask &= ~(1 << batch->idx);
193 if (rsc->write_batch == batch)
194 fd_batch_reference_locked(&rsc->write_batch, NULL);
195 }
196 }
197
198 static void
199 batch_reset_resources(struct fd_batch *batch)
200 {
201 mtx_lock(&batch->ctx->screen->lock);
202 batch_reset_resources_locked(batch);
203 mtx_unlock(&batch->ctx->screen->lock);
204 }
205
206 static void
207 batch_reset(struct fd_batch *batch)
208 {
209 DBG("%p", batch);
210
211 fd_batch_sync(batch);
212
213 batch_flush_reset_dependencies(batch, false);
214 batch_reset_resources(batch);
215
216 batch_fini(batch);
217 batch_init(batch);
218 }
219
220 void
221 fd_batch_reset(struct fd_batch *batch)
222 {
223 if (batch->needs_flush)
224 batch_reset(batch);
225 }
226
227 void
228 __fd_batch_destroy(struct fd_batch *batch)
229 {
230 struct fd_context *ctx = batch->ctx;
231
232 DBG("%p", batch);
233
234 fd_context_assert_locked(batch->ctx);
235
236 fd_bc_invalidate_batch(batch, true);
237
238 batch_reset_resources_locked(batch);
239 debug_assert(batch->resources->entries == 0);
240 _mesa_set_destroy(batch->resources, NULL);
241
242 batch_flush_reset_dependencies(batch, false);
243 debug_assert(batch->dependents_mask == 0);
244
245 fd_context_unlock(ctx);
246 util_copy_framebuffer_state(&batch->framebuffer, NULL);
247 batch_fini(batch);
248 free(batch);
249 fd_context_lock(ctx);
250 }
251
252 void
253 __fd_batch_describe(char* buf, const struct fd_batch *batch)
254 {
255 util_sprintf(buf, "fd_batch<%u>", batch->seqno);
256 }
257
258 void
259 fd_batch_sync(struct fd_batch *batch)
260 {
261 if (!batch->ctx->screen->reorder)
262 return;
263 util_queue_fence_wait(&batch->flush_fence);
264 }
265
266 static void
267 batch_flush_func(void *job, int id)
268 {
269 struct fd_batch *batch = job;
270
271 DBG("%p", batch);
272
273 fd_gmem_render_tiles(batch);
274 batch_reset_resources(batch);
275 }
276
277 static void
278 batch_cleanup_func(void *job, int id)
279 {
280 struct fd_batch *batch = job;
281 fd_batch_reference(&batch, NULL);
282 }
283
284 static void
285 batch_flush(struct fd_batch *batch, bool force)
286 {
287 DBG("%p: needs_flush=%d", batch, batch->needs_flush);
288
289 if (batch->flushed)
290 return;
291
292 batch->needs_flush = false;
293
294 /* close out the draw cmds by making sure any active queries are
295 * paused:
296 */
297 fd_batch_set_stage(batch, FD_STAGE_NULL);
298
299 batch_flush_reset_dependencies(batch, true);
300
301 batch->flushed = true;
302
303 if (batch->ctx->screen->reorder) {
304 struct fd_batch *tmp = NULL;
305 fd_batch_reference(&tmp, batch);
306
307 if (!util_queue_is_initialized(&batch->ctx->flush_queue))
308 util_queue_init(&batch->ctx->flush_queue, "flush_queue", 16, 1, 0);
309
310 util_queue_add_job(&batch->ctx->flush_queue,
311 batch, &batch->flush_fence,
312 batch_flush_func, batch_cleanup_func);
313 } else {
314 fd_gmem_render_tiles(batch);
315 batch_reset_resources(batch);
316 }
317
318 debug_assert(batch->reference.count > 0);
319
320 mtx_lock(&batch->ctx->screen->lock);
321 fd_bc_invalidate_batch(batch, false);
322 mtx_unlock(&batch->ctx->screen->lock);
323 }
324
325 /* NOTE: could drop the last ref to batch
326 *
327 * @sync: synchronize with flush_queue, ensures batch is *actually* flushed
328 * to kernel before this returns, as opposed to just being queued to be
329 * flushed
330 * @force: force a flush even if no rendering, mostly useful if you need
331 * a fence to sync on
332 */
333 void
334 fd_batch_flush(struct fd_batch *batch, bool sync, bool force)
335 {
336 struct fd_batch *tmp = NULL;
337 bool newbatch = false;
338
339 /* NOTE: we need to hold an extra ref across the body of flush,
340 * since the last ref to this batch could be dropped when cleaning
341 * up used_resources
342 */
343 fd_batch_reference(&tmp, batch);
344
345 if (batch == batch->ctx->batch) {
346 batch->ctx->batch = NULL;
347 newbatch = true;
348 }
349
350 batch_flush(tmp, force);
351
352 if (newbatch) {
353 struct fd_context *ctx = batch->ctx;
354 struct fd_batch *new_batch;
355
356 if (ctx->screen->reorder) {
357 /* defer allocating new batch until one is needed for rendering
358 * to avoid unused batches for apps that create many contexts
359 */
360 new_batch = NULL;
361 } else {
362 new_batch = fd_batch_create(ctx, false);
363 util_copy_framebuffer_state(&new_batch->framebuffer, &batch->framebuffer);
364 }
365
366 fd_batch_reference(&batch, NULL);
367 ctx->batch = new_batch;
368 }
369
370 if (sync)
371 fd_batch_sync(tmp);
372
373 fd_batch_reference(&tmp, NULL);
374 }
375
376 /* does 'batch' depend directly or indirectly on 'other' ? */
377 static bool
378 batch_depends_on(struct fd_batch *batch, struct fd_batch *other)
379 {
380 struct fd_batch_cache *cache = &batch->ctx->screen->batch_cache;
381 struct fd_batch *dep;
382
383 if (batch->dependents_mask & (1 << other->idx))
384 return true;
385
386 foreach_batch(dep, cache, batch->dependents_mask)
387 if (batch_depends_on(batch, dep))
388 return true;
389
390 return false;
391 }
392
393 void
394 fd_batch_add_dep(struct fd_batch *batch, struct fd_batch *dep)
395 {
396 if (batch->dependents_mask & (1 << dep->idx))
397 return;
398
399 /* a loop should not be possible */
400 debug_assert(!batch_depends_on(dep, batch));
401
402 struct fd_batch *other = NULL;
403 fd_batch_reference_locked(&other, dep);
404 batch->dependents_mask |= (1 << dep->idx);
405 DBG("%p: added dependency on %p", batch, dep);
406 }
407
408 static void
409 flush_write_batch(struct fd_resource *rsc)
410 {
411 struct fd_batch *b = NULL;
412 fd_batch_reference(&b, rsc->write_batch);
413
414 mtx_unlock(&b->ctx->screen->lock);
415 fd_batch_flush(b, true, false);
416 mtx_lock(&b->ctx->screen->lock);
417
418 fd_bc_invalidate_batch(b, false);
419 fd_batch_reference_locked(&b, NULL);
420 }
421
422 void
423 fd_batch_resource_used(struct fd_batch *batch, struct fd_resource *rsc, bool write)
424 {
425 pipe_mutex_assert_locked(batch->ctx->screen->lock);
426
427 if (rsc->stencil)
428 fd_batch_resource_used(batch, rsc->stencil, write);
429
430 DBG("%p: %s %p", batch, write ? "write" : "read", rsc);
431
432 if (write)
433 rsc->valid = true;
434
435 /* note, invalidate write batch, to avoid further writes to rsc
436 * resulting in a write-after-read hazard.
437 */
438
439 if (write) {
440 /* if we are pending read or write by any other batch: */
441 if (rsc->batch_mask & ~(1 << batch->idx)) {
442 struct fd_batch_cache *cache = &batch->ctx->screen->batch_cache;
443 struct fd_batch *dep;
444
445 if (rsc->write_batch && rsc->write_batch != batch)
446 flush_write_batch(rsc);
447
448 foreach_batch(dep, cache, rsc->batch_mask) {
449 struct fd_batch *b = NULL;
450 if (dep == batch)
451 continue;
452 /* note that batch_add_dep could flush and unref dep, so
453 * we need to hold a reference to keep it live for the
454 * fd_bc_invalidate_batch()
455 */
456 fd_batch_reference(&b, dep);
457 fd_batch_add_dep(batch, b);
458 fd_bc_invalidate_batch(b, false);
459 fd_batch_reference_locked(&b, NULL);
460 }
461 }
462 fd_batch_reference_locked(&rsc->write_batch, batch);
463 } else {
464 /* If reading a resource pending a write, go ahead and flush the
465 * writer. This avoids situations where we end up having to
466 * flush the current batch in _resource_used()
467 */
468 if (rsc->write_batch && rsc->write_batch != batch)
469 flush_write_batch(rsc);
470 }
471
472 if (rsc->batch_mask & (1 << batch->idx))
473 return;
474
475 debug_assert(!_mesa_set_search(batch->resources, rsc));
476
477 _mesa_set_add(batch->resources, rsc);
478 rsc->batch_mask |= (1 << batch->idx);
479 }
480
481 void
482 fd_batch_check_size(struct fd_batch *batch)
483 {
484 debug_assert(!batch->flushed);
485
486 if (fd_device_version(batch->ctx->screen->dev) >= FD_VERSION_UNLIMITED_CMDS)
487 return;
488
489 struct fd_ringbuffer *ring = batch->draw;
490 if (((ring->cur - ring->start) > (ring->size/4 - 0x1000)) ||
491 (fd_mesa_debug & FD_DBG_FLUSH))
492 fd_batch_flush(batch, true, false);
493 }
494
495 /* emit a WAIT_FOR_IDLE only if needed, ie. if there has not already
496 * been one since last draw:
497 */
498 void
499 fd_wfi(struct fd_batch *batch, struct fd_ringbuffer *ring)
500 {
501 if (batch->needs_wfi) {
502 if (batch->ctx->screen->gpu_id >= 500)
503 OUT_WFI5(ring);
504 else
505 OUT_WFI(ring);
506 batch->needs_wfi = false;
507 }
508 }