st/nine: Queries: allow app to call GetData without Issuing first
[mesa.git] / src / gallium / state_trackers / nine / query9.c
1 /*
2 * Copyright 2011 Joakim Sindholt <opensource@zhasha.com>
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 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE. */
22
23 #include "device9.h"
24 #include "query9.h"
25 #include "nine_helpers.h"
26 #include "pipe/p_screen.h"
27 #include "pipe/p_context.h"
28 #include "util/u_math.h"
29 #include "nine_dump.h"
30
31 #define DBG_CHANNEL DBG_QUERY
32
33 static inline unsigned
34 d3dquerytype_to_pipe_query(struct pipe_screen *screen, D3DQUERYTYPE type)
35 {
36 switch (type) {
37 case D3DQUERYTYPE_EVENT:
38 return PIPE_QUERY_GPU_FINISHED;
39 case D3DQUERYTYPE_OCCLUSION:
40 return screen->get_param(screen, PIPE_CAP_OCCLUSION_QUERY) ?
41 PIPE_QUERY_OCCLUSION_COUNTER : PIPE_QUERY_TYPES;
42 case D3DQUERYTYPE_TIMESTAMP:
43 return screen->get_param(screen, PIPE_CAP_QUERY_TIMESTAMP) ?
44 PIPE_QUERY_TIMESTAMP : PIPE_QUERY_TYPES;
45 case D3DQUERYTYPE_TIMESTAMPDISJOINT:
46 case D3DQUERYTYPE_TIMESTAMPFREQ:
47 return screen->get_param(screen, PIPE_CAP_QUERY_TIMESTAMP) ?
48 PIPE_QUERY_TIMESTAMP_DISJOINT : PIPE_QUERY_TYPES;
49 case D3DQUERYTYPE_VERTEXSTATS:
50 return screen->get_param(screen,
51 PIPE_CAP_QUERY_PIPELINE_STATISTICS) ?
52 PIPE_QUERY_PIPELINE_STATISTICS : PIPE_QUERY_TYPES;
53 default:
54 return PIPE_QUERY_TYPES; /* Query not supported */
55 }
56 }
57
58 #define GET_DATA_SIZE_CASE2(a, b) case D3DQUERYTYPE_##a: return sizeof(D3DDEVINFO_##b)
59 #define GET_DATA_SIZE_CASET(a, b) case D3DQUERYTYPE_##a: return sizeof(b)
60 static INLINE DWORD
61 nine_query_result_size(D3DQUERYTYPE type)
62 {
63 switch (type) {
64 GET_DATA_SIZE_CASE2(VERTEXSTATS, D3DVERTEXSTATS);
65 GET_DATA_SIZE_CASET(EVENT, BOOL);
66 GET_DATA_SIZE_CASET(OCCLUSION, DWORD);
67 GET_DATA_SIZE_CASET(TIMESTAMP, UINT64);
68 GET_DATA_SIZE_CASET(TIMESTAMPDISJOINT, BOOL);
69 GET_DATA_SIZE_CASET(TIMESTAMPFREQ, UINT64);
70 default:
71 assert(0);
72 return 0;
73 }
74 }
75
76 HRESULT
77 nine_is_query_supported(struct pipe_screen *screen, D3DQUERYTYPE type)
78 {
79 const unsigned ptype = d3dquerytype_to_pipe_query(screen, type);
80
81 user_assert(ptype != ~0, D3DERR_INVALIDCALL);
82
83 if (ptype == PIPE_QUERY_TYPES) {
84 DBG("Query type %u (%s) not supported.\n",
85 type, nine_D3DQUERYTYPE_to_str(type));
86 return D3DERR_NOTAVAILABLE;
87 }
88 return D3D_OK;
89 }
90
91 HRESULT
92 NineQuery9_ctor( struct NineQuery9 *This,
93 struct NineUnknownParams *pParams,
94 D3DQUERYTYPE Type )
95 {
96 struct pipe_context *pipe = pParams->device->pipe;
97 const unsigned ptype = d3dquerytype_to_pipe_query(pParams->device->screen, Type);
98 HRESULT hr;
99
100 DBG("This=%p pParams=%p Type=%d\n", This, pParams, Type);
101
102 hr = NineUnknown_ctor(&This->base, pParams);
103 if (FAILED(hr))
104 return hr;
105
106 This->state = NINE_QUERY_STATE_FRESH;
107 This->type = Type;
108
109 user_assert(ptype != ~0, D3DERR_INVALIDCALL);
110
111 if (ptype < PIPE_QUERY_TYPES) {
112 This->pq = pipe->create_query(pipe, ptype, 0);
113 if (!This->pq)
114 return E_OUTOFMEMORY;
115 } else {
116 assert(0); /* we have checked this case before */
117 }
118
119 This->instant =
120 Type == D3DQUERYTYPE_EVENT ||
121 Type == D3DQUERYTYPE_RESOURCEMANAGER ||
122 Type == D3DQUERYTYPE_TIMESTAMP ||
123 Type == D3DQUERYTYPE_TIMESTAMPFREQ ||
124 Type == D3DQUERYTYPE_VCACHE ||
125 Type == D3DQUERYTYPE_VERTEXSTATS;
126
127 This->result_size = nine_query_result_size(Type);
128
129 return D3D_OK;
130 }
131
132 void
133 NineQuery9_dtor( struct NineQuery9 *This )
134 {
135 struct pipe_context *pipe = This->base.device->pipe;
136
137 if (This->pq) {
138 if (This->state == NINE_QUERY_STATE_RUNNING)
139 pipe->end_query(pipe, This->pq);
140 pipe->destroy_query(pipe, This->pq);
141 }
142
143 NineUnknown_dtor(&This->base);
144 }
145
146 D3DQUERYTYPE WINAPI
147 NineQuery9_GetType( struct NineQuery9 *This )
148 {
149 return This->type;
150 }
151
152 DWORD WINAPI
153 NineQuery9_GetDataSize( struct NineQuery9 *This )
154 {
155 return This->result_size;
156 }
157
158 HRESULT WINAPI
159 NineQuery9_Issue( struct NineQuery9 *This,
160 DWORD dwIssueFlags )
161 {
162 struct pipe_context *pipe = This->base.device->pipe;
163
164 DBG("This=%p dwIssueFlags=%d\n", This, dwIssueFlags);
165
166 user_assert((dwIssueFlags == D3DISSUE_BEGIN && !This->instant) ||
167 (dwIssueFlags == 0) ||
168 (dwIssueFlags == D3DISSUE_END), D3DERR_INVALIDCALL);
169
170 if (dwIssueFlags == D3DISSUE_BEGIN) {
171 if (This->state == NINE_QUERY_STATE_RUNNING) {
172 pipe->end_query(pipe, This->pq);
173 }
174 pipe->begin_query(pipe, This->pq);
175 This->state = NINE_QUERY_STATE_RUNNING;
176 } else {
177 if (This->state != NINE_QUERY_STATE_RUNNING &&
178 This->type != D3DQUERYTYPE_EVENT &&
179 This->type != D3DQUERYTYPE_TIMESTAMP)
180 pipe->begin_query(pipe, This->pq);
181 pipe->end_query(pipe, This->pq);
182 This->state = NINE_QUERY_STATE_ENDED;
183 }
184 return D3D_OK;
185 }
186
187 union nine_query_result
188 {
189 D3DDEVINFO_D3DVERTEXSTATS vertexstats;
190 DWORD dw;
191 BOOL b;
192 UINT64 u64;
193 };
194
195 HRESULT WINAPI
196 NineQuery9_GetData( struct NineQuery9 *This,
197 void *pData,
198 DWORD dwSize,
199 DWORD dwGetDataFlags )
200 {
201 struct pipe_context *pipe = This->base.device->pipe;
202 boolean ok, wait_query_result = FALSE;
203 unsigned i;
204 union pipe_query_result presult;
205 union nine_query_result nresult;
206
207 DBG("This=%p pData=%p dwSize=%d dwGetDataFlags=%d\n",
208 This, pData, dwSize, dwGetDataFlags);
209
210 /* according to spec we should return D3DERR_INVALIDCALL here, but
211 * wine returns S_FALSE because it is apparently the behaviour
212 * on windows */
213 user_assert(This->state != NINE_QUERY_STATE_RUNNING, S_FALSE);
214 user_assert(dwSize == 0 || pData, D3DERR_INVALIDCALL);
215 user_assert(dwGetDataFlags == 0 ||
216 dwGetDataFlags == D3DGETDATA_FLUSH, D3DERR_INVALIDCALL);
217
218 if (This->state == NINE_QUERY_STATE_FRESH) {
219 /* App forgot calling Issue. call it for it.
220 * However Wine states that return value should
221 * be S_OK, so wait for the result to return S_OK. */
222 NineQuery9_Issue(This, D3DISSUE_END);
223 wait_query_result = TRUE;
224 }
225
226 /* Note: We ignore dwGetDataFlags, because get_query_result will
227 * flush automatically if needed */
228
229 ok = pipe->get_query_result(pipe, This->pq, wait_query_result, &presult);
230
231 if (!ok) return S_FALSE;
232
233 if (!dwSize)
234 return S_OK;
235
236 switch (This->type) {
237 case D3DQUERYTYPE_EVENT:
238 nresult.b = presult.b;
239 break;
240 case D3DQUERYTYPE_OCCLUSION:
241 nresult.dw = presult.u64;
242 break;
243 case D3DQUERYTYPE_TIMESTAMP:
244 nresult.u64 = presult.u64;
245 break;
246 case D3DQUERYTYPE_TIMESTAMPDISJOINT:
247 nresult.b = presult.timestamp_disjoint.disjoint;
248 break;
249 case D3DQUERYTYPE_TIMESTAMPFREQ:
250 nresult.u64 = presult.timestamp_disjoint.frequency;
251 break;
252 case D3DQUERYTYPE_VERTEXSTATS:
253 nresult.vertexstats.NumRenderedTriangles =
254 presult.pipeline_statistics.c_invocations;
255 nresult.vertexstats.NumExtraClippingTriangles =
256 presult.pipeline_statistics.c_primitives;
257 break;
258 default:
259 assert(0);
260 break;
261 }
262 memcpy(pData, &nresult, MIN2(sizeof(nresult), dwSize));
263
264 return S_OK;
265 }
266
267 IDirect3DQuery9Vtbl NineQuery9_vtable = {
268 (void *)NineUnknown_QueryInterface,
269 (void *)NineUnknown_AddRef,
270 (void *)NineUnknown_Release,
271 (void *)NineUnknown_GetDevice, /* actually part of Query9 iface */
272 (void *)NineQuery9_GetType,
273 (void *)NineQuery9_GetDataSize,
274 (void *)NineQuery9_Issue,
275 (void *)NineQuery9_GetData
276 };
277
278 static const GUID *NineQuery9_IIDs[] = {
279 &IID_IDirect3DQuery9,
280 &IID_IUnknown,
281 NULL
282 };
283
284 HRESULT
285 NineQuery9_new( struct NineDevice9 *pDevice,
286 struct NineQuery9 **ppOut,
287 D3DQUERYTYPE Type )
288 {
289 NINE_DEVICE_CHILD_NEW(Query9, ppOut, pDevice, Type);
290 }