1 /*
2  * Copyright 2015 Samuel Pitoiset
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 shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "nv50/nv50_context.h"
24 #include "nv50/nv50_query_hw_metric.h"
25 #include "nv50/nv50_query_hw_sm.h"
26 
27 /* === PERFORMANCE MONITORING METRICS for NV84+ === */
28 static const char *nv50_hw_metric_names[] =
29 {
30    "metric-branch_efficiency",
31 };
32 
33 struct nv50_hw_metric_query_cfg {
34    uint32_t queries[4];
35    uint32_t num_queries;
36 };
37 
38 #define _SM(n) NV50_HW_SM_QUERY(NV50_HW_SM_QUERY_ ##n)
39 #define _M(n, c) [NV50_HW_METRIC_QUERY_##n] = c
40 
41 /* ==== Compute capability 1.1 (G84+) ==== */
42 static const struct nv50_hw_metric_query_cfg
43 sm11_branch_efficiency =
44 {
45    .queries[0]  = _SM(BRANCH),
46    .queries[1]  = _SM(DIVERGENT_BRANCH),
47    .num_queries = 2,
48 };
49 
50 static const struct nv50_hw_metric_query_cfg *sm11_hw_metric_queries[] =
51 {
52    _M(BRANCH_EFFICIENCY, &sm11_branch_efficiency),
53 };
54 
55 #undef _SM
56 #undef _M
57 
58 static const struct nv50_hw_metric_query_cfg *
nv50_hw_metric_query_get_cfg(struct nv50_context * nv50,struct nv50_hw_query * hq)59 nv50_hw_metric_query_get_cfg(struct nv50_context *nv50,
60                              struct nv50_hw_query *hq)
61 {
62    struct nv50_query *q = &hq->base;
63    return sm11_hw_metric_queries[q->type - NV50_HW_METRIC_QUERY(0)];
64 }
65 
66 static void
nv50_hw_metric_destroy_query(struct nv50_context * nv50,struct nv50_hw_query * hq)67 nv50_hw_metric_destroy_query(struct nv50_context *nv50,
68                              struct nv50_hw_query *hq)
69 {
70    struct nv50_hw_metric_query *hmq = nv50_hw_metric_query(hq);
71    unsigned i;
72 
73    for (i = 0; i < hmq->num_queries; i++)
74       if (hmq->queries[i]->funcs->destroy_query)
75          hmq->queries[i]->funcs->destroy_query(nv50, hmq->queries[i]);
76    FREE(hmq);
77 }
78 
79 static boolean
nv50_hw_metric_begin_query(struct nv50_context * nv50,struct nv50_hw_query * hq)80 nv50_hw_metric_begin_query(struct nv50_context *nv50, struct nv50_hw_query *hq)
81 {
82    struct nv50_hw_metric_query *hmq = nv50_hw_metric_query(hq);
83    boolean ret = false;
84    unsigned i;
85 
86    for (i = 0; i < hmq->num_queries; i++) {
87       ret = hmq->queries[i]->funcs->begin_query(nv50, hmq->queries[i]);
88       if (!ret)
89          return ret;
90    }
91    return ret;
92 }
93 
94 static void
nv50_hw_metric_end_query(struct nv50_context * nv50,struct nv50_hw_query * hq)95 nv50_hw_metric_end_query(struct nv50_context *nv50, struct nv50_hw_query *hq)
96 {
97    struct nv50_hw_metric_query *hmq = nv50_hw_metric_query(hq);
98    unsigned i;
99 
100    for (i = 0; i < hmq->num_queries; i++)
101       hmq->queries[i]->funcs->end_query(nv50, hmq->queries[i]);
102 }
103 
104 static uint64_t
sm11_hw_metric_calc_result(struct nv50_hw_query * hq,uint64_t res64[8])105 sm11_hw_metric_calc_result(struct nv50_hw_query *hq, uint64_t res64[8])
106 {
107    switch (hq->base.type - NV50_HW_METRIC_QUERY(0)) {
108    case NV50_HW_METRIC_QUERY_BRANCH_EFFICIENCY:
109       /* (branch / (branch + divergent_branch)) * 100 */
110       if (res64[0] + res64[1])
111          return (res64[0] / (double)(res64[0] + res64[1])) * 100;
112       break;
113    default:
114       debug_printf("invalid metric type: %d\n",
115                    hq->base.type - NV50_HW_METRIC_QUERY(0));
116       break;
117    }
118    return 0;
119 }
120 
121 static boolean
nv50_hw_metric_get_query_result(struct nv50_context * nv50,struct nv50_hw_query * hq,boolean wait,union pipe_query_result * result)122 nv50_hw_metric_get_query_result(struct nv50_context *nv50,
123                                 struct nv50_hw_query *hq, boolean wait,
124                                 union pipe_query_result *result)
125 {
126    struct nv50_hw_metric_query *hmq = nv50_hw_metric_query(hq);
127    union pipe_query_result results[4] = {};
128    uint64_t res64[4] = {};
129    boolean ret = false;
130    unsigned i;
131 
132    for (i = 0; i < hmq->num_queries; i++) {
133       ret = hmq->queries[i]->funcs->get_query_result(nv50, hmq->queries[i],
134                                                      wait, &results[i]);
135       if (!ret)
136          return ret;
137       res64[i] = *(uint64_t *)&results[i];
138    }
139 
140    *(uint64_t *)result = sm11_hw_metric_calc_result(hq, res64);
141    return ret;
142 }
143 
144 static const struct nv50_hw_query_funcs hw_metric_query_funcs = {
145    .destroy_query = nv50_hw_metric_destroy_query,
146    .begin_query = nv50_hw_metric_begin_query,
147    .end_query = nv50_hw_metric_end_query,
148    .get_query_result = nv50_hw_metric_get_query_result,
149 };
150 
151 struct nv50_hw_query *
nv50_hw_metric_create_query(struct nv50_context * nv50,unsigned type)152 nv50_hw_metric_create_query(struct nv50_context *nv50, unsigned type)
153 {
154    const struct nv50_hw_metric_query_cfg *cfg;
155    struct nv50_hw_metric_query *hmq;
156    struct nv50_hw_query *hq;
157    unsigned i;
158 
159    if (type < NV50_HW_METRIC_QUERY(0) || type > NV50_HW_METRIC_QUERY_LAST)
160       return NULL;
161 
162    hmq = CALLOC_STRUCT(nv50_hw_metric_query);
163    if (!hmq)
164       return NULL;
165 
166    hq = &hmq->base;
167    hq->funcs = &hw_metric_query_funcs;
168    hq->base.type = type;
169 
170    cfg = nv50_hw_metric_query_get_cfg(nv50, hq);
171 
172    for (i = 0; i < cfg->num_queries; i++) {
173       hmq->queries[i] = nv50_hw_sm_create_query(nv50, cfg->queries[i]);
174       if (!hmq->queries[i]) {
175          nv50_hw_metric_destroy_query(nv50, hq);
176          return NULL;
177       }
178       hmq->num_queries++;
179    }
180 
181    return hq;
182 }
183 
184 int
nv50_hw_metric_get_driver_query_info(struct nv50_screen * screen,unsigned id,struct pipe_driver_query_info * info)185 nv50_hw_metric_get_driver_query_info(struct nv50_screen *screen, unsigned id,
186                                      struct pipe_driver_query_info *info)
187 {
188    int count = 0;
189 
190    if (screen->compute)
191       if (screen->base.class_3d >= NV84_3D_CLASS)
192          count += NV50_HW_METRIC_QUERY_COUNT;
193 
194    if (!info)
195       return count;
196 
197    if (id < count) {
198       if (screen->compute) {
199          if (screen->base.class_3d >= NV84_3D_CLASS) {
200             info->name = nv50_hw_metric_names[id];
201             info->query_type = NV50_HW_METRIC_QUERY(id);
202             info->group_id = NV50_HW_METRIC_QUERY_GROUP;
203             return 1;
204          }
205       }
206    }
207    return 0;
208 }
209