• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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