1 /* 2 * Copyright (c) 2003, 2008, 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.BufferCapabilities.FlipContents; 31 import java.awt.Color; 32 import java.awt.Component; 33 import java.awt.Graphics; 34 import java.awt.Graphics2D; 35 import java.awt.Image; 36 import java.awt.ImageCapabilities; 37 import java.awt.Transparency; 38 import java.awt.color.ColorSpace; 39 import java.awt.image.BufferedImage; 40 import java.awt.image.ColorModel; 41 import java.awt.image.DataBuffer; 42 import java.awt.image.DirectColorModel; 43 import java.awt.image.VolatileImage; 44 import java.awt.image.WritableRaster; 45 import sun.awt.X11ComponentPeer; 46 import sun.awt.X11GraphicsConfig; 47 import sun.awt.X11GraphicsDevice; 48 import sun.awt.X11GraphicsEnvironment; 49 import sun.awt.image.OffScreenImage; 50 import sun.awt.image.SunVolatileImage; 51 import sun.awt.image.SurfaceManager; 52 import sun.java2d.SunGraphics2D; 53 import sun.java2d.Surface; 54 import sun.java2d.SurfaceData; 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.*; 60 import static sun.java2d.opengl.OGLContext.OGLContextCaps.*; 61 import sun.java2d.opengl.GLXSurfaceData.GLXVSyncOffScreenSurfaceData; 62 import sun.java2d.pipe.hw.AccelDeviceEventListener; 63 import sun.java2d.pipe.hw.AccelDeviceEventNotifier; 64 65 public class GLXGraphicsConfig 66 extends X11GraphicsConfig 67 implements OGLGraphicsConfig 68 { 69 private static ImageCapabilities imageCaps = new GLXImageCaps(); 70 private BufferCapabilities bufferCaps; 71 private long pConfigInfo; 72 private ContextCapabilities oglCaps; 73 private OGLContext context; 74 75 private static native long getGLXConfigInfo(int screennum, int visualnum); 76 private static native int getOGLCapabilities(long configInfo); 77 private native void initConfig(long aData, long ctxinfo); 78 79 private GLXGraphicsConfig(X11GraphicsDevice device, int visualnum, 80 long configInfo, ContextCapabilities oglCaps) 81 { 82 super(device, visualnum, 0, 0, 83 (oglCaps.getCaps() & CAPS_DOUBLEBUFFERED) != 0); 84 pConfigInfo = configInfo; 85 initConfig(getAData(), configInfo); 86 this.oglCaps = oglCaps; 87 context = new OGLContext(OGLRenderQueue.getInstance(), this); 88 } 89 90 @Override 91 public Object getProxyKey() { 92 return this; 93 } 94 95 @Override 96 public SurfaceData createManagedSurface(int w, int h, int transparency) { 97 return GLXSurfaceData.createData(this, w, h, 98 getColorModel(transparency), 99 null, 100 OGLSurfaceData.TEXTURE); 101 } 102 103 public static GLXGraphicsConfig getConfig(X11GraphicsDevice device, 104 int visualnum) 105 { 106 if (!X11GraphicsEnvironment.isGLXAvailable()) { 107 return null; 108 } 109 110 long cfginfo = 0; 111 final String ids[] = new String[1]; 112 OGLRenderQueue rq = OGLRenderQueue.getInstance(); 113 rq.lock(); 114 try { 115 // getGLXConfigInfo() creates and destroys temporary 116 // surfaces/contexts, so we should first invalidate the current 117 // Java-level context and flush the queue... 118 OGLContext.invalidateCurrentContext(); 119 GLXGetConfigInfo action = 120 new GLXGetConfigInfo(device.getScreen(), visualnum); 121 rq.flushAndInvokeNow(action); 122 cfginfo = action.getConfigInfo(); 123 if (cfginfo != 0L) { 124 OGLContext.setScratchSurface(cfginfo); 125 rq.flushAndInvokeNow(new Runnable() { 126 public void run() { 127 ids[0] = OGLContext.getOGLIdString(); 128 } 129 }); 130 } 131 } finally { 132 rq.unlock(); 133 } 134 if (cfginfo == 0) { 135 return null; 136 } 137 138 int oglCaps = getOGLCapabilities(cfginfo); 139 ContextCapabilities caps = new OGLContextCaps(oglCaps, ids[0]); 140 141 return new GLXGraphicsConfig(device, visualnum, cfginfo, caps); 142 } 143 144 /** 145 * This is a small helper class that allows us to execute 146 * getGLXConfigInfo() on the queue flushing thread. 147 */ 148 private static class GLXGetConfigInfo implements Runnable { 149 private int screen; 150 private int visual; 151 private long cfginfo; 152 private GLXGetConfigInfo(int screen, int visual) { 153 this.screen = screen; 154 this.visual = visual; 155 } 156 public void run() { 157 cfginfo = getGLXConfigInfo(screen, visual); 158 } 159 public long getConfigInfo() { 160 return cfginfo; 161 } 162 } 163 164 /** 165 * Returns true if the provided capability bit is present for this config. 166 * See OGLContext.java for a list of supported capabilities. 167 */ 168 @Override 169 public final boolean isCapPresent(int cap) { 170 return ((oglCaps.getCaps() & cap) != 0); 171 } 172 173 @Override 174 public final long getNativeConfigInfo() { 175 return pConfigInfo; 176 } 177 178 /** 179 * {@inheritDoc} 180 * 181 * @see sun.java2d.pipe.hw.BufferedContextProvider#getContext 182 */ 183 @Override 184 public final OGLContext getContext() { 185 return context; 186 } 187 188 @Override 189 public BufferedImage createCompatibleImage(int width, int height) { 190 ColorModel model = new DirectColorModel(24, 0xff0000, 0xff00, 0xff); 191 WritableRaster 192 raster = model.createCompatibleWritableRaster(width, height); 193 return new BufferedImage(model, raster, model.isAlphaPremultiplied(), 194 null); 195 } 196 197 @Override 198 public ColorModel getColorModel(int transparency) { 199 switch (transparency) { 200 case Transparency.OPAQUE: 201 // REMIND: once the ColorModel spec is changed, this should be 202 // an opaque premultiplied DCM... 203 return new DirectColorModel(24, 0xff0000, 0xff00, 0xff); 204 case Transparency.BITMASK: 205 return new DirectColorModel(25, 0xff0000, 0xff00, 0xff, 0x1000000); 206 case Transparency.TRANSLUCENT: 207 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); 208 return new DirectColorModel(cs, 32, 209 0xff0000, 0xff00, 0xff, 0xff000000, 210 true, DataBuffer.TYPE_INT); 211 default: 212 return null; 213 } 214 } 215 216 public String toString() { 217 return ("GLXGraphicsConfig[dev="+screen+ 218 ",vis=0x"+Integer.toHexString(visual)+ 219 "]"); 220 } 221 222 /** 223 * The following methods are invoked from MToolkit or XToolkit.java and 224 * X11ComponentPeer.java rather than having the X11-dependent 225 * implementations hardcoded in those classes. This way the appropriate 226 * actions are taken based on the peer's GraphicsConfig, whether it is 227 * an X11GraphicsConfig or a GLXGraphicsConfig. 228 */ 229 230 /** 231 * Creates a new SurfaceData that will be associated with the given 232 * X11ComponentPeer. 233 */ 234 @Override 235 public SurfaceData createSurfaceData(X11ComponentPeer peer) { 236 return GLXSurfaceData.createData(peer); 237 } 238 239 /** 240 * Creates a new hidden-acceleration image of the given width and height 241 * that is associated with the target Component. 242 */ 243 @Override 244 public Image createAcceleratedImage(Component target, 245 int width, int height) 246 { 247 ColorModel model = getColorModel(Transparency.OPAQUE); 248 WritableRaster wr = 249 model.createCompatibleWritableRaster(width, height); 250 return new OffScreenImage(target, model, wr, 251 model.isAlphaPremultiplied()); 252 } 253 254 /** 255 * The following methods correspond to the multibuffering methods in 256 * X11ComponentPeer.java... 257 */ 258 259 /** 260 * Attempts to create a GLX-based backbuffer for the given peer. If 261 * the requested configuration is not natively supported, an AWTException 262 * is thrown. Otherwise, if the backbuffer creation is successful, a 263 * value of 1 is returned. 264 */ 265 @Override 266 public long createBackBuffer(X11ComponentPeer peer, 267 int numBuffers, BufferCapabilities caps) 268 throws AWTException 269 { 270 if (numBuffers > 2) { 271 throw new AWTException( 272 "Only double or single buffering is supported"); 273 } 274 BufferCapabilities configCaps = getBufferCapabilities(); 275 if (!configCaps.isPageFlipping()) { 276 throw new AWTException("Page flipping is not supported"); 277 } 278 if (caps.getFlipContents() == BufferCapabilities.FlipContents.PRIOR) { 279 throw new AWTException("FlipContents.PRIOR is not supported"); 280 } 281 282 // non-zero return value means backbuffer creation was successful 283 // (checked in X11ComponentPeer.flip(), etc.) 284 return 1; 285 } 286 287 /** 288 * Destroys the backbuffer object represented by the given handle value. 289 */ 290 @Override 291 public void destroyBackBuffer(long backBuffer) { 292 } 293 294 /** 295 * Creates a VolatileImage that essentially wraps the target Component's 296 * backbuffer (the provided backbuffer handle is essentially ignored). 297 */ 298 @Override 299 public VolatileImage createBackBufferImage(Component target, 300 long backBuffer) 301 { 302 return new SunVolatileImage(target, 303 target.getWidth(), target.getHeight(), 304 Boolean.TRUE); 305 } 306 307 /** 308 * Performs the native GLX flip operation for the given target Component. 309 */ 310 @Override 311 public void flip(X11ComponentPeer peer, 312 Component target, VolatileImage xBackBuffer, 313 int x1, int y1, int x2, int y2, 314 BufferCapabilities.FlipContents flipAction) 315 { 316 if (flipAction == BufferCapabilities.FlipContents.COPIED) { 317 SurfaceManager vsm = SurfaceManager.getManager(xBackBuffer); 318 SurfaceData sd = vsm.getPrimarySurfaceData(); 319 320 if (sd instanceof GLXVSyncOffScreenSurfaceData) { 321 GLXVSyncOffScreenSurfaceData vsd = 322 (GLXVSyncOffScreenSurfaceData)sd; 323 SurfaceData bbsd = vsd.getFlipSurface(); 324 Graphics2D bbg = 325 new SunGraphics2D(bbsd, Color.black, Color.white, null); 326 try { 327 bbg.drawImage(xBackBuffer, 0, 0, null); 328 } finally { 329 bbg.dispose(); 330 } 331 } else { 332 Graphics g = peer.getGraphics(); 333 try { 334 g.drawImage(xBackBuffer, 335 x1, y1, x2, y2, 336 x1, y1, x2, y2, 337 null); 338 } finally { 339 g.dispose(); 340 } 341 return; 342 } 343 } else if (flipAction == BufferCapabilities.FlipContents.PRIOR) { 344 // not supported by GLX... 345 return; 346 } 347 348 OGLSurfaceData.swapBuffers(peer.getContentWindow()); 349 350 if (flipAction == BufferCapabilities.FlipContents.BACKGROUND) { 351 Graphics g = xBackBuffer.getGraphics(); 352 try { 353 g.setColor(target.getBackground()); 354 g.fillRect(0, 0, 355 xBackBuffer.getWidth(), 356 xBackBuffer.getHeight()); 357 } finally { 358 g.dispose(); 359 } 360 } 361 } 362 363 private static class GLXBufferCaps extends BufferCapabilities { 364 public GLXBufferCaps(boolean dblBuf) { 365 super(imageCaps, imageCaps, 366 dblBuf ? FlipContents.UNDEFINED : null); 367 } 368 } 369 370 @Override 371 public BufferCapabilities getBufferCapabilities() { 372 if (bufferCaps == null) { 373 bufferCaps = new GLXBufferCaps(isDoubleBuffered()); 374 } 375 return bufferCaps; 376 } 377 378 private static class GLXImageCaps extends ImageCapabilities { 379 private GLXImageCaps() { 380 super(true); 381 } 382 public boolean isTrueVolatile() { 383 return true; 384 } 385 } 386 387 @Override 388 public ImageCapabilities getImageCapabilities() { 389 return imageCaps; 390 } 391 392 /** 393 * {@inheritDoc} 394 * 395 * @see sun.java2d.pipe.hw.AccelGraphicsConfig#createCompatibleVolatileImage 396 */ 397 @Override 398 public VolatileImage 399 createCompatibleVolatileImage(int width, int height, 400 int transparency, int type) 401 { 402 if (type == FLIP_BACKBUFFER || type == WINDOW || type == UNDEFINED || 403 transparency == Transparency.BITMASK) 404 { 405 return null; 406 } 407 408 if (type == FBOBJECT) { 409 if (!isCapPresent(CAPS_EXT_FBOBJECT)) { 410 return null; 411 } 412 } else if (type == PBUFFER) { 413 boolean isOpaque = transparency == Transparency.OPAQUE; 414 if (!isOpaque && !isCapPresent(CAPS_STORED_ALPHA)) { 415 return null; 416 } 417 } 418 419 SunVolatileImage vi = new AccelTypedVolatileImage(this, width, height, 420 transparency, type); 421 Surface sd = vi.getDestSurface(); 422 if (!(sd instanceof AccelSurface) || 423 ((AccelSurface)sd).getType() != type) 424 { 425 vi.flush(); 426 vi = null; 427 } 428 429 return vi; 430 } 431 432 /** 433 * {@inheritDoc} 434 * 435 * @see sun.java2d.pipe.hw.AccelGraphicsConfig#getContextCapabilities 436 */ 437 @Override 438 public ContextCapabilities getContextCapabilities() { 439 return oglCaps; 440 } 441 442 @Override 443 public void addDeviceEventListener(AccelDeviceEventListener l) { 444 AccelDeviceEventNotifier.addListener(l, screen.getScreen()); 445 } 446 447 @Override 448 public void removeDeviceEventListener(AccelDeviceEventListener l) { 449 AccelDeviceEventNotifier.removeListener(l); 450 } 451 }