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 
  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     // Since we are not creating hwnd with WS_EX_TOPMOST flag, so Windowed flag 
 182     // needs to be TRUE for fullscreen window 
 183     newParams.Windowed = TRUE;  
 184     newParams.BackBufferCount = 1;
 185     newParams.BackBufferFormat = dm.Format;
 186     newParams.FullScreen_RefreshRateInHz = dm.RefreshRate;
 187     newParams.BackBufferWidth = dm.Width;
 188     newParams.BackBufferHeight = dm.Height;
 189     newParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
 190     newParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
 191 
 192     res = pCtx->ConfigureContext(&newParams);
 193     D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
 194     return SUCCEEDED(res);
 195 }
 196 
 197 /*
 198  * Class:     sun_java2d_d3d_D3DGraphicsDevice
 199  * Method:    exitFullScreenExclusiveNative
 200  * Signature: (I)V
 201  */
 202 JNIEXPORT jboolean JNICALL
 203 Java_sun_java2d_d3d_D3DGraphicsDevice_exitFullScreenExclusiveNative
 204   (JNIEnv *env, jclass gdc, jint gdiScreen)
 205 {
 206     HRESULT res;
 207     D3DPipelineManager *pMgr;
 208     D3DContext *pCtx;
 209     D3DPRESENT_PARAMETERS newParams, *pCurParams;
 210     UINT adapter;
 211 
 212     J2dTraceLn(J2D_TRACE_INFO, "D3DGD_exitFullScreenExclusiveNative");
 213 
 214     RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), JNI_FALSE);
 215     adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
 216 
 217     if (FAILED(res = pMgr->GetD3DContext(adapter, &pCtx))) {
 218         D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
 219         return JNI_FALSE;
 220     }
 221 
 222     pCurParams = pCtx->GetPresentationParams();
 223 
 224     newParams = *pCurParams;
 225     // we're exiting fs, the device window can be 0
 226     newParams.hDeviceWindow = 0;
 227     newParams.Windowed = TRUE;
 228     newParams.BackBufferFormat = D3DFMT_UNKNOWN;
 229     newParams.BackBufferCount = 1;
 230     newParams.FullScreen_RefreshRateInHz = 0;
 231     newParams.BackBufferWidth = 0;
 232     newParams.BackBufferHeight = 0;
 233     newParams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
 234     newParams.SwapEffect = D3DSWAPEFFECT_COPY;
 235 
 236     res = pCtx->ConfigureContext(&newParams);
 237     D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
 238 
 239     // exited fs, update current focus window
 240     // note that we call this after this adapter exited fs mode so that
 241     // the rest of the adapters can be reset
 242     pMgr->SetFSFocusWindow(adapter, 0);
 243 
 244     return SUCCEEDED(res);
 245 }
 246 
 247 /*
 248  * Class:     sun_java2d_d3d_D3DGraphicsDevice
 249  * Method:    configDisplayModeNative
 250  * Signature: (IJIIII)V
 251  */
 252 JNIEXPORT void JNICALL
 253 Java_sun_java2d_d3d_D3DGraphicsDevice_configDisplayModeNative
 254   (JNIEnv *env, jclass gdc, jint gdiScreen, jlong window,
 255    jint width, jint height, jint bitDepth, jint refreshRate)
 256 {
 257     HRESULT res;
 258     D3DPipelineManager *pMgr;
 259     D3DContext *pCtx;
 260     D3DPRESENT_PARAMETERS newParams, *pCurParams;
 261     UINT adapter;
 262 
 263     J2dTraceLn(J2D_TRACE_INFO, "D3DGD_configDisplayModeNative");
 264 
 265     RETURN_IF_NULL(pMgr = D3DPipelineManager::GetInstance());
 266     adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
 267 
 268     if (FAILED(res = pMgr->GetD3DContext(adapter, &pCtx))) {
 269         D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
 270         return;
 271     }
 272 
 273     pCurParams = pCtx->GetPresentationParams();
 274 
 275     newParams = *pCurParams;
 276     newParams.BackBufferWidth = width;
 277     newParams.BackBufferHeight = height;
 278     newParams.FullScreen_RefreshRateInHz = refreshRate;
 279     newParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
 280     // we leave the swap effect so that it's more likely
 281     // to be the one user selected initially
 282 //    newParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
 283 
 284     if (bitDepth == 32) {
 285         newParams.BackBufferFormat = D3DFMT_X8R8G8B8;
 286     } else if (bitDepth == 16) {
 287         UINT modeNum;
 288         D3DDISPLAYMODE mode;
 289         IDirect3D9 *pd3d9;
 290         UINT modesCount;
 291 
 292         RETURN_IF_NULL(pd3d9 = pMgr->GetD3DObject());
 293 
 294         modesCount = pd3d9->GetAdapterModeCount(adapter, D3DFMT_R5G6B5);
 295         if (modesCount == 0) {
 296             modesCount = pd3d9->GetAdapterModeCount(adapter, D3DFMT_X1R5G5B5);
 297         }
 298 
 299         newParams.BackBufferFormat = D3DFMT_UNKNOWN;
 300         for (modeNum = 0; modeNum < modesCount; modeNum++) {
 301             if (SUCCEEDED(pd3d9->EnumAdapterModes(adapter, D3DFMT_R5G6B5,
 302                                                   modeNum, &mode)))
 303             {
 304                 if (mode.Width == width && mode.Height == height &&
 305                     mode.RefreshRate == refreshRate)
 306                 {
 307                     // prefer 565 over 555
 308                     if (mode.Format == D3DFMT_R5G6B5) {
 309                         newParams.BackBufferFormat = D3DFMT_R5G6B5;
 310                         break;
 311                     } else if (mode.Format == D3DFMT_X1R5G5B5) {
 312                         newParams.BackBufferFormat = D3DFMT_X1R5G5B5;
 313                     }
 314                 }
 315             }
 316         }
 317         if (newParams.BackBufferFormat == D3DFMT_UNKNOWN) {
 318             J2dRlsTraceLn(J2D_TRACE_ERROR,
 319                           "D3DGD_configDisplayModeNative: no 16-bit formats");
 320             return;
 321         }
 322     } else {
 323         J2dRlsTraceLn1(J2D_TRACE_ERROR,
 324                        "D3DGD_configDisplayModeNative: unsupported depth: %d",
 325                        bitDepth);
 326         return;
 327     }
 328 
 329     J2dTraceLn4(J2D_TRACE_VERBOSE, "  changing to dm: %dx%dx%d@%d",
 330                 newParams.BackBufferWidth, newParams.BackBufferHeight,
 331                 bitDepth, refreshRate);
 332     J2dTraceLn1(J2D_TRACE_VERBOSE, "  selected backbuffer format: %d",
 333                 newParams.BackBufferFormat);
 334 
 335     res = pCtx->ConfigureContext(&newParams);
 336     if (SUCCEEDED(res)) {
 337         // the full screen window doesn't receive WM_SIZE event when
 338         // the display mode changes (it does get resized though) so we need to
 339         // generate the event ourselves
 340         ::SendMessage(newParams.hDeviceWindow, WM_SIZE, width, height);
 341     }
 342     D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
 343 }
 344 
 345 
 346 /*
 347  * Class:     sun_java2d_d3d_D3DGraphicsDevice
 348  * Method:    getCurrentDisplayModeNative
 349  * Signature: (I)Ljava/awt/DisplayMode;
 350  */
 351 JNIEXPORT jobject JNICALL
 352 Java_sun_java2d_d3d_D3DGraphicsDevice_getCurrentDisplayModeNative
 353   (JNIEnv *env, jclass gdc, jint gdiScreen)
 354 {
 355     D3DPipelineManager *pMgr;
 356     IDirect3D9 *pd3d9;
 357     jobject ret = NULL;
 358     D3DDISPLAYMODE mode;
 359     UINT adapter;
 360 
 361     J2dTraceLn(J2D_TRACE_INFO, "D3DGD_getCurrentDisplayModeNative");
 362 
 363     RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), NULL);
 364     RETURN_STATUS_IF_NULL(pd3d9 = pMgr->GetD3DObject(), NULL);
 365     adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
 366 
 367     if (SUCCEEDED(pd3d9->GetAdapterDisplayMode(adapter, &mode))) {
 368         int bitDepth = -1;
 369         // these are the only three valid screen formats
 370         switch (mode.Format) {
 371             case D3DFMT_X8R8G8B8: bitDepth = 32; break;
 372             case D3DFMT_R5G6B5:
 373             case D3DFMT_X1R5G5B5: bitDepth = 16; break;
 374         }
 375         J2dTraceLn4(J2D_TRACE_VERBOSE,
 376                     "  current dm: %dx%dx%d@%d",
 377                     mode.Width, mode.Height, bitDepth, mode.RefreshRate);
 378         ret = CreateDisplayMode(env, mode.Width, mode.Height, bitDepth,
 379                                 mode.RefreshRate);
 380     }
 381     return ret;
 382 }
 383 
 384 /*
 385  * Class:     sun_java2d_d3d_D3DGraphicsDevice
 386  * Method:    enumDisplayModesNative
 387  * Signature: (ILjava/util/ArrayList;)V
 388  */
 389 JNIEXPORT void JNICALL
 390 Java_sun_java2d_d3d_D3DGraphicsDevice_enumDisplayModesNative
 391   (JNIEnv *env, jclass gdc, jint gdiScreen, jobject arrayList)
 392 {
 393     D3DPipelineManager *pMgr;
 394     IDirect3D9 *pd3d9;
 395     jobject ret = NULL;
 396     D3DDISPLAYMODE mode;
 397     UINT formatNum, modeNum, modesCount;
 398     UINT adapter;
 399     // EnumAdapterModes treats 555 and 565 formats as equivalents
 400     static D3DFORMAT formats[] =
 401       { D3DFMT_X8R8G8B8, D3DFMT_R5G6B5 };
 402 
 403     J2dTraceLn(J2D_TRACE_INFO, "D3DGD_enumDisplayModesNative");
 404 
 405     RETURN_IF_NULL(pMgr = D3DPipelineManager::GetInstance());
 406     RETURN_IF_NULL(pd3d9 = pMgr->GetD3DObject());
 407     adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
 408 
 409     for (formatNum = 0; formatNum < (sizeof formats)/(sizeof *formats); formatNum++) {
 410         modesCount = pd3d9->GetAdapterModeCount(adapter, formats[formatNum]);
 411         for (modeNum = 0; modeNum < modesCount; modeNum++) {
 412             if (SUCCEEDED(pd3d9->EnumAdapterModes(adapter, formats[formatNum],
 413                                                   modeNum, &mode)))
 414             {
 415                 int bitDepth = -1;
 416                 // these are the only three valid screen formats,
 417                 // 30-bit is returned as X8R8G8B8
 418                 switch (mode.Format) {
 419                     case D3DFMT_X8R8G8B8: bitDepth = 32; break;
 420                     case D3DFMT_R5G6B5:
 421                     case D3DFMT_X1R5G5B5: bitDepth = 16; break;
 422                 }
 423                 J2dTraceLn4(J2D_TRACE_VERBOSE, "  found dm: %dx%dx%d@%d",
 424                             mode.Width, mode.Height, bitDepth,mode.RefreshRate);
 425                 addDisplayMode(env, arrayList, mode.Width, mode.Height,
 426                                bitDepth, mode.RefreshRate);
 427             }
 428         }
 429     }
 430 }
 431 
 432 /*
 433  * Class:     sun_java2d_d3d_D3DGraphicsDevice
 434  * Method:    getAvailableAcceleratedMemoryNative
 435  * Signature: (I)J
 436  */
 437 JNIEXPORT jlong JNICALL
 438 Java_sun_java2d_d3d_D3DGraphicsDevice_getAvailableAcceleratedMemoryNative
 439   (JNIEnv *env, jclass gdc, jint gdiScreen)
 440 {
 441     // REMIND: looks like Direct3D provides information about texture memory
 442     // only via IDirect3DDevice9::GetAvailableTextureMem, however, it
 443     // seems to report the same amount as direct draw used to.
 444     HRESULT res;
 445     D3DPipelineManager *pMgr;
 446     D3DContext *pCtx;
 447     IDirect3DDevice9 *pd3dDevice;
 448     UINT adapter;
 449 
 450     J2dTraceLn(J2D_TRACE_INFO, "D3DGD_getAvailableAcceleratedMemoryNative");
 451 
 452     RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), 0L);
 453     adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
 454 
 455     if (FAILED(res = pMgr->GetD3DContext(adapter, &pCtx))) {
 456         D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
 457         return 0L;
 458     }
 459     RETURN_STATUS_IF_NULL(pd3dDevice = pCtx->Get3DDevice(), 0L);
 460 
 461     UINT mem = pd3dDevice->GetAvailableTextureMem();
 462     J2dTraceLn1(J2D_TRACE_VERBOSE, "  available memory=%d", mem);
 463     return mem;
 464 }
 465 
 466 /*
 467  * Class:     sun_java2d_d3d_D3DGraphicsDevice
 468  * Method:    isD3DAvailableOnDeviceNative
 469  * Signature: (I)Z
 470  */
 471 JNIEXPORT jboolean JNICALL
 472 Java_sun_java2d_d3d_D3DGraphicsDevice_isD3DAvailableOnDeviceNative
 473   (JNIEnv *env, jclass gdc, jint gdiScreen)
 474 {
 475     HRESULT res;
 476     D3DPipelineManager *pMgr;
 477     D3DContext *pCtx;
 478     UINT adapter;
 479 
 480     J2dTraceLn(J2D_TRACE_INFO, "D3DGD_isD3DAvailableOnDeviceNative");
 481 
 482     RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), JNI_FALSE);
 483     adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
 484 
 485     if (FAILED(res = pMgr->GetD3DContext(adapter, &pCtx))) {
 486         D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
 487         return JNI_FALSE;
 488     }
 489 
 490     return JNI_TRUE;
 491 }
 492 
 493 } // extern "C"