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