1/*
2 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#include "sun_java2d_d3d_D3DGraphicsDevice.h"
27#include "D3DGraphicsDevice.h"
28#include "D3DPipelineManager.h"
29#include "D3DRenderQueue.h"
30#include "Trace.h"
31#include "awt_Toolkit.h"
32#include "awt_Window.h"
33
34extern jobject CreateDisplayMode(JNIEnv* env, jint width, jint height,
35                                 jint bitDepth, jint refreshRate);
36extern void addDisplayMode(JNIEnv* env, jobject arrayList, jint width,
37                           jint height, jint bitDepth, jint refreshRate);
38
39extern "C" {
40/*
41 * Class:     sun_java2d_d3d_D3DGraphicsDevice
42 * Method:    initD3D
43 * Signature: ()Z
44 */
45JNIEXPORT jboolean JNICALL Java_sun_java2d_d3d_D3DGraphicsDevice_initD3D
46  (JNIEnv *env, jclass)
47{
48    J2dTraceLn(J2D_TRACE_INFO, "D3DGD_initD3D");
49
50    jboolean result = D3DInitializer::GetInstance().EnsureInited()
51                      ? JNI_TRUE : JNI_FALSE;
52    J2dTraceLn1(J2D_TRACE_INFO, "D3DGD_initD3D: result=%x", result);
53    return result;
54}
55
56/*
57 * Class:     sun_java2d_d3d_D3DGraphicsDevice
58 * Method:    getDeviceIdNative
59 * Signature: (I)Ljava/lang/String;
60 */
61JNIEXPORT jstring JNICALL Java_sun_java2d_d3d_D3DGraphicsDevice_getDeviceIdNative
62  (JNIEnv *env, jclass d3dsdc, jint gdiScreen)
63{
64    D3DPipelineManager *pMgr;
65    UINT adapter;
66    D3DADAPTER_IDENTIFIER9 aid;
67    IDirect3D9 *pd3d9;
68
69    J2dTraceLn(J2D_TRACE_INFO, "D3DGD_getDeviceIdNative");
70
71    pMgr = D3DPipelineManager::GetInstance();
72    RETURN_STATUS_IF_NULL(pMgr, NULL);
73    pd3d9 = pMgr->GetD3DObject();
74    RETURN_STATUS_IF_NULL(pd3d9, NULL);
75
76    adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
77    if (FAILED(pd3d9->GetAdapterIdentifier(adapter, 0, &aid))) {
78        return NULL;
79    }
80
81    // ('%d.' will take no more than 6+1 chars since we are printing a WORD)
82    //            AAAA&BBBB MAX_DEVICE_IDENTIFIER_STRING (%d.%d.%d.%d)0
83    size_t len = (4+1+4  +1+MAX_DEVICE_IDENTIFIER_STRING+1 +1+(6+1)*4+1 +1);
84    WCHAR *pAdapterId = new WCHAR[len];
85    RETURN_STATUS_IF_NULL(pAdapterId, NULL);
86
87    _snwprintf(pAdapterId, len, L"%x&%x %S (%d.%d.%d.%d)",
88               0xffff & aid.VendorId, 0xffff & aid.DeviceId, aid.Description,
89               HIWORD(aid.DriverVersion.HighPart),
90               LOWORD(aid.DriverVersion.HighPart),
91               HIWORD(aid.DriverVersion.LowPart),
92               LOWORD(aid.DriverVersion.LowPart));
93    // _snwprintf doesn't add 0 at the end if the formatted string didn't fit
94    // in the buffer so we have to make sure it is null terminated
95    pAdapterId[len-1] = (WCHAR)0;
96
97    J2dTraceLn1(J2D_TRACE_VERBOSE, "  id=%S", pAdapterId);
98
99    jstring ret = JNU_NewStringPlatform(env, pAdapterId);
100
101    delete pAdapterId;
102
103    return ret;
104}
105
106/*
107 * Class:     sun_java2d_d3d_D3DGraphicsDevice
108 * Method:    getDeviceCapsNative
109 * Signature: (I)I
110 */
111JNIEXPORT jint JNICALL
112Java_sun_java2d_d3d_D3DGraphicsDevice_getDeviceCapsNative
113  (JNIEnv *env, jclass d3dsdc, jint gdiScreen)
114{
115    D3DPipelineManager *pMgr;
116    D3DContext *pCtx;
117    UINT adapter;
118
119    J2dRlsTraceLn(J2D_TRACE_INFO, "D3DGD_getDeviceCapsNative");
120
121    pMgr = D3DPipelineManager::GetInstance();
122    RETURN_STATUS_IF_NULL(pMgr, CAPS_EMPTY);
123    adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
124
125    if (FAILED(pMgr->GetD3DContext(adapter, &pCtx))) {
126        J2dRlsTraceLn1(J2D_TRACE_ERROR,
127                      "D3DGD_getDeviceCapsNative: device %d disabled", adapter);
128        return CAPS_EMPTY;
129    }
130    return pCtx->GetContextCaps();
131}
132
133/*
134 * Class:     sun_java2d_d3d_D3DGraphicsDevice
135 * Method:    enterFullScreenExclusiveNative
136 * Signature: (IJ)V
137 */
138JNIEXPORT jboolean JNICALL
139Java_sun_java2d_d3d_D3DGraphicsDevice_enterFullScreenExclusiveNative
140  (JNIEnv *env, jclass gdc, jint gdiScreen, jlong window)
141{
142    HRESULT res;
143    D3DPipelineManager *pMgr;
144    D3DContext *pCtx;
145    HWND hWnd;
146    AwtWindow *w;
147    D3DPRESENT_PARAMETERS newParams, *pCurParams;
148    D3DDISPLAYMODE dm;
149    UINT adapter;
150
151    J2dTraceLn(J2D_TRACE_INFO, "D3DGD_enterFullScreenExclusiveNative");
152
153    RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), JNI_FALSE);
154    adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
155
156    if (FAILED(res = pMgr->GetD3DContext(adapter, &pCtx))) {
157        D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
158        return JNI_FALSE;
159    }
160
161    w = (AwtWindow *)AwtComponent::GetComponent((HWND)window);
162    if (w == NULL || !::IsWindow(hWnd = w->GetTopLevelHWnd())) {
163        J2dTraceLn(J2D_TRACE_WARNING,
164                   "D3DGD_enterFullScreenExclusiveNative: disposed window");
165        return JNI_FALSE;
166    }
167
168    // REMIND: should we also move the non-topleve window from
169    // being on top here (it's moved to front in GraphicsDevice.setFSW())?
170
171    pCtx->Get3DObject()->GetAdapterDisplayMode(adapter, &dm);
172    pCurParams = pCtx->GetPresentationParams();
173
174    // let the mananger know that we're entering the fs mode, it will
175    // set the proper current focus window for us, which ConfigureContext will
176    // use when creating the device
177    pMgr->SetFSFocusWindow(adapter, hWnd);
178
179    newParams = *pCurParams;
180    newParams.hDeviceWindow = hWnd;
181    newParams.Windowed = FALSE;
182    newParams.BackBufferCount = 1;
183    newParams.BackBufferFormat = dm.Format;
184    newParams.FullScreen_RefreshRateInHz = dm.RefreshRate;
185    newParams.BackBufferWidth = dm.Width;
186    newParams.BackBufferHeight = dm.Height;
187    newParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
188    newParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
189
190    res = pCtx->ConfigureContext(&newParams);
191    D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
192    return SUCCEEDED(res);
193}
194
195/*
196 * Class:     sun_java2d_d3d_D3DGraphicsDevice
197 * Method:    exitFullScreenExclusiveNative
198 * Signature: (I)V
199 */
200JNIEXPORT jboolean JNICALL
201Java_sun_java2d_d3d_D3DGraphicsDevice_exitFullScreenExclusiveNative
202  (JNIEnv *env, jclass gdc, jint gdiScreen)
203{
204    HRESULT res;
205    D3DPipelineManager *pMgr;
206    D3DContext *pCtx;
207    D3DPRESENT_PARAMETERS newParams, *pCurParams;
208    UINT adapter;
209
210    J2dTraceLn(J2D_TRACE_INFO, "D3DGD_exitFullScreenExclusiveNative");
211
212    RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), JNI_FALSE);
213    adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
214
215    if (FAILED(res = pMgr->GetD3DContext(adapter, &pCtx))) {
216        D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
217        return JNI_FALSE;
218    }
219
220    pCurParams = pCtx->GetPresentationParams();
221
222    newParams = *pCurParams;
223    // we're exiting fs, the device window can be 0
224    newParams.hDeviceWindow = 0;
225    newParams.Windowed = TRUE;
226    newParams.BackBufferFormat = D3DFMT_UNKNOWN;
227    newParams.BackBufferCount = 1;
228    newParams.FullScreen_RefreshRateInHz = 0;
229    newParams.BackBufferWidth = 0;
230    newParams.BackBufferHeight = 0;
231    newParams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
232    newParams.SwapEffect = D3DSWAPEFFECT_COPY;
233
234    res = pCtx->ConfigureContext(&newParams);
235    D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
236
237    // exited fs, update current focus window
238    // note that we call this after this adapter exited fs mode so that
239    // the rest of the adapters can be reset
240    pMgr->SetFSFocusWindow(adapter, 0);
241
242    return SUCCEEDED(res);
243}
244
245/*
246 * Class:     sun_java2d_d3d_D3DGraphicsDevice
247 * Method:    configDisplayModeNative
248 * Signature: (IJIIII)V
249 */
250JNIEXPORT void JNICALL
251Java_sun_java2d_d3d_D3DGraphicsDevice_configDisplayModeNative
252  (JNIEnv *env, jclass gdc, jint gdiScreen, jlong window,
253   jint width, jint height, jint bitDepth, jint refreshRate)
254{
255    HRESULT res;
256    D3DPipelineManager *pMgr;
257    D3DContext *pCtx;
258    D3DPRESENT_PARAMETERS newParams, *pCurParams;
259    UINT adapter;
260
261    J2dTraceLn(J2D_TRACE_INFO, "D3DGD_configDisplayModeNative");
262
263    RETURN_IF_NULL(pMgr = D3DPipelineManager::GetInstance());
264    adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
265
266    if (FAILED(res = pMgr->GetD3DContext(adapter, &pCtx))) {
267        D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
268        return;
269    }
270
271    pCurParams = pCtx->GetPresentationParams();
272
273    newParams = *pCurParams;
274    newParams.BackBufferWidth = width;
275    newParams.BackBufferHeight = height;
276    newParams.FullScreen_RefreshRateInHz = refreshRate;
277    newParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
278    // we leave the swap effect so that it's more likely
279    // to be the one user selected initially
280//    newParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
281
282    if (bitDepth == 32) {
283        newParams.BackBufferFormat = D3DFMT_X8R8G8B8;
284    } else if (bitDepth == 16) {
285        UINT modeNum;
286        D3DDISPLAYMODE mode;
287        IDirect3D9 *pd3d9;
288        UINT modesCount;
289
290        RETURN_IF_NULL(pd3d9 = pMgr->GetD3DObject());
291
292        modesCount = pd3d9->GetAdapterModeCount(adapter, D3DFMT_R5G6B5);
293        if (modesCount == 0) {
294            modesCount = pd3d9->GetAdapterModeCount(adapter, D3DFMT_X1R5G5B5);
295        }
296
297        newParams.BackBufferFormat = D3DFMT_UNKNOWN;
298        for (modeNum = 0; modeNum < modesCount; modeNum++) {
299            if (SUCCEEDED(pd3d9->EnumAdapterModes(adapter, D3DFMT_R5G6B5,
300                                                  modeNum, &mode)))
301            {
302                if (mode.Width == width && mode.Height == height &&
303                    mode.RefreshRate == refreshRate)
304                {
305                    // prefer 565 over 555
306                    if (mode.Format == D3DFMT_R5G6B5) {
307                        newParams.BackBufferFormat = D3DFMT_R5G6B5;
308                        break;
309                    } else if (mode.Format == D3DFMT_X1R5G5B5) {
310                        newParams.BackBufferFormat = D3DFMT_X1R5G5B5;
311                    }
312                }
313            }
314        }
315        if (newParams.BackBufferFormat == D3DFMT_UNKNOWN) {
316            J2dRlsTraceLn(J2D_TRACE_ERROR,
317                          "D3DGD_configDisplayModeNative: no 16-bit formats");
318            return;
319        }
320    } else {
321        J2dRlsTraceLn1(J2D_TRACE_ERROR,
322                       "D3DGD_configDisplayModeNative: unsupported depth: %d",
323                       bitDepth);
324        return;
325    }
326
327    J2dTraceLn4(J2D_TRACE_VERBOSE, "  changing to dm: %dx%dx%d@%d",
328                newParams.BackBufferWidth, newParams.BackBufferHeight,
329                bitDepth, refreshRate);
330    J2dTraceLn1(J2D_TRACE_VERBOSE, "  selected backbuffer format: %d",
331                newParams.BackBufferFormat);
332
333    res = pCtx->ConfigureContext(&newParams);
334    if (SUCCEEDED(res)) {
335        // the full screen window doesn't receive WM_SIZE event when
336        // the display mode changes (it does get resized though) so we need to
337        // generate the event ourselves
338        ::SendMessage(newParams.hDeviceWindow, WM_SIZE, width, height);
339    }
340    D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
341}
342
343
344/*
345 * Class:     sun_java2d_d3d_D3DGraphicsDevice
346 * Method:    getCurrentDisplayModeNative
347 * Signature: (I)Ljava/awt/DisplayMode;
348 */
349JNIEXPORT jobject JNICALL
350Java_sun_java2d_d3d_D3DGraphicsDevice_getCurrentDisplayModeNative
351  (JNIEnv *env, jclass gdc, jint gdiScreen)
352{
353    D3DPipelineManager *pMgr;
354    IDirect3D9 *pd3d9;
355    jobject ret = NULL;
356    D3DDISPLAYMODE mode;
357    UINT adapter;
358
359    J2dTraceLn(J2D_TRACE_INFO, "D3DGD_getCurrentDisplayModeNative");
360
361    RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), NULL);
362    RETURN_STATUS_IF_NULL(pd3d9 = pMgr->GetD3DObject(), NULL);
363    adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
364
365    if (SUCCEEDED(pd3d9->GetAdapterDisplayMode(adapter, &mode))) {
366        int bitDepth = -1;
367        // these are the only three valid screen formats
368        switch (mode.Format) {
369            case D3DFMT_X8R8G8B8: bitDepth = 32; break;
370            case D3DFMT_R5G6B5:
371            case D3DFMT_X1R5G5B5: bitDepth = 16; break;
372        }
373        J2dTraceLn4(J2D_TRACE_VERBOSE,
374                    "  current dm: %dx%dx%d@%d",
375                    mode.Width, mode.Height, bitDepth, mode.RefreshRate);
376        ret = CreateDisplayMode(env, mode.Width, mode.Height, bitDepth,
377                                mode.RefreshRate);
378    }
379    return ret;
380}
381
382/*
383 * Class:     sun_java2d_d3d_D3DGraphicsDevice
384 * Method:    enumDisplayModesNative
385 * Signature: (ILjava/util/ArrayList;)V
386 */
387JNIEXPORT void JNICALL
388Java_sun_java2d_d3d_D3DGraphicsDevice_enumDisplayModesNative
389  (JNIEnv *env, jclass gdc, jint gdiScreen, jobject arrayList)
390{
391    D3DPipelineManager *pMgr;
392    IDirect3D9 *pd3d9;
393    jobject ret = NULL;
394    D3DDISPLAYMODE mode;
395    UINT formatNum, modeNum, modesCount;
396    UINT adapter;
397    // EnumAdapterModes treats 555 and 565 formats as equivalents
398    static D3DFORMAT formats[] =
399      { D3DFMT_X8R8G8B8, D3DFMT_R5G6B5 };
400
401    J2dTraceLn(J2D_TRACE_INFO, "D3DGD_enumDisplayModesNative");
402
403    RETURN_IF_NULL(pMgr = D3DPipelineManager::GetInstance());
404    RETURN_IF_NULL(pd3d9 = pMgr->GetD3DObject());
405    adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
406
407    for (formatNum = 0; formatNum < (sizeof formats)/(sizeof *formats); formatNum++) {
408        modesCount = pd3d9->GetAdapterModeCount(adapter, formats[formatNum]);
409        for (modeNum = 0; modeNum < modesCount; modeNum++) {
410            if (SUCCEEDED(pd3d9->EnumAdapterModes(adapter, formats[formatNum],
411                                                  modeNum, &mode)))
412            {
413                int bitDepth = -1;
414                // these are the only three valid screen formats,
415                // 30-bit is returned as X8R8G8B8
416                switch (mode.Format) {
417                    case D3DFMT_X8R8G8B8: bitDepth = 32; break;
418                    case D3DFMT_R5G6B5:
419                    case D3DFMT_X1R5G5B5: bitDepth = 16; break;
420                }
421                J2dTraceLn4(J2D_TRACE_VERBOSE, "  found dm: %dx%dx%d@%d",
422                            mode.Width, mode.Height, bitDepth,mode.RefreshRate);
423                addDisplayMode(env, arrayList, mode.Width, mode.Height,
424                               bitDepth, mode.RefreshRate);
425            }
426        }
427    }
428}
429
430/*
431 * Class:     sun_java2d_d3d_D3DGraphicsDevice
432 * Method:    getAvailableAcceleratedMemoryNative
433 * Signature: (I)J
434 */
435JNIEXPORT jlong JNICALL
436Java_sun_java2d_d3d_D3DGraphicsDevice_getAvailableAcceleratedMemoryNative
437  (JNIEnv *env, jclass gdc, jint gdiScreen)
438{
439    // REMIND: looks like Direct3D provides information about texture memory
440    // only via IDirect3DDevice9::GetAvailableTextureMem, however, it
441    // seems to report the same amount as direct draw used to.
442    HRESULT res;
443    D3DPipelineManager *pMgr;
444    D3DContext *pCtx;
445    IDirect3DDevice9 *pd3dDevice;
446    UINT adapter;
447
448    J2dTraceLn(J2D_TRACE_INFO, "D3DGD_getAvailableAcceleratedMemoryNative");
449
450    RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), 0L);
451    adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
452
453    if (FAILED(res = pMgr->GetD3DContext(adapter, &pCtx))) {
454        D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
455        return 0L;
456    }
457    RETURN_STATUS_IF_NULL(pd3dDevice = pCtx->Get3DDevice(), 0L);
458
459    UINT mem = pd3dDevice->GetAvailableTextureMem();
460    J2dTraceLn1(J2D_TRACE_VERBOSE, "  available memory=%d", mem);
461    return mem;
462}
463
464/*
465 * Class:     sun_java2d_d3d_D3DGraphicsDevice
466 * Method:    isD3DAvailableOnDeviceNative
467 * Signature: (I)Z
468 */
469JNIEXPORT jboolean JNICALL
470Java_sun_java2d_d3d_D3DGraphicsDevice_isD3DAvailableOnDeviceNative
471  (JNIEnv *env, jclass gdc, jint gdiScreen)
472{
473    HRESULT res;
474    D3DPipelineManager *pMgr;
475    D3DContext *pCtx;
476    UINT adapter;
477
478    J2dTraceLn(J2D_TRACE_INFO, "D3DGD_isD3DAvailableOnDeviceNative");
479
480    RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), JNI_FALSE);
481    adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
482
483    if (FAILED(res = pMgr->GetD3DContext(adapter, &pCtx))) {
484        D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
485        return JNI_FALSE;
486    }
487
488    return JNI_TRUE;
489}
490
491} // extern "C"
492