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