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