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 26 package sun.java2d.opengl; 27 28 import sun.awt.util.ThreadGroupUtils; 29 import sun.java2d.pipe.RenderBuffer; 30 import sun.java2d.pipe.RenderQueue; 31 32 import static sun.java2d.pipe.BufferedOpCodes.*; 33 import java.security.AccessController; 34 import 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 */ 42 public 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 }