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