1 /* 2 * Copyright (c) 2009, 2014, 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.es2; 27 28 import java.util.HashMap; 29 import com.sun.glass.ui.Screen; 30 import com.sun.javafx.PlatformUtil; 31 import com.sun.javafx.geom.Rectangle; 32 import com.sun.javafx.geom.Vec3d; 33 import com.sun.javafx.geom.transform.Affine2D; 34 import com.sun.javafx.geom.transform.BaseTransform; 35 import com.sun.javafx.geom.transform.GeneralTransform3D; 36 import com.sun.javafx.sg.prism.NGCamera; 37 import com.sun.javafx.sg.prism.NGDefaultCamera; 38 import com.sun.prism.CompositeMode; 39 import com.sun.prism.Material; 40 import com.sun.prism.PixelFormat; 41 import com.sun.prism.RTTexture; 42 import com.sun.prism.RenderTarget; 43 import com.sun.prism.Texture; 44 import com.sun.prism.impl.PrismSettings; 45 import com.sun.prism.impl.ps.BaseShaderContext; 46 import com.sun.prism.ps.Shader; 47 import com.sun.prism.ps.ShaderFactory; 48 49 class ES2Context extends BaseShaderContext { 50 51 // Temporary variables 52 private static GeneralTransform3D scratchTx = new GeneralTransform3D(); 53 private static final GeneralTransform3D flipTx = new GeneralTransform3D(); 54 // contains the combined projection/modelview matrix (elements 0-15) 55 private static float rawMatrix[] = new float[GLContext.NUM_MATRIX_ELEMENTS]; 56 57 private GeneralTransform3D projViewTx = new GeneralTransform3D(); 58 private GeneralTransform3D worldTx = new GeneralTransform3D(); 59 private Vec3d cameraPos = new Vec3d(); 60 61 private RenderTarget currentTarget; 62 private final GLContext glContext; 63 private final GLDrawable dummyGLDrawable; 64 private final GLPixelFormat pixelFormat; 65 private State state; 66 private int quadIndices; 67 // The drawable that is current to the glContext 68 private GLDrawable currentDrawable = null; 69 private int indexBuffer = 0; 70 private int shaderProgram; 71 72 public static final int NUM_QUADS = PrismSettings.superShader ? 4096 : 256; 73 74 private static ES2VertexBuffer createVertexBuffer() { 75 return new ES2VertexBuffer(NUM_QUADS); 76 } 77 78 ES2Context(Screen screen, ShaderFactory factory) { 79 super(screen, factory, createVertexBuffer()); 80 GLFactory glF = ES2Pipeline.glFactory; 81 82 // NOTE: There is issue with the returned value of getNativeScreen. 83 // HMonitor (Windows), GTKMonitor index (Linux) ... 84 // We would prefer HDC (Windows) and screen number(index) (Linux) 85 pixelFormat = 86 glF.createGLPixelFormat(screen.getNativeScreen(), 87 ES2Pipeline.pixelFormatAttributes); 88 89 dummyGLDrawable = glF.createDummyGLDrawable(pixelFormat); 90 glContext = glF.createGLContext(dummyGLDrawable, pixelFormat, 91 glF.getShareContext(), PrismSettings.isVsyncEnabled); 92 makeCurrent(dummyGLDrawable); 93 ES2VertexBuffer vb = (ES2VertexBuffer) getVertexBuffer(); 94 vb.enableVertexAttributes(glContext); 95 96 quadIndices = vb.genQuadsIndexBuffer(NUM_QUADS); 97 setIndexBuffer(quadIndices); 98 state = new State(); 99 100 // JIRA: RT-21739 101 // TODO: This is a temporary mechanism to work well with Glass on Mac due 102 // to the CALayer work. Need to be removed in the early future for 3.0 103 if (PlatformUtil.isMac() || PlatformUtil.isIOS()) { 104 HashMap devDetails = (HashMap) ES2Pipeline.getInstance().getDeviceDetails(); 105 ES2Pipeline.glFactory.updateDeviceDetails(devDetails, glContext); 106 } 107 } 108 109 final void clearContext() { 110 if (currentDrawable != null) { 111 currentDrawable.swapBuffers(glContext); 112 } 113 } 114 115 final void setIndexBuffer(int ib) { 116 if (indexBuffer != ib) { 117 glContext.setIndexBuffer(indexBuffer = ib); 118 } 119 } 120 121 GLContext getGLContext() { 122 return glContext; 123 } 124 125 GLPixelFormat getPixelFormat() { 126 return pixelFormat; 127 } 128 129 ES2Shader getPhongShader(ES2MeshView meshView) { 130 return ES2PhongShader.getShader(meshView, this); 131 } 132 133 // JIRA: RT-21738 134 // TODO: If we can't resolve this platform specific treatment code 135 // by 3.0, we need to refactor it to platform specific project 136 private int savedFBO = 0; 137 void makeCurrent(GLDrawable drawable) { 138 if (drawable == null) { 139 drawable = dummyGLDrawable; 140 } 141 if (PlatformUtil.isMac() || PlatformUtil.isIOS()) { 142 if (drawable != currentDrawable) { 143 if (drawable == dummyGLDrawable) { 144 // Need to restore FBO to Glass' boundFBO 145 glContext.bindFBO(savedFBO); 146 } else { 147 savedFBO = glContext.getBoundFBO(); 148 glContext.makeCurrent(drawable); 149 } 150 currentDrawable = drawable; 151 } 152 } else { // Linux and Windows 153 if (drawable != currentDrawable) { 154 glContext.makeCurrent(drawable); 155 // Need to restore FBO to on screen framebuffer 156 glContext.bindFBO(0); 157 currentDrawable = drawable; 158 } 159 } 160 } 161 162 /** 163 * Called from ES2Graphics.updateRenderTarget() in response to a window 164 * resize event. This method ensures that the context is made current 165 * after the resize event, which is required on Mac OS X in order to 166 * force a call to [NSOpenGLContext update]. 167 */ 168 void forceRenderTarget(ES2Graphics g) { 169 updateRenderTarget(g.getRenderTarget(), g.getCameraNoClone(), 170 g.isDepthTest() && g.isDepthBuffer()); 171 } 172 173 int getShaderProgram() { 174 return shaderProgram; 175 } 176 177 // Forcibly sets the current shader program to the given object. 178 void setShaderProgram(int progid) { 179 shaderProgram = progid; 180 glContext.setShaderProgram(progid); 181 } 182 183 // Sets the current shader program to the given object only if it was 184 // not already the current program. 185 void updateShaderProgram(int progid) { 186 if (progid != shaderProgram) { 187 setShaderProgram(progid); 188 } 189 } 190 191 @Override 192 protected void init() { 193 super.init(); 194 } 195 196 @Override 197 protected void releaseRenderTarget() { 198 currentTarget = null; 199 super.releaseRenderTarget(); 200 } 201 202 @Override 203 protected State updateRenderTarget(RenderTarget target, NGCamera camera, 204 boolean depthTest) { 205 int fboID = ((ES2RenderTarget)target).getFboID(); 206 glContext.bindFBO(fboID); 207 208 boolean msaa = false; 209 if (target instanceof ES2RTTexture) { 210 // Attach a depth buffer to the currently bound FBO 211 ES2RTTexture rtTarget = (ES2RTTexture)target; 212 msaa = rtTarget.isMSAA(); 213 if (depthTest) { 214 rtTarget.attachDepthBuffer(this); 215 } 216 } 217 218 // update viewport 219 int x = target.getContentX(); 220 int y = target.getContentY(); 221 int w = target.getContentWidth(); 222 int h = target.getContentHeight(); 223 glContext.updateViewportAndDepthTest(x, y, w, h, depthTest); 224 glContext.updateMSAAState(msaa); 225 226 if (camera instanceof NGDefaultCamera) { 227 // update projection matrix; this will be uploaded to the shader 228 // along with the modelview matrix in updateShaderTransform() 229 ((NGDefaultCamera) camera).validate(w, h); 230 scratchTx = camera.getProjViewTx(scratchTx); 231 } else { 232 scratchTx = camera.getProjViewTx(scratchTx); 233 // TODO: verify that this is the right solution. There may be 234 // other use-cases where rendering needs different viewport size. 235 double vw = camera.getViewWidth(); 236 double vh = camera.getViewHeight(); 237 if (w != vw || h != vh) { 238 scratchTx.scale(vw / w, vh / h, 1.0); 239 } 240 } 241 242 if (target instanceof ES2RTTexture) { 243 // Compute a flipped version of projViewTx 244 projViewTx.set(flipTx); 245 projViewTx.mul(scratchTx); 246 } else { 247 projViewTx.set(scratchTx); 248 } 249 250 // update camera position; this will be uploaded to the shader 251 // when we switch to 3D state 252 cameraPos = camera.getPositionInWorld(cameraPos); 253 254 currentTarget = target; 255 return state; 256 } 257 258 @Override 259 protected void updateTexture(int texUnit, Texture tex) { 260 glContext.updateActiveTextureUnit(texUnit); 261 262 if (tex == null) { 263 glContext.updateBoundTexture(0); 264 } else { 265 ES2Texture es2Tex = (ES2Texture)tex; 266 glContext.updateBoundTexture(es2Tex.getNativeSourceHandle()); 267 es2Tex.updateWrapState(); 268 es2Tex.updateFilterState(); 269 } 270 } 271 272 @Override 273 protected void updateShaderTransform(Shader shader, BaseTransform xform) { 274 if (xform == null) { 275 xform = BaseTransform.IDENTITY_TRANSFORM; 276 } 277 278 scratchTx.set(projViewTx); 279 updateRawMatrix(scratchTx.mul(xform)); 280 281 ES2Shader es2shader = (ES2Shader) shader; 282 es2shader.setMatrix("mvpMatrix", rawMatrix); 283 // printRawMatrix("mvpMatrix"); 284 285 if (es2shader.isPixcoordUsed()) { 286 // the gl_FragCoord variable is in window coordinates and 287 // does not take the viewport origin into account (or the fact 288 // that we do a y-flip of the projection matrix in the case 289 // of onscreen windows for that matter); we need to update 290 // the special jsl_pixCoordOffset param here so that the shader 291 // can continue to treat pixcoord as if it were in the range 292 // [0,0] to [contentWidth,contentHeight] of the destination surface 293 float xoff = currentTarget.getContentX(); 294 float yoff = currentTarget.getContentY(); 295 float yinv, yflip; 296 if (currentTarget instanceof ES2SwapChain) { 297 // there is a y-flip in this case 298 yinv = currentTarget.getPhysicalHeight(); 299 yflip = 1f; 300 } else { 301 // no y-flip for RTTextures 302 yinv = 0f; 303 yflip = -1f; 304 } 305 shader.setConstant("jsl_pixCoordOffset", xoff, yoff, yinv, yflip); 306 } 307 } 308 309 @Override 310 protected void updateWorldTransform(BaseTransform xform) { 311 worldTx.setIdentity(); 312 if ((xform != null) && (!xform.isIdentity())) { 313 worldTx.mul(xform); 314 } 315 } 316 317 @Override 318 protected void updateClipRect(Rectangle clipRect) { 319 if (clipRect == null || clipRect.isEmpty()) { 320 glContext.scissorTest(false, 0, 0, 0, 0); 321 } else { 322 // the scissor rectangle is specified using the lower-left 323 // origin of the clip region (in the framebuffer's coordinate 324 // space), so we must account for the x/y offsets of the 325 // destination surface, and use a flipped y origin when rendering 326 // to an ES2SwapChain 327 int w = clipRect.width; 328 int h = clipRect.height; 329 int x = currentTarget.getContentX(); 330 int y = currentTarget.getContentY(); 331 if (currentTarget instanceof ES2RTTexture) { 332 x += clipRect.x; 333 y += clipRect.y; 334 } else { 335 int dsth = currentTarget.getPhysicalHeight(); 336 x += clipRect.x; 337 y += dsth - (clipRect.y + h); 338 } 339 glContext.scissorTest(true, x, y, w, h); 340 } 341 } 342 343 @Override 344 protected void updateCompositeMode(CompositeMode mode) { 345 switch (mode) { 346 case CLEAR: 347 glContext.blendFunc(GLContext.GL_ZERO, GLContext.GL_ZERO); 348 break; 349 case SRC: 350 glContext.blendFunc(GLContext.GL_ONE, GLContext.GL_ZERO); 351 break; 352 case SRC_OVER: 353 glContext.blendFunc(GLContext.GL_ONE, GLContext.GL_ONE_MINUS_SRC_ALPHA); 354 break; 355 case DST_OUT: 356 glContext.blendFunc(GLContext.GL_ZERO, GLContext.GL_ONE_MINUS_SRC_ALPHA); 357 break; 358 case ADD: 359 glContext.blendFunc(GLContext.GL_ONE, GLContext.GL_ONE); 360 break; 361 default: 362 throw new InternalError("Unrecognized composite mode: " + mode); 363 } 364 } 365 366 @Override 367 public void setDeviceParametersFor2D() { 368 // invalidate cache data 369 indexBuffer = 0; 370 shaderProgram = 0; 371 glContext.setDeviceParametersFor2D(); 372 373 ES2VertexBuffer vb = (ES2VertexBuffer) getVertexBuffer(); 374 375 // Bind vertex attributes and index buffer 376 vb.enableVertexAttributes(glContext); 377 setIndexBuffer(quadIndices); 378 } 379 380 @Override 381 public void setDeviceParametersFor3D() { 382 383 ES2VertexBuffer vb = (ES2VertexBuffer) getVertexBuffer(); 384 385 // unbind vertex attributes and index buffer 386 vb.disableVertexAttributes(glContext); 387 glContext.setDeviceParametersFor3D(); 388 } 389 390 long createES2Mesh() { 391 return glContext.createES2Mesh(); 392 } 393 394 // TODO: 3D - Should this be called dispose? 395 void releaseES2Mesh(long nativeHandle) { 396 glContext.releaseES2Mesh(nativeHandle); 397 } 398 399 boolean buildNativeGeometry(long nativeHandle, float[] vertexBuffer, 400 int vertexBufferLength, short[] indexBuffer, int indexBufferLength) { 401 return glContext.buildNativeGeometry(nativeHandle, vertexBuffer, 402 vertexBufferLength, indexBuffer, indexBufferLength); 403 } 404 405 boolean buildNativeGeometry(long nativeHandle, float[] vertexBuffer, 406 int vertexBufferLength, int[] indexBuffer, int indexBufferLength) { 407 return glContext.buildNativeGeometry(nativeHandle, vertexBuffer, 408 vertexBufferLength, indexBuffer, indexBufferLength); 409 } 410 411 long createES2PhongMaterial() { 412 return glContext.createES2PhongMaterial(); 413 } 414 415 // TODO: 3D - Should this be called dispose? 416 void releaseES2PhongMaterial(long nativeHandle) { 417 glContext.releaseES2PhongMaterial(nativeHandle); 418 } 419 420 void setSolidColor(long nativeHandle, float r, float g, float b, float a) { 421 glContext.setSolidColor(nativeHandle, r, g, b, a); 422 } 423 424 void setMap(long nativeHandle, int mapType, int texID) { 425 glContext.setMap(nativeHandle, mapType, texID); 426 } 427 428 long createES2MeshView(ES2Mesh mesh) { 429 return glContext.createES2MeshView(mesh.getNativeHandle()); 430 } 431 432 // TODO: 3D - Should this be called dispose? 433 void releaseES2MeshView(long nativeHandle) { 434 glContext.releaseES2MeshView(nativeHandle); 435 } 436 437 void setCullingMode(long nativeHandle, int cullingMode) { 438 // NOTE: Native code has set clockwise order as front-facing 439 glContext.setCullingMode(nativeHandle, cullingMode); 440 } 441 442 void setMaterial(long nativeHandle, Material material) { 443 ES2PhongMaterial es2Material = (ES2PhongMaterial)material; 444 445 glContext.setMaterial(nativeHandle, 446 (es2Material).getNativeHandle()); 447 } 448 449 void setWireframe(long nativeHandle, boolean wireframe) { 450 glContext.setWireframe(nativeHandle, wireframe); 451 } 452 453 void setAmbientLight(long nativeHandle, float r, float g, float b) { 454 glContext.setAmbientLight(nativeHandle, r, g, b); 455 } 456 457 void setPointLight(long nativeHandle, int index, float x, float y, float z, float r, float g, float b, float w) { 458 glContext.setPointLight(nativeHandle, index, x, y, z, r, g, b, w); 459 } 460 461 @Override 462 public void blit(RTTexture srcRTT, RTTexture dstRTT, 463 int srcX0, int srcY0, int srcX1, int srcY1, 464 int dstX0, int dstY0, int dstX1, int dstY1) 465 { 466 // If dstRTT is null then will blit to currently bound fbo 467 int dstFboID = dstRTT == null ? 0 : ((ES2RTTexture)dstRTT).getFboID(); 468 int srcFboID = ((ES2RTTexture)srcRTT).getFboID(); 469 glContext.blitFBO(srcFboID, dstFboID, 470 srcX0, srcY0, srcX1, srcY1, 471 dstX0, dstY0, dstX1, dstY1); 472 } 473 474 void renderMeshView(long nativeHandle, BaseTransform xform, ES2MeshView meshView) { 475 476 ES2Shader shader = (ES2Shader) getPhongShader(meshView); 477 setShaderProgram(shader.getProgramObject()); 478 479 updateRawMatrix(projViewTx); 480 shader.setMatrix("viewProjectionMatrix", rawMatrix); 481 shader.setConstant("camPos", (float) cameraPos.x, 482 (float) cameraPos.y, (float)cameraPos.z); 483 484 updateWorldTransform(xform); 485 updateRawMatrix(worldTx); 486 487 shader.setMatrix("worldMatrix", rawMatrix); 488 // printRawMatrix("worldMatrix"); 489 490 ES2PhongShader.setShaderParamaters(shader, meshView, this); 491 492 glContext.renderMeshView(nativeHandle); 493 } 494 495 void printRawMatrix(String mesg) { 496 System.err.println(mesg + " = "); 497 for (int i = 0; i < 4; i++) { 498 System.err.println(rawMatrix[i] + ", " + rawMatrix[i+4] 499 + ", " + rawMatrix[i+8] + ", " + rawMatrix[i+12]); 500 } 501 } 502 503 // Need to transpose the matrix because OpenGL stores its matrix in 504 // column major (though matrix computation is done in row major) 505 private void updateRawMatrix(GeneralTransform3D src) { 506 rawMatrix[0] = (float)src.get(0); // Scale X 507 rawMatrix[1] = (float)src.get(4); // Shear Y 508 rawMatrix[2] = (float)src.get(8); 509 rawMatrix[3] = (float)src.get(12); 510 rawMatrix[4] = (float)src.get(1); // Shear X 511 rawMatrix[5] = (float)src.get(5); // Scale Y 512 rawMatrix[6] = (float)src.get(9); 513 rawMatrix[7] = (float)src.get(13); 514 rawMatrix[8] = (float)src.get(2); 515 rawMatrix[9] = (float)src.get(6); 516 rawMatrix[10] = (float)src.get(10); 517 rawMatrix[11] = (float)src.get(14); 518 rawMatrix[12] = (float)src.get(3); // Translate X 519 rawMatrix[13] = (float)src.get(7); // Translate Y 520 rawMatrix[14] = (float)src.get(11); 521 rawMatrix[15] = (float)src.get(15); 522 } 523 524 static { 525 BaseTransform tx = Affine2D.getScaleInstance(1.0, -1.0); 526 flipTx.setIdentity(); 527 flipTx.mul(tx); 528 } 529 }