1 /* 2 * Copyright (c) 2008, 2015, 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 package com.sun.prism.d3d; 27 28 import com.sun.glass.ui.Screen; 29 import com.sun.javafx.geom.Rectangle; 30 import com.sun.javafx.geom.Vec3d; 31 import com.sun.javafx.geom.transform.Affine3D; 32 import com.sun.javafx.geom.transform.BaseTransform; 33 import com.sun.javafx.geom.transform.GeneralTransform3D; 34 import com.sun.javafx.sg.prism.NGCamera; 35 import com.sun.javafx.sg.prism.NGDefaultCamera; 36 import com.sun.prism.CompositeMode; 37 import com.sun.prism.Graphics; 38 import com.sun.prism.MeshView; 39 import com.sun.prism.RTTexture; 40 import com.sun.prism.RenderTarget; 41 import com.sun.prism.Texture; 42 import com.sun.prism.impl.PrismSettings; 43 import com.sun.prism.impl.ps.BaseShaderContext; 44 import com.sun.prism.ps.Shader; 45 46 class D3DContext extends BaseShaderContext { 47 48 public static final int D3DERR_DEVICENOTRESET = 0x88760869; 49 public static final int D3DERR_DEVICELOST = 0x88760868; 50 public static final int E_FAIL = 0x80004005; 51 public static final int D3DERR_OUTOFVIDEOMEMORY = 0x8876017c; 52 public static final int D3D_OK = 0x0; 53 54 public static final int D3DCOMPMODE_CLEAR = 0; 55 public static final int D3DCOMPMODE_SRC = 1; 56 public static final int D3DCOMPMODE_SRCOVER = 2; 57 public static final int D3DCOMPMODE_DSTOUT = 3; 58 public static final int D3DCOMPMODE_ADD = 4; 59 60 public static final int D3DTADDRESS_NOP = 0; 61 public static final int D3DTADDRESS_WRAP = 1; 62 public static final int D3DTADDRESS_MIRROR = 2; 63 public static final int D3DTADDRESS_CLAMP = 3; 64 public static final int D3DTADDRESS_BORDER = 4; 65 66 // Use by face culling for 3D implementation 67 public final static int CULL_BACK = 110; 68 public final static int CULL_FRONT = 111; 69 public final static int CULL_NONE = 112; 70 /** 71 * WIN32 COM bool FAILED(HRESULT hr) macro synonym 72 * @param hr 73 * @return 74 */ 75 public static boolean FAILED(int hr) { 76 return hr<0; 77 } 78 79 // Temp. variables (Not Thread Safe) 80 private static GeneralTransform3D scratchTx = new GeneralTransform3D(); 81 private static final Affine3D scratchAffine3DTx = new Affine3D(); 82 private static double[] tempAdjustClipSpaceMat = new double[16]; 83 84 private State state; 85 private boolean isLost = false; 86 87 private final long pContext; 88 89 private Vec3d cameraPos = new Vec3d(); 90 private GeneralTransform3D projViewTx = new GeneralTransform3D(); 91 private int targetWidth = 0, targetHeight = 0; 92 93 private final D3DResourceFactory factory; 94 95 public static final int NUM_QUADS = PrismSettings.superShader ? 4096 : 256; 96 97 D3DContext(long pContext, Screen screen, D3DResourceFactory factory) { 98 super(screen, factory, NUM_QUADS); 99 this.pContext = pContext; 100 this.factory = factory; 101 } 102 103 @Override 104 public D3DResourceFactory getResourceFactory() { 105 return factory; 106 } 107 108 protected void initState() { 109 init(); 110 state = new State(); 111 validate(nSetBlendEnabled(pContext, D3DCOMPMODE_SRCOVER)); 112 validate(nSetDeviceParametersFor2D(pContext)); 113 } 114 115 long getContextHandle() { 116 return pContext; 117 } 118 119 /** 120 * Returns whether the context is lost. 121 * @return true if lost, false otherwise 122 */ 123 boolean isLost() { 124 return isLost; 125 } 126 127 /** 128 * Does D3D native return value validation for DEBUG interests 129 */ 130 static void validate(int res) { 131 if (PrismSettings.verbose && FAILED(res)) { 132 System.out.println("D3D hresult failed :" + hResultToString(res)); 133 new Exception("Stack trace").printStackTrace(System.out); 134 } 135 } 136 137 /** 138 * set device to lost state 139 */ 140 private void setLost() { 141 isLost = true; 142 } 143 144 /** 145 * Validates the device, sets the context lost 146 * status if necessary, and tries to restore the context if needed. 147 */ 148 boolean testLostStateAndReset() { 149 int hr = D3DResourceFactory.nTestCooperativeLevel(pContext); 150 151 if (hr == D3DERR_DEVICELOST) { 152 setLost(); 153 } 154 155 if (hr == D3DERR_DEVICENOTRESET) { 156 boolean wasLost = isLost(); 157 setLost(); 158 // disposing the lcd buffer because the device is about to be lost 159 disposeLCDBuffer(); 160 factory.notifyReset(); 161 162 hr = D3DResourceFactory.nResetDevice(pContext); 163 164 if (hr == D3D_OK) { 165 isLost = false; 166 initState(); 167 // Notify caller that the device was reset 168 if (!wasLost) return false; 169 } 170 } 171 172 return !FAILED(hr); 173 } 174 175 /** 176 * Validates result of present operation, 177 * sets the context lost status if necessary 178 */ 179 boolean validatePresent(int res) { 180 if (res == D3DERR_DEVICELOST || res == D3DERR_DEVICENOTRESET) { 181 setLost(); 182 } else { 183 validate(res); 184 } 185 186 return !FAILED(res); 187 } 188 189 /** 190 * OpenGL projection transform use z-range of [-1, 1] while D3D expects it 191 * to be [0, 1], so we need to adjust the matrix, see RT-32880. 192 */ 193 private GeneralTransform3D adjustClipSpace(GeneralTransform3D projViewTx) { 194 double[] m = projViewTx.get(tempAdjustClipSpaceMat); 195 m[8] = (m[8] + m[12])/2; 196 m[9] = (m[9] + m[13])/2; 197 m[10] = (m[10] + m[14])/2; 198 m[11] = (m[11] + m[15])/2; 199 projViewTx.set(m); 200 return projViewTx; 201 } 202 203 @Override 204 protected State updateRenderTarget(RenderTarget target, NGCamera camera, 205 boolean depthTest) { 206 long resourceHandle = ((D3DRenderTarget)target).getResourceHandle(); 207 int res = nSetRenderTarget(pContext, resourceHandle, depthTest, target.isMSAA()); 208 validate(res); 209 // resetLastClip should be called only if render target was changed 210 // return value is S_FALSE (success with negative result) 211 // if render target wasn't changed 212 if (res == D3D_OK) { 213 resetLastClip(state); 214 } 215 216 targetWidth = target.getPhysicalWidth(); 217 targetHeight = target.getPhysicalHeight(); 218 219 // Need to validate the camera before getting its computed data. 220 if (camera instanceof NGDefaultCamera) { 221 ((NGDefaultCamera) camera).validate(targetWidth, targetHeight); 222 projViewTx = adjustClipSpace(camera.getProjViewTx(projViewTx)); 223 } else { 224 projViewTx = adjustClipSpace(camera.getProjViewTx(projViewTx)); 225 // TODO: verify that this is the right solution. There may be 226 // other use-cases where rendering needs different viewport size. 227 double vw = camera.getViewWidth(); 228 double vh = camera.getViewHeight(); 229 if (targetWidth != vw || targetHeight != vh) { 230 projViewTx.scale(vw / targetWidth, vh / targetHeight, 1.0); 231 } 232 } 233 234 // Set projection view matrix 235 res = nSetProjViewMatrix(pContext, depthTest, 236 projViewTx.get(0), projViewTx.get(1), projViewTx.get(2), projViewTx.get(3), 237 projViewTx.get(4), projViewTx.get(5), projViewTx.get(6), projViewTx.get(7), 238 projViewTx.get(8), projViewTx.get(9), projViewTx.get(10), projViewTx.get(11), 239 projViewTx.get(12), projViewTx.get(13), projViewTx.get(14), projViewTx.get(15)); 240 validate(res); 241 242 cameraPos = camera.getPositionInWorld(cameraPos); 243 // System.err.println("Camera position in world = " + cameraPos); 244 245 return state; 246 } 247 248 @Override 249 protected void updateTexture(int texUnit, Texture tex) { 250 long texHandle; 251 boolean linear; 252 int wrapMode; 253 if (tex != null) { 254 D3DTexture d3dtex = (D3DTexture)tex; 255 texHandle = d3dtex.getNativeSourceHandle(); 256 linear = tex.getLinearFiltering(); 257 switch (tex.getWrapMode()) { 258 case CLAMP_NOT_NEEDED: 259 wrapMode = D3DTADDRESS_NOP; 260 break; 261 case CLAMP_TO_EDGE: 262 case CLAMP_TO_EDGE_SIMULATED: 263 case CLAMP_TO_ZERO_SIMULATED: 264 wrapMode = D3DTADDRESS_CLAMP; 265 break; 266 case CLAMP_TO_ZERO: 267 wrapMode = D3DTADDRESS_BORDER; 268 break; 269 case REPEAT: 270 case REPEAT_SIMULATED: 271 wrapMode = D3DTADDRESS_WRAP; 272 break; 273 default: 274 throw new InternalError("Unrecognized wrap mode: "+tex.getWrapMode()); 275 } 276 } else { 277 texHandle = 0L; 278 linear = false; 279 wrapMode = D3DTADDRESS_CLAMP; 280 } 281 validate(nSetTexture(pContext, texHandle, texUnit, linear, wrapMode)); 282 } 283 284 @Override 285 protected void updateShaderTransform(Shader shader, BaseTransform xform) { 286 int res; 287 if (xform == null || xform.isIdentity()) { 288 res = nResetTransform(pContext); 289 } else { 290 res = nSetTransform(pContext, 291 xform.getMxx(), xform.getMxy(), xform.getMxz(), xform.getMxt(), 292 xform.getMyx(), xform.getMyy(), xform.getMyz(), xform.getMyt(), 293 xform.getMzx(), xform.getMzy(), xform.getMzz(), xform.getMzt(), 294 0.0, 0.0, 0.0, 1.0); 295 } 296 validate(res); 297 } 298 299 protected void updateWorldTransform(BaseTransform xform) { 300 if ((xform == null) || xform.isIdentity()) { 301 nSetWorldTransformToIdentity(pContext); 302 } else { 303 nSetWorldTransform(pContext, 304 xform.getMxx(), xform.getMxy(), xform.getMxz(), xform.getMxt(), 305 xform.getMyx(), xform.getMyy(), xform.getMyz(), xform.getMyt(), 306 xform.getMzx(), xform.getMzy(), xform.getMzz(), xform.getMzt(), 307 0.0, 0.0, 0.0, 1.0); 308 } 309 } 310 311 @Override 312 protected void updateClipRect(Rectangle clipRect) { 313 int res; 314 if (clipRect == null || clipRect.isEmpty()) { 315 res = nResetClipRect(pContext); 316 } else { 317 int x1 = clipRect.x; 318 int y1 = clipRect.y; 319 int x2 = x1 + clipRect.width; 320 int y2 = y1 + clipRect.height; 321 res = nSetClipRect(pContext, x1, y1, x2, y2); 322 } 323 validate(res); 324 } 325 326 @Override 327 protected void updateCompositeMode(CompositeMode mode) { 328 int d3dmode; 329 switch (mode) { 330 case CLEAR: 331 d3dmode = D3DCOMPMODE_CLEAR; 332 break; 333 case SRC: 334 d3dmode = D3DCOMPMODE_SRC; 335 break; 336 case SRC_OVER: 337 d3dmode = D3DCOMPMODE_SRCOVER; 338 break; 339 case DST_OUT: 340 d3dmode = D3DCOMPMODE_DSTOUT; 341 break; 342 case ADD: 343 d3dmode = D3DCOMPMODE_ADD; 344 break; 345 default: 346 throw new InternalError("Unrecognized composite mode: "+mode); 347 } 348 validate(nSetBlendEnabled(pContext, d3dmode)); 349 } 350 351 D3DFrameStats getFrameStats(boolean reset, D3DFrameStats result) { 352 if (result == null) { 353 result = new D3DFrameStats(); 354 } 355 return nGetFrameStats(pContext, result, reset) ? result : null; 356 } 357 358 /* 359 * @param depthBuffer if true will create and attach a depthBuffer, 360 * if needed, of the same format as the render target. The depth test state 361 * is handled elsewhere. 362 */ 363 private static native int nSetRenderTarget(long pContext, long pDest, boolean depthBuffer, boolean msaa); 364 private static native int nSetTexture(long pContext, long pTex, int texUnit, 365 boolean linear, int wrapMode); 366 private static native int nResetTransform(long pContext); 367 private static native int nSetTransform(long pContext, 368 double m00, double m01, double m02, double m03, 369 double m10, double m11, double m12, double m13, 370 double m20, double m21, double m22, double m23, 371 double m30, double m31, double m32, double m33); 372 private static native void nSetWorldTransformToIdentity(long pContext); 373 private static native void nSetWorldTransform(long pContext, 374 double m00, double m01, double m02, double m03, 375 double m10, double m11, double m12, double m13, 376 double m20, double m21, double m22, double m23, 377 double m30, double m31, double m32, double m33); 378 private static native int nSetCameraPosition(long pContext, double x, double y, double z); 379 private static native int nSetProjViewMatrix(long pContext, boolean isOrtho, 380 double m00, double m01, double m02, double m03, 381 double m10, double m11, double m12, double m13, 382 double m20, double m21, double m22, double m23, 383 double m30, double m31, double m32, double m33); 384 private static native int nResetClipRect(long pContext); 385 private static native int nSetClipRect(long pContext, 386 int x1, int y1, int x2, int y2); 387 private static native int nSetBlendEnabled(long pContext, int mode); 388 private static native int nSetDeviceParametersFor2D(long pContext); 389 private static native int nSetDeviceParametersFor3D(long pContext); 390 391 private static native long nCreateD3DMesh(long pContext); 392 private static native void nReleaseD3DMesh(long pContext, long nativeHandle); 393 private static native boolean nBuildNativeGeometryShort(long pContext, long nativeHandle, 394 float[] vertexBuffer, int vertexBufferLength, short[] indexBuffer, int indexBufferLength); 395 private static native boolean nBuildNativeGeometryInt(long pContext, long nativeHandle, 396 float[] vertexBuffer, int vertexBufferLength, int[] indexBuffer, int indexBufferLength); 397 private static native long nCreateD3DPhongMaterial(long pContext); 398 private static native void nReleaseD3DPhongMaterial(long pContext, long nativeHandle); 399 private static native void nSetDiffuseColor(long pContext, long nativePhongMaterial, 400 float r, float g, float b, float a); 401 private static native void nSetSpecularColor(long pContext, long nativePhongMaterial, 402 boolean set, float r, float g, float b, float a); 403 private static native void nSetMap(long pContext, long nativePhongMaterial, 404 int mapType, long texID); 405 private static native long nCreateD3DMeshView(long pContext, long nativeMesh); 406 private static native void nReleaseD3DMeshView(long pContext, long nativeHandle); 407 private static native void nSetCullingMode(long pContext, long nativeMeshView, 408 int cullingMode); 409 private static native void nSetMaterial(long pContext, long nativeMeshView, 410 long nativePhongMaterialInfo); 411 private static native void nSetWireframe(long pContext, long nativeMeshView, 412 boolean wireframe); 413 private static native void nSetAmbientLight(long pContext, long nativeMeshView, 414 float r, float g, float b); 415 private static native void nSetPointLight(long pContext, long nativeMeshView, 416 int index, float x, float y, float z, float r, float g, float b, float w); 417 private static native void nRenderMeshView(long pContext, long nativeMeshView); 418 private static native int nDrawIndexedQuads(long pContext, 419 float coords[], byte colors[], int numVertices); 420 421 422 /* 423 * @param nSrcRTT must be valid native resource 424 * @param nDstRTT can be NULL if a valide render target is set 425 */ 426 private static native void nBlit(long pContext, long nSrcRTT, long nDstRTT, 427 int srcX0, int srcY0, int srcX1, int srcY1, 428 int dstX0, int dstY0, int dstX1, int dstY1); 429 430 private static native boolean nGetFrameStats(long pContext, 431 D3DFrameStats returnValue, boolean bReset); 432 433 private static native boolean nIsRTTVolatile(long contextHandle); 434 435 public boolean isRTTVolatile() { 436 return nIsRTTVolatile(pContext); 437 } 438 439 public static String hResultToString(long hResult) { 440 switch ((int)hResult) { 441 case (int)D3DERR_DEVICENOTRESET: 442 return "D3DERR_DEVICENOTRESET"; 443 case (int)D3DERR_DEVICELOST: 444 return "D3DERR_DEVICELOST"; 445 case (int)D3DERR_OUTOFVIDEOMEMORY: 446 return "D3DERR_OUTOFVIDEOMEMORY"; 447 case (int)D3D_OK: 448 return "D3D_OK"; 449 default: 450 return "D3D_ERROR " + Long.toHexString(hResult); 451 } 452 } 453 454 @Override 455 public void setDeviceParametersFor2D() { 456 nSetDeviceParametersFor2D(pContext); 457 } 458 459 @Override 460 protected void setDeviceParametersFor3D() { 461 nSetDeviceParametersFor3D(pContext); 462 } 463 464 long createD3DMesh() { 465 return nCreateD3DMesh(pContext); 466 } 467 468 // TODO: 3D - Should this be called dispose? 469 void releaseD3DMesh(long nativeHandle) { 470 nReleaseD3DMesh(pContext, nativeHandle); 471 } 472 473 boolean buildNativeGeometry(long nativeHandle, float[] vertexBuffer, int vertexBufferLength, 474 short[] indexBuffer, int indexBufferLength) { 475 return nBuildNativeGeometryShort(pContext, nativeHandle, vertexBuffer, 476 vertexBufferLength, indexBuffer, indexBufferLength); 477 } 478 479 boolean buildNativeGeometry(long nativeHandle, float[] vertexBuffer, int vertexBufferLength, 480 int[] indexBuffer, int indexBufferLength) { 481 return nBuildNativeGeometryInt(pContext, nativeHandle, vertexBuffer, 482 vertexBufferLength, indexBuffer, indexBufferLength); 483 } 484 485 long createD3DPhongMaterial() { 486 return nCreateD3DPhongMaterial(pContext); 487 } 488 489 // TODO: 3D - Should this be called dispose? 490 void releaseD3DPhongMaterial(long nativeHandle) { 491 nReleaseD3DPhongMaterial(pContext, nativeHandle); 492 } 493 494 void setDiffuseColor(long nativePhongMaterial, float r, float g, float b, float a) { 495 nSetDiffuseColor(pContext, nativePhongMaterial, r, g, b, a); 496 } 497 498 void setSpecularColor(long nativePhongMaterial, boolean set, float r, float g, float b, float a) { 499 nSetSpecularColor(pContext, nativePhongMaterial, set, r, g, b, a); 500 } 501 502 void setMap(long nativePhongMaterial, int mapType, long nativeTexture) { 503 nSetMap(pContext, nativePhongMaterial, mapType, nativeTexture); 504 } 505 506 long createD3DMeshView(long nativeMesh) { 507 return nCreateD3DMeshView(pContext, nativeMesh); 508 } 509 510 // TODO: 3D - Should this be called dispose? 511 void releaseD3DMeshView(long nativeMeshView) { 512 nReleaseD3DMeshView(pContext, nativeMeshView); 513 } 514 515 void setCullingMode(long nativeMeshView, int cullMode) { 516 int cm; 517 if (cullMode == MeshView.CULL_NONE) { 518 cm = CULL_NONE; 519 } else if (cullMode == MeshView.CULL_BACK) { 520 cm = CULL_BACK; 521 } else if (cullMode == MeshView.CULL_FRONT) { 522 cm = CULL_FRONT; 523 } else { 524 throw new IllegalArgumentException("illegal value for CullMode: " + cullMode); 525 } 526 nSetCullingMode(pContext, nativeMeshView, cm); 527 } 528 529 void setMaterial(long nativeMeshView, long nativePhongMaterial) { 530 nSetMaterial(pContext, nativeMeshView, nativePhongMaterial); 531 } 532 533 void setWireframe(long nativeMeshView, boolean wireframe) { 534 nSetWireframe(pContext, nativeMeshView, wireframe); 535 } 536 537 void setAmbientLight(long nativeMeshView, float r, float g, float b) { 538 nSetAmbientLight(pContext, nativeMeshView, r, g, b); 539 } 540 541 void setPointLight(long nativeMeshView, int index, float x, float y, float z, float r, float g, float b, float w) { 542 nSetPointLight(pContext, nativeMeshView, index, x, y, z, r, g, b, w); 543 } 544 545 @Override 546 protected void renderQuads(float coordArray[], byte colorArray[], int numVertices) { 547 int res = nDrawIndexedQuads(pContext, coordArray, colorArray, numVertices); 548 D3DContext.validate(res); 549 } 550 551 void renderMeshView(long nativeMeshView, Graphics g) { 552 553 // Support retina display by scaling the projViewTx and pass it to the shader. 554 scratchTx = scratchTx.set(projViewTx); 555 float pixelScaleFactorX = g.getPixelScaleFactorX(); 556 float pixelScaleFactorY = g.getPixelScaleFactorY(); 557 if (pixelScaleFactorX != 1.0 || pixelScaleFactorY != 1.0) { 558 scratchTx.scale(pixelScaleFactorX, pixelScaleFactorY, 1.0); 559 } 560 561 // Set projection view matrix 562 int res = nSetProjViewMatrix(pContext, g.isDepthTest(), 563 scratchTx.get(0), scratchTx.get(1), scratchTx.get(2), scratchTx.get(3), 564 scratchTx.get(4), scratchTx.get(5), scratchTx.get(6), scratchTx.get(7), 565 scratchTx.get(8), scratchTx.get(9), scratchTx.get(10), scratchTx.get(11), 566 scratchTx.get(12), scratchTx.get(13), scratchTx.get(14), scratchTx.get(15)); 567 validate(res); 568 569 res = nSetCameraPosition(pContext, cameraPos.x, cameraPos.y, cameraPos.z); 570 validate(res); 571 572 // Undo the SwapChain scaling done in createGraphics() because 3D needs 573 // this information in the shader (via projViewTx) 574 BaseTransform xform = g.getTransformNoClone(); 575 if (pixelScaleFactorX != 1.0 || pixelScaleFactorY != 1.0) { 576 scratchAffine3DTx.setToIdentity(); 577 scratchAffine3DTx.scale(1.0 / pixelScaleFactorX, 1.0 / pixelScaleFactorY); 578 scratchAffine3DTx.concatenate(xform); 579 updateWorldTransform(scratchAffine3DTx); 580 } else { 581 updateWorldTransform(xform); 582 } 583 584 nRenderMeshView(pContext, nativeMeshView); 585 } 586 587 @Override 588 public void blit(RTTexture srcRTT, RTTexture dstRTT, 589 int srcX0, int srcY0, int srcX1, int srcY1, 590 int dstX0, int dstY0, int dstX1, int dstY1) { 591 long dstNativeHandle = dstRTT == null ? 0L : ((D3DTexture)dstRTT).getNativeSourceHandle(); 592 long srcNativeHandle = ((D3DTexture)srcRTT).getNativeSourceHandle(); 593 nBlit(pContext, srcNativeHandle, dstNativeHandle, 594 srcX0, srcY0, srcX1, srcY1, 595 dstX0, dstY0, dstX1, dstY1); 596 } 597 }