1 /* 2 * Copyright (c) 2011, 2019, 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 sun.java2d.opengl; 27 28 import java.awt.AWTException; 29 import java.awt.BufferCapabilities; 30 import java.awt.Component; 31 import java.awt.Graphics; 32 import java.awt.Graphics2D; 33 import java.awt.Image; 34 import java.awt.ImageCapabilities; 35 import java.awt.Rectangle; 36 import java.awt.Transparency; 37 import java.awt.color.ColorSpace; 38 import java.awt.image.BufferedImage; 39 import java.awt.image.ColorModel; 40 import java.awt.image.DataBuffer; 41 import java.awt.image.DirectColorModel; 42 import java.awt.image.VolatileImage; 43 import java.awt.image.WritableRaster; 44 45 import sun.awt.CGraphicsConfig; 46 import sun.awt.CGraphicsDevice; 47 import sun.awt.image.OffScreenImage; 48 import sun.awt.image.SunVolatileImage; 49 import sun.java2d.Disposer; 50 import sun.java2d.DisposerRecord; 51 import sun.java2d.Surface; 52 import sun.java2d.SurfaceData; 53 import sun.java2d.opengl.OGLContext.OGLContextCaps; 54 import sun.java2d.pipe.hw.AccelSurface; 55 import sun.java2d.pipe.hw.AccelTypedVolatileImage; 56 import sun.java2d.pipe.hw.ContextCapabilities; 57 import sun.lwawt.LWComponentPeer; 58 import sun.lwawt.macosx.CPlatformView; 59 60 import static sun.java2d.opengl.OGLContext.OGLContextCaps.CAPS_DOUBLEBUFFERED; 61 import static sun.java2d.opengl.OGLContext.OGLContextCaps.CAPS_EXT_FBOBJECT; 62 import static sun.java2d.opengl.OGLSurfaceData.FBOBJECT; 63 import static sun.java2d.opengl.OGLSurfaceData.TEXTURE; 64 65 public final class CGLGraphicsConfig extends CGraphicsConfig 66 implements OGLGraphicsConfig 67 { 68 //private static final int kOpenGLSwapInterval = 69 // RuntimeOptions.getCurrentOptions().OpenGLSwapInterval; 70 private static final int kOpenGLSwapInterval = 0; // TODO 71 private static boolean cglAvailable; 72 private static ImageCapabilities imageCaps = new CGLImageCaps(); 73 74 private int pixfmt; 75 private BufferCapabilities bufferCaps; 76 private long pConfigInfo; 77 private ContextCapabilities oglCaps; 78 private OGLContext context; 79 private final Object disposerReferent = new Object(); 80 private final int maxTextureSize; 81 82 private static native boolean initCGL(); 83 private static native long getCGLConfigInfo(int displayID, int visualnum, 84 int swapInterval); 85 private static native int getOGLCapabilities(long configInfo); 86 87 /** 88 * Returns GL_MAX_TEXTURE_SIZE from the shared opengl context. Must be 89 * called under OGLRQ lock, because this method change current context. 90 * 91 * @return GL_MAX_TEXTURE_SIZE 92 */ 93 private static native int nativeGetMaxTextureSize(); 94 95 static { 96 cglAvailable = initCGL(); 97 } 98 99 private CGLGraphicsConfig(CGraphicsDevice device, int pixfmt, 100 long configInfo, int maxTextureSize, 101 ContextCapabilities oglCaps) { 102 super(device); 103 104 this.pixfmt = pixfmt; 105 this.pConfigInfo = configInfo; 106 this.oglCaps = oglCaps; 107 this.maxTextureSize = maxTextureSize; 108 context = new OGLContext(OGLRenderQueue.getInstance(), this); 109 110 // add a record to the Disposer so that we destroy the native 111 // CGLGraphicsConfigInfo data when this object goes away 112 Disposer.addRecord(disposerReferent, 113 new CGLGCDisposerRecord(pConfigInfo)); 114 } 115 116 @Override 117 public Object getProxyKey() { 118 return this; 119 } 120 121 @Override 122 public SurfaceData createManagedSurface(int w, int h, int transparency) { 123 return CGLSurfaceData.createData(this, w, h, 124 getColorModel(transparency), 125 null, 126 OGLSurfaceData.TEXTURE); 127 } 128 129 public static CGLGraphicsConfig getConfig(CGraphicsDevice device, 130 int displayID, int pixfmt) 131 { 132 if (!cglAvailable) { 133 return null; 134 } 135 136 long cfginfo = 0; 137 int textureSize = 0; 138 final String[] ids = new String[1]; 139 OGLRenderQueue rq = OGLRenderQueue.getInstance(); 140 rq.lock(); 141 try { 142 // getCGLConfigInfo() creates and destroys temporary 143 // surfaces/contexts, so we should first invalidate the current 144 // Java-level context and flush the queue... 145 OGLContext.invalidateCurrentContext(); 146 cfginfo = getCGLConfigInfo(displayID, pixfmt, kOpenGLSwapInterval); 147 if (cfginfo != 0L) { 148 textureSize = nativeGetMaxTextureSize(); 149 // 7160609: GL still fails to create a square texture of this 150 // size. Half should be safe enough. 151 // Explicitly not support a texture more than 2^14, see 8010999. 152 textureSize = textureSize <= 16384 ? textureSize / 2 : 8192; 153 OGLContext.setScratchSurface(cfginfo); 154 rq.flushAndInvokeNow(() -> { 155 ids[0] = OGLContext.getOGLIdString(); 156 }); 157 } 158 } finally { 159 rq.unlock(); 160 } 161 if (cfginfo == 0) { 162 return null; 163 } 164 165 int oglCaps = getOGLCapabilities(cfginfo); 166 ContextCapabilities caps = new OGLContextCaps(oglCaps, ids[0]); 167 return new CGLGraphicsConfig(device, pixfmt, cfginfo, textureSize, caps); 168 } 169 170 public static boolean isCGLAvailable() { 171 return cglAvailable; 172 } 173 174 /** 175 * Returns true if the provided capability bit is present for this config. 176 * See OGLContext.java for a list of supported capabilities. 177 */ 178 @Override 179 public boolean isCapPresent(int cap) { 180 return ((oglCaps.getCaps() & cap) != 0); 181 } 182 183 @Override 184 public long getNativeConfigInfo() { 185 return pConfigInfo; 186 } 187 188 @Override 189 public OGLContext getContext() { 190 return context; 191 } 192 193 @Override 194 public BufferedImage createCompatibleImage(int width, int height) { 195 ColorModel model = new DirectColorModel(24, 0xff0000, 0xff00, 0xff); 196 WritableRaster 197 raster = model.createCompatibleWritableRaster(width, height); 198 return new BufferedImage(model, raster, model.isAlphaPremultiplied(), 199 null); 200 } 201 202 @Override 203 public ColorModel getColorModel(int transparency) { 204 switch (transparency) { 205 case Transparency.OPAQUE: 206 // REMIND: once the ColorModel spec is changed, this should be 207 // an opaque premultiplied DCM... 208 return new DirectColorModel(24, 0xff0000, 0xff00, 0xff); 209 case Transparency.BITMASK: 210 return new DirectColorModel(25, 0xff0000, 0xff00, 0xff, 0x1000000); 211 case Transparency.TRANSLUCENT: 212 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); 213 return new DirectColorModel(cs, 32, 214 0xff0000, 0xff00, 0xff, 0xff000000, 215 true, DataBuffer.TYPE_INT); 216 default: 217 return null; 218 } 219 } 220 221 public boolean isDoubleBuffered() { 222 return isCapPresent(CAPS_DOUBLEBUFFERED); 223 } 224 225 private static class CGLGCDisposerRecord implements DisposerRecord { 226 private long pCfgInfo; 227 public CGLGCDisposerRecord(long pCfgInfo) { 228 this.pCfgInfo = pCfgInfo; 229 } 230 public void dispose() { 231 if (pCfgInfo != 0) { 232 OGLRenderQueue.disposeGraphicsConfig(pCfgInfo); 233 pCfgInfo = 0; 234 } 235 } 236 } 237 238 // TODO: CGraphicsConfig doesn't implement displayChanged() yet 239 //@Override 240 public synchronized void displayChanged() { 241 //super.displayChanged(); 242 243 // the context could hold a reference to a CGLSurfaceData, which in 244 // turn has a reference back to this CGLGraphicsConfig, so in order 245 // for this instance to be disposed we need to break the connection 246 OGLRenderQueue rq = OGLRenderQueue.getInstance(); 247 rq.lock(); 248 try { 249 OGLContext.invalidateCurrentContext(); 250 } finally { 251 rq.unlock(); 252 } 253 } 254 255 @Override 256 public String toString() { 257 String display = getDevice().getIDstring(); 258 return ("CGLGraphicsConfig[" + display + ", pixfmt=" + pixfmt + "]"); 259 } 260 261 @Override 262 public SurfaceData createSurfaceData(CPlatformView pView) { 263 return CGLSurfaceData.createData(pView); 264 } 265 266 @Override 267 public SurfaceData createSurfaceData(CGLLayer layer) { 268 return CGLSurfaceData.createData(layer); 269 } 270 271 @Override 272 public Image createAcceleratedImage(Component target, 273 int width, int height) 274 { 275 ColorModel model = getColorModel(Transparency.OPAQUE); 276 WritableRaster wr = model.createCompatibleWritableRaster(width, height); 277 return new OffScreenImage(target, model, wr, 278 model.isAlphaPremultiplied()); 279 } 280 281 @Override 282 public void assertOperationSupported(final int numBuffers, 283 final BufferCapabilities caps) 284 throws AWTException { 285 // Assume this method is never called with numBuffers != 2, as 0 is 286 // unsupported, and 1 corresponds to a SingleBufferStrategy which 287 // doesn't depend on the peer. Screen is considered as a separate 288 // "buffer". 289 if (numBuffers != 2) { 290 throw new AWTException("Only double buffering is supported"); 291 } 292 final BufferCapabilities configCaps = getBufferCapabilities(); 293 if (!configCaps.isPageFlipping()) { 294 throw new AWTException("Page flipping is not supported"); 295 } 296 if (caps.getFlipContents() == BufferCapabilities.FlipContents.PRIOR) { 297 throw new AWTException("FlipContents.PRIOR is not supported"); 298 } 299 } 300 301 @Override 302 public Image createBackBuffer(final LWComponentPeer<?, ?> peer) { 303 final Rectangle r = peer.getBounds(); 304 // It is possible for the component to have size 0x0, adjust it to 305 // be at least 1x1 to avoid IAE 306 final int w = Math.max(1, r.width); 307 final int h = Math.max(1, r.height); 308 final int transparency = peer.isTranslucent() ? Transparency.TRANSLUCENT 309 : Transparency.OPAQUE; 310 return new SunVolatileImage(this, w, h, transparency, null); 311 } 312 313 @Override 314 public void destroyBackBuffer(final Image backBuffer) { 315 if (backBuffer != null) { 316 backBuffer.flush(); 317 } 318 } 319 320 @Override 321 public void flip(final LWComponentPeer<?, ?> peer, final Image backBuffer, 322 final int x1, final int y1, final int x2, final int y2, 323 final BufferCapabilities.FlipContents flipAction) { 324 final Graphics g = peer.getGraphics(); 325 try { 326 g.drawImage(backBuffer, x1, y1, x2, y2, x1, y1, x2, y2, null); 327 } finally { 328 g.dispose(); 329 } 330 if (flipAction == BufferCapabilities.FlipContents.BACKGROUND) { 331 final Graphics2D bg = (Graphics2D) backBuffer.getGraphics(); 332 try { 333 bg.setBackground(peer.getBackground()); 334 bg.clearRect(0, 0, backBuffer.getWidth(null), 335 backBuffer.getHeight(null)); 336 } finally { 337 bg.dispose(); 338 } 339 } 340 } 341 342 private static class CGLBufferCaps extends BufferCapabilities { 343 public CGLBufferCaps(boolean dblBuf) { 344 super(imageCaps, imageCaps, 345 dblBuf ? FlipContents.UNDEFINED : null); 346 } 347 } 348 349 @Override 350 public BufferCapabilities getBufferCapabilities() { 351 if (bufferCaps == null) { 352 bufferCaps = new CGLBufferCaps(isDoubleBuffered()); 353 } 354 return bufferCaps; 355 } 356 357 private static class CGLImageCaps extends ImageCapabilities { 358 private CGLImageCaps() { 359 super(true); 360 } 361 public boolean isTrueVolatile() { 362 return true; 363 } 364 } 365 366 @Override 367 public ImageCapabilities getImageCapabilities() { 368 return imageCaps; 369 } 370 371 @Override 372 public VolatileImage createCompatibleVolatileImage(int width, int height, 373 int transparency, 374 int type) { 375 if ((type != FBOBJECT && type != TEXTURE) 376 || transparency == Transparency.BITMASK 377 || type == FBOBJECT && !isCapPresent(CAPS_EXT_FBOBJECT)) { 378 return null; 379 } 380 SunVolatileImage vi = new AccelTypedVolatileImage(this, width, height, 381 transparency, type); 382 Surface sd = vi.getDestSurface(); 383 if (!(sd instanceof AccelSurface) || 384 ((AccelSurface)sd).getType() != type) 385 { 386 vi.flush(); 387 vi = null; 388 } 389 390 return vi; 391 } 392 393 @Override 394 public ContextCapabilities getContextCapabilities() { 395 return oglCaps; 396 } 397 398 @Override 399 public int getMaxTextureWidth() { 400 return Math.max(maxTextureSize / getDevice().getScaleFactor(), 401 getBounds().width); 402 } 403 404 @Override 405 public int getMaxTextureHeight() { 406 return Math.max(maxTextureSize / getDevice().getScaleFactor(), 407 getBounds().height); 408 } 409 }