| /* | |
| * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package sun.java2d.opengl; | |
| import sun.awt.util.ThreadGroupUtils; | |
| import sun.java2d.pipe.RenderBuffer; | |
| import sun.java2d.pipe.RenderQueue; | |
| import static sun.java2d.pipe.BufferedOpCodes.*; | |
| /** | |
| * OGL-specific implementation of RenderQueue. This class provides a | |
| * single (daemon) thread that is responsible for periodically flushing | |
| * the queue, thus ensuring that only one thread communicates with the native | |
| * OpenGL libraries for the entire process. | |
| */ | |
| public class OGLRenderQueue extends RenderQueue { | |
| private static OGLRenderQueue theInstance; | |
| private final QueueFlusher flusher; | |
| private OGLRenderQueue() { | |
| /* | |
| * The thread must be a member of a thread group | |
| * which will not get GCed before VM exit. | |
| */ | |
| flusher = new QueueFlusher(); | |
| } | |
| /** | |
| * Returns the single OGLRenderQueue instance. If it has not yet been | |
| * initialized, this method will first construct the single instance | |
| * before returning it. | |
| */ | |
| public static synchronized OGLRenderQueue getInstance() { | |
| if (theInstance == null) { | |
| theInstance = new OGLRenderQueue(); | |
| } | |
| return theInstance; | |
| } | |
| /** | |
| * Flushes the single OGLRenderQueue instance synchronously. If an | |
| * OGLRenderQueue has not yet been instantiated, this method is a no-op. | |
| * This method is useful in the case of Toolkit.sync(), in which we want | |
| * to flush the OGL pipeline, but only if the OGL pipeline is currently | |
| * enabled. Since this class has few external dependencies, callers need | |
| * not be concerned that calling this method will trigger initialization | |
| * of the OGL pipeline and related classes. | |
| */ | |
| public static void sync() { | |
| if (theInstance != null) { | |
| theInstance.lock(); | |
| try { | |
| theInstance.ensureCapacity(4); | |
| theInstance.getBuffer().putInt(SYNC); | |
| theInstance.flushNow(); | |
| } finally { | |
| theInstance.unlock(); | |
| } | |
| } | |
| } | |
| /** | |
| * Disposes the native memory associated with the given native | |
| * graphics config info pointer on the single queue flushing thread. | |
| */ | |
| public static void disposeGraphicsConfig(long pConfigInfo) { | |
| OGLRenderQueue rq = getInstance(); | |
| rq.lock(); | |
| try { | |
| // make sure we make the context associated with the given | |
| // GraphicsConfig current before disposing the native resources | |
| OGLContext.setScratchSurface(pConfigInfo); | |
| RenderBuffer buf = rq.getBuffer(); | |
| rq.ensureCapacityAndAlignment(12, 4); | |
| buf.putInt(DISPOSE_CONFIG); | |
| buf.putLong(pConfigInfo); | |
| // this call is expected to complete synchronously, so flush now | |
| rq.flushNow(); | |
| } finally { | |
| rq.unlock(); | |
| } | |
| } | |
| /** | |
| * Returns true if the current thread is the OGL QueueFlusher thread. | |
| */ | |
| public static boolean isQueueFlusherThread() { | |
| return (Thread.currentThread() == getInstance().flusher.thread); | |
| } | |
| public void flushNow() { | |
| // assert lock.isHeldByCurrentThread(); | |
| try { | |
| flusher.flushNow(); | |
| } catch (Exception e) { | |
| System.err.println("exception in flushNow:"); | |
| e.printStackTrace(); | |
| } | |
| } | |
| public void flushAndInvokeNow(Runnable r) { | |
| // assert lock.isHeldByCurrentThread(); | |
| try { | |
| flusher.flushAndInvokeNow(r); | |
| } catch (Exception e) { | |
| System.err.println("exception in flushAndInvokeNow:"); | |
| e.printStackTrace(); | |
| } | |
| } | |
| private native void flushBuffer(long buf, int limit); | |
| private void flushBuffer() { | |
| // assert lock.isHeldByCurrentThread(); | |
| int limit = buf.position(); | |
| if (limit > 0) { | |
| // process the queue | |
| flushBuffer(buf.getAddress(), limit); | |
| } | |
| // reset the buffer position | |
| buf.clear(); | |
| // clear the set of references, since we no longer need them | |
| refSet.clear(); | |
| } | |
| private class QueueFlusher implements Runnable { | |
| private boolean needsFlush; | |
| private Runnable task; | |
| private Error error; | |
| private final Thread thread; | |
| public QueueFlusher() { | |
| String name = "Java2D Queue Flusher"; | |
| thread = new Thread(ThreadGroupUtils.getRootThreadGroup(), | |
| this, name, 0, false); | |
| thread.setDaemon(true); | |
| thread.setPriority(Thread.MAX_PRIORITY); | |
| thread.start(); | |
| } | |
| public synchronized void flushNow() { | |
| // wake up the flusher | |
| needsFlush = true; | |
| notify(); | |
| // wait for flush to complete | |
| while (needsFlush) { | |
| try { | |
| wait(); | |
| } catch (InterruptedException e) { | |
| } | |
| } | |
| // re-throw any error that may have occurred during the flush | |
| if (error != null) { | |
| throw error; | |
| } | |
| } | |
| public synchronized void flushAndInvokeNow(Runnable task) { | |
| this.task = task; | |
| flushNow(); | |
| } | |
| public synchronized void run() { | |
| boolean timedOut = false; | |
| while (true) { | |
| while (!needsFlush) { | |
| try { | |
| timedOut = false; | |
| /* | |
| * Wait until we're woken up with a flushNow() call, | |
| * or the timeout period elapses (so that we can | |
| * flush the queue periodically). | |
| */ | |
| wait(100); | |
| /* | |
| * We will automatically flush the queue if the | |
| * following conditions apply: | |
| * - the wait() timed out | |
| * - we can lock the queue (without blocking) | |
| * - there is something in the queue to flush | |
| * Otherwise, just continue (we'll flush eventually). | |
| */ | |
| if (!needsFlush && (timedOut = tryLock())) { | |
| if (buf.position() > 0) { | |
| needsFlush = true; | |
| } else { | |
| unlock(); | |
| } | |
| } | |
| } catch (InterruptedException e) { | |
| } | |
| } | |
| try { | |
| // reset the throwable state | |
| error = null; | |
| // flush the buffer now | |
| flushBuffer(); | |
| // if there's a task, invoke that now as well | |
| if (task != null) { | |
| task.run(); | |
| } | |
| } catch (Error e) { | |
| error = e; | |
| } catch (Exception x) { | |
| System.err.println("exception in QueueFlusher:"); | |
| x.printStackTrace(); | |
| } finally { | |
| if (timedOut) { | |
| unlock(); | |
| } | |
| task = null; | |
| // allow the waiting thread to continue | |
| needsFlush = false; | |
| notify(); | |
| } | |
| } | |
| } | |
| } | |
| } |