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