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