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