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 }