Path: blob/master/src/java.desktop/macosx/classes/sun/java2d/metal/MTLSurfaceData.java
41159 views
/*1* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.java2d.metal;2627import sun.awt.SunHints;28import sun.awt.image.PixelConverter;29import sun.java2d.SunGraphics2D;30import sun.java2d.SurfaceData;31import sun.java2d.SurfaceDataProxy;32import sun.java2d.loops.CompositeType;33import sun.java2d.loops.GraphicsPrimitive;34import sun.java2d.loops.MaskFill;35import sun.java2d.loops.SurfaceType;36import sun.java2d.pipe.ParallelogramPipe;37import sun.java2d.pipe.PixelToParallelogramConverter;38import sun.java2d.pipe.RenderBuffer;39import sun.java2d.pipe.TextPipe;40import sun.java2d.pipe.hw.AccelSurface;4142import java.awt.AlphaComposite;43import java.awt.Composite;44import java.awt.GraphicsConfiguration;45import java.awt.GraphicsEnvironment;46import java.awt.Image;47import java.awt.Rectangle;48import java.awt.Transparency;4950import java.awt.image.ColorModel;51import java.awt.image.Raster;5253import static sun.java2d.pipe.BufferedOpCodes.DISPOSE_SURFACE;54import static sun.java2d.pipe.BufferedOpCodes.FLUSH_SURFACE;55import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_MULTITEXTURE;56import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_PS30;575859public abstract class MTLSurfaceData extends SurfaceData60implements AccelSurface {6162/**63* Pixel formats64*/65public static final int PF_INT_ARGB = 0;66public static final int PF_INT_ARGB_PRE = 1;67public static final int PF_INT_RGB = 2;68public static final int PF_INT_RGBX = 3;69public static final int PF_INT_BGR = 4;70public static final int PF_INT_BGRX = 5;71public static final int PF_USHORT_565_RGB = 6;72public static final int PF_USHORT_555_RGB = 7;73public static final int PF_USHORT_555_RGBX = 8;74public static final int PF_BYTE_GRAY = 9;75public static final int PF_USHORT_GRAY = 10;76public static final int PF_3BYTE_BGR = 11;77/**78* SurfaceTypes79*/8081private static final String DESC_MTL_SURFACE = "MTL Surface";82private static final String DESC_MTL_SURFACE_RTT =83"MTL Surface (render-to-texture)";84private static final String DESC_MTL_TEXTURE = "MTL Texture";858687static final SurfaceType MTLSurface =88SurfaceType.Any.deriveSubType(DESC_MTL_SURFACE,89PixelConverter.ArgbPre.instance);90static final SurfaceType MTLSurfaceRTT =91MTLSurface.deriveSubType(DESC_MTL_SURFACE_RTT);92static final SurfaceType MTLTexture =93SurfaceType.Any.deriveSubType(DESC_MTL_TEXTURE);9495protected static MTLRenderer mtlRenderPipe;96protected static PixelToParallelogramConverter mtlTxRenderPipe;97protected static ParallelogramPipe mtlAAPgramPipe;98protected static MTLTextRenderer mtlTextPipe;99protected static MTLDrawImage mtlImagePipe;100101static {102if (!GraphicsEnvironment.isHeadless()) {103MTLRenderQueue rq = MTLRenderQueue.getInstance();104mtlImagePipe = new MTLDrawImage();105mtlTextPipe = new MTLTextRenderer(rq);106mtlRenderPipe = new MTLRenderer(rq);107if (GraphicsPrimitive.tracingEnabled()) {108mtlTextPipe = mtlTextPipe.traceWrap();109//The wrapped mtlRenderPipe will wrap the AA pipe as well...110//mtlAAPgramPipe = mtlRenderPipe.traceWrap();111}112mtlAAPgramPipe = mtlRenderPipe.getAAParallelogramPipe();113mtlTxRenderPipe =114new PixelToParallelogramConverter(mtlRenderPipe,115mtlRenderPipe,1161.0, 0.25, true);117118MTLBlitLoops.register();119MTLMaskFill.register();120MTLMaskBlit.register();121}122}123124protected final int scale;125protected final int width;126protected final int height;127protected int type;128private MTLGraphicsConfig graphicsConfig;129// these fields are set from the native code when the surface is130// initialized131private int nativeWidth;132private int nativeHeight;133134/**135* Returns the appropriate SurfaceType corresponding to the given Metal136* surface type constant (e.g. TEXTURE -> MTLTexture).137*/138private static SurfaceType getCustomSurfaceType(int mtlType) {139switch (mtlType) {140case TEXTURE:141return MTLTexture;142case RT_TEXTURE:143return MTLSurfaceRTT;144default:145return MTLSurface;146}147}148149private native void initOps(MTLGraphicsConfig gc, long pConfigInfo, long pPeerData, long layerPtr,150int xoff, int yoff, boolean isOpaque);151152private MTLSurfaceData(MTLLayer layer, MTLGraphicsConfig gc,153ColorModel cm, int type, int width, int height)154{155super(getCustomSurfaceType(type), cm);156this.graphicsConfig = gc;157this.type = type;158setBlitProxyKey(gc.getProxyKey());159160// TEXTURE shouldn't be scaled, it is used for managed BufferedImages.161scale = type == TEXTURE ? 1 : gc.getDevice().getScaleFactor();162this.width = width * scale;163this.height = height * scale;164165long pConfigInfo = gc.getNativeConfigInfo();166long layerPtr = 0L;167boolean isOpaque = true;168if (layer != null) {169layerPtr = layer.getPointer();170isOpaque = layer.isOpaque();171}172initOps(gc, pConfigInfo, 0, layerPtr, 0, 0, isOpaque);173}174175@Override176public GraphicsConfiguration getDeviceConfiguration() {177return graphicsConfig;178}179180/**181* Creates a SurfaceData object representing the intermediate buffer182* between the Java2D flusher thread and the AppKit thread.183*/184public static MTLLayerSurfaceData createData(MTLLayer layer) {185MTLGraphicsConfig gc = (MTLGraphicsConfig)layer.getGraphicsConfiguration();186Rectangle r = layer.getBounds();187return new MTLLayerSurfaceData(layer, gc, r.width, r.height);188}189190/**191* Creates a SurfaceData object representing an off-screen buffer192*/193public static MTLOffScreenSurfaceData createData(MTLGraphicsConfig gc,194int width, int height,195ColorModel cm, Image image,196int type) {197return new MTLOffScreenSurfaceData(gc, width, height, image, cm,198type);199}200201@Override202public double getDefaultScaleX() {203return scale;204}205206@Override207public double getDefaultScaleY() {208return scale;209}210211@Override212public Rectangle getBounds() {213return new Rectangle(width, height);214}215216protected native void clearWindow();217218protected native boolean initTexture(long pData, boolean isOpaque, int width, int height);219220protected native boolean initRTexture(long pData, boolean isOpaque, int width, int height);221222protected native boolean initFlipBackbuffer(long pData);223224@Override225public SurfaceDataProxy makeProxyFor(SurfaceData srcData) {226return MTLSurfaceDataProxy.createProxy(srcData, graphicsConfig);227}228229/**230* Note: This should only be called from the QFT under the AWT lock.231* This method is kept separate from the initSurface() method below just232* to keep the code a bit cleaner.233*/234private void initSurfaceNow(int width, int height) {235boolean isOpaque = (getTransparency() == Transparency.OPAQUE);236boolean success = false;237238switch (type) {239case TEXTURE:240success = initTexture(getNativeOps(), isOpaque, width, height);241break;242243case RT_TEXTURE:244success = initRTexture(getNativeOps(), isOpaque, width, height);245break;246247case FLIP_BACKBUFFER:248success = initFlipBackbuffer(getNativeOps());249break;250251default:252break;253}254255if (!success) {256throw new OutOfMemoryError("can't create offscreen surface");257}258}259260/**261* Initializes the appropriate Metal offscreen surface based on the value262* of the type parameter. If the surface creation fails for any reason,263* an OutOfMemoryError will be thrown.264*/265protected void initSurface(final int width, final int height) {266MTLRenderQueue rq = MTLRenderQueue.getInstance();267rq.lock();268try {269switch (type) {270case TEXTURE:271case RT_TEXTURE:272// need to make sure the context is current before273// creating the texture274MTLContext.setScratchSurface(graphicsConfig);275break;276default:277break;278}279rq.flushAndInvokeNow(new Runnable() {280public void run() {281initSurfaceNow(width, height);282}283});284} finally {285rq.unlock();286}287}288289/**290* Returns the MTLContext for the GraphicsConfig associated with this291* surface.292*/293public final MTLContext getContext() {294return graphicsConfig.getContext();295}296297/**298* Returns the MTLGraphicsConfig associated with this surface.299*/300final MTLGraphicsConfig getMTLGraphicsConfig() {301return graphicsConfig;302}303304/**305* Returns one of the surface type constants defined above.306*/307public final int getType() {308return type;309}310311/**312* For now, we can only render LCD text if:313* - the fragment shader extension is available, and314* - the source color is opaque, and315* - blending is SrcOverNoEa or disabled316* - and the destination is opaque317*318* Eventually, we could enhance the native MTL text rendering code319* and remove the above restrictions, but that would require significantly320* more code just to support a few uncommon cases.321*/322public boolean canRenderLCDText(SunGraphics2D sg2d) {323return324sg2d.surfaceData.getTransparency() == Transparency.OPAQUE &&325sg2d.paintState <= SunGraphics2D.PAINT_OPAQUECOLOR &&326(sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY ||327(sg2d.compositeState <= SunGraphics2D.COMP_ALPHA && canHandleComposite(sg2d.composite)));328}329330private boolean canHandleComposite(Composite c) {331if (c instanceof AlphaComposite) {332AlphaComposite ac = (AlphaComposite)c;333334return ac.getRule() == AlphaComposite.SRC_OVER && ac.getAlpha() >= 1f;335}336return false;337}338339public void validatePipe(SunGraphics2D sg2d) {340TextPipe textpipe;341boolean validated = false;342343// MTLTextRenderer handles both AA and non-AA text, but344// only works with the following modes:345// (Note: For LCD text we only enter this code path if346// canRenderLCDText() has already validated that the mode is347// CompositeType.SrcNoEa (opaque color), which will be subsumed348// by the CompositeType.SrcNoEa (any color) test below.)349350if (/* CompositeType.SrcNoEa (any color) */351(sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY &&352sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) ||353354/* CompositeType.SrcOver (any color) */355(sg2d.compositeState == SunGraphics2D.COMP_ALPHA &&356sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR &&357(((AlphaComposite)sg2d.composite).getRule() ==358AlphaComposite.SRC_OVER)) ||359360/* CompositeType.Xor (any color) */361(sg2d.compositeState == SunGraphics2D.COMP_XOR &&362sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR))363{364textpipe = mtlTextPipe;365} else {366// do this to initialize textpipe correctly; we will attempt367// to override the non-text pipes below368super.validatePipe(sg2d);369textpipe = sg2d.textpipe;370validated = true;371}372373PixelToParallelogramConverter txPipe = null;374MTLRenderer nonTxPipe = null;375376if (sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) {377if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) {378if (sg2d.compositeState <= SunGraphics2D.COMP_XOR) {379txPipe = mtlTxRenderPipe;380nonTxPipe = mtlRenderPipe;381}382} else if (sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) {383if (MTLPaints.isValid(sg2d)) {384txPipe = mtlTxRenderPipe;385nonTxPipe = mtlRenderPipe;386}387// custom paints handled by super.validatePipe() below388}389} else {390if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) {391if (graphicsConfig.isCapPresent(CAPS_PS30) &&392(sg2d.imageComp == CompositeType.SrcOverNoEa ||393sg2d.imageComp == CompositeType.SrcOver))394{395if (!validated) {396super.validatePipe(sg2d);397validated = true;398}399PixelToParallelogramConverter aaConverter =400new PixelToParallelogramConverter(sg2d.shapepipe,401mtlAAPgramPipe,4021.0/8.0, 0.499,403false);404sg2d.drawpipe = aaConverter;405sg2d.fillpipe = aaConverter;406sg2d.shapepipe = aaConverter;407} else if (sg2d.compositeState == SunGraphics2D.COMP_XOR) {408// install the solid pipes when AA and XOR are both enabled409txPipe = mtlTxRenderPipe;410nonTxPipe = mtlRenderPipe;411}412}413// other cases handled by super.validatePipe() below414}415416if (txPipe != null) {417if (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) {418sg2d.drawpipe = txPipe;419sg2d.fillpipe = txPipe;420} else if (sg2d.strokeState != SunGraphics2D.STROKE_THIN) {421sg2d.drawpipe = txPipe;422sg2d.fillpipe = nonTxPipe;423} else {424sg2d.drawpipe = nonTxPipe;425sg2d.fillpipe = nonTxPipe;426}427// Note that we use the transforming pipe here because it428// will examine the shape and possibly perform an optimized429// operation if it can be simplified. The simplifications430// will be valid for all STROKE and TRANSFORM types.431sg2d.shapepipe = txPipe;432} else {433if (!validated) {434super.validatePipe(sg2d);435}436}437438// install the text pipe based on our earlier decision439sg2d.textpipe = textpipe;440441// always override the image pipe with the specialized MTL pipe442sg2d.imagepipe = mtlImagePipe;443}444445@Override446protected MaskFill getMaskFill(SunGraphics2D sg2d) {447if (sg2d.paintState > SunGraphics2D.PAINT_ALPHACOLOR) {448/*449* We can only accelerate non-Color MaskFill operations if450* all of the following conditions hold true:451* - there is an implementation for the given paintState452* - the current Paint can be accelerated for this destination453* - multitexturing is available (since we need to modulate454* the alpha mask texture with the paint texture)455*456* In all other cases, we return null, in which case the457* validation code will choose a more general software-based loop.458*/459if (!MTLPaints.isValid(sg2d) ||460!graphicsConfig.isCapPresent(CAPS_MULTITEXTURE))461{462return null;463}464}465return super.getMaskFill(sg2d);466}467468public void flush() {469invalidate();470MTLRenderQueue rq = MTLRenderQueue.getInstance();471rq.lock();472try {473// make sure we have a current context before474// disposing the native resources (e.g. texture object)475MTLContext.setScratchSurface(graphicsConfig);476477RenderBuffer buf = rq.getBuffer();478rq.ensureCapacityAndAlignment(12, 4);479buf.putInt(FLUSH_SURFACE);480buf.putLong(getNativeOps());481482// this call is expected to complete synchronously, so flush now483rq.flushNow();484} finally {485rq.unlock();486}487}488489public boolean isOnScreen() {490return false;491}492493private native long getMTLTexturePointer(long pData);494495/**496* Returns native resource of specified {@code resType} associated with497* this surface.498*499* Specifically, for {@code MTLSurfaceData} this method returns the500* the following:501* <pre>502* TEXTURE - texture id503* </pre>504*505* Note: the resource returned by this method is only valid on the rendering506* thread.507*508* @return native resource of specified type or 0L if509* such resource doesn't exist or can not be retrieved.510* @see AccelSurface#getNativeResource511*/512public long getNativeResource(int resType) {513if (resType == TEXTURE) {514return getMTLTexturePointer(getNativeOps());515}516return 0L;517}518519public Raster getRaster(int x, int y, int w, int h) {520throw new InternalError("not implemented yet");521}522523@Override524public boolean copyArea(SunGraphics2D sg2d, int x, int y, int w, int h,525int dx, int dy) {526if (sg2d.compositeState >= SunGraphics2D.COMP_XOR) {527return false;528}529mtlRenderPipe.copyArea(sg2d, x, y, w, h, dx, dy);530return true;531}532533public Rectangle getNativeBounds() {534MTLRenderQueue rq = MTLRenderQueue.getInstance();535rq.lock();536try {537return new Rectangle(nativeWidth, nativeHeight);538} finally {539rq.unlock();540}541}542543/**544* A surface which implements an intermediate buffer between545* the Java2D flusher thread and the AppKit thread.546*547* This surface serves as a buffer attached to a MTLLayer and548* the layer redirects all painting to the buffer's graphics.549*/550public static class MTLLayerSurfaceData extends MTLSurfaceData {551552private final MTLLayer layer;553554private MTLLayerSurfaceData(MTLLayer layer, MTLGraphicsConfig gc,555int width, int height) {556super(layer, gc, gc.getColorModel(), RT_TEXTURE, width, height);557this.layer = layer;558initSurface(this.width, this.height);559}560561@Override562public SurfaceData getReplacement() {563return layer.getSurfaceData();564}565566@Override567public boolean isOnScreen() {568return true;569}570571@Override572public Object getDestination() {573return layer.getDestination();574}575576@Override577public int getTransparency() {578return layer.getTransparency();579}580581@Override582public void invalidate() {583super.invalidate();584clearWindow();585}586}587588/**589* SurfaceData object representing an off-screen buffer590*/591public static class MTLOffScreenSurfaceData extends MTLSurfaceData {592private final Image offscreenImage;593594public MTLOffScreenSurfaceData(MTLGraphicsConfig gc, int width,595int height, Image image,596ColorModel cm, int type) {597super(null, gc, cm, type, width, height);598offscreenImage = image;599initSurface(this.width, this.height);600}601602@Override603public SurfaceData getReplacement() {604return restoreContents(offscreenImage);605}606607/**608* Returns destination Image associated with this SurfaceData.609*/610@Override611public Object getDestination() {612return offscreenImage;613}614}615616617/**618* Disposes the native resources associated with the given MTLSurfaceData619* (referenced by the pData parameter). This method is invoked from620* the native Dispose() method from the Disposer thread when the621* Java-level MTLSurfaceData object is about to go away.622*/623public static void dispose(long pData, MTLGraphicsConfig gc) {624MTLRenderQueue rq = MTLRenderQueue.getInstance();625rq.lock();626try {627// make sure we have a current context before628// disposing the native resources (e.g. texture object)629MTLContext.setScratchSurface(gc);630RenderBuffer buf = rq.getBuffer();631rq.ensureCapacityAndAlignment(12, 4);632buf.putInt(DISPOSE_SURFACE);633buf.putLong(pData);634635// this call is expected to complete synchronously, so flush now636rq.flushNow();637} finally {638rq.unlock();639}640}641}642643644