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 
getGLXConfigInfo(int screennum, int visualnum)75     private static native long getGLXConfigInfo(int screennum, int visualnum);
getOGLCapabilities(long configInfo)76     private static native int getOGLCapabilities(long configInfo);
initConfig(long aData, long ctxinfo)77     private native void initConfig(long aData, long ctxinfo);
78 
GLXGraphicsConfig(X11GraphicsDevice device, int visualnum, long configInfo, ContextCapabilities oglCaps)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
getProxyKey()91     public Object getProxyKey() {
92         return this;
93     }
94 
95     @Override
createManagedSurface(int w, int h, int transparency)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 
getConfig(X11GraphicsDevice device, int visualnum)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;
GLXGetConfigInfo(int screen, int visual)152         private GLXGetConfigInfo(int screen, int visual) {
153             this.screen = screen;
154             this.visual = visual;
155         }
run()156         public void run() {
157             cfginfo = getGLXConfigInfo(screen, visual);
158         }
getConfigInfo()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
isCapPresent(int cap)169     public final boolean isCapPresent(int cap) {
170         return ((oglCaps.getCaps() & cap) != 0);
171     }
172 
173     @Override
getNativeConfigInfo()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
getContext()184     public final OGLContext getContext() {
185         return context;
186     }
187 
188     @Override
createCompatibleImage(int width, int height)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
getColorModel(int transparency)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 
toString()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
createSurfaceData(X11ComponentPeer peer)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
createAcceleratedImage(Component target, int width, int height)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
createBackBuffer(X11ComponentPeer peer, int numBuffers, BufferCapabilities caps)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
destroyBackBuffer(long backBuffer)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
createBackBufferImage(Component target, long backBuffer)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
flip(X11ComponentPeer peer, Component target, VolatileImage xBackBuffer, int x1, int y1, int x2, int y2, BufferCapabilities.FlipContents flipAction)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 {
GLXBufferCaps(boolean dblBuf)364         public GLXBufferCaps(boolean dblBuf) {
365             super(imageCaps, imageCaps,
366                   dblBuf ? FlipContents.UNDEFINED : null);
367         }
368     }
369 
370     @Override
getBufferCapabilities()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 {
GLXImageCaps()379         private GLXImageCaps() {
380             super(true);
381         }
isTrueVolatile()382         public boolean isTrueVolatile() {
383             return true;
384         }
385     }
386 
387     @Override
getImageCapabilities()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
createCompatibleVolatileImage(int width, int height, int transparency, int type)399         createCompatibleVolatileImage(int width, int height,
400                                       int transparency, int type)
401     {
402         if ((type != FBOBJECT && type != TEXTURE)
403                 || transparency == Transparency.BITMASK
404                 || type == FBOBJECT && !isCapPresent(CAPS_EXT_FBOBJECT)) {
405             return null;
406         }
407         SunVolatileImage vi = new AccelTypedVolatileImage(this, width, height,
408                                                           transparency, type);
409         Surface sd = vi.getDestSurface();
410         if (!(sd instanceof AccelSurface) ||
411             ((AccelSurface)sd).getType() != type)
412         {
413             vi.flush();
414             vi = null;
415         }
416 
417         return vi;
418     }
419 
420     /**
421      * {@inheritDoc}
422      *
423      * @see sun.java2d.pipe.hw.AccelGraphicsConfig#getContextCapabilities
424      */
425     @Override
getContextCapabilities()426     public ContextCapabilities getContextCapabilities() {
427         return oglCaps;
428     }
429 
430     @Override
addDeviceEventListener(AccelDeviceEventListener l)431     public void addDeviceEventListener(AccelDeviceEventListener l) {
432         AccelDeviceEventNotifier.addListener(l, screen.getScreen());
433     }
434 
435     @Override
removeDeviceEventListener(AccelDeviceEventListener l)436     public void removeDeviceEventListener(AccelDeviceEventListener l) {
437         AccelDeviceEventNotifier.removeListener(l);
438     }
439 }
440