1/*
2 * Copyright (c) 2005, 2012, 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
26package sun.java2d.opengl;
27
28import sun.awt.util.ThreadGroupUtils;
29import sun.java2d.pipe.RenderBuffer;
30import sun.java2d.pipe.RenderQueue;
31
32import static sun.java2d.pipe.BufferedOpCodes.*;
33import java.security.AccessController;
34import java.security.PrivilegedAction;
35
36/**
37 * OGL-specific implementation of RenderQueue.  This class provides a
38 * single (daemon) thread that is responsible for periodically flushing
39 * the queue, thus ensuring that only one thread communicates with the native
40 * OpenGL libraries for the entire process.
41 */
42public class OGLRenderQueue extends RenderQueue {
43
44    private static OGLRenderQueue theInstance;
45    private final QueueFlusher flusher;
46
47    private OGLRenderQueue() {
48        /*
49         * The thread must be a member of a thread group
50         * which will not get GCed before VM exit.
51         */
52        flusher = AccessController.doPrivileged((PrivilegedAction<QueueFlusher>) QueueFlusher::new);
53    }
54
55    /**
56     * Returns the single OGLRenderQueue instance.  If it has not yet been
57     * initialized, this method will first construct the single instance
58     * before returning it.
59     */
60    public static synchronized OGLRenderQueue getInstance() {
61        if (theInstance == null) {
62            theInstance = new OGLRenderQueue();
63        }
64        return theInstance;
65    }
66
67    /**
68     * Flushes the single OGLRenderQueue instance synchronously.  If an
69     * OGLRenderQueue has not yet been instantiated, this method is a no-op.
70     * This method is useful in the case of Toolkit.sync(), in which we want
71     * to flush the OGL pipeline, but only if the OGL pipeline is currently
72     * enabled.  Since this class has few external dependencies, callers need
73     * not be concerned that calling this method will trigger initialization
74     * of the OGL pipeline and related classes.
75     */
76    public static void sync() {
77        if (theInstance != null) {
78            theInstance.lock();
79            try {
80                theInstance.ensureCapacity(4);
81                theInstance.getBuffer().putInt(SYNC);
82                theInstance.flushNow();
83            } finally {
84                theInstance.unlock();
85            }
86        }
87    }
88
89    /**
90     * Disposes the native memory associated with the given native
91     * graphics config info pointer on the single queue flushing thread.
92     */
93    public static void disposeGraphicsConfig(long pConfigInfo) {
94        OGLRenderQueue rq = getInstance();
95        rq.lock();
96        try {
97            // make sure we make the context associated with the given
98            // GraphicsConfig current before disposing the native resources
99            OGLContext.setScratchSurface(pConfigInfo);
100
101            RenderBuffer buf = rq.getBuffer();
102            rq.ensureCapacityAndAlignment(12, 4);
103            buf.putInt(DISPOSE_CONFIG);
104            buf.putLong(pConfigInfo);
105
106            // this call is expected to complete synchronously, so flush now
107            rq.flushNow();
108        } finally {
109            rq.unlock();
110        }
111    }
112
113    /**
114     * Returns true if the current thread is the OGL QueueFlusher thread.
115     */
116    public static boolean isQueueFlusherThread() {
117        return (Thread.currentThread() == getInstance().flusher.thread);
118    }
119
120    public void flushNow() {
121        // assert lock.isHeldByCurrentThread();
122        try {
123            flusher.flushNow();
124        } catch (Exception e) {
125            System.err.println("exception in flushNow:");
126            e.printStackTrace();
127        }
128    }
129
130    public void flushAndInvokeNow(Runnable r) {
131        // assert lock.isHeldByCurrentThread();
132        try {
133            flusher.flushAndInvokeNow(r);
134        } catch (Exception e) {
135            System.err.println("exception in flushAndInvokeNow:");
136            e.printStackTrace();
137        }
138    }
139
140    private native void flushBuffer(long buf, int limit);
141
142    private void flushBuffer() {
143        // assert lock.isHeldByCurrentThread();
144        int limit = buf.position();
145        if (limit > 0) {
146            // process the queue
147            flushBuffer(buf.getAddress(), limit);
148        }
149        // reset the buffer position
150        buf.clear();
151        // clear the set of references, since we no longer need them
152        refSet.clear();
153    }
154
155    private class QueueFlusher implements Runnable {
156        private boolean needsFlush;
157        private Runnable task;
158        private Error error;
159        private final Thread thread;
160
161        public QueueFlusher() {
162            String name = "Java2D Queue Flusher";
163            thread = new Thread(ThreadGroupUtils.getRootThreadGroup(),
164                                this, name, 0, false);
165            thread.setDaemon(true);
166            thread.setPriority(Thread.MAX_PRIORITY);
167            thread.start();
168        }
169
170        public synchronized void flushNow() {
171            // wake up the flusher
172            needsFlush = true;
173            notify();
174
175            // wait for flush to complete
176            while (needsFlush) {
177                try {
178                    wait();
179                } catch (InterruptedException e) {
180                }
181            }
182
183            // re-throw any error that may have occurred during the flush
184            if (error != null) {
185                throw error;
186            }
187        }
188
189        public synchronized void flushAndInvokeNow(Runnable task) {
190            this.task = task;
191            flushNow();
192        }
193
194        public synchronized void run() {
195            boolean timedOut = false;
196            while (true) {
197                while (!needsFlush) {
198                    try {
199                        timedOut = false;
200                        /*
201                         * Wait until we're woken up with a flushNow() call,
202                         * or the timeout period elapses (so that we can
203                         * flush the queue periodically).
204                         */
205                        wait(100);
206                        /*
207                         * We will automatically flush the queue if the
208                         * following conditions apply:
209                         *   - the wait() timed out
210                         *   - we can lock the queue (without blocking)
211                         *   - there is something in the queue to flush
212                         * Otherwise, just continue (we'll flush eventually).
213                         */
214                        if (!needsFlush && (timedOut = tryLock())) {
215                            if (buf.position() > 0) {
216                                needsFlush = true;
217                            } else {
218                                unlock();
219                            }
220                        }
221                    } catch (InterruptedException e) {
222                    }
223                }
224                try {
225                    // reset the throwable state
226                    error = null;
227                    // flush the buffer now
228                    flushBuffer();
229                    // if there's a task, invoke that now as well
230                    if (task != null) {
231                        task.run();
232                    }
233                } catch (Error e) {
234                    error = e;
235                } catch (Exception x) {
236                    System.err.println("exception in QueueFlusher:");
237                    x.printStackTrace();
238                } finally {
239                    if (timedOut) {
240                        unlock();
241                    }
242                    task = null;
243                    // allow the waiting thread to continue
244                    needsFlush = false;
245                    notify();
246                }
247            }
248        }
249    }
250}
251