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