1/* 2 * Copyright (C) 2017 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/u_memory.h" 28#include "util/u_inlines.h" 29 30#include "freedreno_query_acc.h" 31#include "freedreno_context.h" 32#include "freedreno_resource.h" 33#include "freedreno_util.h" 34 35 36static bool 37is_active(struct fd_acc_query *aq, enum fd_render_stage stage) 38{ 39 return !!(aq->provider->active & stage); 40} 41 42static void 43fd_acc_destroy_query(struct fd_context *ctx, struct fd_query *q) 44{ 45 struct fd_acc_query *aq = fd_acc_query(q); 46 47 DBG("%p: active=%d", q, q->active); 48 49 pipe_resource_reference(&aq->prsc, NULL); 50 list_del(&aq->node); 51 52 free(aq->query_data); 53 free(aq); 54} 55 56static void 57realloc_query_bo(struct fd_context *ctx, struct fd_acc_query *aq) 58{ 59 struct fd_resource *rsc; 60 void *map; 61 62 pipe_resource_reference(&aq->prsc, NULL); 63 64 aq->prsc = pipe_buffer_create(&ctx->screen->base, 65 PIPE_BIND_QUERY_BUFFER, 0, 0x1000); 66 67 /* don't assume the buffer is zero-initialized: */ 68 rsc = fd_resource(aq->prsc); 69 70 fd_bo_cpu_prep(rsc->bo, ctx->pipe, DRM_FREEDRENO_PREP_WRITE); 71 72 map = fd_bo_map(rsc->bo); 73 memset(map, 0, aq->size); 74 fd_bo_cpu_fini(rsc->bo); 75} 76 77static boolean 78fd_acc_begin_query(struct fd_context *ctx, struct fd_query *q) 79{ 80 struct fd_batch *batch = fd_context_batch(ctx); 81 struct fd_acc_query *aq = fd_acc_query(q); 82 const struct fd_acc_sample_provider *p = aq->provider; 83 84 DBG("%p: active=%d", q, q->active); 85 86 /* ->begin_query() discards previous results, so realloc bo: */ 87 realloc_query_bo(ctx, aq); 88 89 /* then resume query if needed to collect first sample: */ 90 if (batch && is_active(aq, batch->stage)) 91 p->resume(aq, batch); 92 93 /* add to active list: */ 94 assert(list_empty(&aq->node)); 95 list_addtail(&aq->node, &ctx->acc_active_queries); 96 97 return true; 98} 99 100static void 101fd_acc_end_query(struct fd_context *ctx, struct fd_query *q) 102{ 103 struct fd_batch *batch = fd_context_batch(ctx); 104 struct fd_acc_query *aq = fd_acc_query(q); 105 const struct fd_acc_sample_provider *p = aq->provider; 106 107 DBG("%p: active=%d", q, q->active); 108 109 if (batch && is_active(aq, batch->stage)) 110 p->pause(aq, batch); 111 112 /* remove from active list: */ 113 list_delinit(&aq->node); 114} 115 116static boolean 117fd_acc_get_query_result(struct fd_context *ctx, struct fd_query *q, 118 boolean wait, union pipe_query_result *result) 119{ 120 struct fd_acc_query *aq = fd_acc_query(q); 121 const struct fd_acc_sample_provider *p = aq->provider; 122 struct fd_resource *rsc = fd_resource(aq->prsc); 123 124 DBG("%p: wait=%d, active=%d", q, wait, q->active); 125 126 assert(LIST_IS_EMPTY(&aq->node)); 127 128 /* if !wait, then check the last sample (the one most likely to 129 * not be ready yet) and bail if it is not ready: 130 */ 131 if (!wait) { 132 int ret; 133 134 if (pending(rsc, false)) { 135 /* piglit spec@arb_occlusion_query@occlusion_query_conform 136 * test, and silly apps perhaps, get stuck in a loop trying 137 * to get query result forever with wait==false.. we don't 138 * wait to flush unnecessarily but we also don't want to 139 * spin forever: 140 */ 141 if (aq->no_wait_cnt++ > 5) 142 fd_batch_flush(rsc->write_batch, false, false); 143 return false; 144 } 145 146 ret = fd_bo_cpu_prep(rsc->bo, ctx->pipe, 147 DRM_FREEDRENO_PREP_READ | DRM_FREEDRENO_PREP_NOSYNC); 148 if (ret) 149 return false; 150 151 fd_bo_cpu_fini(rsc->bo); 152 } 153 154 if (rsc->write_batch) 155 fd_batch_flush(rsc->write_batch, true, false); 156 157 /* get the result: */ 158 fd_bo_cpu_prep(rsc->bo, ctx->pipe, DRM_FREEDRENO_PREP_READ); 159 160 void *ptr = fd_bo_map(rsc->bo); 161 p->result(aq, ptr, result); 162 fd_bo_cpu_fini(rsc->bo); 163 164 return true; 165} 166 167static const struct fd_query_funcs acc_query_funcs = { 168 .destroy_query = fd_acc_destroy_query, 169 .begin_query = fd_acc_begin_query, 170 .end_query = fd_acc_end_query, 171 .get_query_result = fd_acc_get_query_result, 172}; 173 174struct fd_query * 175fd_acc_create_query2(struct fd_context *ctx, unsigned query_type, 176 const struct fd_acc_sample_provider *provider) 177{ 178 struct fd_acc_query *aq; 179 struct fd_query *q; 180 181 aq = CALLOC_STRUCT(fd_acc_query); 182 if (!aq) 183 return NULL; 184 185 DBG("%p: query_type=%u", aq, query_type); 186 187 aq->provider = provider; 188 aq->size = provider->size; 189 190 list_inithead(&aq->node); 191 192 q = &aq->base; 193 q->funcs = &acc_query_funcs; 194 q->type = query_type; 195 196 return q; 197} 198 199struct fd_query * 200fd_acc_create_query(struct fd_context *ctx, unsigned query_type) 201{ 202 int idx = pidx(query_type); 203 204 if ((idx < 0) || !ctx->acc_sample_providers[idx]) 205 return NULL; 206 207 return fd_acc_create_query2(ctx, query_type, 208 ctx->acc_sample_providers[idx]); 209} 210 211void 212fd_acc_query_set_stage(struct fd_batch *batch, enum fd_render_stage stage) 213{ 214 if (stage != batch->stage) { 215 struct fd_acc_query *aq; 216 LIST_FOR_EACH_ENTRY(aq, &batch->ctx->acc_active_queries, node) { 217 const struct fd_acc_sample_provider *p = aq->provider; 218 219 bool was_active = is_active(aq, batch->stage); 220 bool now_active = is_active(aq, stage); 221 222 if (now_active && !was_active) 223 p->resume(aq, batch); 224 else if (was_active && !now_active) 225 p->pause(aq, batch); 226 } 227 } 228} 229 230void 231fd_acc_query_register_provider(struct pipe_context *pctx, 232 const struct fd_acc_sample_provider *provider) 233{ 234 struct fd_context *ctx = fd_context(pctx); 235 int idx = pidx(provider->query_type); 236 237 assert((0 <= idx) && (idx < MAX_HW_SAMPLE_PROVIDERS)); 238 assert(!ctx->acc_sample_providers[idx]); 239 240 ctx->acc_sample_providers[idx] = provider; 241} 242