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 com.sun.glass.ui.Screen; 29 import com.sun.javafx.geom.Rectangle; 30 import com.sun.prism.GraphicsResource; 31 import com.sun.prism.Presentable; 32 import com.sun.prism.PresentableState; 33 import com.sun.prism.RTTexture; 34 import com.sun.prism.CompositeMode; 35 import com.sun.prism.impl.PrismSettings; 36 import com.sun.javafx.PlatformUtil; 37 import com.sun.prism.ResourceFactory; 38 import com.sun.prism.Texture.WrapMode; 39 40 class ES2SwapChain implements ES2RenderTarget, Presentable, GraphicsResource { 41 42 private final ES2Context context; 43 private final PresentableState pState; 44 // On screen 45 private GLDrawable drawable; 46 private boolean needsResize; 47 private boolean opaque = false; 48 private int w, h; 49 private float pixelScaleFactor; 50 // a value of zero corresponds to the windowing system-provided 51 // framebuffer object 52 int nativeDestHandle = 0; 53 private final boolean msaa; 54 /** 55 * An offscreen surface that acts as a persistent backbuffer, currently 56 * only used when dirty region optimizations are enabled in the scenegraph. 57 * 58 * In OpenGL, the contents of a window's (hardware) backbuffer are 59 * undefined after a swapBuffers() operation. The dirty region 60 * optimizations used in the Prism scenegraph require the window's 61 * backbuffer to be persistent, so when those optimizations are enabled, 62 * we insert this special stableBackbuffer into the swap chain. 63 * In createGraphics() we return a Graphics object that points to this 64 * stableBackbuffer so that the scenegraph gets rendered into it, 65 * and then at present() time we first copy stableBackbuffer into the 66 * window's hardware backbuffer prior to calling swapBuffers(). 67 */ 68 private RTTexture stableBackbuffer; 69 private boolean copyFullBuffer; 70 71 public boolean isOpaque() { 72 if (stableBackbuffer != null) { 73 return stableBackbuffer.isOpaque(); 74 } else { 75 return opaque; 76 } 77 } 78 79 public void setOpaque(boolean isOpaque) { 80 if (stableBackbuffer != null) { 81 stableBackbuffer.setOpaque(isOpaque); 82 } else { 83 this.opaque = isOpaque; 84 } 85 } 86 87 static float getScale(PresentableState pState) { 88 return PrismSettings.allowHiDPIScaling 89 ? pState.getScale() //TODO fix getScale 90 : 1.0f; 91 } 92 93 ES2SwapChain(ES2Context context, PresentableState pState) { 94 this.context = context; 95 this.pState = pState; 96 this.pixelScaleFactor = getScale(pState); 97 this.msaa = pState.isMSAA(); 98 long nativeWindow = pState.getNativeWindow(); 99 drawable = ES2Pipeline.glFactory.createGLDrawable( 100 nativeWindow, context.getPixelFormat()); 101 } 102 103 public boolean lockResources(PresentableState pState) { 104 if (this.pState != pState || pixelScaleFactor != getScale(pState)) { 105 return true; 106 } 107 needsResize = (w != getPhysicalWidth() || h != getPhysicalHeight()); 108 // the stableBackbuffer will be used as the render target 109 if (stableBackbuffer != null && !needsResize) { 110 stableBackbuffer.lock(); 111 if (stableBackbuffer.isSurfaceLost()) { 112 stableBackbuffer = null; 113 // For resizes we can keep the back buffer, but if we lose 114 // the back buffer then we need the caller to know that a 115 // new buffer is coming so that the entire scene can be 116 // redrawn. To force this, we return true and the Presentable 117 // is recreated and repainted in its entirety. 118 return true; 119 } 120 } 121 return false; 122 } 123 124 public boolean prepare(Rectangle clip) { 125 try { 126 ES2Graphics g = ES2Graphics.create(context, this); 127 if (stableBackbuffer != null) { 128 if (needsResize) { 129 g.forceRenderTarget(); 130 needsResize = false; 131 } 132 // Copy (not blend) the stableBackbuffer into place. 133 //TODO: Determine why w/h is needed here 134 w = getPhysicalWidth(); 135 h = getPhysicalHeight(); 136 Rectangle rectDST = new Rectangle(0, 0, w, h); 137 if (clip != null && !copyFullBuffer) { 138 rectDST.intersectWith(clip); 139 } 140 copyFullBuffer = false; 141 int x0 = rectDST.x; 142 int y0 = rectDST.y; 143 int x1 = x0 + rectDST.width; 144 int y1 = y0 + rectDST.height; 145 if (isMSAA()) { 146 context.flushVertexBuffer(); 147 // Note must flip the z axis during blit 148 g.blit(stableBackbuffer, null, x0, y0, x1, y1, 149 x0, y1, x1, y0); 150 } else { 151 drawTexture(g, stableBackbuffer, x0, y0, x1, y1, 152 x0, y0, x1, y1); 153 } 154 stableBackbuffer.unlock(); 155 } 156 return drawable != null; 157 } catch (Throwable th) { 158 if (PrismSettings.verbose) { 159 th.printStackTrace(); 160 } 161 return false; 162 } 163 } 164 165 private void drawTexture(ES2Graphics g, RTTexture src, 166 float dx1, float dy1, float dx2, float dy2, 167 float sx1, float sy1, float sx2, float sy2) { 168 169 CompositeMode savedMode = g.getCompositeMode(); 170 if (!pState.hasWindowManager()) { 171 // no window manager - we need to do the blending ourselves 172 // pass any window-level alpha setting on to the prism graphics object 173 g.setExtraAlpha(pState.getAlpha()); 174 g.setCompositeMode(CompositeMode.SRC_OVER); 175 } else { 176 // we have a window manager - copy (not blend) stable backbuffer into place 177 g.setCompositeMode(CompositeMode.SRC); 178 } 179 g.drawTexture(src, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2); 180 context.flushVertexBuffer(); 181 // restore the blend 182 g.setCompositeMode(savedMode); 183 } 184 185 public boolean present() { 186 boolean presented = drawable.swapBuffers(context.getGLContext()); 187 context.makeCurrent(null); 188 return presented; 189 } 190 191 public ES2Graphics createGraphics() { 192 context.makeCurrent(drawable); 193 194 nativeDestHandle = pState.getNativeFrameBuffer(); 195 if (nativeDestHandle == 0) { 196 GLContext glContext = context.getGLContext(); 197 nativeDestHandle = glContext.getBoundFBO(); 198 } 199 200 needsResize = (w != getPhysicalWidth() || h != getPhysicalHeight()); 201 // the stableBackbuffer will be used as the render target 202 if (stableBackbuffer == null || needsResize) { 203 // note that we will take care of calling 204 // forceRenderTarget() for the hardware backbuffer and 205 // reset the needsResize flag at present() time... 206 if (stableBackbuffer != null) { 207 stableBackbuffer.dispose(); 208 stableBackbuffer = null; 209 } else { 210 // RT-27554 211 // TODO: this implementation was done to make sure there is a 212 // context current for the hardware backbuffer before we start 213 // attempting to use the FBO associated with the 214 // RTTexture "backbuffer"... 215 ES2Graphics.create(context, this); 216 } 217 w = getPhysicalWidth(); 218 h = getPhysicalHeight(); 219 ResourceFactory factory = context.getResourceFactory(); 220 stableBackbuffer = factory.createRTTexture(w, h, 221 WrapMode.CLAMP_NOT_NEEDED, 222 msaa); 223 if (PrismSettings.dirtyOptsEnabled) { 224 stableBackbuffer.contentsUseful(); 225 } 226 copyFullBuffer = true; 227 } 228 ES2Graphics g = ES2Graphics.create(context, stableBackbuffer); 229 g.scale(pixelScaleFactor, pixelScaleFactor); 230 return g; 231 } 232 233 public int getFboID() { 234 return nativeDestHandle; 235 } 236 237 public Screen getAssociatedScreen() { 238 return context.getAssociatedScreen(); 239 } 240 241 public int getPhysicalWidth() { 242 return (int) (pState.getWidth() * pixelScaleFactor); 243 } 244 245 public int getPhysicalHeight() { 246 return (int) (pState.getHeight() * pixelScaleFactor); 247 } 248 249 public int getContentX() { 250 // EGL doesn't have a window manager, so we need to ask the window for 251 // the x/y offset to use 252 if (PlatformUtil.useEGL()) { 253 return pState.getWindowX(); 254 } else { 255 return 0; 256 } 257 } 258 259 public int getContentY() { 260 // EGL doesn't have a window manager, so we need to ask the window 261 // for the x/y offset to use 262 if (PlatformUtil.useEGL()) { 263 return pState.getScreenHeight() - 264 pState.getHeight() - pState.getWindowY(); 265 } else { 266 return 0; 267 } 268 } 269 270 public int getContentWidth() { 271 return (int) (pState.getWidth() * pixelScaleFactor); 272 } 273 274 public int getContentHeight() { 275 return (int) (pState.getHeight() * pixelScaleFactor); 276 } 277 278 public float getPixelScaleFactor() { 279 return pixelScaleFactor; 280 } 281 282 @Override 283 public void dispose() { 284 if (stableBackbuffer != null) { 285 stableBackbuffer.dispose(); 286 stableBackbuffer = null; 287 } 288 } 289 290 public boolean isMSAA() { 291 return stableBackbuffer != null ? stableBackbuffer.isMSAA() : 292 msaa; 293 } 294 }