1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org> 3bf215546Sopenharmony_ci * 4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10bf215546Sopenharmony_ci * 11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next 12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13bf215546Sopenharmony_ci * Software. 14bf215546Sopenharmony_ci * 15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20bf215546Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21bf215546Sopenharmony_ci * SOFTWARE. 22bf215546Sopenharmony_ci * 23bf215546Sopenharmony_ci * Authors: 24bf215546Sopenharmony_ci * Rob Clark <robclark@freedesktop.org> 25bf215546Sopenharmony_ci */ 26bf215546Sopenharmony_ci 27bf215546Sopenharmony_ci#include "freedreno_context.h" 28bf215546Sopenharmony_ci#include "ir3/ir3_cache.h" 29bf215546Sopenharmony_ci#include "util/u_upload_mgr.h" 30bf215546Sopenharmony_ci#include "freedreno_blitter.h" 31bf215546Sopenharmony_ci#include "freedreno_draw.h" 32bf215546Sopenharmony_ci#include "freedreno_fence.h" 33bf215546Sopenharmony_ci#include "freedreno_gmem.h" 34bf215546Sopenharmony_ci#include "freedreno_program.h" 35bf215546Sopenharmony_ci#include "freedreno_query.h" 36bf215546Sopenharmony_ci#include "freedreno_query_hw.h" 37bf215546Sopenharmony_ci#include "freedreno_resource.h" 38bf215546Sopenharmony_ci#include "freedreno_state.h" 39bf215546Sopenharmony_ci#include "freedreno_texture.h" 40bf215546Sopenharmony_ci#include "freedreno_util.h" 41bf215546Sopenharmony_ci#include "freedreno_tracepoints.h" 42bf215546Sopenharmony_ci#include "util/u_trace_gallium.h" 43bf215546Sopenharmony_ci 44bf215546Sopenharmony_cistatic void 45bf215546Sopenharmony_cifd_context_flush(struct pipe_context *pctx, struct pipe_fence_handle **fencep, 46bf215546Sopenharmony_ci unsigned flags) in_dt 47bf215546Sopenharmony_ci{ 48bf215546Sopenharmony_ci struct fd_context *ctx = fd_context(pctx); 49bf215546Sopenharmony_ci struct pipe_fence_handle *fence = NULL; 50bf215546Sopenharmony_ci struct fd_batch *batch = NULL; 51bf215546Sopenharmony_ci 52bf215546Sopenharmony_ci /* We want to lookup current batch if it exists, but not create a new 53bf215546Sopenharmony_ci * one if not (unless we need a fence) 54bf215546Sopenharmony_ci */ 55bf215546Sopenharmony_ci fd_batch_reference(&batch, ctx->batch); 56bf215546Sopenharmony_ci 57bf215546Sopenharmony_ci DBG("%p: flush: flags=%x, fencep=%p", batch, flags, fencep); 58bf215546Sopenharmony_ci 59bf215546Sopenharmony_ci if (fencep && !batch) { 60bf215546Sopenharmony_ci batch = fd_context_batch(ctx); 61bf215546Sopenharmony_ci } else if (!batch) { 62bf215546Sopenharmony_ci if (ctx->screen->reorder) 63bf215546Sopenharmony_ci fd_bc_flush(ctx, flags & PIPE_FLUSH_DEFERRED); 64bf215546Sopenharmony_ci fd_bc_dump(ctx, "%p: NULL batch, remaining:\n", ctx); 65bf215546Sopenharmony_ci return; 66bf215546Sopenharmony_ci } 67bf215546Sopenharmony_ci 68bf215546Sopenharmony_ci /* With TC_FLUSH_ASYNC, the fence will have been pre-created from 69bf215546Sopenharmony_ci * the front-end thread. But not yet associated with a batch, 70bf215546Sopenharmony_ci * because we cannot safely access ctx->batch outside of the driver 71bf215546Sopenharmony_ci * thread. So instead, replace the existing batch->fence with the 72bf215546Sopenharmony_ci * one created earlier 73bf215546Sopenharmony_ci */ 74bf215546Sopenharmony_ci if ((flags & TC_FLUSH_ASYNC) && fencep) { 75bf215546Sopenharmony_ci /* We don't currently expect async+flush in the fence-fd 76bf215546Sopenharmony_ci * case.. for that to work properly we'd need TC to tell 77bf215546Sopenharmony_ci * us in the create_fence callback that it needs an fd. 78bf215546Sopenharmony_ci */ 79bf215546Sopenharmony_ci assert(!(flags & PIPE_FLUSH_FENCE_FD)); 80bf215546Sopenharmony_ci 81bf215546Sopenharmony_ci fd_fence_set_batch(*fencep, batch); 82bf215546Sopenharmony_ci fd_fence_ref(&batch->fence, *fencep); 83bf215546Sopenharmony_ci 84bf215546Sopenharmony_ci /* If we have nothing to flush, update the pre-created unflushed 85bf215546Sopenharmony_ci * fence with the current state of the last-fence: 86bf215546Sopenharmony_ci */ 87bf215546Sopenharmony_ci if (ctx->last_fence) { 88bf215546Sopenharmony_ci fd_fence_repopulate(*fencep, ctx->last_fence); 89bf215546Sopenharmony_ci fd_fence_ref(&fence, *fencep); 90bf215546Sopenharmony_ci fd_bc_dump(ctx, "%p: (deferred) reuse last_fence, remaining:\n", ctx); 91bf215546Sopenharmony_ci goto out; 92bf215546Sopenharmony_ci } 93bf215546Sopenharmony_ci 94bf215546Sopenharmony_ci /* async flush is not compatible with deferred flush, since 95bf215546Sopenharmony_ci * nothing triggers the batch flush which fence_flush() would 96bf215546Sopenharmony_ci * be waiting for 97bf215546Sopenharmony_ci */ 98bf215546Sopenharmony_ci flags &= ~PIPE_FLUSH_DEFERRED; 99bf215546Sopenharmony_ci } else if (!batch->fence) { 100bf215546Sopenharmony_ci batch->fence = fd_fence_create(batch); 101bf215546Sopenharmony_ci } 102bf215546Sopenharmony_ci 103bf215546Sopenharmony_ci /* In some sequence of events, we can end up with a last_fence that is 104bf215546Sopenharmony_ci * not an "fd" fence, which results in eglDupNativeFenceFDANDROID() 105bf215546Sopenharmony_ci * errors. 106bf215546Sopenharmony_ci */ 107bf215546Sopenharmony_ci if ((flags & PIPE_FLUSH_FENCE_FD) && ctx->last_fence && 108bf215546Sopenharmony_ci !fd_fence_is_fd(ctx->last_fence)) 109bf215546Sopenharmony_ci fd_fence_ref(&ctx->last_fence, NULL); 110bf215546Sopenharmony_ci 111bf215546Sopenharmony_ci /* if no rendering since last flush, ie. app just decided it needed 112bf215546Sopenharmony_ci * a fence, re-use the last one: 113bf215546Sopenharmony_ci */ 114bf215546Sopenharmony_ci if (ctx->last_fence) { 115bf215546Sopenharmony_ci fd_fence_ref(&fence, ctx->last_fence); 116bf215546Sopenharmony_ci fd_bc_dump(ctx, "%p: reuse last_fence, remaining:\n", ctx); 117bf215546Sopenharmony_ci goto out; 118bf215546Sopenharmony_ci } 119bf215546Sopenharmony_ci 120bf215546Sopenharmony_ci /* Take a ref to the batch's fence (batch can be unref'd when flushed: */ 121bf215546Sopenharmony_ci fd_fence_ref(&fence, batch->fence); 122bf215546Sopenharmony_ci 123bf215546Sopenharmony_ci if (flags & PIPE_FLUSH_FENCE_FD) 124bf215546Sopenharmony_ci fence->submit_fence.use_fence_fd = true; 125bf215546Sopenharmony_ci 126bf215546Sopenharmony_ci fd_bc_dump(ctx, "%p: flushing %p<%u>, flags=0x%x, pending:\n", ctx, 127bf215546Sopenharmony_ci batch, batch->seqno, flags); 128bf215546Sopenharmony_ci 129bf215546Sopenharmony_ci /* If we get here, we need to flush for a fence, even if there is 130bf215546Sopenharmony_ci * no rendering yet: 131bf215546Sopenharmony_ci */ 132bf215546Sopenharmony_ci batch->needs_flush = true; 133bf215546Sopenharmony_ci 134bf215546Sopenharmony_ci if (!ctx->screen->reorder) { 135bf215546Sopenharmony_ci fd_batch_flush(batch); 136bf215546Sopenharmony_ci } else { 137bf215546Sopenharmony_ci fd_bc_flush(ctx, flags & PIPE_FLUSH_DEFERRED); 138bf215546Sopenharmony_ci } 139bf215546Sopenharmony_ci 140bf215546Sopenharmony_ci fd_bc_dump(ctx, "%p: remaining:\n", ctx); 141bf215546Sopenharmony_ci 142bf215546Sopenharmony_ciout: 143bf215546Sopenharmony_ci if (fencep) 144bf215546Sopenharmony_ci fd_fence_ref(fencep, fence); 145bf215546Sopenharmony_ci 146bf215546Sopenharmony_ci fd_fence_ref(&ctx->last_fence, fence); 147bf215546Sopenharmony_ci 148bf215546Sopenharmony_ci fd_fence_ref(&fence, NULL); 149bf215546Sopenharmony_ci 150bf215546Sopenharmony_ci fd_batch_reference(&batch, NULL); 151bf215546Sopenharmony_ci 152bf215546Sopenharmony_ci u_trace_context_process(&ctx->trace_context, 153bf215546Sopenharmony_ci !!(flags & PIPE_FLUSH_END_OF_FRAME)); 154bf215546Sopenharmony_ci} 155bf215546Sopenharmony_ci 156bf215546Sopenharmony_cistatic void 157bf215546Sopenharmony_cifd_texture_barrier(struct pipe_context *pctx, unsigned flags) in_dt 158bf215546Sopenharmony_ci{ 159bf215546Sopenharmony_ci if (flags == PIPE_TEXTURE_BARRIER_FRAMEBUFFER) { 160bf215546Sopenharmony_ci struct fd_context *ctx = fd_context(pctx); 161bf215546Sopenharmony_ci 162bf215546Sopenharmony_ci if (ctx->framebuffer_barrier) { 163bf215546Sopenharmony_ci ctx->framebuffer_barrier(ctx); 164bf215546Sopenharmony_ci return; 165bf215546Sopenharmony_ci } 166bf215546Sopenharmony_ci } 167bf215546Sopenharmony_ci 168bf215546Sopenharmony_ci /* On devices that could sample from GMEM we could possibly do better. 169bf215546Sopenharmony_ci * Or if we knew that we were doing GMEM bypass we could just emit a 170bf215546Sopenharmony_ci * cache flush, perhaps? But we don't know if future draws would cause 171bf215546Sopenharmony_ci * us to use GMEM, and a flush in bypass isn't the end of the world. 172bf215546Sopenharmony_ci */ 173bf215546Sopenharmony_ci fd_context_flush(pctx, NULL, 0); 174bf215546Sopenharmony_ci} 175bf215546Sopenharmony_ci 176bf215546Sopenharmony_cistatic void 177bf215546Sopenharmony_cifd_memory_barrier(struct pipe_context *pctx, unsigned flags) 178bf215546Sopenharmony_ci{ 179bf215546Sopenharmony_ci if (!(flags & ~PIPE_BARRIER_UPDATE)) 180bf215546Sopenharmony_ci return; 181bf215546Sopenharmony_ci 182bf215546Sopenharmony_ci fd_context_flush(pctx, NULL, 0); 183bf215546Sopenharmony_ci 184bf215546Sopenharmony_ci /* TODO do we need to check for persistently mapped buffers and 185bf215546Sopenharmony_ci * fd_bo_cpu_prep()?? 186bf215546Sopenharmony_ci */ 187bf215546Sopenharmony_ci} 188bf215546Sopenharmony_ci 189bf215546Sopenharmony_cistatic void 190bf215546Sopenharmony_ciemit_string_tail(struct fd_ringbuffer *ring, const char *string, int len) 191bf215546Sopenharmony_ci{ 192bf215546Sopenharmony_ci const uint32_t *buf = (const void *)string; 193bf215546Sopenharmony_ci 194bf215546Sopenharmony_ci while (len >= 4) { 195bf215546Sopenharmony_ci OUT_RING(ring, *buf); 196bf215546Sopenharmony_ci buf++; 197bf215546Sopenharmony_ci len -= 4; 198bf215546Sopenharmony_ci } 199bf215546Sopenharmony_ci 200bf215546Sopenharmony_ci /* copy remainder bytes without reading past end of input string: */ 201bf215546Sopenharmony_ci if (len > 0) { 202bf215546Sopenharmony_ci uint32_t w = 0; 203bf215546Sopenharmony_ci memcpy(&w, buf, len); 204bf215546Sopenharmony_ci OUT_RING(ring, w); 205bf215546Sopenharmony_ci } 206bf215546Sopenharmony_ci} 207bf215546Sopenharmony_ci 208bf215546Sopenharmony_ci/* for prior to a5xx: */ 209bf215546Sopenharmony_civoid 210bf215546Sopenharmony_cifd_emit_string(struct fd_ringbuffer *ring, const char *string, int len) 211bf215546Sopenharmony_ci{ 212bf215546Sopenharmony_ci /* max packet size is 0x3fff+1 dwords: */ 213bf215546Sopenharmony_ci len = MIN2(len, 0x4000 * 4); 214bf215546Sopenharmony_ci 215bf215546Sopenharmony_ci OUT_PKT3(ring, CP_NOP, align(len, 4) / 4); 216bf215546Sopenharmony_ci emit_string_tail(ring, string, len); 217bf215546Sopenharmony_ci} 218bf215546Sopenharmony_ci 219bf215546Sopenharmony_ci/* for a5xx+ */ 220bf215546Sopenharmony_civoid 221bf215546Sopenharmony_cifd_emit_string5(struct fd_ringbuffer *ring, const char *string, int len) 222bf215546Sopenharmony_ci{ 223bf215546Sopenharmony_ci /* max packet size is 0x3fff dwords: */ 224bf215546Sopenharmony_ci len = MIN2(len, 0x3fff * 4); 225bf215546Sopenharmony_ci 226bf215546Sopenharmony_ci OUT_PKT7(ring, CP_NOP, align(len, 4) / 4); 227bf215546Sopenharmony_ci emit_string_tail(ring, string, len); 228bf215546Sopenharmony_ci} 229bf215546Sopenharmony_ci 230bf215546Sopenharmony_ci/** 231bf215546Sopenharmony_ci * emit marker string as payload of a no-op packet, which can be 232bf215546Sopenharmony_ci * decoded by cffdump. 233bf215546Sopenharmony_ci */ 234bf215546Sopenharmony_cistatic void 235bf215546Sopenharmony_cifd_emit_string_marker(struct pipe_context *pctx, const char *string, 236bf215546Sopenharmony_ci int len) in_dt 237bf215546Sopenharmony_ci{ 238bf215546Sopenharmony_ci struct fd_context *ctx = fd_context(pctx); 239bf215546Sopenharmony_ci 240bf215546Sopenharmony_ci DBG("%.*s", len, string); 241bf215546Sopenharmony_ci 242bf215546Sopenharmony_ci if (!ctx->batch) 243bf215546Sopenharmony_ci return; 244bf215546Sopenharmony_ci 245bf215546Sopenharmony_ci struct fd_batch *batch = fd_context_batch_locked(ctx); 246bf215546Sopenharmony_ci 247bf215546Sopenharmony_ci fd_batch_needs_flush(batch); 248bf215546Sopenharmony_ci 249bf215546Sopenharmony_ci if (ctx->screen->gen >= 5) { 250bf215546Sopenharmony_ci fd_emit_string5(batch->draw, string, len); 251bf215546Sopenharmony_ci } else { 252bf215546Sopenharmony_ci fd_emit_string(batch->draw, string, len); 253bf215546Sopenharmony_ci } 254bf215546Sopenharmony_ci 255bf215546Sopenharmony_ci fd_batch_unlock_submit(batch); 256bf215546Sopenharmony_ci fd_batch_reference(&batch, NULL); 257bf215546Sopenharmony_ci} 258bf215546Sopenharmony_ci 259bf215546Sopenharmony_ci/** 260bf215546Sopenharmony_ci * If we have a pending fence_server_sync() (GPU side sync), flush now. 261bf215546Sopenharmony_ci * The alternative to try to track this with batch dependencies gets 262bf215546Sopenharmony_ci * hairy quickly. 263bf215546Sopenharmony_ci * 264bf215546Sopenharmony_ci * Call this before switching to a different batch, to handle this case. 265bf215546Sopenharmony_ci */ 266bf215546Sopenharmony_civoid 267bf215546Sopenharmony_cifd_context_switch_from(struct fd_context *ctx) 268bf215546Sopenharmony_ci{ 269bf215546Sopenharmony_ci if (ctx->batch && (ctx->batch->in_fence_fd != -1)) 270bf215546Sopenharmony_ci fd_batch_flush(ctx->batch); 271bf215546Sopenharmony_ci} 272bf215546Sopenharmony_ci 273bf215546Sopenharmony_ci/** 274bf215546Sopenharmony_ci * If there is a pending fence-fd that we need to sync on, this will 275bf215546Sopenharmony_ci * transfer the reference to the next batch we are going to render 276bf215546Sopenharmony_ci * to. 277bf215546Sopenharmony_ci */ 278bf215546Sopenharmony_civoid 279bf215546Sopenharmony_cifd_context_switch_to(struct fd_context *ctx, struct fd_batch *batch) 280bf215546Sopenharmony_ci{ 281bf215546Sopenharmony_ci if (ctx->in_fence_fd != -1) { 282bf215546Sopenharmony_ci sync_accumulate("freedreno", &batch->in_fence_fd, ctx->in_fence_fd); 283bf215546Sopenharmony_ci close(ctx->in_fence_fd); 284bf215546Sopenharmony_ci ctx->in_fence_fd = -1; 285bf215546Sopenharmony_ci } 286bf215546Sopenharmony_ci} 287bf215546Sopenharmony_ci 288bf215546Sopenharmony_ci/** 289bf215546Sopenharmony_ci * Return a reference to the current batch, caller must unref. 290bf215546Sopenharmony_ci */ 291bf215546Sopenharmony_cistruct fd_batch * 292bf215546Sopenharmony_cifd_context_batch(struct fd_context *ctx) 293bf215546Sopenharmony_ci{ 294bf215546Sopenharmony_ci struct fd_batch *batch = NULL; 295bf215546Sopenharmony_ci 296bf215546Sopenharmony_ci tc_assert_driver_thread(ctx->tc); 297bf215546Sopenharmony_ci 298bf215546Sopenharmony_ci fd_batch_reference(&batch, ctx->batch); 299bf215546Sopenharmony_ci 300bf215546Sopenharmony_ci if (unlikely(!batch)) { 301bf215546Sopenharmony_ci batch = 302bf215546Sopenharmony_ci fd_batch_from_fb(ctx, &ctx->framebuffer); 303bf215546Sopenharmony_ci util_copy_framebuffer_state(&batch->framebuffer, &ctx->framebuffer); 304bf215546Sopenharmony_ci fd_batch_reference(&ctx->batch, batch); 305bf215546Sopenharmony_ci fd_context_all_dirty(ctx); 306bf215546Sopenharmony_ci } 307bf215546Sopenharmony_ci fd_context_switch_to(ctx, batch); 308bf215546Sopenharmony_ci 309bf215546Sopenharmony_ci return batch; 310bf215546Sopenharmony_ci} 311bf215546Sopenharmony_ci 312bf215546Sopenharmony_ci/** 313bf215546Sopenharmony_ci * Return a locked reference to the current batch. A batch with emit 314bf215546Sopenharmony_ci * lock held is protected against flushing while the lock is held. 315bf215546Sopenharmony_ci * The emit-lock should be acquired before screen-lock. The emit-lock 316bf215546Sopenharmony_ci * should be held while emitting cmdstream. 317bf215546Sopenharmony_ci */ 318bf215546Sopenharmony_cistruct fd_batch * 319bf215546Sopenharmony_cifd_context_batch_locked(struct fd_context *ctx) 320bf215546Sopenharmony_ci{ 321bf215546Sopenharmony_ci struct fd_batch *batch = NULL; 322bf215546Sopenharmony_ci 323bf215546Sopenharmony_ci while (!batch) { 324bf215546Sopenharmony_ci batch = fd_context_batch(ctx); 325bf215546Sopenharmony_ci if (!fd_batch_lock_submit(batch)) { 326bf215546Sopenharmony_ci fd_batch_reference(&batch, NULL); 327bf215546Sopenharmony_ci } 328bf215546Sopenharmony_ci } 329bf215546Sopenharmony_ci 330bf215546Sopenharmony_ci return batch; 331bf215546Sopenharmony_ci} 332bf215546Sopenharmony_ci 333bf215546Sopenharmony_civoid 334bf215546Sopenharmony_cifd_context_destroy(struct pipe_context *pctx) 335bf215546Sopenharmony_ci{ 336bf215546Sopenharmony_ci struct fd_context *ctx = fd_context(pctx); 337bf215546Sopenharmony_ci unsigned i; 338bf215546Sopenharmony_ci 339bf215546Sopenharmony_ci DBG(""); 340bf215546Sopenharmony_ci 341bf215546Sopenharmony_ci fd_screen_lock(ctx->screen); 342bf215546Sopenharmony_ci list_del(&ctx->node); 343bf215546Sopenharmony_ci fd_screen_unlock(ctx->screen); 344bf215546Sopenharmony_ci 345bf215546Sopenharmony_ci fd_fence_ref(&ctx->last_fence, NULL); 346bf215546Sopenharmony_ci 347bf215546Sopenharmony_ci if (ctx->in_fence_fd != -1) 348bf215546Sopenharmony_ci close(ctx->in_fence_fd); 349bf215546Sopenharmony_ci 350bf215546Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctx->pvtmem); i++) { 351bf215546Sopenharmony_ci if (ctx->pvtmem[i].bo) 352bf215546Sopenharmony_ci fd_bo_del(ctx->pvtmem[i].bo); 353bf215546Sopenharmony_ci } 354bf215546Sopenharmony_ci 355bf215546Sopenharmony_ci util_copy_framebuffer_state(&ctx->framebuffer, NULL); 356bf215546Sopenharmony_ci fd_batch_reference(&ctx->batch, NULL); /* unref current batch */ 357bf215546Sopenharmony_ci 358bf215546Sopenharmony_ci /* Make sure nothing in the batch cache references our context any more. */ 359bf215546Sopenharmony_ci fd_bc_flush(ctx, false); 360bf215546Sopenharmony_ci 361bf215546Sopenharmony_ci fd_prog_fini(pctx); 362bf215546Sopenharmony_ci 363bf215546Sopenharmony_ci if (ctx->blitter) 364bf215546Sopenharmony_ci util_blitter_destroy(ctx->blitter); 365bf215546Sopenharmony_ci 366bf215546Sopenharmony_ci if (pctx->stream_uploader) 367bf215546Sopenharmony_ci u_upload_destroy(pctx->stream_uploader); 368bf215546Sopenharmony_ci 369bf215546Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctx->clear_rs_state); i++) 370bf215546Sopenharmony_ci if (ctx->clear_rs_state[i]) 371bf215546Sopenharmony_ci pctx->delete_rasterizer_state(pctx, ctx->clear_rs_state[i]); 372bf215546Sopenharmony_ci 373bf215546Sopenharmony_ci slab_destroy_child(&ctx->transfer_pool); 374bf215546Sopenharmony_ci slab_destroy_child(&ctx->transfer_pool_unsync); 375bf215546Sopenharmony_ci 376bf215546Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctx->vsc_pipe_bo); i++) { 377bf215546Sopenharmony_ci if (!ctx->vsc_pipe_bo[i]) 378bf215546Sopenharmony_ci break; 379bf215546Sopenharmony_ci fd_bo_del(ctx->vsc_pipe_bo[i]); 380bf215546Sopenharmony_ci } 381bf215546Sopenharmony_ci 382bf215546Sopenharmony_ci fd_device_del(ctx->dev); 383bf215546Sopenharmony_ci fd_pipe_purge(ctx->pipe); 384bf215546Sopenharmony_ci fd_pipe_del(ctx->pipe); 385bf215546Sopenharmony_ci 386bf215546Sopenharmony_ci simple_mtx_destroy(&ctx->gmem_lock); 387bf215546Sopenharmony_ci 388bf215546Sopenharmony_ci u_trace_context_fini(&ctx->trace_context); 389bf215546Sopenharmony_ci 390bf215546Sopenharmony_ci fd_autotune_fini(&ctx->autotune); 391bf215546Sopenharmony_ci 392bf215546Sopenharmony_ci ir3_cache_destroy(ctx->shader_cache); 393bf215546Sopenharmony_ci 394bf215546Sopenharmony_ci if (FD_DBG(BSTAT) || FD_DBG(MSGS)) { 395bf215546Sopenharmony_ci mesa_logi( 396bf215546Sopenharmony_ci "batch_total=%u, batch_sysmem=%u, batch_gmem=%u, batch_nondraw=%u, " 397bf215546Sopenharmony_ci "batch_restore=%u\n", 398bf215546Sopenharmony_ci (uint32_t)ctx->stats.batch_total, (uint32_t)ctx->stats.batch_sysmem, 399bf215546Sopenharmony_ci (uint32_t)ctx->stats.batch_gmem, (uint32_t)ctx->stats.batch_nondraw, 400bf215546Sopenharmony_ci (uint32_t)ctx->stats.batch_restore); 401bf215546Sopenharmony_ci } 402bf215546Sopenharmony_ci} 403bf215546Sopenharmony_ci 404bf215546Sopenharmony_cistatic void 405bf215546Sopenharmony_cifd_set_debug_callback(struct pipe_context *pctx, 406bf215546Sopenharmony_ci const struct util_debug_callback *cb) 407bf215546Sopenharmony_ci{ 408bf215546Sopenharmony_ci struct fd_context *ctx = fd_context(pctx); 409bf215546Sopenharmony_ci struct fd_screen *screen = ctx->screen; 410bf215546Sopenharmony_ci 411bf215546Sopenharmony_ci util_queue_finish(&screen->compile_queue); 412bf215546Sopenharmony_ci 413bf215546Sopenharmony_ci if (cb) 414bf215546Sopenharmony_ci ctx->debug = *cb; 415bf215546Sopenharmony_ci else 416bf215546Sopenharmony_ci memset(&ctx->debug, 0, sizeof(ctx->debug)); 417bf215546Sopenharmony_ci} 418bf215546Sopenharmony_ci 419bf215546Sopenharmony_cistatic uint32_t 420bf215546Sopenharmony_cifd_get_reset_count(struct fd_context *ctx, bool per_context) 421bf215546Sopenharmony_ci{ 422bf215546Sopenharmony_ci uint64_t val; 423bf215546Sopenharmony_ci enum fd_param_id param = per_context ? FD_CTX_FAULTS : FD_GLOBAL_FAULTS; 424bf215546Sopenharmony_ci int ret = fd_pipe_get_param(ctx->pipe, param, &val); 425bf215546Sopenharmony_ci assert(!ret); 426bf215546Sopenharmony_ci return val; 427bf215546Sopenharmony_ci} 428bf215546Sopenharmony_ci 429bf215546Sopenharmony_cistatic enum pipe_reset_status 430bf215546Sopenharmony_cifd_get_device_reset_status(struct pipe_context *pctx) 431bf215546Sopenharmony_ci{ 432bf215546Sopenharmony_ci struct fd_context *ctx = fd_context(pctx); 433bf215546Sopenharmony_ci int context_faults = fd_get_reset_count(ctx, true); 434bf215546Sopenharmony_ci int global_faults = fd_get_reset_count(ctx, false); 435bf215546Sopenharmony_ci enum pipe_reset_status status; 436bf215546Sopenharmony_ci 437bf215546Sopenharmony_ci if (context_faults != ctx->context_reset_count) { 438bf215546Sopenharmony_ci status = PIPE_GUILTY_CONTEXT_RESET; 439bf215546Sopenharmony_ci } else if (global_faults != ctx->global_reset_count) { 440bf215546Sopenharmony_ci status = PIPE_INNOCENT_CONTEXT_RESET; 441bf215546Sopenharmony_ci } else { 442bf215546Sopenharmony_ci status = PIPE_NO_RESET; 443bf215546Sopenharmony_ci } 444bf215546Sopenharmony_ci 445bf215546Sopenharmony_ci ctx->context_reset_count = context_faults; 446bf215546Sopenharmony_ci ctx->global_reset_count = global_faults; 447bf215546Sopenharmony_ci 448bf215546Sopenharmony_ci return status; 449bf215546Sopenharmony_ci} 450bf215546Sopenharmony_ci 451bf215546Sopenharmony_cistatic void 452bf215546Sopenharmony_cifd_trace_record_ts(struct u_trace *ut, void *cs, void *timestamps, 453bf215546Sopenharmony_ci unsigned idx, bool end_of_pipe) 454bf215546Sopenharmony_ci{ 455bf215546Sopenharmony_ci struct fd_batch *batch = container_of(ut, struct fd_batch, trace); 456bf215546Sopenharmony_ci struct fd_ringbuffer *ring = cs; 457bf215546Sopenharmony_ci struct pipe_resource *buffer = timestamps; 458bf215546Sopenharmony_ci 459bf215546Sopenharmony_ci if (ring->cur == batch->last_timestamp_cmd) { 460bf215546Sopenharmony_ci uint64_t *ts = fd_bo_map(fd_resource(buffer)->bo); 461bf215546Sopenharmony_ci ts[idx] = U_TRACE_NO_TIMESTAMP; 462bf215546Sopenharmony_ci return; 463bf215546Sopenharmony_ci } 464bf215546Sopenharmony_ci 465bf215546Sopenharmony_ci unsigned ts_offset = idx * sizeof(uint64_t); 466bf215546Sopenharmony_ci batch->ctx->record_timestamp(ring, fd_resource(buffer)->bo, ts_offset); 467bf215546Sopenharmony_ci batch->last_timestamp_cmd = ring->cur; 468bf215546Sopenharmony_ci} 469bf215546Sopenharmony_ci 470bf215546Sopenharmony_cistatic uint64_t 471bf215546Sopenharmony_cifd_trace_read_ts(struct u_trace_context *utctx, 472bf215546Sopenharmony_ci void *timestamps, unsigned idx, void *flush_data) 473bf215546Sopenharmony_ci{ 474bf215546Sopenharmony_ci struct fd_context *ctx = 475bf215546Sopenharmony_ci container_of(utctx, struct fd_context, trace_context); 476bf215546Sopenharmony_ci struct pipe_resource *buffer = timestamps; 477bf215546Sopenharmony_ci struct fd_bo *ts_bo = fd_resource(buffer)->bo; 478bf215546Sopenharmony_ci 479bf215546Sopenharmony_ci /* Only need to stall on results for the first entry: */ 480bf215546Sopenharmony_ci if (idx == 0) { 481bf215546Sopenharmony_ci /* Avoid triggering deferred submits from flushing, since that 482bf215546Sopenharmony_ci * changes the behavior of what we are trying to measure: 483bf215546Sopenharmony_ci */ 484bf215546Sopenharmony_ci while (fd_bo_cpu_prep(ts_bo, ctx->pipe, FD_BO_PREP_NOSYNC)) 485bf215546Sopenharmony_ci usleep(10000); 486bf215546Sopenharmony_ci int ret = fd_bo_cpu_prep(ts_bo, ctx->pipe, FD_BO_PREP_READ); 487bf215546Sopenharmony_ci if (ret) 488bf215546Sopenharmony_ci return U_TRACE_NO_TIMESTAMP; 489bf215546Sopenharmony_ci } 490bf215546Sopenharmony_ci 491bf215546Sopenharmony_ci uint64_t *ts = fd_bo_map(ts_bo); 492bf215546Sopenharmony_ci 493bf215546Sopenharmony_ci /* Don't translate the no-timestamp marker: */ 494bf215546Sopenharmony_ci if (ts[idx] == U_TRACE_NO_TIMESTAMP) 495bf215546Sopenharmony_ci return U_TRACE_NO_TIMESTAMP; 496bf215546Sopenharmony_ci 497bf215546Sopenharmony_ci return ctx->ts_to_ns(ts[idx]); 498bf215546Sopenharmony_ci} 499bf215546Sopenharmony_ci 500bf215546Sopenharmony_cistatic void 501bf215546Sopenharmony_cifd_trace_delete_flush_data(struct u_trace_context *utctx, void *flush_data) 502bf215546Sopenharmony_ci{ 503bf215546Sopenharmony_ci /* We don't use flush_data at the moment. */ 504bf215546Sopenharmony_ci} 505bf215546Sopenharmony_ci 506bf215546Sopenharmony_ci/* TODO we could combine a few of these small buffers (solid_vbuf, 507bf215546Sopenharmony_ci * blit_texcoord_vbuf, and vsc_size_mem, into a single buffer and 508bf215546Sopenharmony_ci * save a tiny bit of memory 509bf215546Sopenharmony_ci */ 510bf215546Sopenharmony_ci 511bf215546Sopenharmony_cistatic struct pipe_resource * 512bf215546Sopenharmony_cicreate_solid_vertexbuf(struct pipe_context *pctx) 513bf215546Sopenharmony_ci{ 514bf215546Sopenharmony_ci static const float init_shader_const[] = { 515bf215546Sopenharmony_ci -1.000000f, +1.000000f, +1.000000f, +1.000000f, -1.000000f, +1.000000f, 516bf215546Sopenharmony_ci }; 517bf215546Sopenharmony_ci struct pipe_resource *prsc = 518bf215546Sopenharmony_ci pipe_buffer_create(pctx->screen, PIPE_BIND_CUSTOM, PIPE_USAGE_IMMUTABLE, 519bf215546Sopenharmony_ci sizeof(init_shader_const)); 520bf215546Sopenharmony_ci pipe_buffer_write(pctx, prsc, 0, sizeof(init_shader_const), 521bf215546Sopenharmony_ci init_shader_const); 522bf215546Sopenharmony_ci return prsc; 523bf215546Sopenharmony_ci} 524bf215546Sopenharmony_ci 525bf215546Sopenharmony_cistatic struct pipe_resource * 526bf215546Sopenharmony_cicreate_blit_texcoord_vertexbuf(struct pipe_context *pctx) 527bf215546Sopenharmony_ci{ 528bf215546Sopenharmony_ci struct pipe_resource *prsc = pipe_buffer_create( 529bf215546Sopenharmony_ci pctx->screen, PIPE_BIND_CUSTOM, PIPE_USAGE_DYNAMIC, 16); 530bf215546Sopenharmony_ci return prsc; 531bf215546Sopenharmony_ci} 532bf215546Sopenharmony_ci 533bf215546Sopenharmony_civoid 534bf215546Sopenharmony_cifd_context_setup_common_vbos(struct fd_context *ctx) 535bf215546Sopenharmony_ci{ 536bf215546Sopenharmony_ci struct pipe_context *pctx = &ctx->base; 537bf215546Sopenharmony_ci 538bf215546Sopenharmony_ci ctx->solid_vbuf = create_solid_vertexbuf(pctx); 539bf215546Sopenharmony_ci ctx->blit_texcoord_vbuf = create_blit_texcoord_vertexbuf(pctx); 540bf215546Sopenharmony_ci 541bf215546Sopenharmony_ci /* setup solid_vbuf_state: */ 542bf215546Sopenharmony_ci ctx->solid_vbuf_state.vtx = pctx->create_vertex_elements_state( 543bf215546Sopenharmony_ci pctx, 1, 544bf215546Sopenharmony_ci (struct pipe_vertex_element[]){{ 545bf215546Sopenharmony_ci .vertex_buffer_index = 0, 546bf215546Sopenharmony_ci .src_offset = 0, 547bf215546Sopenharmony_ci .src_format = PIPE_FORMAT_R32G32B32_FLOAT, 548bf215546Sopenharmony_ci }}); 549bf215546Sopenharmony_ci ctx->solid_vbuf_state.vertexbuf.count = 1; 550bf215546Sopenharmony_ci ctx->solid_vbuf_state.vertexbuf.vb[0].stride = 12; 551bf215546Sopenharmony_ci ctx->solid_vbuf_state.vertexbuf.vb[0].buffer.resource = ctx->solid_vbuf; 552bf215546Sopenharmony_ci 553bf215546Sopenharmony_ci /* setup blit_vbuf_state: */ 554bf215546Sopenharmony_ci ctx->blit_vbuf_state.vtx = pctx->create_vertex_elements_state( 555bf215546Sopenharmony_ci pctx, 2, 556bf215546Sopenharmony_ci (struct pipe_vertex_element[]){ 557bf215546Sopenharmony_ci { 558bf215546Sopenharmony_ci .vertex_buffer_index = 0, 559bf215546Sopenharmony_ci .src_offset = 0, 560bf215546Sopenharmony_ci .src_format = PIPE_FORMAT_R32G32_FLOAT, 561bf215546Sopenharmony_ci }, 562bf215546Sopenharmony_ci { 563bf215546Sopenharmony_ci .vertex_buffer_index = 1, 564bf215546Sopenharmony_ci .src_offset = 0, 565bf215546Sopenharmony_ci .src_format = PIPE_FORMAT_R32G32B32_FLOAT, 566bf215546Sopenharmony_ci }}); 567bf215546Sopenharmony_ci ctx->blit_vbuf_state.vertexbuf.count = 2; 568bf215546Sopenharmony_ci ctx->blit_vbuf_state.vertexbuf.vb[0].stride = 8; 569bf215546Sopenharmony_ci ctx->blit_vbuf_state.vertexbuf.vb[0].buffer.resource = 570bf215546Sopenharmony_ci ctx->blit_texcoord_vbuf; 571bf215546Sopenharmony_ci ctx->blit_vbuf_state.vertexbuf.vb[1].stride = 12; 572bf215546Sopenharmony_ci ctx->blit_vbuf_state.vertexbuf.vb[1].buffer.resource = ctx->solid_vbuf; 573bf215546Sopenharmony_ci} 574bf215546Sopenharmony_ci 575bf215546Sopenharmony_civoid 576bf215546Sopenharmony_cifd_context_cleanup_common_vbos(struct fd_context *ctx) 577bf215546Sopenharmony_ci{ 578bf215546Sopenharmony_ci struct pipe_context *pctx = &ctx->base; 579bf215546Sopenharmony_ci 580bf215546Sopenharmony_ci pctx->delete_vertex_elements_state(pctx, ctx->solid_vbuf_state.vtx); 581bf215546Sopenharmony_ci pctx->delete_vertex_elements_state(pctx, ctx->blit_vbuf_state.vtx); 582bf215546Sopenharmony_ci 583bf215546Sopenharmony_ci pipe_resource_reference(&ctx->solid_vbuf, NULL); 584bf215546Sopenharmony_ci pipe_resource_reference(&ctx->blit_texcoord_vbuf, NULL); 585bf215546Sopenharmony_ci} 586bf215546Sopenharmony_ci 587bf215546Sopenharmony_cistruct pipe_context * 588bf215546Sopenharmony_cifd_context_init(struct fd_context *ctx, struct pipe_screen *pscreen, 589bf215546Sopenharmony_ci void *priv, unsigned flags) 590bf215546Sopenharmony_ci disable_thread_safety_analysis 591bf215546Sopenharmony_ci{ 592bf215546Sopenharmony_ci struct fd_screen *screen = fd_screen(pscreen); 593bf215546Sopenharmony_ci struct pipe_context *pctx; 594bf215546Sopenharmony_ci unsigned prio = 1; 595bf215546Sopenharmony_ci 596bf215546Sopenharmony_ci /* lower numerical value == higher priority: */ 597bf215546Sopenharmony_ci if (FD_DBG(HIPRIO)) 598bf215546Sopenharmony_ci prio = 0; 599bf215546Sopenharmony_ci else if (flags & PIPE_CONTEXT_HIGH_PRIORITY) 600bf215546Sopenharmony_ci prio = 0; 601bf215546Sopenharmony_ci else if (flags & PIPE_CONTEXT_LOW_PRIORITY) 602bf215546Sopenharmony_ci prio = 2; 603bf215546Sopenharmony_ci 604bf215546Sopenharmony_ci /* Some of the stats will get printed out at context destroy, so 605bf215546Sopenharmony_ci * make sure they are collected: 606bf215546Sopenharmony_ci */ 607bf215546Sopenharmony_ci if (FD_DBG(BSTAT) || FD_DBG(MSGS)) 608bf215546Sopenharmony_ci ctx->stats_users++; 609bf215546Sopenharmony_ci 610bf215546Sopenharmony_ci ctx->flags = flags; 611bf215546Sopenharmony_ci ctx->screen = screen; 612bf215546Sopenharmony_ci ctx->pipe = fd_pipe_new2(screen->dev, FD_PIPE_3D, prio); 613bf215546Sopenharmony_ci 614bf215546Sopenharmony_ci ctx->in_fence_fd = -1; 615bf215546Sopenharmony_ci 616bf215546Sopenharmony_ci if (fd_device_version(screen->dev) >= FD_VERSION_ROBUSTNESS) { 617bf215546Sopenharmony_ci ctx->context_reset_count = fd_get_reset_count(ctx, true); 618bf215546Sopenharmony_ci ctx->global_reset_count = fd_get_reset_count(ctx, false); 619bf215546Sopenharmony_ci } 620bf215546Sopenharmony_ci 621bf215546Sopenharmony_ci simple_mtx_init(&ctx->gmem_lock, mtx_plain); 622bf215546Sopenharmony_ci 623bf215546Sopenharmony_ci /* need some sane default in case gallium frontends don't 624bf215546Sopenharmony_ci * set some state: 625bf215546Sopenharmony_ci */ 626bf215546Sopenharmony_ci ctx->sample_mask = 0xffff; 627bf215546Sopenharmony_ci ctx->active_queries = true; 628bf215546Sopenharmony_ci 629bf215546Sopenharmony_ci pctx = &ctx->base; 630bf215546Sopenharmony_ci pctx->screen = pscreen; 631bf215546Sopenharmony_ci pctx->priv = priv; 632bf215546Sopenharmony_ci pctx->flush = fd_context_flush; 633bf215546Sopenharmony_ci pctx->emit_string_marker = fd_emit_string_marker; 634bf215546Sopenharmony_ci pctx->set_debug_callback = fd_set_debug_callback; 635bf215546Sopenharmony_ci pctx->get_device_reset_status = fd_get_device_reset_status; 636bf215546Sopenharmony_ci pctx->create_fence_fd = fd_create_fence_fd; 637bf215546Sopenharmony_ci pctx->fence_server_sync = fd_fence_server_sync; 638bf215546Sopenharmony_ci pctx->fence_server_signal = fd_fence_server_signal; 639bf215546Sopenharmony_ci pctx->texture_barrier = fd_texture_barrier; 640bf215546Sopenharmony_ci pctx->memory_barrier = fd_memory_barrier; 641bf215546Sopenharmony_ci 642bf215546Sopenharmony_ci pctx->stream_uploader = u_upload_create_default(pctx); 643bf215546Sopenharmony_ci if (!pctx->stream_uploader) 644bf215546Sopenharmony_ci goto fail; 645bf215546Sopenharmony_ci pctx->const_uploader = pctx->stream_uploader; 646bf215546Sopenharmony_ci 647bf215546Sopenharmony_ci slab_create_child(&ctx->transfer_pool, &screen->transfer_pool); 648bf215546Sopenharmony_ci slab_create_child(&ctx->transfer_pool_unsync, &screen->transfer_pool); 649bf215546Sopenharmony_ci 650bf215546Sopenharmony_ci fd_draw_init(pctx); 651bf215546Sopenharmony_ci fd_resource_context_init(pctx); 652bf215546Sopenharmony_ci fd_query_context_init(pctx); 653bf215546Sopenharmony_ci fd_texture_init(pctx); 654bf215546Sopenharmony_ci fd_state_init(pctx); 655bf215546Sopenharmony_ci 656bf215546Sopenharmony_ci ctx->blitter = util_blitter_create(pctx); 657bf215546Sopenharmony_ci if (!ctx->blitter) 658bf215546Sopenharmony_ci goto fail; 659bf215546Sopenharmony_ci 660bf215546Sopenharmony_ci list_inithead(&ctx->hw_active_queries); 661bf215546Sopenharmony_ci list_inithead(&ctx->acc_active_queries); 662bf215546Sopenharmony_ci 663bf215546Sopenharmony_ci fd_screen_lock(ctx->screen); 664bf215546Sopenharmony_ci ctx->seqno = ++screen->ctx_seqno; 665bf215546Sopenharmony_ci list_add(&ctx->node, &ctx->screen->context_list); 666bf215546Sopenharmony_ci fd_screen_unlock(ctx->screen); 667bf215546Sopenharmony_ci 668bf215546Sopenharmony_ci ctx->current_scissor = &ctx->disabled_scissor; 669bf215546Sopenharmony_ci 670bf215546Sopenharmony_ci fd_gpu_tracepoint_config_variable(); 671bf215546Sopenharmony_ci u_trace_pipe_context_init(&ctx->trace_context, pctx, 672bf215546Sopenharmony_ci fd_trace_record_ts, 673bf215546Sopenharmony_ci fd_trace_read_ts, 674bf215546Sopenharmony_ci fd_trace_delete_flush_data); 675bf215546Sopenharmony_ci 676bf215546Sopenharmony_ci fd_autotune_init(&ctx->autotune, screen->dev); 677bf215546Sopenharmony_ci 678bf215546Sopenharmony_ci return pctx; 679bf215546Sopenharmony_ci 680bf215546Sopenharmony_cifail: 681bf215546Sopenharmony_ci pctx->destroy(pctx); 682bf215546Sopenharmony_ci return NULL; 683bf215546Sopenharmony_ci} 684bf215546Sopenharmony_ci 685bf215546Sopenharmony_cistruct pipe_context * 686bf215546Sopenharmony_cifd_context_init_tc(struct pipe_context *pctx, unsigned flags) 687bf215546Sopenharmony_ci{ 688bf215546Sopenharmony_ci struct fd_context *ctx = fd_context(pctx); 689bf215546Sopenharmony_ci 690bf215546Sopenharmony_ci if (!(flags & PIPE_CONTEXT_PREFER_THREADED)) 691bf215546Sopenharmony_ci return pctx; 692bf215546Sopenharmony_ci 693bf215546Sopenharmony_ci /* Clover (compute-only) is unsupported. */ 694bf215546Sopenharmony_ci if (flags & PIPE_CONTEXT_COMPUTE_ONLY) 695bf215546Sopenharmony_ci return pctx; 696bf215546Sopenharmony_ci 697bf215546Sopenharmony_ci struct pipe_context *tc = threaded_context_create( 698bf215546Sopenharmony_ci pctx, &ctx->screen->transfer_pool, 699bf215546Sopenharmony_ci fd_replace_buffer_storage, 700bf215546Sopenharmony_ci &(struct threaded_context_options){ 701bf215546Sopenharmony_ci .create_fence = fd_fence_create_unflushed, 702bf215546Sopenharmony_ci .is_resource_busy = fd_resource_busy, 703bf215546Sopenharmony_ci .unsynchronized_get_device_reset_status = true, 704bf215546Sopenharmony_ci }, 705bf215546Sopenharmony_ci &ctx->tc); 706bf215546Sopenharmony_ci 707bf215546Sopenharmony_ci if (tc && tc != pctx) 708bf215546Sopenharmony_ci threaded_context_init_bytes_mapped_limit((struct threaded_context *)tc, 16); 709bf215546Sopenharmony_ci 710bf215546Sopenharmony_ci return tc; 711bf215546Sopenharmony_ci} 712