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.SunHints;
  29 import sun.awt.image.PixelConverter;
  30 import sun.java2d.SunGraphics2D;
  31 import sun.java2d.SurfaceData;
  32 import sun.java2d.SurfaceDataProxy;
  33 import sun.java2d.loops.CompositeType;
  34 import sun.java2d.loops.GraphicsPrimitive;
  35 import sun.java2d.loops.MaskFill;
  36 import sun.java2d.loops.SurfaceType;
  37 import sun.java2d.pipe.ParallelogramPipe;
  38 import sun.java2d.pipe.PixelToParallelogramConverter;
  39 import sun.java2d.pipe.RenderBuffer;
  40 import sun.java2d.pipe.TextPipe;
  41 import sun.java2d.pipe.hw.AccelSurface;
  42 import sun.lwawt.macosx.CPlatformView;
  43 
  44 import java.awt.*;
  45 import java.awt.image.ColorModel;
  46 import java.awt.image.Raster;
  47 
  48 import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_LCD_SHADER;
  49 import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_TEXRECT;
  50 import static sun.java2d.pipe.BufferedOpCodes.FLUSH_SURFACE;
  51 import static sun.java2d.pipe.BufferedOpCodes.SWAP_BUFFERS;
  52 import static sun.java2d.pipe.hw.ContextCapabilities.*;
  53 
  54 public abstract class MTLSurfaceData extends SurfaceData
  55         implements AccelSurface {
  56 
  57     /**
  58      * Pixel formats
  59      */
  60     public static final int PF_INT_ARGB        = 0;
  61     public static final int PF_INT_ARGB_PRE    = 1;
  62     public static final int PF_INT_RGB         = 2;
  63     public static final int PF_INT_RGBX        = 3;
  64     public static final int PF_INT_BGR         = 4;
  65     public static final int PF_INT_BGRX        = 5;
  66     public static final int PF_USHORT_565_RGB  = 6;
  67     public static final int PF_USHORT_555_RGB  = 7;
  68     public static final int PF_USHORT_555_RGBX = 8;
  69     public static final int PF_BYTE_GRAY       = 9;
  70     public static final int PF_USHORT_GRAY     = 10;
  71     public static final int PF_3BYTE_BGR       = 11;
  72     /**
  73      * SurfaceTypes
  74      */
  75 
  76     private static final String DESC_MTL_SURFACE = "MTL Surface";
  77     private static final String DESC_MTL_SURFACE_RTT =
  78             "MTL Surface (render-to-texture)";
  79     private static final String DESC_MTL_TEXTURE = "MTL Texture";
  80 
  81 
  82     static final SurfaceType MTLSurface =
  83         SurfaceType.Any.deriveSubType(DESC_MTL_SURFACE,
  84                                       PixelConverter.ArgbPre.instance);
  85     static final SurfaceType MTLSurfaceRTT =
  86         MTLSurface.deriveSubType(DESC_MTL_SURFACE_RTT);
  87     static final SurfaceType MTLTexture =
  88         SurfaceType.Any.deriveSubType(DESC_MTL_TEXTURE);
  89 
  90     protected static MTLRenderer mtlRenderPipe;
  91     protected static PixelToParallelogramConverter mtlTxRenderPipe;
  92     protected static ParallelogramPipe mtlAAPgramPipe;
  93     protected static MTLTextRenderer mtlTextPipe;
  94     protected static MTLDrawImage mtlImagePipe;
  95     /** This will be true if the fbobject system property has been enabled. */
  96     private static boolean isFBObjectEnabled;
  97     /** This will be true if the lcdshader system property has been enabled.*/
  98     private static boolean isLCDShaderEnabled;
  99     /** This will be true if the biopshader system property has been enabled.*/
 100     private static boolean isBIOpShaderEnabled;
 101     /** This will be true if the gradshader system property has been enabled.*/
 102     private static boolean isGradShaderEnabled;
 103 
 104     static {
 105         if (!GraphicsEnvironment.isHeadless()) {
 106             // fbobject currently enabled by default; use "false" to disable
 107             String fbo = java.security.AccessController.doPrivileged(
 108                 new sun.security.action.GetPropertyAction(
 109                     "java2d.metal.fbobject"));
 110             isFBObjectEnabled = !"false".equals(fbo);
 111 
 112             // lcdshader currently enabled by default; use "false" to disable
 113             String lcd = java.security.AccessController.doPrivileged(
 114                 new sun.security.action.GetPropertyAction(
 115                     "java2d.metal.lcdshader"));
 116             isLCDShaderEnabled = !"false".equals(lcd);
 117 
 118             // biopshader currently enabled by default; use "false" to disable
 119             String biop = java.security.AccessController.doPrivileged(
 120                 new sun.security.action.GetPropertyAction(
 121                     "java2d.metal.biopshader"));
 122             isBIOpShaderEnabled = !"false".equals(biop);
 123 
 124             // gradshader currently enabled by default; use "false" to disable
 125             String grad = java.security.AccessController.doPrivileged(
 126                 new sun.security.action.GetPropertyAction(
 127                     "java2d.metal.gradshader"));
 128             isGradShaderEnabled = !"false".equals(grad);
 129 
 130             MTLRenderQueue rq = MTLRenderQueue.getInstance();
 131             mtlImagePipe = new MTLDrawImage();
 132             mtlTextPipe = new MTLTextRenderer(rq);
 133             mtlRenderPipe = new MTLRenderer(rq);
 134             if (GraphicsPrimitive.tracingEnabled()) {
 135                 mtlTextPipe = mtlTextPipe.traceWrap();
 136                 //The wrapped mtlRenderPipe will wrap the AA pipe as well...
 137                 //mtlAAPgramPipe = mtlRenderPipe.traceWrap();
 138             }
 139             mtlAAPgramPipe = mtlRenderPipe.getAAParallelogramPipe();
 140             mtlTxRenderPipe =
 141                 new PixelToParallelogramConverter(mtlRenderPipe,
 142                         mtlRenderPipe,
 143                                                   1.0, 0.25, true);
 144 
 145             MTLBlitLoops.register();
 146             MTLMaskFill.register();
 147             MTLMaskBlit.register();
 148         }
 149     }
 150 
 151     protected final int scale;
 152     protected final int width;
 153     protected final int height;
 154     protected CPlatformView pView;
 155     protected int type;
 156     private MTLGraphicsConfig graphicsConfig;
 157     // these fields are set from the native code when the surface is
 158     // initialized
 159     private int nativeWidth;
 160     private int nativeHeight;
 161 
 162     /**
 163      * Returns the appropriate SurfaceType corresponding to the given OpenGL
 164      * surface type constant (e.g. TEXTURE -> MTLTexture).
 165      */
 166     private static SurfaceType getCustomSurfaceType(int oglType) {
 167         switch (oglType) {
 168         case TEXTURE:
 169             return MTLTexture;
 170         case RT_TEXTURE:
 171             return MTLSurfaceRTT;
 172         default:
 173             return MTLSurface;
 174         }
 175     }
 176 
 177     static void swapBuffers(long window) {
 178         MTLRenderQueue rq = MTLRenderQueue.getInstance();
 179         rq.lock();
 180         try {
 181             RenderBuffer buf = rq.getBuffer();
 182             rq.ensureCapacityAndAlignment(12, 4);
 183             buf.putInt(SWAP_BUFFERS);
 184             buf.putLong(window);
 185             rq.flushNow();
 186         } finally {
 187             rq.unlock();
 188         }
 189     }
 190 
 191     native void validate(int xoff, int yoff, int width, int height, boolean isOpaque);
 192 
 193     private native void initOps(long pConfigInfo, long pPeerData, long layerPtr,
 194                                 int xoff, int yoff, boolean isOpaque);
 195 
 196     protected MTLSurfaceData(MTLGraphicsConfig gc, ColorModel cm, int type,
 197                              int width, int height) {
 198         super(getCustomSurfaceType(type), cm);
 199         this.graphicsConfig = gc;
 200         this.type = type;
 201         setBlitProxyKey(gc.getProxyKey());
 202 
 203         // TEXTURE shouldn't be scaled, it is used for managed BufferedImages.
 204         scale = type == TEXTURE ? 1 : gc.getDevice().getScaleFactor();
 205         this.width = width * scale;
 206         this.height = height * scale;
 207     }
 208 
 209     protected MTLSurfaceData(CPlatformView pView, MTLGraphicsConfig gc,
 210                              ColorModel cm, int type, int width, int height)
 211     {
 212         this(gc, cm, type, width, height);
 213         this.pView = pView;
 214         this.graphicsConfig = gc;
 215 
 216         long pConfigInfo = gc.getNativeConfigInfo();
 217         long pPeerData = 0L;
 218         boolean isOpaque = true;
 219         if (pView != null) {
 220             pPeerData = pView.getAWTView();
 221             isOpaque = pView.isOpaque();
 222         }
 223         MTLGraphicsConfig.refPConfigInfo(pConfigInfo);
 224         initOps(pConfigInfo, pPeerData, 0, 0, 0, isOpaque);
 225     }
 226 
 227     protected MTLSurfaceData(MTLLayer layer, MTLGraphicsConfig gc,
 228                              ColorModel cm, int type, int width, int height)
 229     {
 230         this(gc, cm, type, width, height);
 231         this.graphicsConfig = gc;
 232 
 233         long pConfigInfo = gc.getNativeConfigInfo();
 234         long layerPtr = 0L;
 235         boolean isOpaque = true;
 236         if (layer != null) {
 237             layerPtr = layer.getPointer();
 238             isOpaque = layer.isOpaque();
 239         }
 240         MTLGraphicsConfig.refPConfigInfo(pConfigInfo);
 241         initOps(pConfigInfo, 0, layerPtr, 0, 0, isOpaque);
 242     }
 243 
 244     @Override //SurfaceData
 245     public GraphicsConfiguration getDeviceConfiguration() {
 246         return graphicsConfig;
 247     }
 248 
 249     /**
 250      * Creates a SurfaceData object representing the primary (front) buffer of
 251      * an on-screen Window.
 252      */
 253     public static MTLWindowSurfaceData createData(CPlatformView pView) {
 254         MTLGraphicsConfig gc = getGC(pView);
 255         return new MTLWindowSurfaceData(pView, gc);
 256     }
 257 
 258     /**
 259      * Creates a SurfaceData object representing the intermediate buffer
 260      * between the Java2D flusher thread and the AppKit thread.
 261      */
 262     public static MTLLayerSurfaceData createData(MTLLayer layer) {
 263         MTLGraphicsConfig gc = getGC(layer);
 264         Rectangle r = layer.getBounds();
 265         return new MTLLayerSurfaceData(layer, gc, r.width, r.height);
 266     }
 267 
 268     /**
 269      * Creates a SurfaceData object representing the back buffer of a
 270      * double-buffered on-screen Window.
 271      */
 272     public static MTLOffScreenSurfaceData createData(CPlatformView pView,
 273                                                      Image image, int type) {
 274         MTLGraphicsConfig gc = getGC(pView);
 275         Rectangle r = pView.getBounds();
 276         if (type == FLIP_BACKBUFFER) {
 277             return new MTLOffScreenSurfaceData(pView, gc, r.width, r.height,
 278                     image, gc.getColorModel(), FLIP_BACKBUFFER);
 279         } else {
 280             return new MTLVSyncOffScreenSurfaceData(pView, gc, r.width,
 281                     r.height, image, gc.getColorModel(), type);
 282         }
 283     }
 284 
 285     /**
 286      * Creates a SurfaceData object representing an off-screen buffer (either a
 287      * FBO or Texture).
 288      */
 289     public static MTLOffScreenSurfaceData createData(MTLGraphicsConfig gc,
 290                                                      int width, int height, ColorModel cm, Image image, int type) {
 291         return new MTLOffScreenSurfaceData(null, gc, width, height, image, cm,
 292                 type);
 293     }
 294 
 295     public static MTLGraphicsConfig getGC(CPlatformView pView) {
 296         if (pView != null) {
 297             return (MTLGraphicsConfig)pView.getGraphicsConfiguration();
 298         } else {
 299             // REMIND: this should rarely (never?) happen, but what if
 300             // default config is not CGL?
 301             GraphicsEnvironment env = GraphicsEnvironment
 302                 .getLocalGraphicsEnvironment();
 303             GraphicsDevice gd = env.getDefaultScreenDevice();
 304             return (MTLGraphicsConfig) gd.getDefaultConfiguration();
 305         }
 306     }
 307 
 308     public static MTLGraphicsConfig getGC(MTLLayer layer) {
 309         return (MTLGraphicsConfig)layer.getGraphicsConfiguration();
 310     }
 311 
 312     public void validate() {
 313         // Overridden in MTLWindowSurfaceData below
 314     }
 315 
 316     @Override
 317     public double getDefaultScaleX() {
 318         return scale;
 319     }
 320 
 321     @Override
 322     public double getDefaultScaleY() {
 323         return scale;
 324     }
 325 
 326     protected native void clearWindow();
 327 
 328     protected native boolean initTexture(long pData,
 329                                          boolean isOpaque, boolean texNonPow2,
 330                                          boolean texRect,
 331                                          int width, int height);
 332 
 333     protected native boolean initRTexture(long pData,
 334                                           boolean isOpaque, boolean texNonPow2,
 335                                           boolean texRect,
 336                                           int width, int height);
 337 
 338     protected native boolean initFlipBackbuffer(long pData);
 339 
 340     @Override
 341     public SurfaceDataProxy makeProxyFor(SurfaceData srcData) {
 342         return MTLSurfaceDataProxy.createProxy(srcData, graphicsConfig);
 343     }
 344 
 345     /**
 346      * Note: This should only be called from the QFT under the AWT lock.
 347      * This method is kept separate from the initSurface() method below just
 348      * to keep the code a bit cleaner.
 349      */
 350     private void initSurfaceNow(int width, int height) {
 351         boolean isOpaque = (getTransparency() == Transparency.OPAQUE);
 352         boolean success = false;
 353 
 354         switch (type) {
 355         case TEXTURE:
 356             success = initTexture(getNativeOps(),
 357                                   isOpaque, isTexNonPow2Available(),
 358                                   isTexRectAvailable(),
 359                                   width, height);
 360             break;
 361 
 362         case RT_TEXTURE:
 363             success = initRTexture(getNativeOps(),
 364                                    isOpaque, isTexNonPow2Available(),
 365                                    isTexRectAvailable(),
 366                                    width, height);
 367             break;
 368 
 369         case FLIP_BACKBUFFER:
 370             success = initFlipBackbuffer(getNativeOps());
 371             break;
 372 
 373         default:
 374             break;
 375         }
 376 
 377         if (!success) {
 378             throw new OutOfMemoryError("can't create offscreen surface");
 379         }
 380     }
 381 
 382     /**
 383      * Initializes the appropriate OpenGL offscreen surface based on the value
 384      * of the type parameter.  If the surface creation fails for any reason,
 385      * an OutOfMemoryError will be thrown.
 386      */
 387     protected void initSurface(final int width, final int height) {
 388         MTLRenderQueue rq = MTLRenderQueue.getInstance();
 389         rq.lock();
 390         try {
 391             switch (type) {
 392             case TEXTURE:
 393             case RT_TEXTURE:
 394                 // need to make sure the context is current before
 395                 // creating the texture or fbobject
 396                 MTLContext.setScratchSurface(graphicsConfig);
 397                 break;
 398             default:
 399                 break;
 400             }
 401             rq.flushAndInvokeNow(new Runnable() {
 402                 public void run() {
 403                     initSurfaceNow(width, height);
 404                 }
 405             });
 406         } finally {
 407             rq.unlock();
 408         }
 409     }
 410 
 411     /**
 412      * Returns the MTLContext for the GraphicsConfig associated with this
 413      * surface.
 414      */
 415     public final MTLContext getContext() {
 416         return graphicsConfig.getContext();
 417     }
 418 
 419     /**
 420      * Returns the MTLGraphicsConfig associated with this surface.
 421      */
 422     final MTLGraphicsConfig getMTLGraphicsConfig() {
 423         return graphicsConfig;
 424     }
 425 
 426     /**
 427      * Returns one of the surface type constants defined above.
 428      */
 429     public final int getType() {
 430         return type;
 431     }
 432 
 433     /**
 434      * For now, we can only render LCD text if:
 435      *   - the fragment shader extension is available, and
 436      *   - the source color is opaque, and
 437      *   - blending is SrcOverNoEa or disabled
 438      *   - and the destination is opaque
 439      *
 440      * Eventually, we could enhance the native OGL text rendering code
 441      * and remove the above restrictions, but that would require significantly
 442      * more code just to support a few uncommon cases.
 443      */
 444     public boolean canRenderLCDText(SunGraphics2D sg2d) {
 445         return
 446             graphicsConfig.isCapPresent(CAPS_EXT_LCD_SHADER) &&
 447             sg2d.surfaceData.getTransparency() == Transparency.OPAQUE &&
 448             sg2d.paintState <= SunGraphics2D.PAINT_OPAQUECOLOR &&
 449             (sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY ||
 450              (sg2d.compositeState <= SunGraphics2D.COMP_ALPHA && canHandleComposite(sg2d.composite)));
 451     }
 452 
 453     private boolean canHandleComposite(Composite c) {
 454         if (c instanceof AlphaComposite) {
 455             AlphaComposite ac = (AlphaComposite)c;
 456 
 457             return ac.getRule() == AlphaComposite.SRC_OVER && ac.getAlpha() >= 1f;
 458         }
 459         return false;
 460     }
 461 
 462     public void validatePipe(SunGraphics2D sg2d) {
 463         TextPipe textpipe;
 464         boolean validated = false;
 465 
 466         // MTLTextRenderer handles both AA and non-AA text, but
 467         // only works with the following modes:
 468         // (Note: For LCD text we only enter this code path if
 469         // canRenderLCDText() has already validated that the mode is
 470         // CompositeType.SrcNoEa (opaque color), which will be subsumed
 471         // by the CompositeType.SrcNoEa (any color) test below.)
 472 
 473         if (/* CompositeType.SrcNoEa (any color) */
 474             (sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY &&
 475              sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR)         ||
 476 
 477             /* CompositeType.SrcOver (any color) */
 478             (sg2d.compositeState == SunGraphics2D.COMP_ALPHA   &&
 479              sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR &&
 480              (((AlphaComposite)sg2d.composite).getRule() ==
 481               AlphaComposite.SRC_OVER))                                 ||
 482 
 483             /* CompositeType.Xor (any color) */
 484             (sg2d.compositeState == SunGraphics2D.COMP_XOR &&
 485              sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR))
 486         {
 487             textpipe = mtlTextPipe;
 488         } else {
 489             // do this to initialize textpipe correctly; we will attempt
 490             // to override the non-text pipes below
 491             super.validatePipe(sg2d);
 492             textpipe = sg2d.textpipe;
 493             validated = true;
 494         }
 495 
 496         PixelToParallelogramConverter txPipe = null;
 497         MTLRenderer nonTxPipe = null;
 498 
 499         if (sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) {
 500             if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) {
 501                 if (sg2d.compositeState <= SunGraphics2D.COMP_XOR) {
 502                     txPipe = mtlTxRenderPipe;
 503                     nonTxPipe = mtlRenderPipe;
 504                 }
 505             } else if (sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) {
 506                 if (MTLPaints.isValid(sg2d)) {
 507                     txPipe = mtlTxRenderPipe;
 508                     nonTxPipe = mtlRenderPipe;
 509                 }
 510                 // custom paints handled by super.validatePipe() below
 511             }
 512         } else {
 513             if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) {
 514                 if (graphicsConfig.isCapPresent(CAPS_PS30) &&
 515                     (sg2d.imageComp == CompositeType.SrcOverNoEa ||
 516                      sg2d.imageComp == CompositeType.SrcOver))
 517                 {
 518                     if (!validated) {
 519                         super.validatePipe(sg2d);
 520                         validated = true;
 521                     }
 522                     PixelToParallelogramConverter aaConverter =
 523                         new PixelToParallelogramConverter(sg2d.shapepipe,
 524                                 mtlAAPgramPipe,
 525                                                           1.0/8.0, 0.499,
 526                                                           false);
 527                     sg2d.drawpipe = aaConverter;
 528                     sg2d.fillpipe = aaConverter;
 529                     sg2d.shapepipe = aaConverter;
 530                 } else if (sg2d.compositeState == SunGraphics2D.COMP_XOR) {
 531                     // install the solid pipes when AA and XOR are both enabled
 532                     txPipe = mtlTxRenderPipe;
 533                     nonTxPipe = mtlRenderPipe;
 534                 }
 535             }
 536             // other cases handled by super.validatePipe() below
 537         }
 538 
 539         if (txPipe != null) {
 540             if (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) {
 541                 sg2d.drawpipe = txPipe;
 542                 sg2d.fillpipe = txPipe;
 543             } else if (sg2d.strokeState != SunGraphics2D.STROKE_THIN) {
 544                 sg2d.drawpipe = txPipe;
 545                 sg2d.fillpipe = nonTxPipe;
 546             } else {
 547                 sg2d.drawpipe = nonTxPipe;
 548                 sg2d.fillpipe = nonTxPipe;
 549             }
 550             // Note that we use the transforming pipe here because it
 551             // will examine the shape and possibly perform an optimized
 552             // operation if it can be simplified.  The simplifications
 553             // will be valid for all STROKE and TRANSFORM types.
 554             sg2d.shapepipe = txPipe;
 555         } else {
 556             if (!validated) {
 557                 super.validatePipe(sg2d);
 558             }
 559         }
 560 
 561         // install the text pipe based on our earlier decision
 562         sg2d.textpipe = textpipe;
 563 
 564         // always override the image pipe with the specialized OGL pipe
 565         sg2d.imagepipe = mtlImagePipe;
 566     }
 567 
 568     @Override
 569     protected MaskFill getMaskFill(SunGraphics2D sg2d) {
 570         if (sg2d.paintState > SunGraphics2D.PAINT_ALPHACOLOR) {
 571             /*
 572              * We can only accelerate non-Color MaskFill operations if
 573              * all of the following conditions hold true:
 574              *   - there is an implementation for the given paintState
 575              *   - the current Paint can be accelerated for this destination
 576              *   - multitexturing is available (since we need to modulate
 577              *     the alpha mask texture with the paint texture)
 578              *
 579              * In all other cases, we return null, in which case the
 580              * validation code will choose a more general software-based loop.
 581              */
 582             if (!MTLPaints.isValid(sg2d) ||
 583                 !graphicsConfig.isCapPresent(CAPS_MULTITEXTURE))
 584             {
 585                 return null;
 586             }
 587         }
 588         return super.getMaskFill(sg2d);
 589     }
 590 
 591     public void flush() {
 592         invalidate();
 593         MTLRenderQueue rq = MTLRenderQueue.getInstance();
 594         rq.lock();
 595         try {
 596             // make sure we have a current context before
 597             // disposing the native resources (e.g. texture object)
 598             MTLContext.setScratchSurface(graphicsConfig);
 599 
 600             RenderBuffer buf = rq.getBuffer();
 601             rq.ensureCapacityAndAlignment(12, 4);
 602             buf.putInt(FLUSH_SURFACE);
 603             buf.putLong(getNativeOps());
 604 
 605             // this call is expected to complete synchronously, so flush now
 606             rq.flushNow();
 607         } finally {
 608             rq.unlock();
 609         }
 610     }
 611 
 612     /**
 613      * Returns true if OpenGL textures can have non-power-of-two dimensions
 614      * when using the basic GL_TEXTURE_2D target.
 615      */
 616     boolean isTexNonPow2Available() {
 617         return graphicsConfig.isCapPresent(CAPS_TEXNONPOW2);
 618     }
 619 
 620     /**
 621      * Returns true if OpenGL textures can have non-power-of-two dimensions
 622      * when using the GL_TEXTURE_RECTANGLE_ARB target (only available when the
 623      * GL_ARB_texture_rectangle extension is present).
 624      */
 625     boolean isTexRectAvailable() {
 626         return graphicsConfig.isCapPresent(CAPS_EXT_TEXRECT);
 627     }
 628 
 629     /**
 630      * Returns true if the surface is an on-screen window surface or
 631      * a FBO texture attached to an on-screen CALayer.
 632      *
 633      * Needed by Mac OS X port.
 634      */
 635     public boolean isOnScreen() {
 636         return getType() == WINDOW;
 637     }
 638 
 639     private native int getTextureTarget(long pData);
 640 
 641     private native int getTextureID(long pData);
 642 
 643     /**
 644      * If this surface is backed by a texture object, returns the target
 645      * for that texture (either GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB).
 646      * Otherwise, this method will return zero.
 647      */
 648     public final int getTextureTarget() {
 649         return getTextureTarget(getNativeOps());
 650     }
 651 
 652     /**
 653      * If this surface is backed by a texture object, returns the texture ID
 654      * for that texture.
 655      * Otherwise, this method will return zero.
 656      */
 657     public final int getTextureID() {
 658         return getTextureID(getNativeOps());
 659     }
 660 
 661     /**
 662      * Returns native resource of specified {@code resType} associated with
 663      * this surface.
 664      *
 665      * Specifically, for {@code MTLSurfaceData} this method returns the
 666      * the following:
 667      * <pre>
 668      * TEXTURE              - texture id
 669      * </pre>
 670      *
 671      * Note: the resource returned by this method is only valid on the rendering
 672      * thread.
 673      *
 674      * @return native resource of specified type or 0L if
 675      * such resource doesn't exist or can not be retrieved.
 676      * @see AccelSurface#getNativeResource
 677      */
 678     public long getNativeResource(int resType) {
 679         if (resType == TEXTURE) {
 680             return getTextureID();
 681         }
 682         return 0L;
 683     }
 684 
 685     public Raster getRaster(int x, int y, int w, int h) {
 686         throw new InternalError("not implemented yet");
 687     }
 688 
 689     @Override
 690     public boolean copyArea(SunGraphics2D sg2d, int x, int y, int w, int h,
 691                             int dx, int dy) {
 692         if (sg2d.compositeState >= SunGraphics2D.COMP_XOR) {
 693             return false;
 694         }
 695         mtlRenderPipe.copyArea(sg2d, x, y, w, h, dx, dy);
 696         return true;
 697     }
 698 
 699     public Rectangle getNativeBounds() {
 700         MTLRenderQueue rq = MTLRenderQueue.getInstance();
 701         rq.lock();
 702         try {
 703             return new Rectangle(nativeWidth, nativeHeight);
 704         } finally {
 705             rq.unlock();
 706         }
 707     }
 708 
 709     public static class MTLWindowSurfaceData extends MTLSurfaceData {
 710 
 711         public MTLWindowSurfaceData(CPlatformView pView,
 712                                     MTLGraphicsConfig gc) {
 713             super(pView, gc, gc.getColorModel(), WINDOW, 0, 0);
 714         }
 715 
 716         @Override
 717         public SurfaceData getReplacement() {
 718             return pView.getSurfaceData();
 719         }
 720 
 721         @Override
 722         public Rectangle getBounds() {
 723             Rectangle r = pView.getBounds();
 724             return new Rectangle(0, 0, r.width, r.height);
 725         }
 726 
 727         /**
 728          * Returns destination Component associated with this SurfaceData.
 729          */
 730         @Override
 731         public Object getDestination() {
 732             return pView.getDestination();
 733         }
 734 
 735         public void validate() {
 736             MTLRenderQueue rq = MTLRenderQueue.getInstance();
 737             rq.lock();
 738             try {
 739                 rq.flushAndInvokeNow(() -> {
 740                     Rectangle peerBounds = pView.getBounds();
 741                     validate(0, 0, peerBounds.width, peerBounds.height, pView.isOpaque());
 742                 });
 743             } finally {
 744                 rq.unlock();
 745             }
 746         }
 747 
 748         @Override
 749         public void invalidate() {
 750             super.invalidate();
 751             clearWindow();
 752         }
 753     }
 754 
 755     /**
 756      * A surface which implements an intermediate buffer between
 757      * the Java2D flusher thread and the AppKit thread.
 758      *
 759      * This surface serves as a buffer attached to a MTLLayer and
 760      * the layer redirects all painting to the buffer's graphics.
 761      */
 762     public static class MTLLayerSurfaceData extends MTLSurfaceData {
 763 
 764         private MTLLayer layer;
 765 
 766         public MTLLayerSurfaceData(MTLLayer layer, MTLGraphicsConfig gc,
 767                                    int width, int height) {
 768             super(layer, gc, gc.getColorModel(), RT_TEXTURE, width, height);
 769             this.layer = layer;
 770             initSurface(this.width, this.height);
 771         }
 772 
 773         @Override
 774         public SurfaceData getReplacement() {
 775             return layer.getSurfaceData();
 776         }
 777 
 778         @Override
 779         public boolean isOnScreen() {
 780             return true;
 781         }
 782 
 783         @Override
 784         public Rectangle getBounds() {
 785             return new Rectangle(width, height);
 786         }
 787 
 788         @Override
 789         public Object getDestination() {
 790             return layer.getDestination();
 791         }
 792 
 793         @Override
 794         public int getTransparency() {
 795             return layer.getTransparency();
 796         }
 797 
 798         @Override
 799         public void invalidate() {
 800             super.invalidate();
 801             clearWindow();
 802         }
 803     }
 804 
 805     /**
 806      * A surface which implements a v-synced flip back-buffer with COPIED
 807      * FlipContents.
 808      *
 809      * This surface serves as a back-buffer to the outside world, while it is
 810      * actually an offscreen surface. When the BufferStrategy this surface
 811      * belongs to is showed, it is first copied to the real private
 812      * FLIP_BACKBUFFER, which is then flipped.
 813      */
 814     public static class MTLVSyncOffScreenSurfaceData extends
 815             MTLOffScreenSurfaceData {
 816         private MTLOffScreenSurfaceData flipSurface;
 817 
 818         public MTLVSyncOffScreenSurfaceData(CPlatformView pView,
 819                                             MTLGraphicsConfig gc, int width, int height, Image image,
 820                                             ColorModel cm, int type) {
 821             super(pView, gc, width, height, image, cm, type);
 822             flipSurface = MTLSurfaceData.createData(pView, image,
 823                     FLIP_BACKBUFFER);
 824         }
 825 
 826         public SurfaceData getFlipSurface() {
 827             return flipSurface;
 828         }
 829 
 830         @Override
 831         public void flush() {
 832             flipSurface.flush();
 833             super.flush();
 834         }
 835     }
 836 
 837     public static class MTLOffScreenSurfaceData extends MTLSurfaceData {
 838         private Image offscreenImage;
 839 
 840         public MTLOffScreenSurfaceData(CPlatformView pView,
 841                                        MTLGraphicsConfig gc, int width, int height, Image image,
 842                                        ColorModel cm, int type) {
 843             super(pView, gc, cm, type, width, height);
 844             offscreenImage = image;
 845             initSurface(this.width, this.height);
 846         }
 847 
 848         @Override
 849         public SurfaceData getReplacement() {
 850             return restoreContents(offscreenImage);
 851         }
 852 
 853         @Override
 854         public Rectangle getBounds() {
 855             if (type == FLIP_BACKBUFFER) {
 856                 Rectangle r = pView.getBounds();
 857                 return new Rectangle(0, 0, r.width, r.height);
 858             } else {
 859                 return new Rectangle(width, height);
 860             }
 861         }
 862 
 863         /**
 864          * Returns destination Image associated with this SurfaceData.
 865          */
 866         @Override
 867         public Object getDestination() {
 868             return offscreenImage;
 869         }
 870     }
 871 
 872 
 873     // additional cleanup
 874     private static native void destroyCGLContext(long ctx);
 875 
 876     public static void destroyOGLContext(long ctx) {
 877         if (ctx != 0L) {
 878             destroyCGLContext(ctx);
 879         }
 880     }
 881 
 882     public static void dispose(long pData, long pConfigInfo) {
 883         MTLGraphicsConfig.deRefPConfigInfo(pConfigInfo);
 884     }
 885 }