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