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