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