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