1 /*
   2  * Copyright (c) 2003, 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.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 != FBOBJECT && type != TEXTURE)
 403                 || transparency == Transparency.BITMASK) {
 404             return null;
 405         }
 406 
 407         if (type == FBOBJECT) {
 408             if (!isCapPresent(CAPS_EXT_FBOBJECT)) {
 409                 return null;
 410             }
 411         }
 412 
 413         SunVolatileImage vi = new AccelTypedVolatileImage(this, width, height,
 414                                                           transparency, type);
 415         Surface sd = vi.getDestSurface();
 416         if (!(sd instanceof AccelSurface) ||
 417             ((AccelSurface)sd).getType() != type)
 418         {
 419             vi.flush();
 420             vi = null;
 421         }
 422 
 423         return vi;
 424     }
 425 
 426     /**
 427      * {@inheritDoc}
 428      *
 429      * @see sun.java2d.pipe.hw.AccelGraphicsConfig#getContextCapabilities
 430      */
 431     @Override
 432     public ContextCapabilities getContextCapabilities() {
 433         return oglCaps;
 434     }
 435 
 436     @Override
 437     public void addDeviceEventListener(AccelDeviceEventListener l) {
 438         AccelDeviceEventNotifier.addListener(l, screen.getScreen());
 439     }
 440 
 441     @Override
 442     public void removeDeviceEventListener(AccelDeviceEventListener l) {
 443         AccelDeviceEventNotifier.removeListener(l);
 444     }
 445 }