st/nine: Refactor how user constbufs sizes are calculated
[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) ||
167 (dwIssueFlags == 0) ||
168 (dwIssueFlags == D3DISSUE_END), D3DERR_INVALIDCALL);
169
170 /* Wine tests: always return D3D_OK on D3DISSUE_BEGIN
171 * even when the call is supposed to be forbidden */
172 if (dwIssueFlags == D3DISSUE_BEGIN && This->instant)
173 return D3D_OK;
174
175 if (dwIssueFlags == D3DISSUE_BEGIN) {
176 if (This->state == NINE_QUERY_STATE_RUNNING) {
177 pipe->end_query(pipe, This->pq);
178 }
179 pipe->begin_query(pipe, This->pq);
180 This->state = NINE_QUERY_STATE_RUNNING;
181 } else {
182 if (This->state != NINE_QUERY_STATE_RUNNING &&
183 This->type != D3DQUERYTYPE_EVENT &&
184 This->type != D3DQUERYTYPE_TIMESTAMP)
185 pipe->begin_query(pipe, This->pq);
186 pipe->end_query(pipe, This->pq);
187 This->state = NINE_QUERY_STATE_ENDED;
188 }
189 return D3D_OK;
190 }
191
192 union nine_query_result
193 {
194 D3DDEVINFO_D3DVERTEXSTATS vertexstats;
195 DWORD dw;
196 BOOL b;
197 UINT64 u64;
198 };
199
200 HRESULT WINAPI
201 NineQuery9_GetData( struct NineQuery9 *This,
202 void *pData,
203 DWORD dwSize,
204 DWORD dwGetDataFlags )
205 {
206 struct pipe_context *pipe = This->base.device->pipe;
207 boolean ok, wait_query_result = FALSE;
208 union pipe_query_result presult;
209 union nine_query_result nresult;
210
211 DBG("This=%p pData=%p dwSize=%d dwGetDataFlags=%d\n",
212 This, pData, dwSize, dwGetDataFlags);
213
214 /* according to spec we should return D3DERR_INVALIDCALL here, but
215 * wine returns S_FALSE because it is apparently the behaviour
216 * on windows */
217 user_assert(This->state != NINE_QUERY_STATE_RUNNING, S_FALSE);
218 user_assert(dwSize == 0 || pData, D3DERR_INVALIDCALL);
219 user_assert(dwGetDataFlags == 0 ||
220 dwGetDataFlags == D3DGETDATA_FLUSH, D3DERR_INVALIDCALL);
221
222 if (This->state == NINE_QUERY_STATE_FRESH) {
223 /* App forgot calling Issue. call it for it.
224 * However Wine states that return value should
225 * be S_OK, so wait for the result to return S_OK. */
226 NineQuery9_Issue(This, D3DISSUE_END);
227 wait_query_result = TRUE;
228 }
229
230 /* Wine tests: D3DQUERYTYPE_TIMESTAMP always succeeds */
231 wait_query_result |= This->type == D3DQUERYTYPE_TIMESTAMP;
232
233 /* Note: We ignore dwGetDataFlags, because get_query_result will
234 * flush automatically if needed */
235
236 ok = pipe->get_query_result(pipe, This->pq, wait_query_result, &presult);
237
238 if (!ok) return S_FALSE;
239
240 if (!dwSize)
241 return S_OK;
242
243 switch (This->type) {
244 case D3DQUERYTYPE_EVENT:
245 nresult.b = presult.b;
246 break;
247 case D3DQUERYTYPE_OCCLUSION:
248 nresult.dw = presult.u64;
249 break;
250 case D3DQUERYTYPE_TIMESTAMP:
251 nresult.u64 = presult.u64;
252 break;
253 case D3DQUERYTYPE_TIMESTAMPDISJOINT:
254 nresult.b = presult.timestamp_disjoint.disjoint;
255 break;
256 case D3DQUERYTYPE_TIMESTAMPFREQ:
257 nresult.u64 = presult.timestamp_disjoint.frequency;
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
292 NineQuery9_new( struct NineDevice9 *pDevice,
293 struct NineQuery9 **ppOut,
294 D3DQUERYTYPE Type )
295 {
296 NINE_DEVICE_CHILD_NEW(Query9, ppOut, pDevice, Type);
297 }