1/* 2 * Copyright (c) 2017 Etnaviv Project 3 * Copyright (C) 2017 Zodiac Inflight Innovations 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sub license, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the 13 * next paragraph) shall be included in all copies or substantial portions 14 * of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: 25 * Rob Clark <robclark@freedesktop.org> 26 * Christian Gmeiner <christian.gmeiner@gmail.com> 27 */ 28 29#include "util/u_inlines.h" 30#include "util/u_memory.h" 31 32#include "etnaviv_context.h" 33#include "etnaviv_debug.h" 34#include "etnaviv_emit.h" 35#include "etnaviv_query_hw.h" 36#include "etnaviv_screen.h" 37 38/* 39 * Occlusion Query: 40 * 41 * OCCLUSION_COUNTER and OCCLUSION_PREDICATE differ only in how they 42 * interpret results 43 */ 44 45static void 46occlusion_start(struct etna_hw_query *hq, struct etna_context *ctx) 47{ 48 struct etna_resource *rsc = etna_resource(hq->prsc); 49 struct etna_reloc r = { 50 .bo = rsc->bo, 51 .flags = ETNA_RELOC_WRITE 52 }; 53 54 if (hq->samples > 63) { 55 hq->samples = 63; 56 BUG("samples overflow"); 57 } 58 59 r.offset = hq->samples * 8; /* 64bit value */ 60 61 etna_set_state_reloc(ctx->stream, VIVS_GL_OCCLUSION_QUERY_ADDR, &r); 62} 63 64static void 65occlusion_stop(struct etna_hw_query *hq, struct etna_context *ctx) 66{ 67 /* 0x1DF5E76 is the value used by blob - but any random value will work */ 68 etna_set_state(ctx->stream, VIVS_GL_OCCLUSION_QUERY_CONTROL, 0x1DF5E76); 69} 70 71static void 72occlusion_suspend(struct etna_hw_query *hq, struct etna_context *ctx) 73{ 74 occlusion_stop(hq, ctx); 75} 76 77static void 78occlusion_resume(struct etna_hw_query *hq, struct etna_context *ctx) 79{ 80 hq->samples++; 81 occlusion_start(hq, ctx); 82} 83 84static void 85occlusion_result(struct etna_hw_query *hq, void *buf, 86 union pipe_query_result *result) 87{ 88 uint64_t sum = 0; 89 uint64_t *ptr = (uint64_t *)buf; 90 91 for (unsigned i = 0; i <= hq->samples; i++) 92 sum += *(ptr + i); 93 94 if (hq->base.type == PIPE_QUERY_OCCLUSION_COUNTER) 95 result->u64 = sum; 96 else 97 result->b = !!sum; 98} 99 100static void 101etna_hw_destroy_query(struct etna_context *ctx, struct etna_query *q) 102{ 103 struct etna_hw_query *hq = etna_hw_query(q); 104 105 pipe_resource_reference(&hq->prsc, NULL); 106 list_del(&hq->node); 107 108 FREE(hq); 109} 110 111static const struct etna_hw_sample_provider occlusion_provider = { 112 .start = occlusion_start, 113 .stop = occlusion_stop, 114 .suspend = occlusion_suspend, 115 .resume = occlusion_resume, 116 .result = occlusion_result, 117}; 118 119static void 120realloc_query_bo(struct etna_context *ctx, struct etna_hw_query *hq) 121{ 122 struct etna_resource *rsc; 123 void *map; 124 125 pipe_resource_reference(&hq->prsc, NULL); 126 127 /* allocate resource with space for 64 * 64bit values */ 128 hq->prsc = pipe_buffer_create(&ctx->screen->base, PIPE_BIND_QUERY_BUFFER, 129 0, 0x1000); 130 131 /* don't assume the buffer is zero-initialized */ 132 rsc = etna_resource(hq->prsc); 133 134 etna_bo_cpu_prep(rsc->bo, DRM_ETNA_PREP_WRITE); 135 136 map = etna_bo_map(rsc->bo); 137 memset(map, 0, 0x1000); 138 etna_bo_cpu_fini(rsc->bo); 139} 140 141static boolean 142etna_hw_begin_query(struct etna_context *ctx, struct etna_query *q) 143{ 144 struct etna_hw_query *hq = etna_hw_query(q); 145 const struct etna_hw_sample_provider *p = hq->provider; 146 147 /* ->begin_query() discards previous results, so realloc bo */ 148 realloc_query_bo(ctx, hq); 149 150 p->start(hq, ctx); 151 152 /* add to active list */ 153 assert(list_empty(&hq->node)); 154 list_addtail(&hq->node, &ctx->active_hw_queries); 155 156 return true; 157} 158 159static void 160etna_hw_end_query(struct etna_context *ctx, struct etna_query *q) 161{ 162 struct etna_hw_query *hq = etna_hw_query(q); 163 const struct etna_hw_sample_provider *p = hq->provider; 164 165 p->stop(hq, ctx); 166 167 /* remove from active list */ 168 list_delinit(&hq->node); 169} 170 171static boolean 172etna_hw_get_query_result(struct etna_context *ctx, struct etna_query *q, 173 boolean wait, union pipe_query_result *result) 174{ 175 struct etna_hw_query *hq = etna_hw_query(q); 176 struct etna_resource *rsc = etna_resource(hq->prsc); 177 const struct etna_hw_sample_provider *p = hq->provider; 178 179 assert(LIST_IS_EMPTY(&hq->node)); 180 181 if (!wait) { 182 int ret; 183 184 if (rsc->status & ETNA_PENDING_WRITE) { 185 /* piglit spec@arb_occlusion_query@occlusion_query_conform 186 * test, and silly apps perhaps, get stuck in a loop trying 187 * to get query result forever with wait==false.. we don't 188 * wait to flush unnecessarily but we also don't want to 189 * spin forever. 190 */ 191 if (hq->no_wait_cnt++ > 5) 192 ctx->base.flush(&ctx->base, NULL, 0); 193 return false; 194 } 195 196 ret = etna_bo_cpu_prep(rsc->bo, DRM_ETNA_PREP_READ | DRM_ETNA_PREP_NOSYNC); 197 if (ret) 198 return false; 199 200 etna_bo_cpu_fini(rsc->bo); 201 } 202 203 /* flush that GPU executes all query related actions */ 204 ctx->base.flush(&ctx->base, NULL, 0); 205 206 /* get the result */ 207 etna_bo_cpu_prep(rsc->bo, DRM_ETNA_PREP_READ); 208 209 void *ptr = etna_bo_map(rsc->bo); 210 p->result(hq, ptr, result); 211 212 etna_bo_cpu_fini(rsc->bo); 213 214 return true; 215} 216 217static const struct etna_query_funcs hw_query_funcs = { 218 .destroy_query = etna_hw_destroy_query, 219 .begin_query = etna_hw_begin_query, 220 .end_query = etna_hw_end_query, 221 .get_query_result = etna_hw_get_query_result, 222}; 223 224static inline const struct etna_hw_sample_provider * 225query_sample_provider(unsigned query_type) 226{ 227 switch (query_type) { 228 case PIPE_QUERY_OCCLUSION_COUNTER: 229 /* fallthrough */ 230 case PIPE_QUERY_OCCLUSION_PREDICATE: 231 /* fallthrough */ 232 case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE: 233 return &occlusion_provider; 234 default: 235 return NULL; 236 } 237} 238 239struct etna_query * 240etna_hw_create_query(struct etna_context *ctx, unsigned query_type) 241{ 242 struct etna_hw_query *hq; 243 struct etna_query *q; 244 const struct etna_hw_sample_provider *p; 245 246 p = query_sample_provider(query_type); 247 if (!p) 248 return NULL; 249 250 hq = CALLOC_STRUCT(etna_hw_query); 251 if (!hq) 252 return NULL; 253 254 hq->provider = p; 255 256 list_inithead(&hq->node); 257 258 q = &hq->base; 259 q->funcs = &hw_query_funcs; 260 q->type = query_type; 261 262 return q; 263} 264