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 43 import java.awt.*; 44 import java.awt.image.ColorModel; 45 import java.awt.image.Raster; 46 47 import static sun.java2d.metal.MTLContext.MTLContextCaps.*; 48 import static sun.java2d.pipe.BufferedOpCodes.*; 49 50 /** 51 * This class describes an MTL "surface", that is, a region of pixels 52 * managed via metal. An MTLSurfaceDataBase can be tagged with one of three 53 * different SurfaceType objects for the purpose of registering loops, etc. 54 * This diagram shows the hierarchy of MTL SurfaceTypes: 55 * 56 * Any 57 * / \ 58 * MTLSurface MTLTexture 59 * | 60 * MTLSurfaceRTT 61 * 62 * MTLSurface 63 * This kind of surface can be rendered to using OpenGL APIs. It is also 64 * possible to copy an MTLSurface to another MTLSurface (or to itself). 65 * This is typically accomplished by calling MakeContextCurrent(dstSD, srcSD) 66 * and then calling glCopyPixels() (although there are other techniques to 67 * achieve the same goal). 68 * 69 * MTLTexture 70 * This kind of surface cannot be rendered to using OpenGL (in the same sense 71 * as in MTLSurface). However, it is possible to upload a region of pixels 72 * to an MTLTexture object via glTexSubImage2D(). One can also copy a 73 * surface of type MTLTexture to an MTLSurface by binding the texture 74 * to a quad and then rendering it to the destination surface (this process 75 * is known as "texture mapping"). 76 * 77 * MTLSurfaceRTT 78 * This kind of surface can be thought of as a sort of hybrid between 79 * MTLSurface and MTLTexture, in that one can render to this kind of 80 * surface as if it were of type MTLSurface, but the process of copying 81 * this kind of surface to another is more like an MTLTexture. (Note that 82 * "RTT" stands for "render-to-texture".) 83 * 84 * In addition to these SurfaceType variants, we have also defined some 85 * constants that describe in more detail the type of underlying OpenGL 86 * surface. This table helps explain the relationships between those 87 * "type" constants and their corresponding SurfaceType: 88 * 89 * OGL Type Corresponding SurfaceType 90 * -------- ------------------------- 91 * WINDOW MTLSurface 92 * TEXTURE MTLTexture 93 * FLIP_BACKBUFFER MTLSurface 94 * FBOBJECT MTLSurfaceRTT 95 */ 96 public abstract class MTLSurfaceDataBase extends SurfaceData 97 implements AccelSurface { 98 99 /** 100 * OGL-specific surface types 101 * 102 * @see AccelSurface 103 */ 104 public static final int FBOBJECT = RT_TEXTURE; 105 106 /** 107 * Pixel formats 108 */ 109 public static final int PF_INT_ARGB = 0; 110 public static final int PF_INT_ARGB_PRE = 1; 111 public static final int PF_INT_RGB = 2; 112 public static final int PF_INT_RGBX = 3; 113 public static final int PF_INT_BGR = 4; 114 public static final int PF_INT_BGRX = 5; 115 public static final int PF_USHORT_565_RGB = 6; 116 public static final int PF_USHORT_555_RGB = 7; 117 public static final int PF_USHORT_555_RGBX = 8; 118 public static final int PF_BYTE_GRAY = 9; 119 public static final int PF_USHORT_GRAY = 10; 120 public static final int PF_3BYTE_BGR = 11; 121 122 /** 123 * SurfaceTypes 124 */ 125 private static final String DESC_MTL_SURFACE = "MTL Surface"; 126 private static final String DESC_MTL_SURFACE_RTT = 127 "MTL Surface (render-to-texture)"; 128 private static final String DESC_MTL_TEXTURE = "MTL Texture"; 129 130 static final SurfaceType MTLSurface = 131 SurfaceType.Any.deriveSubType(DESC_MTL_SURFACE, 132 PixelConverter.ArgbPre.instance); 133 static final SurfaceType MTLSurfaceRTT = 134 MTLSurface.deriveSubType(DESC_MTL_SURFACE_RTT); 135 static final SurfaceType MTLTexture = 136 SurfaceType.Any.deriveSubType(DESC_MTL_TEXTURE); 137 138 /** This will be true if the fbobject system property has been enabled. */ 139 private static boolean isFBObjectEnabled; 140 141 /** This will be true if the lcdshader system property has been enabled.*/ 142 private static boolean isLCDShaderEnabled; 143 144 /** This will be true if the biopshader system property has been enabled.*/ 145 private static boolean isBIOpShaderEnabled; 146 147 /** This will be true if the gradshader system property has been enabled.*/ 148 private static boolean isGradShaderEnabled; 149 150 private MTLGraphicsConfigBase graphicsConfig; 151 protected int type; 152 // these fields are set from the native code when the surface is 153 // initialized 154 private int nativeWidth, nativeHeight; 155 156 protected static MTLRenderer mtlRenderPipe; 157 protected static PixelToParallelogramConverter mtlTxRenderPipe; 158 protected static ParallelogramPipe mtlAAPgramPipe; 159 protected static MTLTextRenderer mtlTextPipe; 160 protected static MTLDrawImage mtlImagePipe; 161 162 protected native boolean initTexture(long pData, 163 boolean isOpaque, boolean texNonPow2, 164 boolean texRect, 165 int width, int height); 166 protected native boolean initFBObject(long pData, 167 boolean isOpaque, boolean texNonPow2, 168 boolean texRect, 169 int width, int height); 170 protected native boolean initFlipBackbuffer(long pData); 171 172 private native int getTextureTarget(long pData); 173 private native int getTextureID(long pData); 174 175 static { 176 if (!GraphicsEnvironment.isHeadless()) { 177 // fbobject currently enabled by default; use "false" to disable 178 String fbo = java.security.AccessController.doPrivileged( 179 new sun.security.action.GetPropertyAction( 180 "java2d.metal.fbobject")); 181 isFBObjectEnabled = !"false".equals(fbo); 182 183 // lcdshader currently enabled by default; use "false" to disable 184 String lcd = java.security.AccessController.doPrivileged( 185 new sun.security.action.GetPropertyAction( 186 "java2d.metal.lcdshader")); 187 isLCDShaderEnabled = !"false".equals(lcd); 188 189 // biopshader currently enabled by default; use "false" to disable 190 String biop = java.security.AccessController.doPrivileged( 191 new sun.security.action.GetPropertyAction( 192 "java2d.metal.biopshader")); 193 isBIOpShaderEnabled = !"false".equals(biop); 194 195 // gradshader currently enabled by default; use "false" to disable 196 String grad = java.security.AccessController.doPrivileged( 197 new sun.security.action.GetPropertyAction( 198 "java2d.metal.gradshader")); 199 isGradShaderEnabled = !"false".equals(grad); 200 201 MTLRenderQueue rq = MTLRenderQueue.getInstance(); 202 mtlImagePipe = new MTLDrawImage(); 203 mtlTextPipe = new MTLTextRenderer(rq); 204 mtlRenderPipe = new MTLRenderer(rq); 205 if (GraphicsPrimitive.tracingEnabled()) { 206 mtlTextPipe = mtlTextPipe.traceWrap(); 207 //The wrapped mtlRenderPipe will wrap the AA pipe as well... 208 //mtlAAPgramPipe = mtlRenderPipe.traceWrap(); 209 } 210 mtlAAPgramPipe = mtlRenderPipe.getAAParallelogramPipe(); 211 mtlTxRenderPipe = 212 new PixelToParallelogramConverter(mtlRenderPipe, 213 mtlRenderPipe, 214 1.0, 0.25, true); 215 216 MTLBlitLoops.register(); 217 MTLMaskFill.register(); 218 MTLMaskBlit.register(); 219 } 220 } 221 222 protected MTLSurfaceDataBase(MTLGraphicsConfigBase gc, 223 ColorModel cm, int type) 224 { 225 super(getCustomSurfaceType(type), cm); 226 this.graphicsConfig = gc; 227 this.type = type; 228 setBlitProxyKey(gc.getProxyKey()); 229 } 230 231 @Override 232 public SurfaceDataProxy makeProxyFor(SurfaceData srcData) { 233 return MTLSurfaceDataProxy.createProxy(srcData, graphicsConfig); 234 } 235 236 /** 237 * Returns the appropriate SurfaceType corresponding to the given OpenGL 238 * surface type constant (e.g. TEXTURE -> MTLTexture). 239 */ 240 private static SurfaceType getCustomSurfaceType(int oglType) { 241 switch (oglType) { 242 case TEXTURE: 243 return MTLTexture; 244 case FBOBJECT: 245 return MTLSurfaceRTT; 246 default: 247 return MTLSurface; 248 } 249 } 250 251 /** 252 * Note: This should only be called from the QFT under the AWT lock. 253 * This method is kept separate from the initSurface() method below just 254 * to keep the code a bit cleaner. 255 */ 256 private void initSurfaceNow(int width, int height) { 257 boolean isOpaque = (getTransparency() == Transparency.OPAQUE); 258 boolean success = false; 259 260 switch (type) { 261 case TEXTURE: 262 success = initTexture(getNativeOps(), 263 isOpaque, isTexNonPow2Available(), 264 isTexRectAvailable(), 265 width, height); 266 break; 267 268 case FBOBJECT: 269 success = initFBObject(getNativeOps(), 270 isOpaque, isTexNonPow2Available(), 271 isTexRectAvailable(), 272 width, height); 273 break; 274 275 case FLIP_BACKBUFFER: 276 success = initFlipBackbuffer(getNativeOps()); 277 break; 278 279 default: 280 break; 281 } 282 283 if (!success) { 284 throw new OutOfMemoryError("can't create offscreen surface"); 285 } 286 } 287 288 /** 289 * Initializes the appropriate OpenGL offscreen surface based on the value 290 * of the type parameter. If the surface creation fails for any reason, 291 * an OutOfMemoryError will be thrown. 292 */ 293 protected void initSurface(final int width, final int height) { 294 MTLRenderQueue rq = MTLRenderQueue.getInstance(); 295 rq.lock(); 296 try { 297 switch (type) { 298 case TEXTURE: 299 case FBOBJECT: 300 // need to make sure the context is current before 301 // creating the texture or fbobject 302 MTLContext.setScratchSurface(graphicsConfig); 303 break; 304 default: 305 break; 306 } 307 rq.flushAndInvokeNow(new Runnable() { 308 public void run() { 309 initSurfaceNow(width, height); 310 } 311 }); 312 } finally { 313 rq.unlock(); 314 } 315 } 316 317 /** 318 * Returns the MTLContext for the GraphicsConfig associated with this 319 * surface. 320 */ 321 public final MTLContext getContext() { 322 return graphicsConfig.getContext(); 323 } 324 325 /** 326 * Returns the MTLGraphicsConfigBase associated with this surface. 327 */ 328 final MTLGraphicsConfigBase getMTLGraphicsConfig() { 329 return graphicsConfig; 330 } 331 332 /** 333 * Returns one of the surface type constants defined above. 334 */ 335 public final int getType() { 336 return type; 337 } 338 339 /** 340 * If this surface is backed by a texture object, returns the target 341 * for that texture (either GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB). 342 * Otherwise, this method will return zero. 343 */ 344 public final int getTextureTarget() { 345 return getTextureTarget(getNativeOps()); 346 } 347 348 /** 349 * If this surface is backed by a texture object, returns the texture ID 350 * for that texture. 351 * Otherwise, this method will return zero. 352 */ 353 public final int getTextureID() { 354 return getTextureID(getNativeOps()); 355 } 356 357 /** 358 * Returns native resource of specified {@code resType} associated with 359 * this surface. 360 * 361 * Specifically, for {@code MTLSurfaceDataBase} this method returns the 362 * the following: 363 * <pre> 364 * TEXTURE - texture id 365 * </pre> 366 * 367 * Note: the resource returned by this method is only valid on the rendering 368 * thread. 369 * 370 * @return native resource of specified type or 0L if 371 * such resource doesn't exist or can not be retrieved. 372 * @see AccelSurface#getNativeResource 373 */ 374 public long getNativeResource(int resType) { 375 if (resType == TEXTURE) { 376 return getTextureID(); 377 } 378 return 0L; 379 } 380 381 public Raster getRaster(int x, int y, int w, int h) { 382 throw new InternalError("not implemented yet"); 383 } 384 385 /** 386 * For now, we can only render LCD text if: 387 * - the fragment shader extension is available, and 388 * - the source color is opaque, and 389 * - blending is SrcOverNoEa or disabled 390 * - and the destination is opaque 391 * 392 * Eventually, we could enhance the native OGL text rendering code 393 * and remove the above restrictions, but that would require significantly 394 * more code just to support a few uncommon cases. 395 */ 396 public boolean canRenderLCDText(SunGraphics2D sg2d) { 397 return 398 graphicsConfig.isCapPresent(CAPS_EXT_LCD_SHADER) && 399 sg2d.surfaceData.getTransparency() == Transparency.OPAQUE && 400 sg2d.paintState <= SunGraphics2D.PAINT_OPAQUECOLOR && 401 (sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY || 402 (sg2d.compositeState <= SunGraphics2D.COMP_ALPHA && canHandleComposite(sg2d.composite))); 403 } 404 405 private boolean canHandleComposite(Composite c) { 406 if (c instanceof AlphaComposite) { 407 AlphaComposite ac = (AlphaComposite)c; 408 409 return ac.getRule() == AlphaComposite.SRC_OVER && ac.getAlpha() >= 1f; 410 } 411 return false; 412 } 413 414 public void validatePipe(SunGraphics2D sg2d) { 415 TextPipe textpipe; 416 boolean validated = false; 417 418 // MTLTextRenderer handles both AA and non-AA text, but 419 // only works with the following modes: 420 // (Note: For LCD text we only enter this code path if 421 // canRenderLCDText() has already validated that the mode is 422 // CompositeType.SrcNoEa (opaque color), which will be subsumed 423 // by the CompositeType.SrcNoEa (any color) test below.) 424 425 if (/* CompositeType.SrcNoEa (any color) */ 426 (sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY && 427 sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) || 428 429 /* CompositeType.SrcOver (any color) */ 430 (sg2d.compositeState == SunGraphics2D.COMP_ALPHA && 431 sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR && 432 (((AlphaComposite)sg2d.composite).getRule() == 433 AlphaComposite.SRC_OVER)) || 434 435 /* CompositeType.Xor (any color) */ 436 (sg2d.compositeState == SunGraphics2D.COMP_XOR && 437 sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR)) 438 { 439 textpipe = mtlTextPipe; 440 } else { 441 // do this to initialize textpipe correctly; we will attempt 442 // to override the non-text pipes below 443 super.validatePipe(sg2d); 444 textpipe = sg2d.textpipe; 445 validated = true; 446 } 447 448 PixelToParallelogramConverter txPipe = null; 449 MTLRenderer nonTxPipe = null; 450 451 if (sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) { 452 if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) { 453 if (sg2d.compositeState <= SunGraphics2D.COMP_XOR) { 454 txPipe = mtlTxRenderPipe; 455 nonTxPipe = mtlRenderPipe; 456 } 457 } else if (sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) { 458 if (MTLPaints.isValid(sg2d)) { 459 txPipe = mtlTxRenderPipe; 460 nonTxPipe = mtlRenderPipe; 461 } 462 // custom paints handled by super.validatePipe() below 463 } 464 } else { 465 if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) { 466 if (graphicsConfig.isCapPresent(CAPS_PS30) && 467 (sg2d.imageComp == CompositeType.SrcOverNoEa || 468 sg2d.imageComp == CompositeType.SrcOver)) 469 { 470 if (!validated) { 471 super.validatePipe(sg2d); 472 validated = true; 473 } 474 PixelToParallelogramConverter aaConverter = 475 new PixelToParallelogramConverter(sg2d.shapepipe, 476 mtlAAPgramPipe, 477 1.0/8.0, 0.499, 478 false); 479 sg2d.drawpipe = aaConverter; 480 sg2d.fillpipe = aaConverter; 481 sg2d.shapepipe = aaConverter; 482 } else if (sg2d.compositeState == SunGraphics2D.COMP_XOR) { 483 // install the solid pipes when AA and XOR are both enabled 484 txPipe = mtlTxRenderPipe; 485 nonTxPipe = mtlRenderPipe; 486 } 487 } 488 // other cases handled by super.validatePipe() below 489 } 490 491 if (txPipe != null) { 492 if (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) { 493 sg2d.drawpipe = txPipe; 494 sg2d.fillpipe = txPipe; 495 } else if (sg2d.strokeState != SunGraphics2D.STROKE_THIN) { 496 sg2d.drawpipe = txPipe; 497 sg2d.fillpipe = nonTxPipe; 498 } else { 499 sg2d.drawpipe = nonTxPipe; 500 sg2d.fillpipe = nonTxPipe; 501 } 502 // Note that we use the transforming pipe here because it 503 // will examine the shape and possibly perform an optimized 504 // operation if it can be simplified. The simplifications 505 // will be valid for all STROKE and TRANSFORM types. 506 sg2d.shapepipe = txPipe; 507 } else { 508 if (!validated) { 509 super.validatePipe(sg2d); 510 } 511 } 512 513 // install the text pipe based on our earlier decision 514 sg2d.textpipe = textpipe; 515 516 // always override the image pipe with the specialized OGL pipe 517 sg2d.imagepipe = mtlImagePipe; 518 } 519 520 @Override 521 protected MaskFill getMaskFill(SunGraphics2D sg2d) { 522 if (sg2d.paintState > SunGraphics2D.PAINT_ALPHACOLOR) { 523 /* 524 * We can only accelerate non-Color MaskFill operations if 525 * all of the following conditions hold true: 526 * - there is an implementation for the given paintState 527 * - the current Paint can be accelerated for this destination 528 * - multitexturing is available (since we need to modulate 529 * the alpha mask texture with the paint texture) 530 * 531 * In all other cases, we return null, in which case the 532 * validation code will choose a more general software-based loop. 533 */ 534 if (!MTLPaints.isValid(sg2d) || 535 !graphicsConfig.isCapPresent(CAPS_MULTITEXTURE)) 536 { 537 return null; 538 } 539 } 540 return super.getMaskFill(sg2d); 541 } 542 543 @Override 544 public boolean copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, 545 int dx, int dy) { 546 if (sg2d.compositeState >= SunGraphics2D.COMP_XOR) { 547 return false; 548 } 549 mtlRenderPipe.copyArea(sg2d, x, y, w, h, dx, dy); 550 return true; 551 } 552 553 public void flush() { 554 invalidate(); 555 MTLRenderQueue rq = MTLRenderQueue.getInstance(); 556 rq.lock(); 557 try { 558 // make sure we have a current context before 559 // disposing the native resources (e.g. texture object) 560 MTLContext.setScratchSurface(graphicsConfig); 561 562 RenderBuffer buf = rq.getBuffer(); 563 rq.ensureCapacityAndAlignment(12, 4); 564 buf.putInt(FLUSH_SURFACE); 565 buf.putLong(getNativeOps()); 566 567 // this call is expected to complete synchronously, so flush now 568 rq.flushNow(); 569 } finally { 570 rq.unlock(); 571 } 572 } 573 574 /** 575 * Disposes the native resources associated with the given MTLSurfaceDataBase 576 * (referenced by the pData parameter). This method is invoked from 577 * the native Dispose() method from the Disposer thread when the 578 * Java-level MTLSurfaceDataBase object is about to go away. Note that we 579 * also pass a reference to the native GLX/WGLGraphicsConfigInfo 580 * (pConfigInfo) for the purposes of making a context current. 581 */ 582 public static void dispose(long pData, long pConfigInfo) { 583 MTLRenderQueue rq = MTLRenderQueue.getInstance(); 584 rq.lock(); 585 try { 586 // make sure we have a current context before 587 // disposing the native resources (e.g. texture object) 588 MTLContext.setScratchSurface(pConfigInfo); 589 590 RenderBuffer buf = rq.getBuffer(); 591 rq.ensureCapacityAndAlignment(12, 4); 592 buf.putInt(DISPOSE_SURFACE); 593 buf.putLong(pData); 594 595 // this call is expected to complete synchronously, so flush now 596 rq.flushNow(); 597 } finally { 598 rq.unlock(); 599 } 600 } 601 602 static void swapBuffers(long window) { 603 MTLRenderQueue rq = MTLRenderQueue.getInstance(); 604 rq.lock(); 605 try { 606 RenderBuffer buf = rq.getBuffer(); 607 rq.ensureCapacityAndAlignment(12, 4); 608 buf.putInt(SWAP_BUFFERS); 609 buf.putLong(window); 610 rq.flushNow(); 611 } finally { 612 rq.unlock(); 613 } 614 } 615 616 /** 617 * Returns true if OpenGL textures can have non-power-of-two dimensions 618 * when using the basic GL_TEXTURE_2D target. 619 */ 620 boolean isTexNonPow2Available() { 621 return graphicsConfig.isCapPresent(CAPS_TEXNONPOW2); 622 } 623 624 /** 625 * Returns true if OpenGL textures can have non-power-of-two dimensions 626 * when using the GL_TEXTURE_RECTANGLE_ARB target (only available when the 627 * GL_ARB_texture_rectangle extension is present). 628 */ 629 boolean isTexRectAvailable() { 630 return graphicsConfig.isCapPresent(CAPS_EXT_TEXRECT); 631 } 632 633 public Rectangle getNativeBounds() { 634 MTLRenderQueue rq = MTLRenderQueue.getInstance(); 635 rq.lock(); 636 try { 637 return new Rectangle(nativeWidth, nativeHeight); 638 } finally { 639 rq.unlock(); 640 } 641 } 642 643 /** 644 * Returns true if the surface is an on-screen window surface or 645 * a FBO texture attached to an on-screen CALayer. 646 * 647 * Needed by Mac OS X port. 648 */ 649 public boolean isOnScreen() { 650 return getType() == WINDOW; 651 } 652 }