--- /dev/null 2019-03-05 14:31:12.000000000 +0300 +++ new/src/java.desktop/macosx/classes/sun/java2d/metal/MTLSurfaceDataBase.java 2019-03-05 14:31:12.000000000 +0300 @@ -0,0 +1,652 @@ +/* + * Copyright (c) 2019, 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.metal; + +import sun.awt.SunHints; +import sun.awt.image.PixelConverter; +import sun.java2d.SunGraphics2D; +import sun.java2d.SurfaceData; +import sun.java2d.SurfaceDataProxy; +import sun.java2d.loops.CompositeType; +import sun.java2d.loops.GraphicsPrimitive; +import sun.java2d.loops.MaskFill; +import sun.java2d.loops.SurfaceType; +import sun.java2d.pipe.ParallelogramPipe; +import sun.java2d.pipe.PixelToParallelogramConverter; +import sun.java2d.pipe.RenderBuffer; +import sun.java2d.pipe.TextPipe; +import sun.java2d.pipe.hw.AccelSurface; + +import java.awt.*; +import java.awt.image.ColorModel; +import java.awt.image.Raster; + +import static sun.java2d.metal.MTLContext.MTLContextCaps.*; +import static sun.java2d.pipe.BufferedOpCodes.*; + +/** + * This class describes an MTL "surface", that is, a region of pixels + * managed via metal. An MTLSurfaceDataBase can be tagged with one of three + * different SurfaceType objects for the purpose of registering loops, etc. + * This diagram shows the hierarchy of MTL SurfaceTypes: + * + * Any + * / \ + * MTLSurface MTLTexture + * | + * MTLSurfaceRTT + * + * MTLSurface + * This kind of surface can be rendered to using OpenGL APIs. It is also + * possible to copy an MTLSurface to another MTLSurface (or to itself). + * This is typically accomplished by calling MakeContextCurrent(dstSD, srcSD) + * and then calling glCopyPixels() (although there are other techniques to + * achieve the same goal). + * + * MTLTexture + * This kind of surface cannot be rendered to using OpenGL (in the same sense + * as in MTLSurface). However, it is possible to upload a region of pixels + * to an MTLTexture object via glTexSubImage2D(). One can also copy a + * surface of type MTLTexture to an MTLSurface by binding the texture + * to a quad and then rendering it to the destination surface (this process + * is known as "texture mapping"). + * + * MTLSurfaceRTT + * This kind of surface can be thought of as a sort of hybrid between + * MTLSurface and MTLTexture, in that one can render to this kind of + * surface as if it were of type MTLSurface, but the process of copying + * this kind of surface to another is more like an MTLTexture. (Note that + * "RTT" stands for "render-to-texture".) + * + * In addition to these SurfaceType variants, we have also defined some + * constants that describe in more detail the type of underlying OpenGL + * surface. This table helps explain the relationships between those + * "type" constants and their corresponding SurfaceType: + * + * OGL Type Corresponding SurfaceType + * -------- ------------------------- + * WINDOW MTLSurface + * TEXTURE MTLTexture + * FLIP_BACKBUFFER MTLSurface + * FBOBJECT MTLSurfaceRTT + */ +public abstract class MTLSurfaceDataBase extends SurfaceData + implements AccelSurface { + + /** + * OGL-specific surface types + * + * @see AccelSurface + */ + public static final int FBOBJECT = RT_TEXTURE; + + /** + * Pixel formats + */ + public static final int PF_INT_ARGB = 0; + public static final int PF_INT_ARGB_PRE = 1; + public static final int PF_INT_RGB = 2; + public static final int PF_INT_RGBX = 3; + public static final int PF_INT_BGR = 4; + public static final int PF_INT_BGRX = 5; + public static final int PF_USHORT_565_RGB = 6; + public static final int PF_USHORT_555_RGB = 7; + public static final int PF_USHORT_555_RGBX = 8; + public static final int PF_BYTE_GRAY = 9; + public static final int PF_USHORT_GRAY = 10; + public static final int PF_3BYTE_BGR = 11; + + /** + * SurfaceTypes + */ + private static final String DESC_MTL_SURFACE = "MTL Surface"; + private static final String DESC_MTL_SURFACE_RTT = + "MTL Surface (render-to-texture)"; + private static final String DESC_MTL_TEXTURE = "MTL Texture"; + + static final SurfaceType MTLSurface = + SurfaceType.Any.deriveSubType(DESC_MTL_SURFACE, + PixelConverter.ArgbPre.instance); + static final SurfaceType MTLSurfaceRTT = + MTLSurface.deriveSubType(DESC_MTL_SURFACE_RTT); + static final SurfaceType MTLTexture = + SurfaceType.Any.deriveSubType(DESC_MTL_TEXTURE); + + /** This will be true if the fbobject system property has been enabled. */ + private static boolean isFBObjectEnabled; + + /** This will be true if the lcdshader system property has been enabled.*/ + private static boolean isLCDShaderEnabled; + + /** This will be true if the biopshader system property has been enabled.*/ + private static boolean isBIOpShaderEnabled; + + /** This will be true if the gradshader system property has been enabled.*/ + private static boolean isGradShaderEnabled; + + private MTLGraphicsConfigBase graphicsConfig; + protected int type; + // these fields are set from the native code when the surface is + // initialized + private int nativeWidth, nativeHeight; + + protected static MTLRenderer mtlRenderPipe; + protected static PixelToParallelogramConverter mtlTxRenderPipe; + protected static ParallelogramPipe mtlAAPgramPipe; + protected static MTLTextRenderer mtlTextPipe; + protected static MTLDrawImage mtlImagePipe; + + protected native boolean initTexture(long pData, + boolean isOpaque, boolean texNonPow2, + boolean texRect, + int width, int height); + protected native boolean initFBObject(long pData, + boolean isOpaque, boolean texNonPow2, + boolean texRect, + int width, int height); + protected native boolean initFlipBackbuffer(long pData); + + private native int getTextureTarget(long pData); + private native int getTextureID(long pData); + + static { + if (!GraphicsEnvironment.isHeadless()) { + // fbobject currently enabled by default; use "false" to disable + String fbo = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction( + "java2d.metal.fbobject")); + isFBObjectEnabled = !"false".equals(fbo); + + // lcdshader currently enabled by default; use "false" to disable + String lcd = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction( + "java2d.metal.lcdshader")); + isLCDShaderEnabled = !"false".equals(lcd); + + // biopshader currently enabled by default; use "false" to disable + String biop = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction( + "java2d.metal.biopshader")); + isBIOpShaderEnabled = !"false".equals(biop); + + // gradshader currently enabled by default; use "false" to disable + String grad = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction( + "java2d.metal.gradshader")); + isGradShaderEnabled = !"false".equals(grad); + + MTLRenderQueue rq = MTLRenderQueue.getInstance(); + mtlImagePipe = new MTLDrawImage(); + mtlTextPipe = new MTLTextRenderer(rq); + mtlRenderPipe = new MTLRenderer(rq); + if (GraphicsPrimitive.tracingEnabled()) { + mtlTextPipe = mtlTextPipe.traceWrap(); + //The wrapped mtlRenderPipe will wrap the AA pipe as well... + //mtlAAPgramPipe = mtlRenderPipe.traceWrap(); + } + mtlAAPgramPipe = mtlRenderPipe.getAAParallelogramPipe(); + mtlTxRenderPipe = + new PixelToParallelogramConverter(mtlRenderPipe, + mtlRenderPipe, + 1.0, 0.25, true); + + MTLBlitLoops.register(); + MTLMaskFill.register(); + MTLMaskBlit.register(); + } + } + + protected MTLSurfaceDataBase(MTLGraphicsConfigBase gc, + ColorModel cm, int type) + { + super(getCustomSurfaceType(type), cm); + this.graphicsConfig = gc; + this.type = type; + setBlitProxyKey(gc.getProxyKey()); + } + + @Override + public SurfaceDataProxy makeProxyFor(SurfaceData srcData) { + return MTLSurfaceDataProxy.createProxy(srcData, graphicsConfig); + } + + /** + * Returns the appropriate SurfaceType corresponding to the given OpenGL + * surface type constant (e.g. TEXTURE -> MTLTexture). + */ + private static SurfaceType getCustomSurfaceType(int oglType) { + switch (oglType) { + case TEXTURE: + return MTLTexture; + case FBOBJECT: + return MTLSurfaceRTT; + default: + return MTLSurface; + } + } + + /** + * Note: This should only be called from the QFT under the AWT lock. + * This method is kept separate from the initSurface() method below just + * to keep the code a bit cleaner. + */ + private void initSurfaceNow(int width, int height) { + boolean isOpaque = (getTransparency() == Transparency.OPAQUE); + boolean success = false; + + switch (type) { + case TEXTURE: + success = initTexture(getNativeOps(), + isOpaque, isTexNonPow2Available(), + isTexRectAvailable(), + width, height); + break; + + case FBOBJECT: + success = initFBObject(getNativeOps(), + isOpaque, isTexNonPow2Available(), + isTexRectAvailable(), + width, height); + break; + + case FLIP_BACKBUFFER: + success = initFlipBackbuffer(getNativeOps()); + break; + + default: + break; + } + + if (!success) { + throw new OutOfMemoryError("can't create offscreen surface"); + } + } + + /** + * Initializes the appropriate OpenGL offscreen surface based on the value + * of the type parameter. If the surface creation fails for any reason, + * an OutOfMemoryError will be thrown. + */ + protected void initSurface(final int width, final int height) { + MTLRenderQueue rq = MTLRenderQueue.getInstance(); + rq.lock(); + try { + switch (type) { + case TEXTURE: + case FBOBJECT: + // need to make sure the context is current before + // creating the texture or fbobject + MTLContext.setScratchSurface(graphicsConfig); + break; + default: + break; + } + rq.flushAndInvokeNow(new Runnable() { + public void run() { + initSurfaceNow(width, height); + } + }); + } finally { + rq.unlock(); + } + } + + /** + * Returns the MTLContext for the GraphicsConfig associated with this + * surface. + */ + public final MTLContext getContext() { + return graphicsConfig.getContext(); + } + + /** + * Returns the MTLGraphicsConfigBase associated with this surface. + */ + final MTLGraphicsConfigBase getMTLGraphicsConfig() { + return graphicsConfig; + } + + /** + * Returns one of the surface type constants defined above. + */ + public final int getType() { + return type; + } + + /** + * If this surface is backed by a texture object, returns the target + * for that texture (either GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB). + * Otherwise, this method will return zero. + */ + public final int getTextureTarget() { + return getTextureTarget(getNativeOps()); + } + + /** + * If this surface is backed by a texture object, returns the texture ID + * for that texture. + * Otherwise, this method will return zero. + */ + public final int getTextureID() { + return getTextureID(getNativeOps()); + } + + /** + * Returns native resource of specified {@code resType} associated with + * this surface. + * + * Specifically, for {@code MTLSurfaceDataBase} this method returns the + * the following: + *
+     * TEXTURE              - texture id
+     * 
+ * + * Note: the resource returned by this method is only valid on the rendering + * thread. + * + * @return native resource of specified type or 0L if + * such resource doesn't exist or can not be retrieved. + * @see AccelSurface#getNativeResource + */ + public long getNativeResource(int resType) { + if (resType == TEXTURE) { + return getTextureID(); + } + return 0L; + } + + public Raster getRaster(int x, int y, int w, int h) { + throw new InternalError("not implemented yet"); + } + + /** + * For now, we can only render LCD text if: + * - the fragment shader extension is available, and + * - the source color is opaque, and + * - blending is SrcOverNoEa or disabled + * - and the destination is opaque + * + * Eventually, we could enhance the native OGL text rendering code + * and remove the above restrictions, but that would require significantly + * more code just to support a few uncommon cases. + */ + public boolean canRenderLCDText(SunGraphics2D sg2d) { + return + graphicsConfig.isCapPresent(CAPS_EXT_LCD_SHADER) && + sg2d.surfaceData.getTransparency() == Transparency.OPAQUE && + sg2d.paintState <= SunGraphics2D.PAINT_OPAQUECOLOR && + (sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY || + (sg2d.compositeState <= SunGraphics2D.COMP_ALPHA && canHandleComposite(sg2d.composite))); + } + + private boolean canHandleComposite(Composite c) { + if (c instanceof AlphaComposite) { + AlphaComposite ac = (AlphaComposite)c; + + return ac.getRule() == AlphaComposite.SRC_OVER && ac.getAlpha() >= 1f; + } + return false; + } + + public void validatePipe(SunGraphics2D sg2d) { + TextPipe textpipe; + boolean validated = false; + + // MTLTextRenderer handles both AA and non-AA text, but + // only works with the following modes: + // (Note: For LCD text we only enter this code path if + // canRenderLCDText() has already validated that the mode is + // CompositeType.SrcNoEa (opaque color), which will be subsumed + // by the CompositeType.SrcNoEa (any color) test below.) + + if (/* CompositeType.SrcNoEa (any color) */ + (sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY && + sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) || + + /* CompositeType.SrcOver (any color) */ + (sg2d.compositeState == SunGraphics2D.COMP_ALPHA && + sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR && + (((AlphaComposite)sg2d.composite).getRule() == + AlphaComposite.SRC_OVER)) || + + /* CompositeType.Xor (any color) */ + (sg2d.compositeState == SunGraphics2D.COMP_XOR && + sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR)) + { + textpipe = mtlTextPipe; + } else { + // do this to initialize textpipe correctly; we will attempt + // to override the non-text pipes below + super.validatePipe(sg2d); + textpipe = sg2d.textpipe; + validated = true; + } + + PixelToParallelogramConverter txPipe = null; + MTLRenderer nonTxPipe = null; + + if (sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) { + if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) { + if (sg2d.compositeState <= SunGraphics2D.COMP_XOR) { + txPipe = mtlTxRenderPipe; + nonTxPipe = mtlRenderPipe; + } + } else if (sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) { + if (MTLPaints.isValid(sg2d)) { + txPipe = mtlTxRenderPipe; + nonTxPipe = mtlRenderPipe; + } + // custom paints handled by super.validatePipe() below + } + } else { + if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) { + if (graphicsConfig.isCapPresent(CAPS_PS30) && + (sg2d.imageComp == CompositeType.SrcOverNoEa || + sg2d.imageComp == CompositeType.SrcOver)) + { + if (!validated) { + super.validatePipe(sg2d); + validated = true; + } + PixelToParallelogramConverter aaConverter = + new PixelToParallelogramConverter(sg2d.shapepipe, + mtlAAPgramPipe, + 1.0/8.0, 0.499, + false); + sg2d.drawpipe = aaConverter; + sg2d.fillpipe = aaConverter; + sg2d.shapepipe = aaConverter; + } else if (sg2d.compositeState == SunGraphics2D.COMP_XOR) { + // install the solid pipes when AA and XOR are both enabled + txPipe = mtlTxRenderPipe; + nonTxPipe = mtlRenderPipe; + } + } + // other cases handled by super.validatePipe() below + } + + if (txPipe != null) { + if (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) { + sg2d.drawpipe = txPipe; + sg2d.fillpipe = txPipe; + } else if (sg2d.strokeState != SunGraphics2D.STROKE_THIN) { + sg2d.drawpipe = txPipe; + sg2d.fillpipe = nonTxPipe; + } else { + sg2d.drawpipe = nonTxPipe; + sg2d.fillpipe = nonTxPipe; + } + // Note that we use the transforming pipe here because it + // will examine the shape and possibly perform an optimized + // operation if it can be simplified. The simplifications + // will be valid for all STROKE and TRANSFORM types. + sg2d.shapepipe = txPipe; + } else { + if (!validated) { + super.validatePipe(sg2d); + } + } + + // install the text pipe based on our earlier decision + sg2d.textpipe = textpipe; + + // always override the image pipe with the specialized OGL pipe + sg2d.imagepipe = mtlImagePipe; + } + + @Override + protected MaskFill getMaskFill(SunGraphics2D sg2d) { + if (sg2d.paintState > SunGraphics2D.PAINT_ALPHACOLOR) { + /* + * We can only accelerate non-Color MaskFill operations if + * all of the following conditions hold true: + * - there is an implementation for the given paintState + * - the current Paint can be accelerated for this destination + * - multitexturing is available (since we need to modulate + * the alpha mask texture with the paint texture) + * + * In all other cases, we return null, in which case the + * validation code will choose a more general software-based loop. + */ + if (!MTLPaints.isValid(sg2d) || + !graphicsConfig.isCapPresent(CAPS_MULTITEXTURE)) + { + return null; + } + } + return super.getMaskFill(sg2d); + } + + @Override + public boolean copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, + int dx, int dy) { + if (sg2d.compositeState >= SunGraphics2D.COMP_XOR) { + return false; + } + mtlRenderPipe.copyArea(sg2d, x, y, w, h, dx, dy); + return true; + } + + public void flush() { + invalidate(); + MTLRenderQueue rq = MTLRenderQueue.getInstance(); + rq.lock(); + try { + // make sure we have a current context before + // disposing the native resources (e.g. texture object) + MTLContext.setScratchSurface(graphicsConfig); + + RenderBuffer buf = rq.getBuffer(); + rq.ensureCapacityAndAlignment(12, 4); + buf.putInt(FLUSH_SURFACE); + buf.putLong(getNativeOps()); + + // this call is expected to complete synchronously, so flush now + rq.flushNow(); + } finally { + rq.unlock(); + } + } + + /** + * Disposes the native resources associated with the given MTLSurfaceDataBase + * (referenced by the pData parameter). This method is invoked from + * the native Dispose() method from the Disposer thread when the + * Java-level MTLSurfaceDataBase object is about to go away. Note that we + * also pass a reference to the native GLX/WGLGraphicsConfigInfo + * (pConfigInfo) for the purposes of making a context current. + */ + public static void dispose(long pData, long pConfigInfo) { + MTLRenderQueue rq = MTLRenderQueue.getInstance(); + rq.lock(); + try { + // make sure we have a current context before + // disposing the native resources (e.g. texture object) + MTLContext.setScratchSurface(pConfigInfo); + + RenderBuffer buf = rq.getBuffer(); + rq.ensureCapacityAndAlignment(12, 4); + buf.putInt(DISPOSE_SURFACE); + buf.putLong(pData); + + // this call is expected to complete synchronously, so flush now + rq.flushNow(); + } finally { + rq.unlock(); + } + } + + static void swapBuffers(long window) { + MTLRenderQueue rq = MTLRenderQueue.getInstance(); + rq.lock(); + try { + RenderBuffer buf = rq.getBuffer(); + rq.ensureCapacityAndAlignment(12, 4); + buf.putInt(SWAP_BUFFERS); + buf.putLong(window); + rq.flushNow(); + } finally { + rq.unlock(); + } + } + + /** + * Returns true if OpenGL textures can have non-power-of-two dimensions + * when using the basic GL_TEXTURE_2D target. + */ + boolean isTexNonPow2Available() { + return graphicsConfig.isCapPresent(CAPS_TEXNONPOW2); + } + + /** + * Returns true if OpenGL textures can have non-power-of-two dimensions + * when using the GL_TEXTURE_RECTANGLE_ARB target (only available when the + * GL_ARB_texture_rectangle extension is present). + */ + boolean isTexRectAvailable() { + return graphicsConfig.isCapPresent(CAPS_EXT_TEXRECT); + } + + public Rectangle getNativeBounds() { + MTLRenderQueue rq = MTLRenderQueue.getInstance(); + rq.lock(); + try { + return new Rectangle(nativeWidth, nativeHeight); + } finally { + rq.unlock(); + } + } + + /** + * Returns true if the surface is an on-screen window surface or + * a FBO texture attached to an on-screen CALayer. + * + * Needed by Mac OS X port. + */ + public boolean isOnScreen() { + return getType() == WINDOW; + } +}