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