xref: /aosp_15_r20/external/mesa3d/src/gallium/frontends/nine/query9.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright 2011 Joakim Sindholt <[email protected]>
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include "device9.h"
7 #include "nine_state.h"
8 #include "query9.h"
9 #include "nine_helpers.h"
10 #include "pipe/p_screen.h"
11 #include "pipe/p_context.h"
12 #include "util/u_math.h"
13 #include "nine_dump.h"
14 
15 #define DBG_CHANNEL DBG_QUERY
16 
17 static inline unsigned
d3dquerytype_to_pipe_query(struct pipe_screen * screen,D3DQUERYTYPE type)18 d3dquerytype_to_pipe_query(struct pipe_screen *screen, D3DQUERYTYPE type)
19 {
20     switch (type) {
21     case D3DQUERYTYPE_EVENT:
22         return PIPE_QUERY_GPU_FINISHED;
23     case D3DQUERYTYPE_OCCLUSION:
24         return screen->get_param(screen, PIPE_CAP_OCCLUSION_QUERY) ?
25                PIPE_QUERY_OCCLUSION_COUNTER : PIPE_QUERY_TYPES;
26     case D3DQUERYTYPE_TIMESTAMP:
27         return screen->get_param(screen, PIPE_CAP_QUERY_TIMESTAMP) ?
28                PIPE_QUERY_TIMESTAMP : PIPE_QUERY_TYPES;
29     case D3DQUERYTYPE_TIMESTAMPDISJOINT:
30     case D3DQUERYTYPE_TIMESTAMPFREQ:
31         return screen->get_param(screen, PIPE_CAP_QUERY_TIMESTAMP) ?
32                PIPE_QUERY_TIMESTAMP_DISJOINT : PIPE_QUERY_TYPES;
33     case D3DQUERYTYPE_VERTEXSTATS:
34         return screen->get_param(screen,
35                                  PIPE_CAP_QUERY_PIPELINE_STATISTICS) ?
36                PIPE_QUERY_PIPELINE_STATISTICS : PIPE_QUERY_TYPES;
37     default:
38         return PIPE_QUERY_TYPES; /* Query not supported */
39     }
40 }
41 
42 #define GET_DATA_SIZE_CASE2(a, b) case D3DQUERYTYPE_##a: return sizeof(D3DDEVINFO_##b)
43 #define GET_DATA_SIZE_CASET(a, b) case D3DQUERYTYPE_##a: return sizeof(b)
44 static inline DWORD
nine_query_result_size(D3DQUERYTYPE type)45 nine_query_result_size(D3DQUERYTYPE type)
46 {
47     switch (type) {
48     GET_DATA_SIZE_CASE2(VERTEXSTATS, D3DVERTEXSTATS);
49     GET_DATA_SIZE_CASET(EVENT, BOOL);
50     GET_DATA_SIZE_CASET(OCCLUSION, DWORD);
51     GET_DATA_SIZE_CASET(TIMESTAMP, UINT64);
52     GET_DATA_SIZE_CASET(TIMESTAMPDISJOINT, BOOL);
53     GET_DATA_SIZE_CASET(TIMESTAMPFREQ, UINT64);
54     default:
55         assert(0);
56         return 0;
57     }
58 }
59 
60 HRESULT
nine_is_query_supported(struct pipe_screen * screen,D3DQUERYTYPE type)61 nine_is_query_supported(struct pipe_screen *screen, D3DQUERYTYPE type)
62 {
63     const unsigned ptype = d3dquerytype_to_pipe_query(screen, type);
64 
65     user_assert(ptype != ~0, D3DERR_INVALIDCALL);
66 
67     if (ptype == PIPE_QUERY_TYPES) {
68         DBG("Query type %u (%s) not supported.\n",
69             type, nine_D3DQUERYTYPE_to_str(type));
70         return D3DERR_NOTAVAILABLE;
71     }
72     return D3D_OK;
73 }
74 
75 HRESULT
NineQuery9_ctor(struct NineQuery9 * This,struct NineUnknownParams * pParams,D3DQUERYTYPE Type)76 NineQuery9_ctor( struct NineQuery9 *This,
77                  struct NineUnknownParams *pParams,
78                  D3DQUERYTYPE Type )
79 {
80     struct NineDevice9 *device = pParams->device;
81     const unsigned ptype = d3dquerytype_to_pipe_query(device->screen, Type);
82     HRESULT hr;
83 
84     DBG("This=%p pParams=%p Type=%d\n", This, pParams, Type);
85 
86     hr = NineUnknown_ctor(&This->base, pParams);
87     if (FAILED(hr))
88         return hr;
89 
90     This->state = NINE_QUERY_STATE_FRESH;
91     This->type = Type;
92 
93     user_assert(ptype != ~0, D3DERR_INVALIDCALL);
94 
95     if (ptype < PIPE_QUERY_TYPES) {
96         This->pq = nine_context_create_query(device, ptype);
97         if (!This->pq)
98             return E_OUTOFMEMORY;
99     } else {
100         assert(0); /* we have checked this case before */
101     }
102 
103     This->instant =
104         Type == D3DQUERYTYPE_EVENT ||
105         Type == D3DQUERYTYPE_RESOURCEMANAGER ||
106         Type == D3DQUERYTYPE_TIMESTAMP ||
107         Type == D3DQUERYTYPE_TIMESTAMPFREQ ||
108         Type == D3DQUERYTYPE_VCACHE ||
109         Type == D3DQUERYTYPE_VERTEXSTATS;
110 
111     This->result_size = nine_query_result_size(Type);
112 
113     return D3D_OK;
114 }
115 
116 void
NineQuery9_dtor(struct NineQuery9 * This)117 NineQuery9_dtor( struct NineQuery9 *This )
118 {
119     struct NineDevice9 *device = This->base.device;
120 
121     DBG("This=%p\n", This);
122 
123     if (This->pq) {
124         if (This->state == NINE_QUERY_STATE_RUNNING)
125             nine_context_end_query(device, &This->counter, This->pq);
126         nine_context_destroy_query(device, This->pq);
127     }
128 
129     NineUnknown_dtor(&This->base);
130 }
131 
132 D3DQUERYTYPE NINE_WINAPI
NineQuery9_GetType(struct NineQuery9 * This)133 NineQuery9_GetType( struct NineQuery9 *This )
134 {
135     return This->type;
136 }
137 
138 DWORD NINE_WINAPI
NineQuery9_GetDataSize(struct NineQuery9 * This)139 NineQuery9_GetDataSize( struct NineQuery9 *This )
140 {
141     return This->result_size;
142 }
143 
144 HRESULT NINE_WINAPI
NineQuery9_Issue(struct NineQuery9 * This,DWORD dwIssueFlags)145 NineQuery9_Issue( struct NineQuery9 *This,
146                   DWORD dwIssueFlags )
147 {
148     struct NineDevice9 *device = This->base.device;
149 
150     DBG("This=%p dwIssueFlags=%d\n", This, dwIssueFlags);
151 
152     user_assert((dwIssueFlags == D3DISSUE_BEGIN) ||
153                 (dwIssueFlags == 0) ||
154                 (dwIssueFlags == D3DISSUE_END), D3DERR_INVALIDCALL);
155 
156     /* Wine tests: always return D3D_OK on D3DISSUE_BEGIN
157      * even when the call is supposed to be forbidden */
158     if (dwIssueFlags == D3DISSUE_BEGIN && This->instant)
159         return D3D_OK;
160 
161     if (dwIssueFlags == D3DISSUE_BEGIN) {
162         if (This->state == NINE_QUERY_STATE_RUNNING)
163             nine_context_end_query(device, &This->counter, This->pq);
164         nine_context_begin_query(device, &This->counter, This->pq);
165         This->state = NINE_QUERY_STATE_RUNNING;
166     } else {
167         if (This->state != NINE_QUERY_STATE_RUNNING &&
168             This->type != D3DQUERYTYPE_EVENT &&
169             This->type != D3DQUERYTYPE_TIMESTAMP)
170             nine_context_begin_query(device, &This->counter, This->pq);
171         nine_context_end_query(device, &This->counter, This->pq);
172         This->state = NINE_QUERY_STATE_ENDED;
173     }
174     return D3D_OK;
175 }
176 
177 union nine_query_result
178 {
179     D3DDEVINFO_D3DVERTEXSTATS vertexstats;
180     DWORD dw;
181     BOOL b;
182     UINT64 u64;
183 };
184 
185 HRESULT NINE_WINAPI
NineQuery9_GetData(struct NineQuery9 * This,void * pData,DWORD dwSize,DWORD dwGetDataFlags)186 NineQuery9_GetData( struct NineQuery9 *This,
187                     void *pData,
188                     DWORD dwSize,
189                     DWORD dwGetDataFlags )
190 {
191     struct NineDevice9 *device = This->base.device;
192     bool ok, wait_query_result = false;
193     union pipe_query_result presult;
194     union nine_query_result nresult;
195 
196     DBG("This=%p pData=%p dwSize=%d dwGetDataFlags=%d\n",
197         This, pData, dwSize, dwGetDataFlags);
198 
199     /* according to spec we should return D3DERR_INVALIDCALL here, but
200      * wine returns S_FALSE because it is apparently the behaviour
201      * on windows */
202     user_assert(This->state != NINE_QUERY_STATE_RUNNING, S_FALSE);
203     user_assert(dwSize == 0 || pData, D3DERR_INVALIDCALL);
204     user_assert(dwGetDataFlags == 0 ||
205                 dwGetDataFlags == D3DGETDATA_FLUSH, D3DERR_INVALIDCALL);
206 
207     if (This->state == NINE_QUERY_STATE_FRESH) {
208         /* App forgot calling Issue. call it for it.
209          * However Wine states that return value should
210          * be S_OK, so wait for the result to return S_OK. */
211         NineQuery9_Issue(This, D3DISSUE_END);
212         wait_query_result = true;
213     }
214 
215     /* The documentation mentions no special case for D3DQUERYTYPE_TIMESTAMP.
216      * However Windows tests show that the query always succeeds when
217      * D3DGETDATA_FLUSH is specified. */
218     if (This->type == D3DQUERYTYPE_TIMESTAMP &&
219         (dwGetDataFlags & D3DGETDATA_FLUSH))
220         wait_query_result = true;
221 
222 
223     /* Note: We ignore dwGetDataFlags, because get_query_result will
224      * flush automatically if needed */
225 
226     ok = nine_context_get_query_result(device, This->pq, &This->counter,
227                                        !!(dwGetDataFlags & D3DGETDATA_FLUSH),
228                                        wait_query_result, &presult);
229 
230     if (!ok) return S_FALSE;
231 
232     if (!dwSize)
233         return S_OK;
234 
235     switch (This->type) {
236     case D3DQUERYTYPE_EVENT:
237         nresult.b = presult.b;
238         break;
239     case D3DQUERYTYPE_OCCLUSION:
240         nresult.dw = presult.u64;
241         break;
242     case D3DQUERYTYPE_TIMESTAMP:
243         nresult.u64 = presult.u64;
244         break;
245     case D3DQUERYTYPE_TIMESTAMPDISJOINT:
246         nresult.b = presult.timestamp_disjoint.disjoint;
247         break;
248     case D3DQUERYTYPE_TIMESTAMPFREQ:
249         /* Applications use it to convert the TIMESTAMP value to time.
250            AMD drivers on win seem to return the actual hardware clock
251            resolution and corresponding values in TIMESTAMP.
252            However, this behaviour is not easy to replicate here.
253            So instead we do what wine and opengl do, and use
254            nanoseconds TIMESTAMPs.
255            (Which is also the unit used by PIPE_QUERY_TIMESTAMP.)
256         */
257         nresult.u64 = 1000000000;
258         break;
259     case D3DQUERYTYPE_VERTEXSTATS:
260         nresult.vertexstats.NumRenderedTriangles =
261             presult.pipeline_statistics.c_invocations;
262         nresult.vertexstats.NumExtraClippingTriangles =
263             presult.pipeline_statistics.c_primitives;
264         break;
265     default:
266         assert(0);
267         break;
268     }
269     memcpy(pData, &nresult, MIN2(sizeof(nresult), dwSize));
270 
271     return S_OK;
272 }
273 
274 IDirect3DQuery9Vtbl NineQuery9_vtable = {
275     (void *)NineUnknown_QueryInterface,
276     (void *)NineUnknown_AddRef,
277     (void *)NineUnknown_Release,
278     (void *)NineUnknown_GetDevice, /* actually part of Query9 iface */
279     (void *)NineQuery9_GetType,
280     (void *)NineQuery9_GetDataSize,
281     (void *)NineQuery9_Issue,
282     (void *)NineQuery9_GetData
283 };
284 
285 static const GUID *NineQuery9_IIDs[] = {
286     &IID_IDirect3DQuery9,
287     &IID_IUnknown,
288     NULL
289 };
290 
291 HRESULT
NineQuery9_new(struct NineDevice9 * pDevice,struct NineQuery9 ** ppOut,D3DQUERYTYPE Type)292 NineQuery9_new( struct NineDevice9 *pDevice,
293                 struct NineQuery9 **ppOut,
294                 D3DQUERYTYPE Type )
295 {
296     NINE_DEVICE_CHILD_NEW(Query9, ppOut, pDevice, Type);
297 }
298