Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5564 | serge | 1 | /* |
2 | * Copyright 2011 Joakim Sindholt |
||
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 | /* The documention mentions no special case for D3DQUERYTYPE_TIMESTAMP. |
||
231 | * However Windows tests show that the query always succeeds when |
||
232 | * D3DGETDATA_FLUSH is specified. */ |
||
233 | if (This->type == D3DQUERYTYPE_TIMESTAMP && |
||
234 | (dwGetDataFlags & D3DGETDATA_FLUSH)) |
||
235 | wait_query_result = TRUE; |
||
236 | |||
237 | |||
238 | /* Note: We ignore dwGetDataFlags, because get_query_result will |
||
239 | * flush automatically if needed */ |
||
240 | |||
241 | ok = pipe->get_query_result(pipe, This->pq, wait_query_result, &presult); |
||
242 | |||
243 | if (!ok) return S_FALSE; |
||
244 | |||
245 | if (!dwSize) |
||
246 | return S_OK; |
||
247 | |||
248 | switch (This->type) { |
||
249 | case D3DQUERYTYPE_EVENT: |
||
250 | nresult.b = presult.b; |
||
251 | break; |
||
252 | case D3DQUERYTYPE_OCCLUSION: |
||
253 | nresult.dw = presult.u64; |
||
254 | break; |
||
255 | case D3DQUERYTYPE_TIMESTAMP: |
||
256 | nresult.u64 = presult.u64; |
||
257 | break; |
||
258 | case D3DQUERYTYPE_TIMESTAMPDISJOINT: |
||
259 | nresult.b = presult.timestamp_disjoint.disjoint; |
||
260 | break; |
||
261 | case D3DQUERYTYPE_TIMESTAMPFREQ: |
||
262 | /* Applications use it to convert the TIMESTAMP value to time. |
||
263 | AMD drivers on win seem to return the actual hardware clock |
||
264 | resolution and corresponding values in TIMESTAMP. |
||
265 | However, this behaviour is not easy to replicate here. |
||
266 | So instead we do what wine and opengl do, and use |
||
267 | nanoseconds TIMESTAMPs. |
||
268 | (Which is also the unit used by PIPE_QUERY_TIMESTAMP.) |
||
269 | */ |
||
270 | nresult.u64 = 1000000000; |
||
271 | break; |
||
272 | case D3DQUERYTYPE_VERTEXSTATS: |
||
273 | nresult.vertexstats.NumRenderedTriangles = |
||
274 | presult.pipeline_statistics.c_invocations; |
||
275 | nresult.vertexstats.NumExtraClippingTriangles = |
||
276 | presult.pipeline_statistics.c_primitives; |
||
277 | break; |
||
278 | default: |
||
279 | assert(0); |
||
280 | break; |
||
281 | } |
||
282 | memcpy(pData, &nresult, MIN2(sizeof(nresult), dwSize)); |
||
283 | |||
284 | return S_OK; |
||
285 | } |
||
286 | |||
287 | IDirect3DQuery9Vtbl NineQuery9_vtable = { |
||
288 | (void *)NineUnknown_QueryInterface, |
||
289 | (void *)NineUnknown_AddRef, |
||
290 | (void *)NineUnknown_Release, |
||
291 | (void *)NineUnknown_GetDevice, /* actually part of Query9 iface */ |
||
292 | (void *)NineQuery9_GetType, |
||
293 | (void *)NineQuery9_GetDataSize, |
||
294 | (void *)NineQuery9_Issue, |
||
295 | (void *)NineQuery9_GetData |
||
296 | }; |
||
297 | |||
298 | static const GUID *NineQuery9_IIDs[] = { |
||
299 | &IID_IDirect3DQuery9, |
||
300 | &IID_IUnknown, |
||
301 | NULL |
||
302 | }; |
||
303 | |||
304 | HRESULT |
||
305 | NineQuery9_new( struct NineDevice9 *pDevice, |
||
306 | struct NineQuery9 **ppOut, |
||
307 | D3DQUERYTYPE Type ) |
||
308 | { |
||
309 | NINE_DEVICE_CHILD_NEW(Query9, ppOut, pDevice, Type); |
||
310 | }> |