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