1 /*
   2  * Copyright (c) 2007, 2010, 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 
  34 extern jobject CreateDisplayMode(JNIEnv* env, jint width, jint height,
  35                                  jint bitDepth, jint refreshRate);
  36 extern void addDisplayMode(JNIEnv* env, jobject arrayList, jint width,
  37                            jint height, jint bitDepth, jint refreshRate);
  38 
  39 extern "C" {
  40 /*
  41  * Class:     sun_java2d_d3d_D3DGraphicsDevice
  42  * Method:    initD3D
  43  * Signature: ()Z
  44  */
  45 JNIEXPORT 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  */
  61 JNIEXPORT 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  */
 111 JNIEXPORT jint JNICALL
 112 Java_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  */
 138 JNIEXPORT jboolean JNICALL
 139 Java_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  */
 200 JNIEXPORT jboolean JNICALL
 201 Java_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  */
 250 JNIEXPORT void JNICALL
 251 Java_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  */
 349 JNIEXPORT jobject JNICALL
 350 Java_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  */
 387 JNIEXPORT void JNICALL
 388 Java_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 < 3; 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  */
 435 JNIEXPORT jlong JNICALL
 436 Java_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  */
 469 JNIEXPORT jboolean JNICALL
 470 Java_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"