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.Color; 31 import java.awt.Component; 32 import java.awt.Graphics; 33 import java.awt.Graphics2D; 34 import java.awt.GraphicsConfiguration; 35 import java.awt.GraphicsDevice; 36 import java.awt.GraphicsEnvironment; 37 import java.awt.Image; 38 import java.awt.ImageCapabilities; 39 import java.awt.Rectangle; 40 import java.awt.Transparency; 41 import java.awt.color.ColorSpace; 42 import java.awt.image.BufferedImage; 43 import java.awt.image.ColorModel; 44 import java.awt.image.DataBuffer; 45 import java.awt.image.DirectColorModel; 46 import java.awt.image.VolatileImage; 47 import java.awt.image.WritableRaster; 48 49 import sun.awt.CGraphicsConfig; 50 import sun.awt.CGraphicsDevice; 51 import sun.awt.TextureSizeConstraining; 52 import sun.awt.image.OffScreenImage; 53 import sun.awt.image.SunVolatileImage; 54 import sun.awt.image.SurfaceManager; 55 import sun.java2d.Disposer; 56 import sun.java2d.DisposerRecord; 57 import sun.java2d.SunGraphics2D; 58 import sun.java2d.Surface; 59 import sun.java2d.SurfaceData; 60 import sun.java2d.opengl.OGLContext.OGLContextCaps; 61 import sun.java2d.pipe.hw.AccelSurface; 62 import sun.java2d.pipe.hw.AccelTypedVolatileImage; 63 import sun.java2d.pipe.hw.ContextCapabilities; 64 import static sun.java2d.opengl.OGLSurfaceData.*; 65 import static sun.java2d.opengl.OGLContext.OGLContextCaps.*; 66 import sun.java2d.opengl.CGLSurfaceData.CGLVSyncOffScreenSurfaceData; 67 import sun.java2d.pipe.hw.AccelDeviceEventListener; 68 import sun.java2d.pipe.hw.AccelDeviceEventNotifier; 69 70 import sun.lwawt.macosx.CPlatformView; 71 72 public class CGLGraphicsConfig extends CGraphicsConfig 73 implements OGLGraphicsConfig, TextureSizeConstraining 74 { 75 //private static final int kOpenGLSwapInterval = RuntimeOptions.getCurrentOptions().OpenGLSwapInterval; 76 private static final int kOpenGLSwapInterval = 0; // TODO 77 protected static boolean cglAvailable; 78 private static ImageCapabilities imageCaps = new CGLImageCaps(); 79 80 private int pixfmt; 81 private BufferCapabilities bufferCaps; 82 private long pConfigInfo; 83 private ContextCapabilities oglCaps; 84 private OGLContext context; 85 private Object disposerReferent = new Object(); 86 87 private final int cachedMaxTextureSize; 88 89 public static native int getDefaultPixFmt(int screennum); 90 private static native boolean initCGL(); 91 private static native long getCGLConfigInfo(int screennum, int visualnum, 92 int swapInterval); 93 private static native int getOGLCapabilities(long configInfo); 94 private static native int _getMaxTextureSize(); 95 96 static { 97 cglAvailable = initCGL(); 98 } 99 100 protected CGLGraphicsConfig(CGraphicsDevice device, int pixfmt, 101 long configInfo, ContextCapabilities oglCaps) 102 { 103 super(device); 104 105 this.pixfmt = pixfmt; 106 this.pConfigInfo = configInfo; 107 this.oglCaps = oglCaps; 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 // 7200762: Workaround a deadlock by caching the value 116 // A fix for JDK 8 will remove the workaround 117 this.cachedMaxTextureSize = _getMaxTextureSize(); 118 } 119 120 @Override 121 public Object getProxyKey() { 122 return this; 123 } 124 125 @Override 126 public SurfaceData createManagedSurface(int w, int h, int transparency) { 127 return CGLSurfaceData.createData(this, w, h, 128 getColorModel(transparency), 129 null, 130 OGLSurfaceData.TEXTURE); 131 } 132 133 public static CGLGraphicsConfig getConfig(CGraphicsDevice device, 134 int pixfmt) 135 { 136 if (!cglAvailable) { 137 return null; 138 } 139 140 long cfginfo = 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.getCoreGraphicsScreen(), pixfmt, 151 kOpenGLSwapInterval); 152 153 OGLContext.setScratchSurface(cfginfo); 154 rq.flushAndInvokeNow(new Runnable() { 155 public void run() { 156 ids[0] = OGLContext.getOGLIdString(); 157 } 158 }); 159 } finally { 160 rq.unlock(); 161 } 162 if (cfginfo == 0) { 163 return null; 164 } 165 166 int oglCaps = getOGLCapabilities(cfginfo); 167 ContextCapabilities caps = new OGLContextCaps(oglCaps, ids[0]); 168 169 return new CGLGraphicsConfig(device, pixfmt, cfginfo, caps); 170 } 171 172 public static boolean isCGLAvailable() { 173 return cglAvailable; 174 } 175 176 /** 177 * Returns true if the provided capability bit is present for this config. 178 * See OGLContext.java for a list of supported capabilities. 179 */ 180 public final boolean isCapPresent(int cap) { 181 return ((oglCaps.getCaps() & cap) != 0); 182 } 183 184 public final long getNativeConfigInfo() { 185 return pConfigInfo; 186 } 187 188 /** 189 * {@inheritDoc} 190 * 191 * @see sun.java2d.pipe.hw.BufferedContextProvider#getContext 192 */ 193 public final OGLContext getContext() { 194 return context; 195 } 196 197 @Override 198 public BufferedImage createCompatibleImage(int width, int height) { 199 ColorModel model = new DirectColorModel(24, 0xff0000, 0xff00, 0xff); 200 WritableRaster 201 raster = model.createCompatibleWritableRaster(width, height); 202 return new BufferedImage(model, raster, model.isAlphaPremultiplied(), 203 null); 204 } 205 206 @Override 207 public ColorModel getColorModel(int transparency) { 208 switch (transparency) { 209 case Transparency.OPAQUE: 210 // REMIND: once the ColorModel spec is changed, this should be 211 // an opaque premultiplied DCM... 212 return new DirectColorModel(24, 0xff0000, 0xff00, 0xff); 213 case Transparency.BITMASK: 214 return new DirectColorModel(25, 0xff0000, 0xff00, 0xff, 0x1000000); 215 case Transparency.TRANSLUCENT: 216 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); 217 return new DirectColorModel(cs, 32, 218 0xff0000, 0xff00, 0xff, 0xff000000, 219 true, DataBuffer.TYPE_INT); 220 default: 221 return null; 222 } 223 } 224 225 public boolean isDoubleBuffered() { 226 return isCapPresent(CAPS_DOUBLEBUFFERED); 227 } 228 229 private static class CGLGCDisposerRecord implements DisposerRecord { 230 private long pCfgInfo; 231 public CGLGCDisposerRecord(long pCfgInfo) { 232 this.pCfgInfo = pCfgInfo; 233 } 234 public void dispose() { 235 if (pCfgInfo != 0) { 236 OGLRenderQueue.disposeGraphicsConfig(pCfgInfo); 237 pCfgInfo = 0; 238 } 239 } 240 } 241 242 // TODO: CGraphicsConfig doesn't implement displayChanged() yet 243 //@Override 244 public synchronized void displayChanged() { 245 //super.displayChanged(); 246 247 // the context could hold a reference to a CGLSurfaceData, which in 248 // turn has a reference back to this CGLGraphicsConfig, so in order 249 // for this instance to be disposed we need to break the connection 250 OGLRenderQueue rq = OGLRenderQueue.getInstance(); 251 rq.lock(); 252 try { 253 OGLContext.invalidateCurrentContext(); 254 } finally { 255 rq.unlock(); 256 } 257 258 updateTotalDisplayBounds(); 259 } 260 261 @Override 262 public String toString() { 263 int screen = getDevice().getCoreGraphicsScreen(); 264 return ("CGLGraphicsConfig[dev="+screen+",pixfmt="+pixfmt+"]"); 265 } 266 267 268 /** 269 * The following methods are invoked from ComponentModel.java rather 270 * than having the Mac OS X-dependent implementations hardcoded in that 271 * class. This way the appropriate actions are taken based on the peer's 272 * GraphicsConfig, whether it is a CGraphicsConfig or a 273 * CGLGraphicsConfig. 274 */ 275 276 /** 277 * Creates a new SurfaceData that will be associated with the given 278 * LWWindowPeer. 279 */ 280 @Override 281 public SurfaceData createSurfaceData(CPlatformView pView) { 282 return CGLSurfaceData.createData(pView); 283 } 284 285 /** 286 * Creates a new SurfaceData that will be associated with the given 287 * CGLLayer. 288 */ 289 @Override 290 public SurfaceData createSurfaceData(CGLLayer layer) { 291 return CGLSurfaceData.createData(layer); 292 } 293 294 /** 295 * Creates a new hidden-acceleration image of the given width and height 296 * that is associated with the target Component. 297 */ 298 @Override 299 public Image createAcceleratedImage(Component target, 300 int width, int height) 301 { 302 ColorModel model = getColorModel(Transparency.OPAQUE); 303 WritableRaster wr = 304 model.createCompatibleWritableRaster(width, height); 305 return new OffScreenImage(target, model, wr, 306 model.isAlphaPremultiplied()); 307 } 308 309 /** 310 * The following methods correspond to the multibuffering methods in 311 * CWindowPeer.java... 312 */ 313 314 /** 315 * Attempts to create a OGL-based backbuffer for the given peer. If 316 * the requested configuration is not natively supported, an AWTException 317 * is thrown. Otherwise, if the backbuffer creation is successful, a 318 * value of 1 is returned. 319 */ 320 @Override 321 public long createBackBuffer(CPlatformView pView, 322 int numBuffers, BufferCapabilities caps) 323 throws AWTException 324 { 325 if (numBuffers > 2) { 326 throw new AWTException( 327 "Only double or single buffering is supported"); 328 } 329 BufferCapabilities configCaps = getBufferCapabilities(); 330 if (!configCaps.isPageFlipping()) { 331 throw new AWTException("Page flipping is not supported"); 332 } 333 if (caps.getFlipContents() == BufferCapabilities.FlipContents.PRIOR) { 334 throw new AWTException("FlipContents.PRIOR is not supported"); 335 } 336 337 // non-zero return value means backbuffer creation was successful 338 // (checked in CPlatformWindow.flip(), etc.) 339 return 1; 340 } 341 342 /** 343 * Destroys the backbuffer object represented by the given handle value. 344 */ 345 @Override 346 public void destroyBackBuffer(long backBuffer) { 347 } 348 349 /** 350 * Creates a VolatileImage that essentially wraps the target Component's 351 * backbuffer (the provided backbuffer handle is essentially ignored). 352 */ 353 @Override 354 public VolatileImage createBackBufferImage(Component target, 355 long backBuffer) 356 { 357 return new SunVolatileImage(target, 358 target.getWidth(), target.getHeight(), 359 Boolean.TRUE); 360 } 361 362 /** 363 * Performs the native OGL flip operation for the given target Component. 364 */ 365 @Override 366 public void flip(CPlatformView pView, 367 Component target, VolatileImage xBackBuffer, 368 int x1, int y1, int x2, int y2, 369 BufferCapabilities.FlipContents flipAction) 370 { 371 if (flipAction == BufferCapabilities.FlipContents.COPIED) { 372 SurfaceManager vsm = SurfaceManager.getManager(xBackBuffer); 373 SurfaceData sd = vsm.getPrimarySurfaceData(); 374 375 if (sd instanceof CGLVSyncOffScreenSurfaceData) { 376 CGLVSyncOffScreenSurfaceData vsd = 377 (CGLVSyncOffScreenSurfaceData)sd; 378 SurfaceData bbsd = vsd.getFlipSurface(); 379 Graphics2D bbg = 380 new SunGraphics2D(bbsd, Color.black, Color.white, null); 381 try { 382 bbg.drawImage(xBackBuffer, 0, 0, null); 383 } finally { 384 bbg.dispose(); 385 } 386 } else { 387 pView.drawImageOnPeer(xBackBuffer, x1, y1, x2, y2); 388 return; 389 } 390 } else if (flipAction == BufferCapabilities.FlipContents.PRIOR) { 391 // not supported by CGL... 392 return; 393 } 394 395 OGLSurfaceData.swapBuffers(pView.getAWTView()); 396 397 if (flipAction == BufferCapabilities.FlipContents.BACKGROUND) { 398 Graphics g = xBackBuffer.getGraphics(); 399 try { 400 g.setColor(target.getBackground()); 401 g.fillRect(0, 0, 402 xBackBuffer.getWidth(), 403 xBackBuffer.getHeight()); 404 } finally { 405 g.dispose(); 406 } 407 } 408 } 409 410 private static class CGLBufferCaps extends BufferCapabilities { 411 public CGLBufferCaps(boolean dblBuf) { 412 super(imageCaps, imageCaps, 413 dblBuf ? FlipContents.UNDEFINED : null); 414 } 415 } 416 417 @Override 418 public BufferCapabilities getBufferCapabilities() { 419 if (bufferCaps == null) { 420 bufferCaps = new CGLBufferCaps(isDoubleBuffered()); 421 } 422 return bufferCaps; 423 } 424 425 private static class CGLImageCaps extends ImageCapabilities { 426 private CGLImageCaps() { 427 super(true); 428 } 429 public boolean isTrueVolatile() { 430 return true; 431 } 432 } 433 434 @Override 435 public ImageCapabilities getImageCapabilities() { 436 return imageCaps; 437 } 438 439 /** 440 * {@inheritDoc} 441 * 442 * @see sun.java2d.pipe.hw.AccelGraphicsConfig#createCompatibleVolatileImage 443 */ 444 public VolatileImage 445 createCompatibleVolatileImage(int width, int height, 446 int transparency, int type) 447 { 448 if (type == FLIP_BACKBUFFER || type == WINDOW || type == UNDEFINED || 449 transparency == Transparency.BITMASK) 450 { 451 return null; 452 } 453 454 if (type == FBOBJECT) { 455 if (!isCapPresent(CAPS_EXT_FBOBJECT)) { 456 return null; 457 } 458 } else if (type == PBUFFER) { 459 boolean isOpaque = transparency == Transparency.OPAQUE; 460 if (!isOpaque && !isCapPresent(CAPS_STORED_ALPHA)) { 461 return null; 462 } 463 } 464 465 SunVolatileImage vi = new AccelTypedVolatileImage(this, width, height, 466 transparency, type); 467 Surface sd = vi.getDestSurface(); 468 if (!(sd instanceof AccelSurface) || 469 ((AccelSurface)sd).getType() != type) 470 { 471 vi.flush(); 472 vi = null; 473 } 474 475 return vi; 476 } 477 478 /** 479 * {@inheritDoc} 480 * 481 * @see sun.java2d.pipe.hw.AccelGraphicsConfig#getContextCapabilities 482 */ 483 public ContextCapabilities getContextCapabilities() { 484 return oglCaps; 485 } 486 487 public void addDeviceEventListener(AccelDeviceEventListener l) { 488 int screen = getDevice().getCoreGraphicsScreen(); 489 AccelDeviceEventNotifier.addListener(l, screen); 490 } 491 492 public void removeDeviceEventListener(AccelDeviceEventListener l) { 493 AccelDeviceEventNotifier.removeListener(l); 494 } 495 496 private static final Rectangle totalDisplayBounds = new Rectangle(); 497 498 private static void updateTotalDisplayBounds() { 499 synchronized (totalDisplayBounds) { 500 Rectangle virtualBounds = new Rectangle(); 501 for (GraphicsDevice gd : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()) { 502 for (GraphicsConfiguration gc : gd.getConfigurations()) { 503 virtualBounds = virtualBounds.union(gc.getBounds()); 504 } 505 } 506 totalDisplayBounds.setBounds(virtualBounds); 507 } 508 } 509 510 511 // 7160609: GL still fails to create a square texture of this size, 512 // so we use this value to cap the total display bounds. 513 private int getMaxTextureSize() { 514 return cachedMaxTextureSize; 515 } 516 517 @Override 518 public int getMaxTextureWidth() { 519 int width; 520 521 synchronized (totalDisplayBounds) { 522 if (totalDisplayBounds.width == 0) { 523 updateTotalDisplayBounds(); 524 } 525 width = totalDisplayBounds.width; 526 } 527 528 return Math.min(width, getMaxTextureSize()); 529 } 530 531 @Override 532 public int getMaxTextureHeight() { 533 int height; 534 535 synchronized (totalDisplayBounds) { 536 if (totalDisplayBounds.height == 0) { 537 updateTotalDisplayBounds(); 538 } 539 height = totalDisplayBounds.height; 540 } 541 542 return Math.min(height, getMaxTextureSize()); 543 } 544 }