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