1 /*
2  * Copyright (c) 2003, 2016, 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 
getGLXConfigInfo(int screennum, int visualnum)72     private static native long getGLXConfigInfo(int screennum, int visualnum);
getOGLCapabilities(long configInfo)73     private static native int getOGLCapabilities(long configInfo);
initConfig(long aData, long ctxinfo)74     private native void initConfig(long aData, long ctxinfo);
75 
GLXGraphicsConfig(X11GraphicsDevice device, int visualnum, long configInfo, ContextCapabilities oglCaps)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
getProxyKey()88     public Object getProxyKey() {
89         return this;
90     }
91 
92     @Override
createManagedSurface(int w, int h, int transparency)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 
getConfig(X11GraphicsDevice device, int visualnum)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;
GLXGetConfigInfo(int screen, int visual)149         private GLXGetConfigInfo(int screen, int visual) {
150             this.screen = screen;
151             this.visual = visual;
152         }
run()153         public void run() {
154             cfginfo = getGLXConfigInfo(screen, visual);
155         }
getConfigInfo()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
isCapPresent(int cap)166     public final boolean isCapPresent(int cap) {
167         return ((oglCaps.getCaps() & cap) != 0);
168     }
169 
170     @Override
getNativeConfigInfo()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
getContext()181     public final OGLContext getContext() {
182         return context;
183     }
184 
185     @Override
createCompatibleImage(int width, int height)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
getColorModel(int transparency)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 
toString()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
createSurfaceData(X11ComponentPeer peer)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
createAcceleratedImage(Component target, int width, int height)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
createBackBuffer(X11ComponentPeer peer, int numBuffers, BufferCapabilities caps)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
destroyBackBuffer(long backBuffer)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
createBackBufferImage(Component target, long backBuffer)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
flip(X11ComponentPeer peer, Component target, VolatileImage xBackBuffer, int x1, int y1, int x2, int y2, BufferCapabilities.FlipContents flipAction)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 {
GLXBufferCaps(boolean dblBuf)361         public GLXBufferCaps(boolean dblBuf) {
362             super(imageCaps, imageCaps,
363                   dblBuf ? FlipContents.UNDEFINED : null);
364         }
365     }
366 
367     @Override
getBufferCapabilities()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 {
GLXImageCaps()376         private GLXImageCaps() {
377             super(true);
378         }
isTrueVolatile()379         public boolean isTrueVolatile() {
380             return true;
381         }
382     }
383 
384     @Override
getImageCapabilities()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
createCompatibleVolatileImage(int width, int height, int transparency, int type)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
getContextCapabilities()423     public ContextCapabilities getContextCapabilities() {
424         return oglCaps;
425     }
426 }
427