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 OGLContext context;
75 
getGLXConfigInfo(int screennum, int visualnum)76     private static native long getGLXConfigInfo(int screennum, int visualnum);
getOGLCapabilities(long configInfo)77     private static native int getOGLCapabilities(long configInfo);
initConfig(long aData, long ctxinfo)78     private native void initConfig(long aData, long ctxinfo);
79 
GLXGraphicsConfig(X11GraphicsDevice device, int visualnum, long configInfo, ContextCapabilities oglCaps)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(), this);
89     }
90 
91     @Override
getProxyKey()92     public Object getProxyKey() {
93         return this;
94     }
95 
96     @Override
createManagedSurface(int w, int h, int transparency)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 
getConfig(X11GraphicsDevice device, int visualnum)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;
GLXGetConfigInfo(int screen, int visual)153         private GLXGetConfigInfo(int screen, int visual) {
154             this.screen = screen;
155             this.visual = visual;
156         }
run()157         public void run() {
158             cfginfo = getGLXConfigInfo(screen, visual);
159         }
getConfigInfo()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
isCapPresent(int cap)170     public final boolean isCapPresent(int cap) {
171         return ((oglCaps.getCaps() & cap) != 0);
172     }
173 
174     @Override
getNativeConfigInfo()175     public final long getNativeConfigInfo() {
176         return pConfigInfo;
177     }
178 
179     @Override
getContext()180     public final OGLContext getContext() {
181         return context;
182     }
183 
184     @Override
createCompatibleImage(int width, int height)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
getColorModel(int transparency)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 
toString()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
createSurfaceData(X11ComponentPeer peer)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
createAcceleratedImage(Component target, int width, int height)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
createBackBuffer(X11ComponentPeer peer, int numBuffers, BufferCapabilities caps)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
destroyBackBuffer(long backBuffer)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
createBackBufferImage(Component target, long backBuffer)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
flip(X11ComponentPeer peer, Component target, VolatileImage xBackBuffer, int x1, int y1, int x2, int y2, BufferCapabilities.FlipContents flipAction)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 {
GLXBufferCaps(boolean dblBuf)360         public GLXBufferCaps(boolean dblBuf) {
361             super(imageCaps, imageCaps,
362                   dblBuf ? FlipContents.UNDEFINED : null);
363         }
364     }
365 
366     @Override
getBufferCapabilities()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 {
GLXImageCaps()375         private GLXImageCaps() {
376             super(true);
377         }
isTrueVolatile()378         public boolean isTrueVolatile() {
379             return true;
380         }
381     }
382 
383     @Override
getImageCapabilities()384     public ImageCapabilities getImageCapabilities() {
385         return imageCaps;
386     }
387 
388     @Override
389     public VolatileImage
createCompatibleVolatileImage(int width, int height, int transparency, int type)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
getContextCapabilities()412     public ContextCapabilities getContextCapabilities() {
413         return oglCaps;
414     }
415 }
416