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