8260931: Implement JEP 382: New macOS Rendering Pipeline

Co-authored-by: Jayathirth D V <jdv@openjdk.org>
Co-authored-by: Alexey Ushakov <avu@openjdk.org>
Co-authored-by: Artem Bochkarev <abochkarev@openjdk.org>
Co-authored-by: Prasanta Sadhukhan <psadhukhan@openjdk.org>
Co-authored-by: Denis Konoplev <dkonoplev@openjdk.org>
Co-authored-by: Phil Race <prr@openjdk.org>
Co-authored-by: Kevin Rushforth <kcr@openjdk.org>
Co-authored-by: Magnus Ihse Bursie <ihse@openjdk.org>
Co-authored-by: Ajit Ghaisas <aghaisas@openjdk.org>
Reviewed-by: ihse, avu, kcr, gziemski, prr, kizune, jdv, psadhukhan, serb
This commit is contained in:
Ajit Ghaisas 2021-03-15 06:41:44 +00:00
parent 0638303316
commit 8afec70c28
87 changed files with 17610 additions and 39 deletions

View file

@ -576,6 +576,8 @@ CXXFILT:=@CXXFILT@
LIPO:=@LIPO@
INSTALL_NAME_TOOL:=@INSTALL_NAME_TOOL@
METAL := @METAL@
METALLIB := @METALLIB@
# Options to linker to specify a mapfile.
# (Note absence of := assignment, because we do not want to evaluate the macro body here)

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2011, 2021, 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
@ -723,6 +723,32 @@ AC_DEFUN_ONCE([TOOLCHAIN_DETECT_TOOLCHAIN_EXTRA],
UTIL_LOOKUP_PROGS(LIPO, lipo)
UTIL_REQUIRE_PROGS(OTOOL, otool)
UTIL_REQUIRE_PROGS(INSTALL_NAME_TOOL, install_name_tool)
UTIL_LOOKUP_TOOLCHAIN_PROGS(METAL, metal)
if test "x$METAL" = x; then
AC_MSG_CHECKING([if metal can be run using xcrun])
METAL="xcrun -sdk macosx metal"
test_metal=`$METAL --version 2>&1`
if test $? -ne 0; then
AC_MSG_RESULT([no])
AC_MSG_ERROR([XCode tool 'metal' neither found in path nor with xcrun])
else
AC_MSG_RESULT([yes, will be using '$METAL'])
fi
fi
UTIL_LOOKUP_TOOLCHAIN_PROGS(METALLIB, metallib)
if test "x$METALLIB" = x; then
AC_MSG_CHECKING([if metallib can be run using xcrun])
METALLIB="xcrun -sdk macosx metallib"
test_metallib=`$METALLIB --version 2>&1`
if test $? -ne 0; then
AC_MSG_RESULT([no])
AC_MSG_ERROR([XCode tool 'metallib' neither found in path nor with xcrun])
else
AC_MSG_RESULT([yes, will be using '$METALLIB'])
fi
fi
fi
if test "x$TOOLCHAIN_TYPE" = xmicrosoft; then

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2011, 2021, 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
@ -24,6 +24,7 @@
#
include LibCommon.gmk
include Execute.gmk
# Hook to include the corresponding custom file, if present.
$(eval $(call IncludeCustomExtension, modules/java.desktop/Lib.gmk))

View file

@ -160,6 +160,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \
LIBS_macosx := -lmlib_image \
-framework Cocoa \
-framework OpenGL \
-framework Metal \
-framework JavaRuntimeSupport \
-framework ApplicationServices \
-framework AudioToolbox, \
@ -766,7 +767,8 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false)
-framework ApplicationServices \
-framework Foundation \
-framework Security \
-framework Cocoa
-framework Cocoa \
-framework Metal
else ifeq ($(call isTargetOs, windows), true)
LIBSPLASHSCREEN_LIBS += kernel32.lib user32.lib gdi32.lib delayimp.lib $(WIN_JAVA_LIB) jvm.lib
else
@ -827,6 +829,7 @@ ifeq ($(call isTargetOs, macosx), true)
libawt_lwawt/awt \
libawt_lwawt/font \
libawt_lwawt/java2d/opengl \
libawt_lwawt/java2d/metal \
include \
common/awt/debug \
common/java2d/opengl \
@ -862,6 +865,7 @@ ifeq ($(call isTargetOs, macosx), true)
-framework AudioToolbox \
-framework Carbon \
-framework Cocoa \
-framework Metal \
-framework Security \
-framework ExceptionHandling \
-framework JavaRuntimeSupport \
@ -884,6 +888,28 @@ endif
################################################################################
ifeq ($(call isTargetOs, macosx), true)
SHADERS_SRC := $(TOPDIR)/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/shaders.metal
SHADERS_SUPPORT_DIR := $(SUPPORT_OUTPUTDIR)/native/java.desktop/libosxui
SHADERS_AIR := $(SHADERS_SUPPORT_DIR)/shaders.air
SHADERS_LIB := $(INSTALL_LIBRARIES_HERE)/shaders.metallib
$(eval $(call SetupExecute, metal_shaders, \
INFO := Running metal on $(notdir $(SHADERS_SRC)) (for libosxui.dylib), \
DEPS := $(SHADERS_SRC), \
OUTPUT_FILE := $(SHADERS_AIR), \
SUPPORT_DIR := $(SHADERS_SUPPORT_DIR), \
COMMAND := $(METAL) -c -std=osx-metal2.0 -o $(SHADERS_AIR) $(SHADERS_SRC), \
))
$(eval $(call SetupExecute, metallib_shaders, \
INFO := Running metallib on $(notdir $(SHADERS_AIR)) (for libosxui.dylib), \
DEPS := $(SHADERS_AIR), \
OUTPUT_FILE := $(SHADERS_LIB), \
SUPPORT_DIR := $(SHADERS_SUPPORT_DIR), \
COMMAND := $(METALLIB) -o $(SHADERS_LIB) $(SHADERS_AIR), \
))
TARGETS += $(SHADERS_LIB)
$(eval $(call SetupJdkLibrary, BUILD_LIBOSXUI, \
NAME := osxui, \
@ -899,6 +925,7 @@ ifeq ($(call isTargetOs, macosx), true)
-L$(INSTALL_LIBRARIES_HERE), \
LIBS := -lawt -losxapp -lawt_lwawt \
-framework Cocoa \
-framework Metal \
-framework Carbon \
-framework ApplicationServices \
-framework JavaRuntimeSupport \
@ -906,6 +933,7 @@ ifeq ($(call isTargetOs, macosx), true)
))
TARGETS += $(BUILD_LIBOSXUI)
$(BUILD_LIBOSXUI): $(SHADERS_LIB)
$(BUILD_LIBOSXUI): $(BUILD_LIBAWT)

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2021, 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
@ -32,8 +32,8 @@ import java.awt.geom.AffineTransform;
import java.awt.image.ColorModel;
import sun.java2d.SurfaceData;
import sun.java2d.opengl.CGLLayer;
import sun.lwawt.LWGraphicsConfig;
import sun.lwawt.macosx.CFRetainedResource;
public abstract class CGraphicsConfig extends GraphicsConfiguration
implements LWGraphicsConfig {
@ -80,7 +80,7 @@ public abstract class CGraphicsConfig extends GraphicsConfiguration
* Creates a new SurfaceData that will be associated with the given
* CGLLayer.
*/
public abstract SurfaceData createSurfaceData(CGLLayer layer);
public abstract SurfaceData createSurfaceData(CFRetainedResource layer);
@Override
public final boolean isTranslucencyCapable() {

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2021, 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
@ -37,6 +37,8 @@ import java.awt.peer.WindowPeer;
import java.util.Objects;
import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.MacOSFlags;
import sun.java2d.metal.MTLGraphicsConfig;
import sun.java2d.opengl.CGLGraphicsConfig;
import static java.awt.peer.ComponentPeer.SET_BOUNDS;
@ -54,7 +56,10 @@ public final class CGraphicsDevice extends GraphicsDevice
private volatile Rectangle bounds;
private volatile int scale;
private final GraphicsConfiguration config;
private GraphicsConfiguration config;
private static boolean metalPipelineEnabled = false;
private static boolean oglPipelineEnabled = false;
private static AWTPermission fullScreenExclusivePermission;
@ -63,7 +68,64 @@ public final class CGraphicsDevice extends GraphicsDevice
public CGraphicsDevice(final int displayID) {
this.displayID = displayID;
config = CGLGraphicsConfig.getConfig(this);
if (MacOSFlags.isMetalEnabled()) {
// Try to create MTLGraphicsConfig, if it fails,
// try to create CGLGraphicsConfig as a fallback
this.config = MTLGraphicsConfig.getConfig(this, displayID);
if (this.config != null) {
metalPipelineEnabled = true;
} else {
// Try falling back to OpenGL pipeline
if (MacOSFlags.isMetalVerbose()) {
System.out.println("Metal rendering pipeline" +
" initialization failed,using OpenGL" +
" rendering pipeline");
}
this.config = CGLGraphicsConfig.getConfig(this);
if (this.config != null) {
oglPipelineEnabled = true;
}
}
} else {
// Try to create CGLGraphicsConfig, if it fails,
// try to create MTLGraphicsConfig as a fallback
this.config = CGLGraphicsConfig.getConfig(this);
if (this.config != null) {
oglPipelineEnabled = true;
} else {
// Try falling back to Metal pipeline
if (MacOSFlags.isOGLVerbose()) {
System.out.println("OpenGL rendering pipeline" +
" initialization failed,using Metal" +
" rendering pipeline");
}
this.config = MTLGraphicsConfig.getConfig(this, displayID);
if (this.config != null) {
metalPipelineEnabled = true;
}
}
}
if (!metalPipelineEnabled && !oglPipelineEnabled) {
// This indicates fallback to other rendering pipeline also failed.
// Should never reach here
throw new InternalError("Error - unable to initialize any" +
" rendering pipeline.");
}
if (metalPipelineEnabled && MacOSFlags.isMetalVerbose()) {
System.out.println("Metal pipeline enabled on screen " + displayID);
} else if (oglPipelineEnabled && MacOSFlags.isOGLVerbose()) {
System.out.println("OpenGL pipeline enabled on screen " + displayID);
}
// initializes default device state, might be redundant step since we
// call "displayChanged()" later anyway, but we do not want to leave the
// device in an inconsistent state after construction
@ -265,6 +327,10 @@ public final class CGraphicsDevice extends GraphicsDevice
return nativeGetDisplayModes(displayID);
}
public static boolean usingMetalPipeline() {
return metalPipelineEnabled;
}
private void initScaleFactor() {
if (SunGraphicsEnvironment.isUIScaleEnabled()) {
double debugScale = SunGraphicsEnvironment.getDebugScale();

View file

@ -0,0 +1,165 @@
/*
* Copyright (c) 2019, 2021, 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;
import java.security.PrivilegedAction;
import sun.java2d.metal.MTLGraphicsConfig;
import sun.java2d.opengl.CGLGraphicsConfig;
public class MacOSFlags {
/**
* Description of command-line flags. All flags with [true|false]
* values
* metalEnabled: usage: "-Dsun.java2d.metal=[true|false]"
*/
private static boolean oglEnabled;
private static boolean oglVerbose;
private static boolean metalEnabled;
private static boolean metalVerbose;
private enum PropertyState {ENABLED, DISABLED, UNSPECIFIED};
static {
initJavaFlags();
}
private static PropertyState getBooleanProp(String p, PropertyState defaultVal) {
String propString = System.getProperty(p);
PropertyState returnVal = defaultVal;
if (propString != null) {
if (propString.equals("true") ||
propString.equals("t") ||
propString.equals("True") ||
propString.equals("T") ||
propString.equals("")) // having the prop name alone
{ // is equivalent to true
returnVal = PropertyState.ENABLED;
} else if (propString.equals("false") ||
propString.equals("f") ||
propString.equals("False") ||
propString.equals("F"))
{
returnVal = PropertyState.DISABLED;
}
}
return returnVal;
}
private static boolean isBooleanPropTrueVerbose(String p) {
String propString = System.getProperty(p);
if (propString != null) {
if (propString.equals("True") ||
propString.equals("T"))
{
return true;
}
}
return false;
}
private static void initJavaFlags() {
java.security.AccessController.doPrivileged(
(PrivilegedAction<Object>) () -> {
PropertyState oglState = getBooleanProp("sun.java2d.opengl", PropertyState.UNSPECIFIED);
PropertyState metalState = getBooleanProp("sun.java2d.metal", PropertyState.UNSPECIFIED);
// Handle invalid combinations to use the default rendering pipeline
// Current default rendering pipeline is OpenGL
// (The default can be changed to Metal in future just by toggling two states in this if condition block)
if ((oglState == PropertyState.UNSPECIFIED && metalState == PropertyState.UNSPECIFIED) ||
(oglState == PropertyState.DISABLED && metalState == PropertyState.DISABLED) ||
(oglState == PropertyState.ENABLED && metalState == PropertyState.ENABLED)) {
oglState = PropertyState.ENABLED; // Enable default pipeline
metalState = PropertyState.DISABLED; // Disable non-default pipeline
}
if (metalState == PropertyState.UNSPECIFIED) {
if (oglState == PropertyState.DISABLED) {
oglEnabled = false;
metalEnabled = true;
} else {
oglEnabled = true;
metalEnabled = false;
}
} else if (metalState == PropertyState.ENABLED) {
oglEnabled = false;
metalEnabled = true;
} else if (metalState == PropertyState.DISABLED) {
oglEnabled = true;
metalEnabled = false;
}
oglVerbose = isBooleanPropTrueVerbose("sun.java2d.opengl");
metalVerbose = isBooleanPropTrueVerbose("sun.java2d.metal");
if (oglEnabled && !metalEnabled) {
// Check whether OGL is available
if (!CGLGraphicsConfig.isCGLAvailable()) {
if (oglVerbose) {
System.out.println("Could not enable OpenGL pipeline (CGL not available)");
}
oglEnabled = false;
metalEnabled = MTLGraphicsConfig.isMetalAvailable();
}
} else if (metalEnabled && !oglEnabled) {
// Check whether Metal framework is available
if (!MTLGraphicsConfig.isMetalAvailable()) {
if (metalVerbose) {
System.out.println("Could not enable Metal pipeline (Metal framework not available)");
}
metalEnabled = false;
oglEnabled = CGLGraphicsConfig.isCGLAvailable();
}
}
// At this point one of the rendering pipeline must be enabled.
if (!metalEnabled && !oglEnabled) {
throw new InternalError("Error - unable to initialize any rendering pipeline.");
}
return null;
});
}
public static boolean isMetalEnabled() {
return metalEnabled;
}
public static boolean isMetalVerbose() {
return metalVerbose;
}
public static boolean isOGLEnabled() {
return oglEnabled;
}
public static boolean isOGLVerbose() {
return oglVerbose;
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2021, 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
@ -27,6 +27,8 @@ package sun.java2d;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.VolatileSurfaceManager;
import sun.awt.CGraphicsDevice;
import sun.java2d.metal.MTLVolatileSurfaceManager;
import sun.java2d.opengl.CGLVolatileSurfaceManager;
/**
@ -42,13 +44,14 @@ public class MacosxSurfaceManagerFactory extends SurfaceManagerFactory {
* as a way for the caller to pass pipeline-specific context data to
* the VolatileSurfaceManager (such as a backbuffer handle, for example).
*
* For Mac OS X, this method returns either an CGL-specific
* For Mac OS X, this method returns either an CGL/MTL-specific
* VolatileSurfaceManager based on the GraphicsConfiguration
* under which the SunVolatileImage was created.
*/
public VolatileSurfaceManager createVolatileManager(SunVolatileImage vImg,
Object context)
{
return new CGLVolatileSurfaceManager(vImg, context);
return CGraphicsDevice.usingMetalPipeline() ? new MTLVolatileSurfaceManager(vImg, context) :
new CGLVolatileSurfaceManager(vImg, context);
}
}

View file

@ -0,0 +1,905 @@
/*
* Copyright (c) 2019, 2021, 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.java2d.SurfaceData;
import sun.java2d.loops.Blit;
import sun.java2d.loops.CompositeType;
import sun.java2d.loops.GraphicsPrimitive;
import sun.java2d.loops.GraphicsPrimitiveMgr;
import sun.java2d.loops.ScaledBlit;
import sun.java2d.loops.SurfaceType;
import sun.java2d.loops.TransformBlit;
import sun.java2d.pipe.Region;
import sun.java2d.pipe.RenderBuffer;
import sun.java2d.pipe.RenderQueue;
import sun.java2d.pipe.hw.AccelSurface;
import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.Transparency;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.lang.annotation.Native;
import java.lang.ref.WeakReference;
import static sun.java2d.pipe.BufferedOpCodes.BLIT;
import static sun.java2d.pipe.BufferedOpCodes.SURFACE_TO_SW_BLIT;
final class MTLBlitLoops {
static void register() {
Blit blitIntArgbPreToSurface =
new MTLSwToSurfaceBlit(SurfaceType.IntArgbPre,
MTLSurfaceData.PF_INT_ARGB_PRE);
Blit blitIntArgbPreToTexture =
new MTLSwToTextureBlit(SurfaceType.IntArgbPre,
MTLSurfaceData.PF_INT_ARGB_PRE);
TransformBlit transformBlitIntArgbPreToSurface =
new MTLSwToSurfaceTransform(SurfaceType.IntArgbPre,
MTLSurfaceData.PF_INT_ARGB_PRE);
MTLSurfaceToSwBlit blitSurfaceToIntArgbPre =
new MTLSurfaceToSwBlit(SurfaceType.IntArgbPre,
MTLSurfaceData.PF_INT_ARGB_PRE);
GraphicsPrimitive[] primitives = {
// surface->surface ops
new MTLSurfaceToSurfaceBlit(),
new MTLSurfaceToSurfaceScale(),
new MTLSurfaceToSurfaceTransform(),
// render-to-texture surface->surface ops
new MTLRTTSurfaceToSurfaceBlit(),
new MTLRTTSurfaceToSurfaceScale(),
new MTLRTTSurfaceToSurfaceTransform(),
// surface->sw ops
new MTLSurfaceToSwBlit(SurfaceType.IntArgb,
MTLSurfaceData.PF_INT_ARGB),
blitSurfaceToIntArgbPre,
// sw->surface ops
blitIntArgbPreToSurface,
new MTLSwToSurfaceBlit(SurfaceType.IntRgb,
MTLSurfaceData.PF_INT_RGB),
new MTLSwToSurfaceBlit(SurfaceType.IntRgbx,
MTLSurfaceData.PF_INT_RGBX),
new MTLSwToSurfaceBlit(SurfaceType.IntBgr,
MTLSurfaceData.PF_INT_BGR),
new MTLSwToSurfaceBlit(SurfaceType.IntBgrx,
MTLSurfaceData.PF_INT_BGRX),
new MTLGeneralBlit(MTLSurfaceData.MTLSurface,
CompositeType.AnyAlpha,
blitIntArgbPreToSurface),
new MTLAnyCompositeBlit(MTLSurfaceData.MTLSurface,
blitSurfaceToIntArgbPre,
blitSurfaceToIntArgbPre,
blitIntArgbPreToSurface),
new MTLAnyCompositeBlit(SurfaceType.Any,
null,
blitSurfaceToIntArgbPre,
blitIntArgbPreToSurface),
new MTLSwToSurfaceScale(SurfaceType.IntRgb,
MTLSurfaceData.PF_INT_RGB),
new MTLSwToSurfaceScale(SurfaceType.IntRgbx,
MTLSurfaceData.PF_INT_RGBX),
new MTLSwToSurfaceScale(SurfaceType.IntBgr,
MTLSurfaceData.PF_INT_BGR),
new MTLSwToSurfaceScale(SurfaceType.IntBgrx,
MTLSurfaceData.PF_INT_BGRX),
new MTLSwToSurfaceScale(SurfaceType.IntArgbPre,
MTLSurfaceData.PF_INT_ARGB_PRE),
new MTLSwToSurfaceTransform(SurfaceType.IntRgb,
MTLSurfaceData.PF_INT_RGB),
new MTLSwToSurfaceTransform(SurfaceType.IntRgbx,
MTLSurfaceData.PF_INT_RGBX),
new MTLSwToSurfaceTransform(SurfaceType.IntBgr,
MTLSurfaceData.PF_INT_BGR),
new MTLSwToSurfaceTransform(SurfaceType.IntBgrx,
MTLSurfaceData.PF_INT_BGRX),
transformBlitIntArgbPreToSurface,
new MTLGeneralTransformedBlit(transformBlitIntArgbPreToSurface),
// texture->surface ops
new MTLTextureToSurfaceBlit(),
new MTLTextureToSurfaceScale(),
new MTLTextureToSurfaceTransform(),
// sw->texture ops
blitIntArgbPreToTexture,
new MTLSwToTextureBlit(SurfaceType.IntRgb,
MTLSurfaceData.PF_INT_RGB),
new MTLSwToTextureBlit(SurfaceType.IntRgbx,
MTLSurfaceData.PF_INT_RGBX),
new MTLSwToTextureBlit(SurfaceType.IntBgr,
MTLSurfaceData.PF_INT_BGR),
new MTLSwToTextureBlit(SurfaceType.IntBgrx,
MTLSurfaceData.PF_INT_BGRX),
new MTLGeneralBlit(MTLSurfaceData.MTLTexture,
CompositeType.SrcNoEa,
blitIntArgbPreToTexture),
};
GraphicsPrimitiveMgr.register(primitives);
}
/**
* The following offsets are used to pack the parameters in
* createPackedParams(). (They are also used at the native level when
* unpacking the params.)
*/
@Native private static final int OFFSET_SRCTYPE = 16;
@Native private static final int OFFSET_HINT = 8;
@Native private static final int OFFSET_TEXTURE = 3;
@Native private static final int OFFSET_RTT = 2;
@Native private static final int OFFSET_XFORM = 1;
@Native private static final int OFFSET_ISOBLIT = 0;
/**
* Packs the given parameters into a single int value in order to save
* space on the rendering queue.
*/
private static int createPackedParams(boolean isoblit, boolean texture,
boolean rtt, boolean xform,
int hint, int srctype)
{
return
((srctype << OFFSET_SRCTYPE) |
(hint << OFFSET_HINT ) |
((texture ? 1 : 0) << OFFSET_TEXTURE) |
((rtt ? 1 : 0) << OFFSET_RTT ) |
((xform ? 1 : 0) << OFFSET_XFORM ) |
((isoblit ? 1 : 0) << OFFSET_ISOBLIT));
}
/**
* Enqueues a BLIT operation with the given parameters. Note that the
* RenderQueue lock must be held before calling this method.
*/
private static void enqueueBlit(RenderQueue rq,
SurfaceData src, SurfaceData dst,
int packedParams,
int sx1, int sy1,
int sx2, int sy2,
double dx1, double dy1,
double dx2, double dy2)
{
// assert rq.lock.isHeldByCurrentThread();
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(72, 24);
buf.putInt(BLIT);
buf.putInt(packedParams);
buf.putInt(sx1).putInt(sy1);
buf.putInt(sx2).putInt(sy2);
buf.putDouble(dx1).putDouble(dy1);
buf.putDouble(dx2).putDouble(dy2);
buf.putLong(src.getNativeOps());
buf.putLong(dst.getNativeOps());
}
static void Blit(SurfaceData srcData, SurfaceData dstData,
Composite comp, Region clip,
AffineTransform xform, int hint,
int sx1, int sy1,
int sx2, int sy2,
double dx1, double dy1,
double dx2, double dy2,
int srctype, boolean texture)
{
int ctxflags = 0;
if (srcData.getTransparency() == Transparency.OPAQUE) {
ctxflags |= MTLContext.SRC_IS_OPAQUE;
}
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
// make sure the RenderQueue keeps a hard reference to the
// source (sysmem) SurfaceData to prevent it from being
// disposed while the operation is processed on the QFT
rq.addReference(srcData);
MTLSurfaceData mtlDst = (MTLSurfaceData)dstData;
if (texture) {
// make sure we have a current context before uploading
// the sysmem data to the texture object
MTLGraphicsConfig gc = mtlDst.getMTLGraphicsConfig();
MTLContext.setScratchSurface(gc);
} else {
MTLContext.validateContext(mtlDst, mtlDst,
clip, comp, xform, null, null,
ctxflags);
}
int packedParams = createPackedParams(false, texture,
false /*unused*/, xform != null,
hint, srctype);
enqueueBlit(rq, srcData, dstData,
packedParams,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2);
// always flush immediately, since we (currently) have no means
// of tracking changes to the system memory surface
rq.flushNow();
} finally {
rq.unlock();
}
}
/**
* Note: The srcImg and biop parameters are only used when invoked
* from the MTLBufImgOps.renderImageWithOp() method; in all other cases,
* this method can be called with null values for those two parameters,
* and they will be effectively ignored.
*/
static void IsoBlit(SurfaceData srcData, SurfaceData dstData,
BufferedImage srcImg, BufferedImageOp biop,
Composite comp, Region clip,
AffineTransform xform, int hint,
int sx1, int sy1,
int sx2, int sy2,
double dx1, double dy1,
double dx2, double dy2,
boolean texture)
{
int ctxflags = 0;
if (srcData.getTransparency() == Transparency.OPAQUE) {
ctxflags |= MTLContext.SRC_IS_OPAQUE;
}
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
MTLSurfaceData mtlSrc = (MTLSurfaceData)srcData;
MTLSurfaceData mtlDst = (MTLSurfaceData)dstData;
int srctype = mtlSrc.getType();
boolean rtt;
MTLSurfaceData srcCtxData;
if (srctype == MTLSurfaceData.TEXTURE) {
// the source is a regular texture object; we substitute
// the destination surface for the purposes of making a
// context current
rtt = false;
srcCtxData = mtlDst;
} else {
// the source is a pbuffer, backbuffer, or render-to-texture
// surface; we set rtt to true to differentiate this kind
// of surface from a regular texture object
rtt = true;
if (srctype == AccelSurface.RT_TEXTURE) {
srcCtxData = mtlDst;
} else {
srcCtxData = mtlSrc;
}
}
MTLContext.validateContext(srcCtxData, mtlDst,
clip, comp, xform, null, null,
ctxflags);
if (biop != null) {
MTLBufImgOps.enableBufImgOp(rq, mtlSrc, srcImg, biop);
}
int packedParams = createPackedParams(true, texture,
false /*unused*/, xform != null,
hint, 0 /*unused*/);
enqueueBlit(rq, srcData, dstData,
packedParams,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2);
if (biop != null) {
MTLBufImgOps.disableBufImgOp(rq, biop);
}
if (rtt && mtlDst.isOnScreen()) {
// we only have to flush immediately when copying from a
// (non-texture) surface to the screen; otherwise Swing apps
// might appear unresponsive until the auto-flush completes
rq.flushNow();
}
} finally {
rq.unlock();
}
}
}
class MTLSurfaceToSurfaceBlit extends Blit {
MTLSurfaceToSurfaceBlit() {
super(MTLSurfaceData.MTLSurface,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy, int w, int h)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
false);
}
}
class MTLSurfaceToSurfaceScale extends ScaledBlit {
MTLSurfaceToSurfaceScale() {
super(MTLSurfaceData.MTLSurface,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Scale(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx1, int sy1,
int sx2, int sy2,
double dx1, double dy1,
double dx2, double dy2)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2,
false);
}
}
class MTLSurfaceToSurfaceTransform extends TransformBlit {
MTLSurfaceToSurfaceTransform() {
super(MTLSurfaceData.MTLSurface,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Transform(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
AffineTransform at, int hint,
int sx, int sy, int dx, int dy,
int w, int h)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, at, hint,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
false);
}
}
class MTLRTTSurfaceToSurfaceBlit extends Blit {
MTLRTTSurfaceToSurfaceBlit() {
super(MTLSurfaceData.MTLSurfaceRTT,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy, int w, int h)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
true);
}
}
class MTLRTTSurfaceToSurfaceScale extends ScaledBlit {
MTLRTTSurfaceToSurfaceScale() {
super(MTLSurfaceData.MTLSurfaceRTT,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Scale(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx1, int sy1,
int sx2, int sy2,
double dx1, double dy1,
double dx2, double dy2)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2,
true);
}
}
class MTLRTTSurfaceToSurfaceTransform extends TransformBlit {
MTLRTTSurfaceToSurfaceTransform() {
super(MTLSurfaceData.MTLSurfaceRTT,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Transform(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
AffineTransform at, int hint,
int sx, int sy, int dx, int dy, int w, int h)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, at, hint,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
true);
}
}
final class MTLSurfaceToSwBlit extends Blit {
private final int typeval;
private WeakReference<SurfaceData> srcTmp;
// destination will actually be ArgbPre or Argb
MTLSurfaceToSwBlit(final SurfaceType dstType, final int typeval) {
super(MTLSurfaceData.MTLSurface,
CompositeType.SrcNoEa,
dstType);
this.typeval = typeval;
}
private synchronized void complexClipBlit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy,
int w, int h) {
SurfaceData cachedSrc = null;
if (srcTmp != null) {
// use cached intermediate surface, if available
cachedSrc = srcTmp.get();
}
// We can convert argb_pre data from MTL surface in two places:
// - During MTL surface -> SW blit
// - During SW -> SW blit
// The first one is faster when we use opaque MTL surface, because in
// this case we simply skip conversion and use color components as is.
// Because of this we align intermediate buffer type with type of
// destination not source.
final int type = typeval == MTLSurfaceData.PF_INT_ARGB_PRE ?
BufferedImage.TYPE_INT_ARGB_PRE :
BufferedImage.TYPE_INT_ARGB;
src = convertFrom(this, src, sx, sy, w, h, cachedSrc, type);
// copy intermediate SW to destination SW using complex clip
final Blit performop = Blit.getFromCache(src.getSurfaceType(),
CompositeType.SrcNoEa,
dst.getSurfaceType());
performop.Blit(src, dst, comp, clip, 0, 0, dx, dy, w, h);
if (src != cachedSrc) {
// cache the intermediate surface
srcTmp = new WeakReference<>(src);
}
}
public void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy,
int w, int h)
{
if (clip != null) {
clip = clip.getIntersectionXYWH(dx, dy, w, h);
// At the end this method will flush the RenderQueue, we should exit
// from it as soon as possible.
if (clip.isEmpty()) {
return;
}
sx += clip.getLoX() - dx;
sy += clip.getLoY() - dy;
dx = clip.getLoX();
dy = clip.getLoY();
w = clip.getWidth();
h = clip.getHeight();
if (!clip.isRectangular()) {
complexClipBlit(src, dst, comp, clip, sx, sy, dx, dy, w, h);
return;
}
}
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
// make sure the RenderQueue keeps a hard reference to the
// destination (sysmem) SurfaceData to prevent it from being
// disposed while the operation is processed on the QFT
rq.addReference(dst);
RenderBuffer buf = rq.getBuffer();
MTLContext.validateContext((MTLSurfaceData)src);
rq.ensureCapacityAndAlignment(48, 32);
buf.putInt(SURFACE_TO_SW_BLIT);
buf.putInt(sx).putInt(sy);
buf.putInt(dx).putInt(dy);
buf.putInt(w).putInt(h);
buf.putInt(typeval);
buf.putLong(src.getNativeOps());
buf.putLong(dst.getNativeOps());
// always flush immediately
rq.flushNow();
} finally {
rq.unlock();
}
}
}
class MTLSwToSurfaceBlit extends Blit {
private int typeval;
MTLSwToSurfaceBlit(SurfaceType srcType, int typeval) {
super(srcType,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
this.typeval = typeval;
}
public void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy, int w, int h)
{
MTLBlitLoops.Blit(src, dst,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
typeval, false);
}
}
class MTLSwToSurfaceScale extends ScaledBlit {
private int typeval;
MTLSwToSurfaceScale(SurfaceType srcType, int typeval) {
super(srcType,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
this.typeval = typeval;
}
public void Scale(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx1, int sy1,
int sx2, int sy2,
double dx1, double dy1,
double dx2, double dy2)
{
MTLBlitLoops.Blit(src, dst,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2,
typeval, false);
}
}
class MTLSwToSurfaceTransform extends TransformBlit {
private int typeval;
MTLSwToSurfaceTransform(SurfaceType srcType, int typeval) {
super(srcType,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
this.typeval = typeval;
}
public void Transform(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
AffineTransform at, int hint,
int sx, int sy, int dx, int dy, int w, int h)
{
MTLBlitLoops.Blit(src, dst,
comp, clip, at, hint,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
typeval, false);
}
}
class MTLSwToTextureBlit extends Blit {
private int typeval;
MTLSwToTextureBlit(SurfaceType srcType, int typeval) {
super(srcType,
CompositeType.SrcNoEa,
MTLSurfaceData.MTLTexture);
this.typeval = typeval;
}
public void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy, int w, int h)
{
MTLBlitLoops.Blit(src, dst,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
typeval, true);
}
}
class MTLTextureToSurfaceBlit extends Blit {
MTLTextureToSurfaceBlit() {
super(MTLSurfaceData.MTLTexture,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy, int w, int h)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
true);
}
}
class MTLTextureToSurfaceScale extends ScaledBlit {
MTLTextureToSurfaceScale() {
super(MTLSurfaceData.MTLTexture,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Scale(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx1, int sy1,
int sx2, int sy2,
double dx1, double dy1,
double dx2, double dy2)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2,
true);
}
}
class MTLTextureToSurfaceTransform extends TransformBlit {
MTLTextureToSurfaceTransform() {
super(MTLSurfaceData.MTLTexture,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Transform(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
AffineTransform at, int hint,
int sx, int sy, int dx, int dy,
int w, int h)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, at, hint,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
true);
}
}
/**
* This general Blit implementation converts any source surface to an
* intermediate IntArgbPre surface, and then uses the more specific
* IntArgbPre->MTLSurface/Texture loop to get the intermediate
* (premultiplied) surface down to Metal using simple blit.
*/
class MTLGeneralBlit extends Blit {
private final Blit performop;
private WeakReference<SurfaceData> srcTmp;
MTLGeneralBlit(SurfaceType dstType,
CompositeType compType,
Blit performop)
{
super(SurfaceType.Any, compType, dstType);
this.performop = performop;
}
public synchronized void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy,
int w, int h)
{
Blit convertsrc = Blit.getFromCache(src.getSurfaceType(),
CompositeType.SrcNoEa,
SurfaceType.IntArgbPre);
SurfaceData cachedSrc = null;
if (srcTmp != null) {
// use cached intermediate surface, if available
cachedSrc = srcTmp.get();
}
// convert source to IntArgbPre
src = convertFrom(convertsrc, src, sx, sy, w, h,
cachedSrc, BufferedImage.TYPE_INT_ARGB_PRE);
// copy IntArgbPre intermediate surface to Metal surface
performop.Blit(src, dst, comp, clip,
0, 0, dx, dy, w, h);
if (src != cachedSrc) {
// cache the intermediate surface
srcTmp = new WeakReference<>(src);
}
}
}
/**
* This general TransformedBlit implementation converts any source surface to an
* intermediate IntArgbPre surface, and then uses the more specific
* IntArgbPre->MTLSurface/Texture loop to get the intermediate
* (premultiplied) surface down to Metal using simple transformBlit.
*/
final class MTLGeneralTransformedBlit extends TransformBlit {
private final TransformBlit performop;
private WeakReference<SurfaceData> srcTmp;
MTLGeneralTransformedBlit(final TransformBlit performop) {
super(SurfaceType.Any, CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
this.performop = performop;
}
@Override
public synchronized void Transform(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
AffineTransform at, int hint, int srcx,
int srcy, int dstx, int dsty, int width,
int height){
Blit convertsrc = Blit.getFromCache(src.getSurfaceType(),
CompositeType.SrcNoEa,
SurfaceType.IntArgbPre);
// use cached intermediate surface, if available
final SurfaceData cachedSrc = srcTmp != null ? srcTmp.get() : null;
// convert source to IntArgbPre
src = convertFrom(convertsrc, src, srcx, srcy, width, height, cachedSrc,
BufferedImage.TYPE_INT_ARGB_PRE);
// transform IntArgbPre intermediate surface to Metal surface
performop.Transform(src, dst, comp, clip, at, hint, 0, 0, dstx, dsty,
width, height);
if (src != cachedSrc) {
// cache the intermediate surface
srcTmp = new WeakReference<>(src);
}
}
}
/**
* This general MTLAnyCompositeBlit implementation can convert any source/target
* surface to an intermediate surface using convertsrc/convertdst loops, applies
* necessary composite operation, and then uses convertresult loop to get the
* intermediate surface down to Metal.
*/
final class MTLAnyCompositeBlit extends Blit {
private WeakReference<SurfaceData> dstTmp;
private WeakReference<SurfaceData> srcTmp;
private final Blit convertsrc;
private final Blit convertdst;
private final Blit convertresult;
MTLAnyCompositeBlit(SurfaceType srctype, Blit convertsrc, Blit convertdst,
Blit convertresult) {
super(srctype, CompositeType.Any, MTLSurfaceData.MTLSurface);
this.convertsrc = convertsrc;
this.convertdst = convertdst;
this.convertresult = convertresult;
}
public synchronized void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy,
int w, int h)
{
if (convertsrc != null) {
SurfaceData cachedSrc = null;
if (srcTmp != null) {
// use cached intermediate surface, if available
cachedSrc = srcTmp.get();
}
// convert source to IntArgbPre
src = convertFrom(convertsrc, src, sx, sy, w, h, cachedSrc,
BufferedImage.TYPE_INT_ARGB_PRE);
if (src != cachedSrc) {
// cache the intermediate surface
srcTmp = new WeakReference<>(src);
}
}
SurfaceData cachedDst = null;
if (dstTmp != null) {
// use cached intermediate surface, if available
cachedDst = dstTmp.get();
}
// convert destination to IntArgbPre
SurfaceData dstBuffer = convertFrom(convertdst, dst, dx, dy, w, h,
cachedDst, BufferedImage.TYPE_INT_ARGB_PRE);
Region bufferClip =
clip == null ? null : clip.getTranslatedRegion(-dx, -dy);
Blit performop = Blit.getFromCache(src.getSurfaceType(),
CompositeType.Any, dstBuffer.getSurfaceType());
performop.Blit(src, dstBuffer, comp, bufferClip, sx, sy, 0, 0, w, h);
if (dstBuffer != cachedDst) {
// cache the intermediate surface
dstTmp = new WeakReference<>(dstBuffer);
}
// now blit the buffer back to the destination
convertresult.Blit(dstBuffer, dst, AlphaComposite.Src, clip, 0, 0, dx,
dy, w, h);
}
}

View file

@ -0,0 +1,117 @@
/*
* Copyright (c) 2019, 2021, 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.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.loops.CompositeType;
import sun.java2d.pipe.BufferedBufImgOps;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.LookupOp;
import java.awt.image.RescaleOp;
import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_BIOP_SHADER;
class MTLBufImgOps extends BufferedBufImgOps {
/**
* This method is called from MTLDrawImage.transformImage() only. It
* validates the provided BufferedImageOp to determine whether the op
* is one that can be accelerated by the MTL pipeline. If the operation
* cannot be completed for any reason, this method returns false;
* otherwise, the given BufferedImage is rendered to the destination
* using the provided BufferedImageOp and this method returns true.
*/
static boolean renderImageWithOp(SunGraphics2D sg, BufferedImage img,
BufferedImageOp biop, int x, int y)
{
// Validate the provided BufferedImage (make sure it is one that
// is supported, and that its properties are acceleratable)
if (biop instanceof ConvolveOp) {
if (!isConvolveOpValid((ConvolveOp)biop)) {
return false;
}
} else if (biop instanceof RescaleOp) {
if (!isRescaleOpValid((RescaleOp)biop, img)) {
return false;
}
} else if (biop instanceof LookupOp) {
if (!isLookupOpValid((LookupOp)biop, img)) {
return false;
}
} else {
// No acceleration for other BufferedImageOps (yet)
return false;
}
SurfaceData dstData = sg.surfaceData;
if (!(dstData instanceof MTLSurfaceData) ||
(sg.interpolationType == AffineTransformOp.TYPE_BICUBIC) ||
(sg.compositeState > SunGraphics2D.COMP_ALPHA))
{
return false;
}
SurfaceData srcData =
dstData.getSourceSurfaceData(img, SunGraphics2D.TRANSFORM_ISIDENT,
CompositeType.SrcOver, null);
if (!(srcData instanceof MTLSurfaceData)) {
// REMIND: this hack tries to ensure that we have a cached texture
srcData =
dstData.getSourceSurfaceData(img, SunGraphics2D.TRANSFORM_ISIDENT,
CompositeType.SrcOver, null);
if (!(srcData instanceof MTLSurfaceData)) {
return false;
}
}
// Verify that the source surface is actually a texture and
// that the operation is supported
MTLSurfaceData mtlSrc = (MTLSurfaceData)srcData;
MTLGraphicsConfig gc = mtlSrc.getMTLGraphicsConfig();
if (mtlSrc.getType() != MTLSurfaceData.TEXTURE ||
!gc.isCapPresent(CAPS_EXT_BIOP_SHADER))
{
return false;
}
int sw = img.getWidth();
int sh = img.getHeight();
MTLBlitLoops.IsoBlit(srcData, dstData,
img, biop,
sg.composite, sg.getCompClip(),
sg.transform, sg.interpolationType,
0, 0, sw, sh,
x, y, x+sw, y+sh,
true);
return true;
}
}

View file

@ -0,0 +1,154 @@
/*
* Copyright (c) 2019, 2021, 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.java2d.pipe.BufferedContext;
import sun.java2d.pipe.RenderBuffer;
import sun.java2d.pipe.RenderQueue;
import sun.java2d.pipe.hw.ContextCapabilities;
import java.lang.annotation.Native;
import static sun.java2d.pipe.BufferedOpCodes.INVALIDATE_CONTEXT;
import static sun.java2d.pipe.BufferedOpCodes.SET_SCRATCH_SURFACE;
/**
* Note that the RenderQueue lock must be acquired before calling any of
* the methods in this class.
*/
final class MTLContext extends BufferedContext {
public MTLContext(RenderQueue rq) {
super(rq);
}
/**
* Convenience method that delegates to setScratchSurface() below.
*/
static void setScratchSurface(MTLGraphicsConfig gc) {
setScratchSurface(gc.getNativeConfigInfo());
}
/**
* Makes the given GraphicsConfig's context current to its associated
* "scratch surface". Each GraphicsConfig maintains a native context
* (MTLDevice) as well as a native MTLTexture
* known as the "scratch surface". By making the context current to the
* scratch surface, we are assured that we have a current context for
* the relevant GraphicsConfig, and can therefore perform operations
* depending on the capabilities of that GraphicsConfig.
* This method should be used for operations with an MTL texture
* as the destination surface (e.g. a sw->texture blit loop), or in those
* situations where we may not otherwise have a current context (e.g.
* when disposing a texture-based surface).
*/
public static void setScratchSurface(long pConfigInfo) {
// assert MTLRenderQueue.getInstance().lock.isHeldByCurrentThread();
// invalidate the current context
currentContext = null;
// set the scratch context
MTLRenderQueue rq = MTLRenderQueue.getInstance();
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(12, 4);
buf.putInt(SET_SCRATCH_SURFACE);
buf.putLong(pConfigInfo);
}
/**
* Invalidates the currentContext field to ensure that we properly
* revalidate the MTLContext (make it current, etc.) next time through
* the validate() method. This is typically invoked from methods
* that affect the current context state (e.g. disposing a context or
* surface).
*/
public static void invalidateCurrentContext() {
// assert MTLRenderQueue.getInstance().lock.isHeldByCurrentThread();
// invalidate the current Java-level context so that we
// revalidate everything the next time around
if (currentContext != null) {
currentContext.invalidateContext();
currentContext = null;
}
// invalidate the context reference at the native level, and
// then flush the queue so that we have no pending operations
// dependent on the current context
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.ensureCapacity(4);
rq.getBuffer().putInt(INVALIDATE_CONTEXT);
rq.flushNow();
}
public static class MTLContextCaps extends ContextCapabilities {
/** Indicates that the context is doublebuffered. */
@Native
public static final int CAPS_DOUBLEBUFFERED = (FIRST_PRIVATE_CAP << 0);
/**
* This cap will only be set if the lcdshader system property has been
* enabled and the hardware supports the minimum number of texture units
*/
@Native
static final int CAPS_EXT_LCD_SHADER = (FIRST_PRIVATE_CAP << 1);
/**
* This cap will only be set if the biopshader system property has been
* enabled and the hardware meets our minimum requirements.
*/
@Native
public static final int CAPS_EXT_BIOP_SHADER = (FIRST_PRIVATE_CAP << 2);
/**
* This cap will only be set if the gradshader system property has been
* enabled and the hardware meets our minimum requirements.
*/
@Native
static final int CAPS_EXT_GRAD_SHADER = (FIRST_PRIVATE_CAP << 3);
public MTLContextCaps(int caps, String adapterId) {
super(caps, adapterId);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
if ((caps & CAPS_DOUBLEBUFFERED) != 0) {
sb.append("CAPS_DOUBLEBUFFERED|");
}
if ((caps & CAPS_EXT_LCD_SHADER) != 0) {
sb.append("CAPS_EXT_LCD_SHADER|");
}
if ((caps & CAPS_EXT_BIOP_SHADER) != 0) {
sb.append("CAPS_BIOP_SHADER|");
}
if ((caps & CAPS_EXT_GRAD_SHADER) != 0) {
sb.append("CAPS_EXT_GRAD_SHADER|");
}
return sb.toString();
}
}
}

View file

@ -0,0 +1,114 @@
/*
* Copyright (c) 2019, 2021, 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.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.loops.SurfaceType;
import sun.java2d.loops.TransformBlit;
import sun.java2d.pipe.DrawImage;
import java.awt.Color;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
public class MTLDrawImage extends DrawImage {
@Override
protected void renderImageXform(SunGraphics2D sg, Image img,
AffineTransform tx, int interpType,
int sx1, int sy1, int sx2, int sy2,
Color bgColor)
{
// punt to the MediaLib-based transformImage() in the superclass if:
// - bicubic interpolation is specified
// - a background color is specified and will be used
// - the source surface is neither a texture nor render-to-texture
// surface, and a non-default interpolation hint is specified
// (we can only control the filtering for texture->surface
// copies)
// REMIND: we should tweak the sw->texture->surface
// transform case to handle filtering appropriately
// (see 4841762)...
// - an appropriate TransformBlit primitive could not be found
if (interpType != AffineTransformOp.TYPE_BICUBIC) {
SurfaceData dstData = sg.surfaceData;
SurfaceData srcData =
dstData.getSourceSurfaceData(img,
SunGraphics2D.TRANSFORM_GENERIC,
sg.imageComp,
bgColor);
if (srcData != null &&
!isBgOperation(srcData, bgColor) &&
(srcData.getSurfaceType() == MTLSurfaceData.MTLTexture ||
srcData.getSurfaceType() == MTLSurfaceData.MTLSurfaceRTT ||
interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR))
{
SurfaceType srcType = srcData.getSurfaceType();
SurfaceType dstType = dstData.getSurfaceType();
TransformBlit blit = TransformBlit.getFromCache(srcType,
sg.imageComp,
dstType);
if (blit != null) {
blit.Transform(srcData, dstData,
sg.composite, sg.getCompClip(),
tx, interpType,
sx1, sy1, 0, 0, sx2-sx1, sy2-sy1);
return;
}
}
}
super.renderImageXform(sg, img, tx, interpType,
sx1, sy1, sx2, sy2, bgColor);
}
@Override
public void transformImage(SunGraphics2D sg, BufferedImage img,
BufferedImageOp op, int x, int y)
{
if (op != null) {
if (op instanceof AffineTransformOp) {
AffineTransformOp atop = (AffineTransformOp) op;
transformImage(sg, img, x, y,
atop.getTransform(),
atop.getInterpolationType());
return;
} else {
if (MTLBufImgOps.renderImageWithOp(sg, img, op, x, y)) {
return;
}
}
img = op.filter(img, null);
}
copyImage(sg, img, x, y, null);
}
}

View file

@ -0,0 +1,401 @@
/*
* Copyright (c) 2019, 2021, 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.CGraphicsConfig;
import sun.awt.CGraphicsDevice;
import sun.awt.image.OffScreenImage;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.SurfaceManager;
import sun.java2d.Disposer;
import sun.java2d.DisposerRecord;
import sun.java2d.Surface;
import sun.java2d.SurfaceData;
import sun.java2d.pipe.hw.AccelGraphicsConfig;
import sun.java2d.pipe.hw.AccelSurface;
import sun.java2d.pipe.hw.AccelTypedVolatileImage;
import sun.java2d.pipe.hw.ContextCapabilities;
import sun.lwawt.LWComponentPeer;
import sun.lwawt.macosx.CFRetainedResource;
import java.awt.AWTException;
import java.awt.BufferCapabilities;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.ImageCapabilities;
import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DirectColorModel;
import java.awt.image.VolatileImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.security.AccessController;
import java.security.PrivilegedAction;
import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_GRAD_SHADER;
import static sun.java2d.pipe.hw.AccelSurface.TEXTURE;
import static sun.java2d.pipe.hw.AccelSurface.RT_TEXTURE;
import static sun.java2d.pipe.hw.ContextCapabilities.*;
import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_BIOP_SHADER;
public final class MTLGraphicsConfig extends CGraphicsConfig
implements AccelGraphicsConfig, SurfaceManager.ProxiedGraphicsConfig
{
private static boolean mtlAvailable;
private static ImageCapabilities imageCaps = new MTLImageCaps();
private static final String mtlShadersLib = AccessController.doPrivileged(
(PrivilegedAction<String>) () ->
System.getProperty("java.home", "") + File.separator +
"lib" + File.separator + "shaders.metallib");
private BufferCapabilities bufferCaps;
private long pConfigInfo;
private ContextCapabilities mtlCaps;
private final MTLContext context;
private final Object disposerReferent = new Object();
private final int maxTextureSize;
private static native boolean isMetalFrameworkAvailable();
private static native boolean tryLoadMetalLibrary(int displayID, String shaderLib);
private static native long getMTLConfigInfo(int displayID, String mtlShadersLib);
/**
* Returns maximum texture size supported by Metal. Must be
* called under MTLRQ lock.
*/
private static native int nativeGetMaxTextureSize();
static {
mtlAvailable = isMetalFrameworkAvailable();
}
private MTLGraphicsConfig(CGraphicsDevice device,
long configInfo, int maxTextureSize,
ContextCapabilities mtlCaps) {
super(device);
this.pConfigInfo = configInfo;
this.mtlCaps = mtlCaps;
this.maxTextureSize = maxTextureSize;
context = new MTLContext(MTLRenderQueue.getInstance());
// add a record to the Disposer so that we destroy the native
// MTLGraphicsConfigInfo data when this object goes away
Disposer.addRecord(disposerReferent,
new MTLGCDisposerRecord(pConfigInfo));
}
@Override
public Object getProxyKey() {
return this;
}
public SurfaceData createManagedSurface(int w, int h, int transparency) {
return MTLSurfaceData.createData(this, w, h,
getColorModel(transparency),
null,
MTLSurfaceData.TEXTURE);
}
public static MTLGraphicsConfig getConfig(CGraphicsDevice device,
int displayID)
{
if (!mtlAvailable) {
return null;
}
if (!tryLoadMetalLibrary(displayID, mtlShadersLib)) {
return null;
}
long cfginfo = 0;
int textureSize = 0;
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
// getMTLConfigInfo() creates and destroys temporary
// surfaces/contexts, so we should first invalidate the current
// Java-level context and flush the queue...
MTLContext.invalidateCurrentContext();
cfginfo = getMTLConfigInfo(displayID, mtlShadersLib);
if (cfginfo != 0L) {
textureSize = nativeGetMaxTextureSize();
// TODO : This clamping code is same as in OpenGL.
// Whether we need such clamping or not in case of Metal
// will be pursued under 8260644
textureSize = textureSize <= 16384 ? textureSize / 2 : 8192;
MTLContext.setScratchSurface(cfginfo);
}
} finally {
rq.unlock();
}
if (cfginfo == 0) {
return null;
}
ContextCapabilities caps = new MTLContext.MTLContextCaps(
CAPS_PS30 | CAPS_PS20 |
CAPS_RT_TEXTURE_ALPHA | CAPS_RT_TEXTURE_OPAQUE |
CAPS_MULTITEXTURE | CAPS_TEXNONPOW2 | CAPS_TEXNONSQUARE |
CAPS_EXT_BIOP_SHADER | CAPS_EXT_GRAD_SHADER,
null);
return new MTLGraphicsConfig(device, cfginfo, textureSize, caps);
}
public static boolean isMetalAvailable() {
return mtlAvailable;
}
/**
* Returns true if the provided capability bit is present for this config.
* See MTLContext.java for a list of supported capabilities.
*/
public boolean isCapPresent(int cap) {
return ((mtlCaps.getCaps() & cap) != 0);
}
public long getNativeConfigInfo() {
return pConfigInfo;
}
/**
* {@inheritDoc}
*
* @see sun.java2d.pipe.hw.BufferedContextProvider#getContext
*/
@Override
public MTLContext getContext() {
return context;
}
@Override
public BufferedImage createCompatibleImage(int width, int height) {
ColorModel model = new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
WritableRaster
raster = model.createCompatibleWritableRaster(width, height);
return new BufferedImage(model, raster, model.isAlphaPremultiplied(),
null);
}
@Override
public ColorModel getColorModel(int transparency) {
switch (transparency) {
case Transparency.OPAQUE:
// REMIND: once the ColorModel spec is changed, this should be
// an opaque premultiplied DCM...
return new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
case Transparency.BITMASK:
return new DirectColorModel(25, 0xff0000, 0xff00, 0xff, 0x1000000);
case Transparency.TRANSLUCENT:
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
return new DirectColorModel(cs, 32,
0xff0000, 0xff00, 0xff, 0xff000000,
true, DataBuffer.TYPE_INT);
default:
return null;
}
}
public boolean isDoubleBuffered() {
return true;
}
private static class MTLGCDisposerRecord implements DisposerRecord {
private long pCfgInfo;
public MTLGCDisposerRecord(long pCfgInfo) {
this.pCfgInfo = pCfgInfo;
}
public void dispose() {
if (pCfgInfo != 0) {
MTLRenderQueue.disposeGraphicsConfig(pCfgInfo);
pCfgInfo = 0;
}
}
}
@Override
public String toString() {
return ("MTLGraphicsConfig[" + getDevice().getIDstring() + "]");
}
@Override
public SurfaceData createSurfaceData(CFRetainedResource layer) {
return MTLSurfaceData.createData((MTLLayer) layer);
}
@Override
public Image createAcceleratedImage(Component target,
int width, int height)
{
ColorModel model = getColorModel(Transparency.OPAQUE);
WritableRaster wr = model.createCompatibleWritableRaster(width, height);
return new OffScreenImage(target, model, wr,
model.isAlphaPremultiplied());
}
@Override
public void assertOperationSupported(final int numBuffers,
final BufferCapabilities caps)
throws AWTException {
// Assume this method is never called with numBuffers != 2, as 0 is
// unsupported, and 1 corresponds to a SingleBufferStrategy which
// doesn't depend on the peer. Screen is considered as a separate
// "buffer".
if (numBuffers != 2) {
throw new AWTException("Only double buffering is supported");
}
final BufferCapabilities configCaps = getBufferCapabilities();
if (!configCaps.isPageFlipping()) {
throw new AWTException("Page flipping is not supported");
}
if (caps.getFlipContents() == BufferCapabilities.FlipContents.PRIOR) {
throw new AWTException("FlipContents.PRIOR is not supported");
}
}
@Override
public Image createBackBuffer(final LWComponentPeer<?, ?> peer) {
final Rectangle r = peer.getBounds();
// It is possible for the component to have size 0x0, adjust it to
// be at least 1x1 to avoid IAE
final int w = Math.max(1, r.width);
final int h = Math.max(1, r.height);
final int transparency = peer.isTranslucent() ? Transparency.TRANSLUCENT
: Transparency.OPAQUE;
return new SunVolatileImage(this, w, h, transparency, null);
}
@Override
public void destroyBackBuffer(final Image backBuffer) {
if (backBuffer != null) {
backBuffer.flush();
}
}
@Override
public void flip(final LWComponentPeer<?, ?> peer, final Image backBuffer,
final int x1, final int y1, final int x2, final int y2,
final BufferCapabilities.FlipContents flipAction) {
final Graphics g = peer.getGraphics();
try {
g.drawImage(backBuffer, x1, y1, x2, y2, x1, y1, x2, y2, null);
} finally {
g.dispose();
}
if (flipAction == BufferCapabilities.FlipContents.BACKGROUND) {
final Graphics2D bg = (Graphics2D) backBuffer.getGraphics();
try {
bg.setBackground(peer.getBackground());
bg.clearRect(0, 0, backBuffer.getWidth(null),
backBuffer.getHeight(null));
} finally {
bg.dispose();
}
}
}
private static class MTLBufferCaps extends BufferCapabilities {
public MTLBufferCaps(boolean dblBuf) {
super(imageCaps, imageCaps,
dblBuf ? FlipContents.UNDEFINED : null);
}
}
@Override
public BufferCapabilities getBufferCapabilities() {
if (bufferCaps == null) {
bufferCaps = new MTLBufferCaps(isDoubleBuffered());
}
return bufferCaps;
}
private static class MTLImageCaps extends ImageCapabilities {
private MTLImageCaps() {
super(true);
}
public boolean isTrueVolatile() {
return true;
}
}
@Override
public ImageCapabilities getImageCapabilities() {
return imageCaps;
}
@Override
public VolatileImage createCompatibleVolatileImage(int width, int height,
int transparency,
int type) {
if ((type != RT_TEXTURE && type != TEXTURE) ||
transparency == Transparency.BITMASK) {
return null;
}
SunVolatileImage vi = new AccelTypedVolatileImage(this, width, height,
transparency, type);
Surface sd = vi.getDestSurface();
if (!(sd instanceof AccelSurface) ||
((AccelSurface)sd).getType() != type)
{
vi.flush();
vi = null;
}
return vi;
}
/**
* {@inheritDoc}
*
* @see sun.java2d.pipe.hw.AccelGraphicsConfig#getContextCapabilities
*/
@Override
public ContextCapabilities getContextCapabilities() {
return mtlCaps;
}
@Override
public int getMaxTextureWidth() {
return Math.max(maxTextureSize / getDevice().getScaleFactor(),
getBounds().width);
}
@Override
public int getMaxTextureHeight() {
return Math.max(maxTextureSize / getDevice().getScaleFactor(),
getBounds().height);
}
}

View file

@ -0,0 +1,154 @@
/*
* Copyright (c) 2019, 2021, 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.java2d.NullSurfaceData;
import sun.java2d.SurfaceData;
import sun.lwawt.LWWindowPeer;
import sun.lwawt.macosx.CFRetainedResource;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Transparency;
public class MTLLayer extends CFRetainedResource {
private native long nativeCreateLayer();
private static native void nativeSetScale(long layerPtr, double scale);
// Pass the insets to native code to make adjustments in blitTexture
private static native void nativeSetInsets(long layerPtr, int top, int left);
private static native void validate(long layerPtr, MTLSurfaceData mtlsd);
private static native void blitTexture(long layerPtr);
private LWWindowPeer peer;
private int scale = 1;
private SurfaceData surfaceData; // represents intermediate buffer (texture)
public MTLLayer(LWWindowPeer peer) {
super(0, true);
setPtr(nativeCreateLayer());
this.peer = peer;
}
public long getPointer() {
return ptr;
}
public Rectangle getBounds() {
return peer.getBounds();
}
public GraphicsConfiguration getGraphicsConfiguration() {
return peer.getGraphicsConfiguration();
}
public boolean isOpaque() {
return !peer.isTranslucent();
}
public int getTransparency() {
return isOpaque() ? Transparency.OPAQUE : Transparency.TRANSLUCENT;
}
public Object getDestination() {
return peer.getTarget();
}
public SurfaceData replaceSurfaceData() {
if (getBounds().isEmpty()) {
surfaceData = NullSurfaceData.theInstance;
return surfaceData;
}
// the layer redirects all painting to the buffer's graphics
// and blits the buffer to the layer surface (in display callback)
MTLGraphicsConfig gc = (MTLGraphicsConfig)getGraphicsConfiguration();
surfaceData = gc.createSurfaceData(this);
setScale(gc.getDevice().getScaleFactor());
Insets insets = peer.getInsets();
execute(ptr -> nativeSetInsets(ptr, insets.top, insets.left));
// the layer holds a reference to the buffer, which in
// turn has a reference back to this layer
if (surfaceData instanceof MTLSurfaceData) {
validate((MTLSurfaceData)surfaceData);
}
return surfaceData;
}
public SurfaceData getSurfaceData() {
return surfaceData;
}
public void validate(final MTLSurfaceData mtlsd) {
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
execute(ptr -> validate(ptr, mtlsd));
} finally {
rq.unlock();
}
}
@Override
public void dispose() {
// break the connection between the layer and the buffer
validate(null);
SurfaceData oldData = surfaceData;
surfaceData = NullSurfaceData.theInstance;;
if (oldData != null) {
oldData.flush();
}
super.dispose();
}
private void setScale(final int _scale) {
if (scale != _scale) {
scale = _scale;
execute(ptr -> nativeSetScale(ptr, scale));
}
}
// ----------------------------------------------------------------------
// NATIVE CALLBACKS
// ----------------------------------------------------------------------
private void drawInMTLContext() {
// tell the flusher thread not to update the intermediate buffer
// until we are done blitting from it
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
execute(ptr -> blitTexture(ptr));
} finally {
rq.unlock();
}
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2019, 2021, 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.java2d.SurfaceData;
import sun.java2d.loops.CompositeType;
import sun.java2d.loops.GraphicsPrimitive;
import sun.java2d.loops.GraphicsPrimitiveMgr;
import sun.java2d.loops.SurfaceType;
import sun.java2d.pipe.BufferedMaskBlit;
import sun.java2d.pipe.Region;
import java.awt.Composite;
import static sun.java2d.loops.CompositeType.SrcNoEa;
import static sun.java2d.loops.CompositeType.SrcOver;
import static sun.java2d.loops.SurfaceType.*;
class MTLMaskBlit extends BufferedMaskBlit {
static void register() {
GraphicsPrimitive[] primitives = {
new MTLMaskBlit(IntArgb, SrcOver),
new MTLMaskBlit(IntArgbPre, SrcOver),
new MTLMaskBlit(IntRgb, SrcOver),
new MTLMaskBlit(IntRgb, SrcNoEa),
new MTLMaskBlit(IntBgr, SrcOver),
new MTLMaskBlit(IntBgr, SrcNoEa),
};
GraphicsPrimitiveMgr.register(primitives);
}
private MTLMaskBlit(SurfaceType srcType,
CompositeType compType)
{
super(MTLRenderQueue.getInstance(),
srcType, compType, MTLSurfaceData.MTLSurface);
}
@Override
protected void validateContext(SurfaceData dstData,
Composite comp, Region clip)
{
MTLSurfaceData mtlDst = (MTLSurfaceData)dstData;
MTLContext.validateContext(mtlDst, mtlDst,
clip, comp, null, null, null,
MTLContext.NO_CONTEXT_FLAGS);
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2019, 2021, 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.java2d.InvalidPipeException;
import sun.java2d.SunGraphics2D;
import sun.java2d.loops.CompositeType;
import sun.java2d.loops.GraphicsPrimitive;
import sun.java2d.loops.GraphicsPrimitiveMgr;
import sun.java2d.loops.SurfaceType;
import sun.java2d.pipe.BufferedMaskFill;
import java.awt.Composite;
import static sun.java2d.loops.CompositeType.SrcNoEa;
import static sun.java2d.loops.CompositeType.SrcOver;
import static sun.java2d.loops.SurfaceType.*;
class MTLMaskFill extends BufferedMaskFill {
static void register() {
GraphicsPrimitive[] primitives = {
new MTLMaskFill(AnyColor, SrcOver),
new MTLMaskFill(OpaqueColor, SrcNoEa),
new MTLMaskFill(GradientPaint, SrcOver),
new MTLMaskFill(OpaqueGradientPaint, SrcNoEa),
new MTLMaskFill(LinearGradientPaint, SrcOver),
new MTLMaskFill(OpaqueLinearGradientPaint, SrcNoEa),
new MTLMaskFill(RadialGradientPaint, SrcOver),
new MTLMaskFill(OpaqueRadialGradientPaint, SrcNoEa),
new MTLMaskFill(TexturePaint, SrcOver),
new MTLMaskFill(OpaqueTexturePaint, SrcNoEa),
};
GraphicsPrimitiveMgr.register(primitives);
}
protected MTLMaskFill(SurfaceType srcType, CompositeType compType) {
super(MTLRenderQueue.getInstance(),
srcType, compType, MTLSurfaceData.MTLSurface);
}
@Override
protected native void maskFill(int x, int y, int w, int h,
int maskoff, int maskscan, int masklen,
byte[] mask);
@Override
protected void validateContext(SunGraphics2D sg2d,
Composite comp, int ctxflags)
{
MTLSurfaceData dstData;
try {
dstData = (MTLSurfaceData) sg2d.surfaceData;
} catch (ClassCastException e) {
throw new InvalidPipeException("wrong surface data type: " +
sg2d.surfaceData);
}
MTLContext.validateContext(dstData, dstData,
sg2d.getCompClip(), comp,
null, sg2d.paint, sg2d, ctxflags);
}
}

View file

@ -0,0 +1,199 @@
/*
* Copyright (c) 2019, 2021, 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.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.loops.CompositeType;
import java.awt.LinearGradientPaint;
import java.awt.MultipleGradientPaint;
import java.awt.TexturePaint;
import java.awt.MultipleGradientPaint.ColorSpaceType;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_GRAD_SHADER;
import static sun.java2d.pipe.BufferedPaints.MULTI_MAX_FRACTIONS;
abstract class MTLPaints {
/**
* Holds all registered implementations, using the corresponding
* SunGraphics2D.PAINT_* constant as the hash key.
*/
private static Map<Integer, MTLPaints> impls =
new HashMap<Integer, MTLPaints>(4, 1.0f);
static {
impls.put(SunGraphics2D.PAINT_GRADIENT, new Gradient());
impls.put(SunGraphics2D.PAINT_LIN_GRADIENT, new LinearGradient());
impls.put(SunGraphics2D.PAINT_RAD_GRADIENT, new RadialGradient());
impls.put(SunGraphics2D.PAINT_TEXTURE, new Texture());
}
/**
* Attempts to locate an implementation corresponding to the paint state
* of the provided SunGraphics2D object. If no implementation can be
* found, or if the paint cannot be accelerated under the conditions
* of the SunGraphics2D, this method returns false; otherwise, returns
* true.
*/
static boolean isValid(SunGraphics2D sg2d) {
MTLPaints impl = impls.get(sg2d.paintState);
return (impl != null && impl.isPaintValid(sg2d));
}
/**
* Returns true if this implementation is able to accelerate the
* Paint object associated with, and under the conditions of, the
* provided SunGraphics2D instance; otherwise returns false.
*/
abstract boolean isPaintValid(SunGraphics2D sg2d);
/************************* GradientPaint support ****************************/
private static class Gradient extends MTLPaints {
private Gradient() {}
/**
* There are no restrictions for accelerating GradientPaint, so
* this method always returns true.
*/
@Override
boolean isPaintValid(SunGraphics2D sg2d) {
return true;
}
}
/************************** TexturePaint support ****************************/
private static class Texture extends MTLPaints {
private Texture() {}
/**
* Returns true if the given TexturePaint instance can be used by the
* accelerated MTLPaints.Texture implementation. A TexturePaint is
* considered valid if the following conditions are met:
* - the texture image dimensions are power-of-two
* - the texture image can be (or is already) cached in an Metal
* texture object
*/
@Override
boolean isPaintValid(SunGraphics2D sg2d) {
TexturePaint paint = (TexturePaint)sg2d.paint;
MTLSurfaceData dstData = (MTLSurfaceData)sg2d.surfaceData;
BufferedImage bi = paint.getImage();
SurfaceData srcData =
dstData.getSourceSurfaceData(bi,
SunGraphics2D.TRANSFORM_ISIDENT,
CompositeType.SrcOver, null);
if (!(srcData instanceof MTLSurfaceData)) {
// REMIND: this is a hack that attempts to cache the system
// memory image from the TexturePaint instance into an
// Metal texture...
srcData =
dstData.getSourceSurfaceData(bi,
SunGraphics2D.TRANSFORM_ISIDENT,
CompositeType.SrcOver, null);
if (!(srcData instanceof MTLSurfaceData)) {
return false;
}
}
// verify that the source surface is actually a texture
MTLSurfaceData mtlData = (MTLSurfaceData)srcData;
if (mtlData.getType() != MTLSurfaceData.TEXTURE) {
return false;
}
return true;
}
}
/****************** Shared MultipleGradientPaint support ********************/
private abstract static class MultiGradient extends MTLPaints {
protected MultiGradient() {}
/**
* Returns true if the given MultipleGradientPaint instance can be
* used by the accelerated MTLPaints.MultiGradient implementation.
* A MultipleGradientPaint is considered valid if the following
* conditions are met:
* - the number of gradient "stops" is <= MAX_FRACTIONS
* - the destination has support for fragment shaders
*/
@Override
boolean isPaintValid(SunGraphics2D sg2d) {
MultipleGradientPaint paint = (MultipleGradientPaint)sg2d.paint;
// REMIND: ugh, this creates garbage; would be nicer if
// we had a MultipleGradientPaint.getNumStops() method...
if (paint.getFractions().length > MULTI_MAX_FRACTIONS) {
return false;
}
MTLSurfaceData dstData = (MTLSurfaceData)sg2d.surfaceData;
MTLGraphicsConfig gc = dstData.getMTLGraphicsConfig();
if (!gc.isCapPresent(CAPS_EXT_GRAD_SHADER)) {
return false;
}
return true;
}
}
/********************** LinearGradientPaint support *************************/
private static class LinearGradient extends MultiGradient {
private LinearGradient() {}
@Override
boolean isPaintValid(SunGraphics2D sg2d) {
LinearGradientPaint paint = (LinearGradientPaint)sg2d.paint;
if (paint.getFractions().length == 2 &&
paint.getCycleMethod() != CycleMethod.REPEAT &&
paint.getColorSpace() != ColorSpaceType.LINEAR_RGB)
{
// we can delegate to the optimized two-color gradient
// codepath, which does not require fragment shader support
return true;
}
return super.isPaintValid(sg2d);
}
}
/********************** RadialGradientPaint support *************************/
private static class RadialGradient extends MultiGradient {
private RadialGradient() {}
}
}

View file

@ -0,0 +1,254 @@
/*
* Copyright (c) 2007, 2021, 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.util.ThreadGroupUtils;
import sun.java2d.pipe.RenderBuffer;
import sun.java2d.pipe.RenderQueue;
import java.security.AccessController;
import java.security.PrivilegedAction;
import static sun.java2d.pipe.BufferedOpCodes.DISPOSE_CONFIG;
import static sun.java2d.pipe.BufferedOpCodes.SYNC;
/**
* MTL-specific implementation of RenderQueue. This class provides a
* single (daemon) thread that is responsible for periodically flushing
* the queue, thus ensuring that only one thread communicates with the native
* OpenGL libraries for the entire process.
*/
public class MTLRenderQueue extends RenderQueue {
private static MTLRenderQueue theInstance;
private final QueueFlusher flusher;
private MTLRenderQueue() {
/*
* The thread must be a member of a thread group
* which will not get GCed before VM exit.
*/
flusher = AccessController.doPrivileged((PrivilegedAction<QueueFlusher>) QueueFlusher::new);
}
/**
* Returns the single MTLRenderQueue instance. If it has not yet been
* initialized, this method will first construct the single instance
* before returning it.
*/
public static synchronized MTLRenderQueue getInstance() {
if (theInstance == null) {
theInstance = new MTLRenderQueue();
}
return theInstance;
}
/**
* Flushes the single MTLRenderQueue instance synchronously. If an
* MTLRenderQueue has not yet been instantiated, this method is a no-op.
* This method is useful in the case of Toolkit.sync(), in which we want
* to flush the MTL pipeline, but only if the MTL pipeline is currently
* enabled. Since this class has few external dependencies, callers need
* not be concerned that calling this method will trigger initialization
* of the MTL pipeline and related classes.
*/
public static void sync() {
if (theInstance != null) {
theInstance.lock();
try {
theInstance.ensureCapacity(4);
theInstance.getBuffer().putInt(SYNC);
theInstance.flushNow();
} finally {
theInstance.unlock();
}
}
}
/**
* Disposes the native memory associated with the given native
* graphics config info pointer on the single queue flushing thread.
*/
public static void disposeGraphicsConfig(long pConfigInfo) {
MTLRenderQueue rq = getInstance();
rq.lock();
try {
// make sure we make the context associated with the given
// GraphicsConfig current before disposing the native resources
MTLContext.setScratchSurface(pConfigInfo);
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(12, 4);
buf.putInt(DISPOSE_CONFIG);
buf.putLong(pConfigInfo);
// this call is expected to complete synchronously, so flush now
rq.flushNow();
} finally {
rq.unlock();
}
}
/**
* Returns true if the current thread is the MTL QueueFlusher thread.
*/
public static boolean isQueueFlusherThread() {
return (Thread.currentThread() == getInstance().flusher.thread);
}
@Override
public void flushNow() {
// assert lock.isHeldByCurrentThread();
try {
flusher.flushNow();
} catch (Exception e) {
System.err.println("exception in flushNow:");
e.printStackTrace();
}
}
public void flushAndInvokeNow(Runnable r) {
// assert lock.isHeldByCurrentThread();
try {
flusher.flushAndInvokeNow(r);
} catch (Exception e) {
System.err.println("exception in flushAndInvokeNow:");
e.printStackTrace();
}
}
private native void flushBuffer(long buf, int limit);
private void flushBuffer() {
// assert lock.isHeldByCurrentThread();
int limit = buf.position();
if (limit > 0) {
// process the queue
flushBuffer(buf.getAddress(), limit);
}
// reset the buffer position
buf.clear();
// clear the set of references, since we no longer need them
refSet.clear();
}
private class QueueFlusher implements Runnable {
private boolean needsFlush;
private Runnable task;
private Error error;
private final Thread thread;
public QueueFlusher() {
String name = "Java2D Queue Flusher";
thread = new Thread(ThreadGroupUtils.getRootThreadGroup(),
this, name, 0, false);
thread.setDaemon(true);
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
public synchronized void flushNow() {
// wake up the flusher
needsFlush = true;
notify();
// wait for flush to complete
while (needsFlush) {
try {
wait();
} catch (InterruptedException e) {
}
}
// re-throw any error that may have occurred during the flush
if (error != null) {
throw error;
}
}
public synchronized void flushAndInvokeNow(Runnable task) {
this.task = task;
flushNow();
}
public synchronized void run() {
boolean timedOut = false;
while (true) {
while (!needsFlush) {
try {
timedOut = false;
/*
* Wait until we're woken up with a flushNow() call,
* or the timeout period elapses (so that we can
* flush the queue periodically).
*/
wait(100);
/*
* We will automatically flush the queue if the
* following conditions apply:
* - the wait() timed out
* - we can lock the queue (without blocking)
* - there is something in the queue to flush
* Otherwise, just continue (we'll flush eventually).
*/
if (!needsFlush && (timedOut = tryLock())) {
if (buf.position() > 0) {
needsFlush = true;
} else {
unlock();
}
}
} catch (InterruptedException e) {
}
}
try {
// reset the throwable state
error = null;
// flush the buffer now
flushBuffer();
// if there's a task, invoke that now as well
if (task != null) {
task.run();
}
} catch (Error e) {
error = e;
} catch (Exception x) {
System.err.println("exception in QueueFlusher:");
x.printStackTrace();
} finally {
if (timedOut) {
unlock();
}
task = null;
// allow the waiting thread to continue
needsFlush = false;
notify();
}
}
}
}
}

View file

@ -0,0 +1,224 @@
/*
* Copyright (c) 2019, 2021, 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.java2d.InvalidPipeException;
import sun.java2d.SunGraphics2D;
import sun.java2d.loops.GraphicsPrimitive;
import sun.java2d.pipe.BufferedRenderPipe;
import sun.java2d.pipe.ParallelogramPipe;
import sun.java2d.pipe.RenderQueue;
import sun.java2d.pipe.SpanIterator;
import java.awt.Transparency;
import java.awt.geom.Path2D;
import static sun.java2d.pipe.BufferedOpCodes.COPY_AREA;
class MTLRenderer extends BufferedRenderPipe {
MTLRenderer(RenderQueue rq) {
super(rq);
}
@Override
protected void validateContext(SunGraphics2D sg2d) {
int ctxflags =
sg2d.paint.getTransparency() == Transparency.OPAQUE ?
MTLContext.SRC_IS_OPAQUE : MTLContext.NO_CONTEXT_FLAGS;
MTLSurfaceData dstData;
try {
dstData = (MTLSurfaceData)sg2d.surfaceData;
} catch (ClassCastException e) {
throw new InvalidPipeException("wrong surface data type: " + sg2d.surfaceData);
}
MTLContext.validateContext(dstData, dstData,
sg2d.getCompClip(), sg2d.composite,
null, sg2d.paint, sg2d, ctxflags);
}
@Override
protected void validateContextAA(SunGraphics2D sg2d) {
int ctxflags = MTLContext.NO_CONTEXT_FLAGS;
MTLSurfaceData dstData;
try {
dstData = (MTLSurfaceData)sg2d.surfaceData;
} catch (ClassCastException e) {
throw new InvalidPipeException("wrong surface data type: " + sg2d.surfaceData);
}
MTLContext.validateContext(dstData, dstData,
sg2d.getCompClip(), sg2d.composite,
null, sg2d.paint, sg2d, ctxflags);
}
void copyArea(SunGraphics2D sg2d,
int x, int y, int w, int h, int dx, int dy)
{
rq.lock();
try {
int ctxflags =
sg2d.surfaceData.getTransparency() == Transparency.OPAQUE ?
MTLContext.SRC_IS_OPAQUE : MTLContext.NO_CONTEXT_FLAGS;
MTLSurfaceData dstData;
try {
dstData = (MTLSurfaceData)sg2d.surfaceData;
} catch (ClassCastException e) {
throw new InvalidPipeException("wrong surface data type: " + sg2d.surfaceData);
}
MTLContext.validateContext(dstData, dstData,
sg2d.getCompClip(), sg2d.composite,
null, null, null, ctxflags);
rq.ensureCapacity(28);
buf.putInt(COPY_AREA);
buf.putInt(x).putInt(y).putInt(w).putInt(h);
buf.putInt(dx).putInt(dy);
} finally {
rq.unlock();
}
}
@Override
protected native void drawPoly(int[] xPoints, int[] yPoints,
int nPoints, boolean isClosed,
int transX, int transY);
MTLRenderer traceWrap() {
return new Tracer(this);
}
private class Tracer extends MTLRenderer {
private MTLRenderer mtlr;
Tracer(MTLRenderer mtlr) {
super(mtlr.rq);
this.mtlr = mtlr;
}
public ParallelogramPipe getAAParallelogramPipe() {
final ParallelogramPipe realpipe = mtlr.getAAParallelogramPipe();
return new ParallelogramPipe() {
public void fillParallelogram(SunGraphics2D sg2d,
double ux1, double uy1,
double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2)
{
GraphicsPrimitive.tracePrimitive("MTLFillAAParallelogram");
realpipe.fillParallelogram(sg2d,
ux1, uy1, ux2, uy2,
x, y, dx1, dy1, dx2, dy2);
}
public void drawParallelogram(SunGraphics2D sg2d,
double ux1, double uy1,
double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2,
double lw1, double lw2)
{
GraphicsPrimitive.tracePrimitive("MTLDrawAAParallelogram");
realpipe.drawParallelogram(sg2d,
ux1, uy1, ux2, uy2,
x, y, dx1, dy1, dx2, dy2,
lw1, lw2);
}
};
}
protected void validateContext(SunGraphics2D sg2d) {
mtlr.validateContext(sg2d);
}
public void drawLine(SunGraphics2D sg2d,
int x1, int y1, int x2, int y2)
{
GraphicsPrimitive.tracePrimitive("MTLDrawLine");
mtlr.drawLine(sg2d, x1, y1, x2, y2);
}
public void drawRect(SunGraphics2D sg2d, int x, int y, int w, int h) {
GraphicsPrimitive.tracePrimitive("MTLDrawRect");
mtlr.drawRect(sg2d, x, y, w, h);
}
protected void drawPoly(SunGraphics2D sg2d,
int[] xPoints, int[] yPoints,
int nPoints, boolean isClosed)
{
GraphicsPrimitive.tracePrimitive("MTLDrawPoly");
mtlr.drawPoly(sg2d, xPoints, yPoints, nPoints, isClosed);
}
public void fillRect(SunGraphics2D sg2d, int x, int y, int w, int h) {
GraphicsPrimitive.tracePrimitive("MTLFillRect");
mtlr.fillRect(sg2d, x, y, w, h);
}
protected void drawPath(SunGraphics2D sg2d,
Path2D.Float p2df, int transx, int transy)
{
GraphicsPrimitive.tracePrimitive("MTLDrawPath");
mtlr.drawPath(sg2d, p2df, transx, transy);
}
protected void fillPath(SunGraphics2D sg2d,
Path2D.Float p2df, int transx, int transy)
{
GraphicsPrimitive.tracePrimitive("MTLFillPath");
mtlr.fillPath(sg2d, p2df, transx, transy);
}
protected void fillSpans(SunGraphics2D sg2d, SpanIterator si,
int transx, int transy)
{
GraphicsPrimitive.tracePrimitive("MTLFillSpans");
mtlr.fillSpans(sg2d, si, transx, transy);
}
public void fillParallelogram(SunGraphics2D sg2d,
double ux1, double uy1,
double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2)
{
GraphicsPrimitive.tracePrimitive("MTLFillParallelogram");
mtlr.fillParallelogram(sg2d,
ux1, uy1, ux2, uy2,
x, y, dx1, dy1, dx2, dy2);
}
public void drawParallelogram(SunGraphics2D sg2d,
double ux1, double uy1,
double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2,
double lw1, double lw2)
{
GraphicsPrimitive.tracePrimitive("MTLDrawParallelogram");
mtlr.drawParallelogram(sg2d,
ux1, uy1, ux2, uy2,
x, y, dx1, dy1, dx2, dy2, lw1, lw2);
}
public void copyArea(SunGraphics2D sg2d,
int x, int y, int w, int h, int dx, int dy)
{
GraphicsPrimitive.tracePrimitive("MTLCopyArea");
mtlr.copyArea(sg2d, x, y, w, h, dx, dy);
}
}
}

View file

@ -0,0 +1,642 @@
/*
* Copyright (c) 2019, 2021, 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.AlphaComposite;
import java.awt.Composite;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import static sun.java2d.pipe.BufferedOpCodes.DISPOSE_SURFACE;
import static sun.java2d.pipe.BufferedOpCodes.FLUSH_SURFACE;
import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_MULTITEXTURE;
import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_PS30;
public abstract class MTLSurfaceData extends SurfaceData
implements AccelSurface {
/**
* 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);
protected static MTLRenderer mtlRenderPipe;
protected static PixelToParallelogramConverter mtlTxRenderPipe;
protected static ParallelogramPipe mtlAAPgramPipe;
protected static MTLTextRenderer mtlTextPipe;
protected static MTLDrawImage mtlImagePipe;
static {
if (!GraphicsEnvironment.isHeadless()) {
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 final int scale;
protected final int width;
protected final int height;
protected int type;
private MTLGraphicsConfig graphicsConfig;
// these fields are set from the native code when the surface is
// initialized
private int nativeWidth;
private int nativeHeight;
/**
* Returns the appropriate SurfaceType corresponding to the given Metal
* surface type constant (e.g. TEXTURE -> MTLTexture).
*/
private static SurfaceType getCustomSurfaceType(int mtlType) {
switch (mtlType) {
case TEXTURE:
return MTLTexture;
case RT_TEXTURE:
return MTLSurfaceRTT;
default:
return MTLSurface;
}
}
private native void initOps(MTLGraphicsConfig gc, long pConfigInfo, long pPeerData, long layerPtr,
int xoff, int yoff, boolean isOpaque);
private MTLSurfaceData(MTLLayer layer, MTLGraphicsConfig gc,
ColorModel cm, int type, int width, int height)
{
super(getCustomSurfaceType(type), cm);
this.graphicsConfig = gc;
this.type = type;
setBlitProxyKey(gc.getProxyKey());
// TEXTURE shouldn't be scaled, it is used for managed BufferedImages.
scale = type == TEXTURE ? 1 : gc.getDevice().getScaleFactor();
this.width = width * scale;
this.height = height * scale;
long pConfigInfo = gc.getNativeConfigInfo();
long layerPtr = 0L;
boolean isOpaque = true;
if (layer != null) {
layerPtr = layer.getPointer();
isOpaque = layer.isOpaque();
}
initOps(gc, pConfigInfo, 0, layerPtr, 0, 0, isOpaque);
}
@Override
public GraphicsConfiguration getDeviceConfiguration() {
return graphicsConfig;
}
/**
* Creates a SurfaceData object representing the intermediate buffer
* between the Java2D flusher thread and the AppKit thread.
*/
public static MTLLayerSurfaceData createData(MTLLayer layer) {
MTLGraphicsConfig gc = (MTLGraphicsConfig)layer.getGraphicsConfiguration();
Rectangle r = layer.getBounds();
return new MTLLayerSurfaceData(layer, gc, r.width, r.height);
}
/**
* Creates a SurfaceData object representing an off-screen buffer
*/
public static MTLOffScreenSurfaceData createData(MTLGraphicsConfig gc,
int width, int height,
ColorModel cm, Image image,
int type) {
return new MTLOffScreenSurfaceData(gc, width, height, image, cm,
type);
}
@Override
public double getDefaultScaleX() {
return scale;
}
@Override
public double getDefaultScaleY() {
return scale;
}
@Override
public Rectangle getBounds() {
return new Rectangle(width, height);
}
protected native void clearWindow();
protected native boolean initTexture(long pData, boolean isOpaque, int width, int height);
protected native boolean initRTexture(long pData, boolean isOpaque, int width, int height);
protected native boolean initFlipBackbuffer(long pData);
@Override
public SurfaceDataProxy makeProxyFor(SurfaceData srcData) {
return MTLSurfaceDataProxy.createProxy(srcData, graphicsConfig);
}
/**
* 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, width, height);
break;
case RT_TEXTURE:
success = initRTexture(getNativeOps(), isOpaque, 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 Metal 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 RT_TEXTURE:
// need to make sure the context is current before
// creating the texture
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 MTLGraphicsConfig associated with this surface.
*/
final MTLGraphicsConfig getMTLGraphicsConfig() {
return graphicsConfig;
}
/**
* Returns one of the surface type constants defined above.
*/
public final int getType() {
return type;
}
/**
* 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 MTL 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
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 MTL 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);
}
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();
}
}
public boolean isOnScreen() {
return false;
}
private native long getMTLTexturePointer(long pData);
/**
* Returns native resource of specified {@code resType} associated with
* this surface.
*
* Specifically, for {@code MTLSurfaceData} this method returns the
* the following:
* <pre>
* TEXTURE - texture id
* </pre>
*
* 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 getMTLTexturePointer(getNativeOps());
}
return 0L;
}
public Raster getRaster(int x, int y, int w, int h) {
throw new InternalError("not implemented yet");
}
@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 Rectangle getNativeBounds() {
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
return new Rectangle(nativeWidth, nativeHeight);
} finally {
rq.unlock();
}
}
/**
* A surface which implements an intermediate buffer between
* the Java2D flusher thread and the AppKit thread.
*
* This surface serves as a buffer attached to a MTLLayer and
* the layer redirects all painting to the buffer's graphics.
*/
public static class MTLLayerSurfaceData extends MTLSurfaceData {
private final MTLLayer layer;
private MTLLayerSurfaceData(MTLLayer layer, MTLGraphicsConfig gc,
int width, int height) {
super(layer, gc, gc.getColorModel(), RT_TEXTURE, width, height);
this.layer = layer;
initSurface(this.width, this.height);
}
@Override
public SurfaceData getReplacement() {
return layer.getSurfaceData();
}
@Override
public boolean isOnScreen() {
return true;
}
@Override
public Object getDestination() {
return layer.getDestination();
}
@Override
public int getTransparency() {
return layer.getTransparency();
}
@Override
public void invalidate() {
super.invalidate();
clearWindow();
}
}
/**
* SurfaceData object representing an off-screen buffer
*/
public static class MTLOffScreenSurfaceData extends MTLSurfaceData {
private final Image offscreenImage;
public MTLOffScreenSurfaceData(MTLGraphicsConfig gc, int width,
int height, Image image,
ColorModel cm, int type) {
super(null, gc, cm, type, width, height);
offscreenImage = image;
initSurface(this.width, this.height);
}
@Override
public SurfaceData getReplacement() {
return restoreContents(offscreenImage);
}
/**
* Returns destination Image associated with this SurfaceData.
*/
@Override
public Object getDestination() {
return offscreenImage;
}
}
/**
* Disposes the native resources associated with the given MTLSurfaceData
* (referenced by the pData parameter). This method is invoked from
* the native Dispose() method from the Disposer thread when the
* Java-level MTLSurfaceData object is about to go away.
*/
public static void dispose(long pData, MTLGraphicsConfig gc) {
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(gc);
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();
}
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2019, 2021, 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.java2d.SurfaceData;
import sun.java2d.SurfaceDataProxy;
import sun.java2d.loops.CompositeType;
import java.awt.Color;
import java.awt.Transparency;
/**
* The proxy class contains the logic for when to replace a
* SurfaceData with a cached MTL Texture and the code to create
* the accelerated surfaces.
*/
public class MTLSurfaceDataProxy extends SurfaceDataProxy {
public static SurfaceDataProxy createProxy(SurfaceData srcData,
MTLGraphicsConfig dstConfig)
{
if (srcData instanceof MTLSurfaceData) {
// srcData must be a VolatileImage which either matches
// our pixel format or not - either way we do not cache it...
return UNCACHED;
}
return new MTLSurfaceDataProxy(dstConfig, srcData.getTransparency());
}
MTLGraphicsConfig mtlgc;
int transparency;
public MTLSurfaceDataProxy(MTLGraphicsConfig mtlgc, int transparency) {
this.mtlgc = mtlgc;
this.transparency = transparency;
}
@Override
public SurfaceData validateSurfaceData(SurfaceData srcData,
SurfaceData cachedData,
int w, int h)
{
if (cachedData == null) {
try {
cachedData = mtlgc.createManagedSurface(w, h, transparency);
} catch (OutOfMemoryError er) {
return null;
}
}
return cachedData;
}
@Override
public boolean isSupportedOperation(SurfaceData srcData,
int txtype,
CompositeType comp,
Color bgColor)
{
return comp.isDerivedFrom(CompositeType.AnyAlpha) &&
(bgColor == null || transparency == Transparency.OPAQUE);
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2011, 2021, 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.font.GlyphList;
import sun.java2d.SunGraphics2D;
import sun.java2d.loops.GraphicsPrimitive;
import sun.java2d.pipe.BufferedTextPipe;
import sun.java2d.pipe.RenderQueue;
import java.awt.Composite;
class MTLTextRenderer extends BufferedTextPipe {
MTLTextRenderer(RenderQueue rq) {
super(rq);
}
@Override
protected native void drawGlyphList(int numGlyphs, boolean usePositions,
boolean subPixPos, boolean rgbOrder,
int lcdContrast,
float glOrigX, float glOrigY,
long[] images, float[] positions);
@Override
protected void validateContext(SunGraphics2D sg2d, Composite comp) {
// assert rq.lock.isHeldByCurrentThread();
MTLSurfaceData mtlDst = (MTLSurfaceData)sg2d.surfaceData;
MTLContext.validateContext(mtlDst, mtlDst,
sg2d.getCompClip(), comp,
null, sg2d.paint, sg2d,
MTLContext.NO_CONTEXT_FLAGS);
}
MTLTextRenderer traceWrap() {
return new Tracer(this);
}
private static class Tracer extends MTLTextRenderer {
Tracer(MTLTextRenderer mtltr) {
super(mtltr.rq);
}
protected void drawGlyphList(SunGraphics2D sg2d, GlyphList gl) {
GraphicsPrimitive.tracePrimitive("MTLDrawGlyphs");
super.drawGlyphList(sg2d, gl);
}
}
}

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2021, 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.image.SunVolatileImage;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.SurfaceData;
import java.awt.GraphicsConfiguration;
import java.awt.Transparency;
import java.awt.image.ColorModel;
import sun.java2d.pipe.hw.AccelSurface;
public class MTLVolatileSurfaceManager extends VolatileSurfaceManager {
private final boolean accelerationEnabled;
public MTLVolatileSurfaceManager(SunVolatileImage vImg, Object context) {
super(vImg, context);
/*
* We will attempt to accelerate this image only
* if the image is not bitmask
*/
int transparency = vImg.getTransparency();
accelerationEnabled = transparency != Transparency.BITMASK;
}
protected boolean isAccelerationEnabled() {
return accelerationEnabled;
}
/**
* Create a SurfaceData object (or init the backbuffer
* of an existing window if this is a double buffered GraphicsConfig)
*/
protected SurfaceData initAcceleratedSurface() {
try {
MTLGraphicsConfig gc =
(MTLGraphicsConfig)vImg.getGraphicsConfig();
ColorModel cm = gc.getColorModel(vImg.getTransparency());
int type = vImg.getForcedAccelSurfaceType();
// if acceleration type is forced (type != UNDEFINED) then
// use the forced type, otherwise choose RT_TEXTURE
if (type == AccelSurface.UNDEFINED) {
type = AccelSurface.RT_TEXTURE;
}
return MTLSurfaceData.createData(gc,
vImg.getWidth(),
vImg.getHeight(),
cm, vImg, type);
} catch (NullPointerException | OutOfMemoryError ignored) {
return null;
}
}
@Override
protected boolean isConfigValid(GraphicsConfiguration gc) {
return ((gc == null) || (gc == vImg.getGraphicsConfig()));
}
@Override
public void initContents() {
if (vImg.getForcedAccelSurfaceType() != AccelSurface.TEXTURE) {
super.initContents();
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2021, 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
@ -55,6 +55,7 @@ import sun.java2d.pipe.hw.AccelSurface;
import sun.java2d.pipe.hw.AccelTypedVolatileImage;
import sun.java2d.pipe.hw.ContextCapabilities;
import sun.lwawt.LWComponentPeer;
import sun.lwawt.macosx.CFRetainedResource;
import static sun.java2d.opengl.OGLContext.OGLContextCaps.CAPS_DOUBLEBUFFERED;
import static sun.java2d.opengl.OGLContext.OGLContextCaps.CAPS_EXT_FBOBJECT;
@ -248,8 +249,8 @@ public final class CGLGraphicsConfig extends CGraphicsConfig
}
@Override
public SurfaceData createSurfaceData(CGLLayer layer) {
return CGLSurfaceData.createData(layer);
public SurfaceData createSurfaceData(CFRetainedResource layer) {
return CGLSurfaceData.createData((CGLLayer) layer);
}
@Override

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2021, 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
@ -68,14 +68,17 @@ import javax.swing.SwingUtilities;
import com.sun.java.swing.SwingUtilities3;
import sun.awt.AWTAccessor;
import sun.awt.CGraphicsDevice;
import sun.awt.PaintEventDispatcher;
import sun.awt.RepaintArea;
import sun.awt.SunToolkit;
import sun.awt.event.IgnorePaintEvent;
import sun.awt.image.SunVolatileImage;
import sun.java2d.SunGraphics2D;
import sun.java2d.metal.MTLRenderQueue;
import sun.java2d.opengl.OGLRenderQueue;
import sun.java2d.pipe.Region;
import sun.java2d.pipe.RenderQueue;
import sun.util.logging.PlatformLogger;
public abstract class LWComponentPeer<T extends Component, D extends JComponent>
@ -1414,7 +1417,8 @@ public abstract class LWComponentPeer<T extends Component, D extends JComponent>
}
protected static final void flushOnscreenGraphics(){
final OGLRenderQueue rq = OGLRenderQueue.getInstance();
RenderQueue rq = CGraphicsDevice.usingMetalPipeline() ?
MTLRenderQueue.getInstance() : OGLRenderQueue.getInstance();
rq.lock();
try {
rq.flushNow();

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2021, 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
@ -28,12 +28,16 @@ package sun.lwawt.macosx;
import java.awt.*;
import java.awt.event.FocusEvent;
import sun.awt.CGraphicsDevice;
import sun.java2d.SurfaceData;
import sun.java2d.metal.MTLLayer;
import sun.java2d.opengl.CGLLayer;
import sun.lwawt.LWWindowPeer;
import sun.lwawt.PlatformWindow;
import sun.lwawt.macosx.CFRetainedResource;
import sun.util.logging.PlatformLogger;
/*
* Provides a lightweight implementation of the EmbeddedFrame.
*/
@ -42,7 +46,7 @@ public class CPlatformEmbeddedFrame implements PlatformWindow {
private static final PlatformLogger focusLogger = PlatformLogger.getLogger(
"sun.lwawt.macosx.focus.CPlatformEmbeddedFrame");
private CGLLayer windowLayer;
private CFRetainedResource windowLayer;
private LWWindowPeer peer;
private CEmbeddedFrame target;
@ -52,7 +56,11 @@ public class CPlatformEmbeddedFrame implements PlatformWindow {
@Override // PlatformWindow
public void initialize(Window target, final LWWindowPeer peer, PlatformWindow owner) {
this.peer = peer;
if (CGraphicsDevice.usingMetalPipeline()) {
this.windowLayer = new MTLLayer(peer);
} else {
this.windowLayer = new CGLLayer(peer);
}
this.target = (CEmbeddedFrame)target;
}
@ -63,12 +71,20 @@ public class CPlatformEmbeddedFrame implements PlatformWindow {
@Override
public long getLayerPtr() {
return windowLayer.getPointer();
if (CGraphicsDevice.usingMetalPipeline()) {
return ((MTLLayer)windowLayer).getPointer();
} else {
return ((CGLLayer)windowLayer).getPointer();
}
}
@Override
public void dispose() {
windowLayer.dispose();
if (CGraphicsDevice.usingMetalPipeline()) {
((MTLLayer)windowLayer).dispose();
} else {
((CGLLayer)windowLayer).dispose();
}
}
@Override
@ -99,12 +115,20 @@ public class CPlatformEmbeddedFrame implements PlatformWindow {
@Override
public SurfaceData getScreenSurface() {
return windowLayer.getSurfaceData();
if ( CGraphicsDevice.usingMetalPipeline()) {
return ((MTLLayer)windowLayer).getSurfaceData();
} else {
return ((CGLLayer)windowLayer).getSurfaceData();
}
}
@Override
public SurfaceData replaceSurfaceData() {
return windowLayer.replaceSurfaceData();
if (CGraphicsDevice.usingMetalPipeline()) {
return ((MTLLayer)windowLayer).replaceSurfaceData();
} else {
return ((CGLLayer)windowLayer).replaceSurfaceData();
}
}
@Override

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2021, 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
@ -35,9 +35,12 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import sun.awt.CGraphicsEnvironment;
import sun.awt.CGraphicsDevice;
import sun.java2d.metal.MTLLayer;
import sun.lwawt.LWWindowPeer;
import sun.java2d.SurfaceData;
import sun.java2d.opengl.CGLLayer;
import sun.lwawt.LWWindowPeer;
public class CPlatformView extends CFRetainedResource {
private native long nativeCreateView(int x, int y, int width, int height, long windowLayerPtr);
@ -48,7 +51,7 @@ public class CPlatformView extends CFRetainedResource {
private LWWindowPeer peer;
private SurfaceData surfaceData;
private CGLLayer windowLayer;
private CFRetainedResource windowLayer;
private CPlatformResponder responder;
public CPlatformView() {
@ -58,7 +61,7 @@ public class CPlatformView extends CFRetainedResource {
public void initialize(LWWindowPeer peer, CPlatformResponder responder) {
initializeBase(peer, responder);
this.windowLayer = createCGLayer();
this.windowLayer = CGraphicsDevice.usingMetalPipeline()? createMTLLayer() : createCGLayer();
setPtr(nativeCreateView(0, 0, 0, 0, getWindowLayerPtr()));
}
@ -66,6 +69,11 @@ public class CPlatformView extends CFRetainedResource {
return new CGLLayer(peer);
}
public MTLLayer createMTLLayer() {
return new MTLLayer(peer);
}
protected void initializeBase(LWWindowPeer peer, CPlatformResponder responder) {
this.peer = peer;
this.responder = responder;
@ -96,7 +104,10 @@ public class CPlatformView extends CFRetainedResource {
// PAINTING METHODS
// ----------------------------------------------------------------------
public SurfaceData replaceSurfaceData() {
surfaceData = windowLayer.replaceSurfaceData();
surfaceData = (CGraphicsDevice.usingMetalPipeline()) ?
((MTLLayer)windowLayer).replaceSurfaceData() :
((CGLLayer)windowLayer).replaceSurfaceData()
;
return surfaceData;
}
@ -111,7 +122,9 @@ public class CPlatformView extends CFRetainedResource {
}
public long getWindowLayerPtr() {
return windowLayer.getPointer();
return CGraphicsDevice.usingMetalPipeline() ?
((MTLLayer)windowLayer).getPointer() :
((CGLLayer)windowLayer).getPointer();
}
public void setAutoResizable(boolean toResize) {

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2021, 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
@ -29,6 +29,7 @@ import sun.awt.AWTAccessor;
import sun.awt.IconInfo;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.metal.MTLLayer;
import sun.java2d.opengl.CGLLayer;
import sun.lwawt.LWWindowPeer;
import sun.lwawt.PlatformEventNotifier;
@ -300,6 +301,23 @@ public final class CWarningWindow extends CPlatformWindow
}
};
}
public MTLLayer createMTLLayer() {
return new MTLLayer(null) {
public Rectangle getBounds() {
return CWarningWindow.this.getBounds();
}
public GraphicsConfiguration getGraphicsConfiguration() {
LWWindowPeer peer = ownerPeer.get();
return peer.getGraphicsConfiguration();
}
public boolean isOpaque() {
return false;
}
};
}
};
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2021, 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
@ -109,6 +109,7 @@ import sun.awt.SunToolkit;
import sun.awt.datatransfer.DataTransferer;
import sun.awt.dnd.SunDragSourceContextPeer;
import sun.awt.util.ThreadGroupUtils;
import sun.java2d.metal.MTLRenderQueue;
import sun.java2d.opengl.OGLRenderQueue;
import sun.lwawt.LWComponentPeer;
import sun.lwawt.LWCursorManager;
@ -500,8 +501,12 @@ public final class LWCToolkit extends LWToolkit {
@Override
public void sync() {
// flush the OGL pipeline (this is a no-op if OGL is not enabled)
// flush the rendering pipeline
if (CGraphicsDevice.usingMetalPipeline()) {
MTLRenderQueue.sync();
} else {
OGLRenderQueue.sync();
}
// setNeedsDisplay() selector was sent to the appropriate CALayer so now
// we have to flush the native selectors queue.
flushNativeSelectors();

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2021, 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
@ -29,6 +29,7 @@
#import "JNIUtilities.h"
#import <QuartzCore/CATransaction.h>
#import <QuartzCore/CAMetalLayer.h>
@implementation AWTSurfaceLayers
@ -67,10 +68,11 @@
}
}
// Updates back buffer size of the layer if it's an OpenGL layer
// including all OpenGL sublayers
// Updates back buffer size of the layer if it's an OpenGL/Metal layer
// including all OpenGL/Metal sublayers
+ (void) repaintLayersRecursively:(CALayer*)aLayer {
if ([aLayer isKindOfClass:[CAOpenGLLayer class]]) {
if ([aLayer isKindOfClass:[CAOpenGLLayer class]] ||
[aLayer isKindOfClass:[CAMetalLayer class]]) {
[aLayer setNeedsDisplay];
}
for(CALayer *child in aLayer.sublayers) {
@ -92,7 +94,7 @@
/*
* Class: sun_lwawt_macosx_CPlatformComponent
* Method: nativeCreateLayer
* Method: nativeCreateComponent
* Signature: ()J
*/
JNIEXPORT jlong JNICALL

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef EncoderManager_h_Included
#define EncoderManager_h_Included
#import <Metal/Metal.h>
#include "RenderOptions.h"
@class MTLContex;
/**
* The EncoderManager class used to obtain MTLRenderCommandEncoder (or MTLBlitCommandEncoder) corresponding
* to the current state of MTLContext.
*
* Due to performance issues (creation of MTLRenderCommandEncoder isn't cheap), each getXXXEncoder invocation
* updates properties of common (cached) encoder and returns this encoder.
*
* Base method getEncoder does the following:
* 1. Checks whether common encoder must be closed and recreated (some of encoder properties is 'persistent',
* for example destination, stencil, or any other property of MTLRenderPassDescriptor)
* 2. Updates 'mutable' properties encoder: pipelineState (with corresponding buffers), clip, transform, e.t.c. To avoid
* unnecessary calls of [encoder setXXX] this manager compares requested state with cached one.
*/
@interface EncoderManager : NSObject
- (id _Nonnull)init;
- (void)dealloc;
- (void)setContext:(MTLContex * _Nonnull)mtlc;
// returns encoder that renders/fills geometry with current paint and composite
- (id<MTLRenderCommandEncoder> _Nonnull)getRenderEncoder:(const BMTLSDOps * _Nonnull)dstOps;
- (id<MTLRenderCommandEncoder> _Nonnull)getAARenderEncoder:(const BMTLSDOps * _Nonnull)dstOps;
- (id<MTLRenderCommandEncoder> _Nonnull)getRenderEncoder:(id<MTLTexture> _Nonnull)dest
isDstOpaque:(bool)isOpaque;
- (id<MTLRenderCommandEncoder> _Nonnull)getAAShaderRenderEncoder:(const BMTLSDOps * _Nonnull)dstOps;
// returns encoder that renders/fills geometry with current composite and with given texture
// (user must call [encoder setFragmentTexture] before any rendering)
- (id<MTLRenderCommandEncoder> _Nonnull)getTextureEncoder:(const BMTLSDOps * _Nonnull)dstOps
isSrcOpaque:(bool)isSrcOpaque;
- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque;
- (id<MTLRenderCommandEncoder> _Nonnull) getLCDEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque;
- (id<MTLRenderCommandEncoder> _Nonnull)getTextureEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque
interpolation:(int)interpolation;
- (id<MTLRenderCommandEncoder> _Nonnull)getTextureEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque
interpolation:(int)interpolation
isAA:(jboolean)isAA;
- (id<MTLRenderCommandEncoder> _Nonnull)getTextEncoder:(const BMTLSDOps * _Nonnull)dstOps
isSrcOpaque:(bool)isSrcOpaque;
// Base method to obtain any MTLRenderCommandEncoder
- (id<MTLRenderCommandEncoder> _Nonnull) getEncoder:(id<MTLTexture> _Nonnull)dest
isDestOpaque:(jboolean)isDestOpaque
renderOptions:(const RenderOptions * _Nonnull)renderOptions;
- (id<MTLBlitCommandEncoder> _Nonnull)createBlitEncoder;
- (void)endEncoder;
@end
#endif // EncoderManager_h_Included

View file

@ -0,0 +1,465 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include "EncoderManager.h"
#include "MTLContext.h"
#include "sun_java2d_SunGraphics2D.h"
#import "common.h"
// NOTE: uncomment to disable comparing cached encoder states with requested (for debugging)
// #define ALWAYS_UPDATE_ENCODER_STATES
const SurfaceRasterFlags defaultRasterFlags = { JNI_FALSE, JNI_TRUE };
// Internal utility class that represents the set of 'mutable' encoder properties
@interface EncoderStates : NSObject
@property (readonly) MTLClip * clip;
- (id)init;
- (void)dealloc;
- (void)reset:(id<MTLTexture>)destination
isDstOpaque:(jboolean)isDstOpaque
isDstPremultiplied:(jboolean)isDstPremultiplied
isAA:(jboolean)isAA
isText:(jboolean)isText
isLCD:(jboolean)isLCD;
- (void)updateEncoder:(id<MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
forceUpdate:(jboolean)forceUpdate;
@property (assign) jboolean aa;
@property (assign) jboolean text;
@property (assign) jboolean lcd;
@property (assign) jboolean aaShader;
@property (retain) MTLPaint* paint;
@end
@implementation EncoderStates {
MTLPipelineStatesStorage * _pipelineStateStorage;
id<MTLDevice> _device;
// Persistent encoder properties
id<MTLTexture> _destination;
SurfaceRasterFlags _dstFlags;
jboolean _isAA;
jboolean _isText;
jboolean _isLCD;
jboolean _isAAShader;
//
// Cached 'mutable' states of encoder
//
// Composite rule and source raster flags (it affects the CAD-multipliers (of pipelineState))
MTLComposite * _composite;
SurfaceRasterFlags _srcFlags;
// Paint mode (it affects shaders (of pipelineState) and corresponding buffers)
MTLPaint * _paint;
// If true, indicates that encoder is used for texture drawing (user must do [encoder setFragmentTexture:] before drawing)
jboolean _isTexture;
int _interpolationMode;
// Clip rect or stencil
MTLClip * _clip;
// Transform (affects transformation inside vertex shader)
MTLTransform * _transform;
}
@synthesize aa = _isAA;
@synthesize text = _isText;
@synthesize lcd = _isLCD;
@synthesize aaShader = _isAAShader;
@synthesize paint = _paint;
- (id)init {
self = [super init];
if (self) {
_destination = nil;
_composite = [[MTLComposite alloc] init];
_paint = [[MTLPaint alloc] init];
_transform = [[MTLTransform alloc] init];
_clip = [[MTLClip alloc] init];
}
return self;
}
- (void)dealloc {
[_composite release];
[_paint release];
[_transform release];
[super dealloc];
}
- (void)setContext:(MTLContext * _Nonnull)mtlc {
self->_pipelineStateStorage = mtlc.pipelineStateStorage;
self->_device = mtlc.device;
}
- (void)reset:(id<MTLTexture>)destination
isDstOpaque:(jboolean)isDstOpaque
isDstPremultiplied:(jboolean)isDstPremultiplied
isAA:(jboolean)isAA
isText:(jboolean)isText
isLCD:(jboolean)isLCD {
_destination = destination;
_dstFlags.isOpaque = isDstOpaque;
_dstFlags.isPremultiplied = isDstPremultiplied;
_isAA = isAA;
_isText = isText;
_isLCD = isLCD;
// NOTE: probably it's better to invalidate/reset all cached states now
}
- (void)updateEncoder:(id<MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
forceUpdate:(jboolean)forceUpdate
{
// 1. Process special case for stencil mask generation
if (mtlc.clip.stencilMaskGenerationInProgress == JNI_TRUE) {
// use separate pipeline state for stencil generation
if (forceUpdate || (_clip.stencilMaskGenerationInProgress != JNI_TRUE)) {
[_clip copyFrom:mtlc.clip];
[_clip setMaskGenerationPipelineState:encoder
destWidth:_destination.width
destHeight:_destination.height
pipelineStateStorage:_pipelineStateStorage];
}
[self updateTransform:encoder transform:mtlc.transform forceUpdate:forceUpdate];
return;
}
// 2. Otherwise update all 'mutable' properties of encoder
[self updatePipelineState:encoder
context:mtlc
renderOptions:renderOptions
forceUpdate:forceUpdate];
[self updateTransform:encoder transform:mtlc.transform forceUpdate:forceUpdate];
[self updateClip:encoder clip:mtlc.clip forceUpdate:forceUpdate];
}
//
// Internal methods that update states when necessary (compare with cached states)
//
// Updates pipelineState (and corresponding buffers) with use of paint+composite+flags
- (void)updatePipelineState:(id<MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
forceUpdate:(jboolean)forceUpdate
{
if (!forceUpdate
&& [_paint isEqual:mtlc.paint]
&& [_composite isEqual:mtlc.composite]
&& (_isTexture == renderOptions->isTexture && (!renderOptions->isTexture || _interpolationMode == renderOptions->interpolation)) // interpolation is used only in texture mode
&& _isAA == renderOptions->isAA
&& _isAAShader == renderOptions->isAAShader
&& _isText == renderOptions->isText
&& _isLCD == renderOptions->isLCD
&& _srcFlags.isOpaque == renderOptions->srcFlags.isOpaque && _srcFlags.isPremultiplied == renderOptions->srcFlags.isPremultiplied)
return;
self.paint = mtlc.paint;
[_composite copyFrom:mtlc.composite];
_isTexture = renderOptions->isTexture;
_interpolationMode = renderOptions->interpolation;
_isAA = renderOptions->isAA;
_isAAShader = renderOptions->isAAShader;
_isText = renderOptions->isText;
_isLCD = renderOptions->isLCD;
_srcFlags = renderOptions->srcFlags;
if ((jint)[mtlc.composite getCompositeState] == sun_java2d_SunGraphics2D_COMP_XOR) {
[mtlc.paint setXorModePipelineState:encoder
context:mtlc
renderOptions:renderOptions
pipelineStateStorage:_pipelineStateStorage];
} else {
[mtlc.paint setPipelineState:encoder
context:mtlc
renderOptions:renderOptions
pipelineStateStorage:_pipelineStateStorage];
}
}
- (void) updateClip:(id<MTLRenderCommandEncoder>)encoder clip:(MTLClip *)clip forceUpdate:(jboolean)forceUpdate
{
if (clip.stencilMaskGenerationInProgress == JNI_TRUE) {
// don't set setScissorOrStencil when generation in progress
return;
}
if (!forceUpdate && [_clip isEqual:clip])
return;
[_clip copyFrom:clip];
[_clip setScissorOrStencil:encoder
destWidth:_destination.width
destHeight:_destination.height
device:_device];
}
- (void)updateTransform:(id <MTLRenderCommandEncoder>)encoder
transform:(MTLTransform *)transform
forceUpdate:(jboolean)forceUpdate
{
if (!forceUpdate
&& [_transform isEqual:transform])
return;
[_transform copyFrom:transform];
[_transform setVertexMatrix:encoder
destWidth:_destination.width
destHeight:_destination.height];
}
@end
@implementation EncoderManager {
MTLContext * _mtlc; // used to obtain CommandBufferWrapper and Composite/Paint/Transform
id<MTLRenderCommandEncoder> _encoder;
// 'Persistent' properties of encoder
id<MTLTexture> _destination;
id<MTLTexture> _aaDestination;
BOOL _useStencil;
// 'Mutable' states of encoder
EncoderStates * _encoderStates;
}
- (id _Nonnull)init {
self = [super init];
if (self) {
_encoder = nil;
_destination = nil;
_aaDestination = nil;
_useStencil = NO;
_encoderStates = [[EncoderStates alloc] init];
}
return self;
}
- (void)dealloc {
[_encoderStates release];
[super dealloc];
}
- (void)setContext:(MTLContex * _Nonnull)mtlc {
self->_mtlc = mtlc;
[self->_encoderStates setContext:mtlc];
}
- (id<MTLRenderCommandEncoder> _Nonnull) getRenderEncoder:(const BMTLSDOps * _Nonnull)dstOps
{
return [self getRenderEncoder:dstOps->pTexture isDstOpaque:dstOps->isOpaque];
}
- (id<MTLRenderCommandEncoder> _Nonnull)getAARenderEncoder:(const BMTLSDOps * _Nonnull)dstOps {
id<MTLTexture> dstTxt = dstOps->pTexture;
RenderOptions roptions = {JNI_FALSE, JNI_TRUE, INTERPOLATION_NEAREST_NEIGHBOR, defaultRasterFlags, {dstOps->isOpaque, JNI_TRUE}, JNI_FALSE, JNI_FALSE, JNI_FALSE};
return [self getEncoder:dstTxt renderOptions:&roptions];
}
- (id<MTLRenderCommandEncoder> _Nonnull)getAAShaderRenderEncoder:(const BMTLSDOps * _Nonnull)dstOps
{
RenderOptions roptions = {JNI_FALSE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, defaultRasterFlags, {dstOps->isOpaque, JNI_TRUE}, JNI_FALSE, JNI_FALSE, JNI_TRUE};
return [self getEncoder:dstOps->pTexture renderOptions:&roptions];
}
- (id<MTLRenderCommandEncoder> _Nonnull)getRenderEncoder:(id<MTLTexture> _Nonnull)dest
isDstOpaque:(bool)isOpaque
{
RenderOptions roptions = {JNI_FALSE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, defaultRasterFlags, {isOpaque, JNI_TRUE}, JNI_FALSE, JNI_FALSE, JNI_FALSE};
return [self getEncoder:dest renderOptions:&roptions];
}
- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(const BMTLSDOps * _Nonnull)dstOps
isSrcOpaque:(bool)isSrcOpaque
{
return [self getTextureEncoder:dstOps->pTexture
isSrcOpaque:isSrcOpaque
isDstOpaque:dstOps->isOpaque
interpolation:INTERPOLATION_NEAREST_NEIGHBOR];
}
- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque
{
return [self getTextureEncoder:dest
isSrcOpaque:isSrcOpaque
isDstOpaque:isDstOpaque
interpolation:INTERPOLATION_NEAREST_NEIGHBOR
isAA:JNI_FALSE];
}
- (id<MTLRenderCommandEncoder> _Nonnull) getLCDEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque
{
RenderOptions roptions = {JNI_TRUE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, {isSrcOpaque, JNI_TRUE }, {isDstOpaque, JNI_TRUE}, JNI_FALSE, JNI_TRUE, JNI_FALSE};
return [self getEncoder:dest renderOptions:&roptions];
}
- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque
interpolation:(int)interpolation
isAA:(jboolean)isAA
{
RenderOptions roptions = {JNI_TRUE, isAA, interpolation, { isSrcOpaque, JNI_TRUE }, {isDstOpaque, JNI_TRUE}, JNI_FALSE, JNI_FALSE, JNI_FALSE};
return [self getEncoder:dest renderOptions:&roptions];
}
- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque
interpolation:(int)interpolation
{
return [self getTextureEncoder:dest isSrcOpaque:isSrcOpaque isDstOpaque:isDstOpaque interpolation:interpolation isAA:JNI_FALSE];
}
- (id<MTLRenderCommandEncoder> _Nonnull) getTextEncoder:(const BMTLSDOps * _Nonnull)dstOps
isSrcOpaque:(bool)isSrcOpaque
{
RenderOptions roptions = {JNI_TRUE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, { isSrcOpaque, JNI_TRUE }, {dstOps->isOpaque, JNI_TRUE}, JNI_TRUE, JNI_FALSE, JNI_FALSE};
return [self getEncoder:dstOps->pTexture renderOptions:&roptions];
}
- (id<MTLRenderCommandEncoder> _Nonnull) getEncoder:(id <MTLTexture> _Nonnull)dest
renderOptions:(const RenderOptions * _Nonnull)renderOptions
{
//
// 1. check whether it's necessary to call endEncoder
//
jboolean needEnd = JNI_FALSE;
if (_encoder != nil) {
if (_destination != dest || renderOptions->isAA != _encoderStates.aa) {
J2dTraceLn2(J2D_TRACE_VERBOSE,
"end common encoder because of dest change: %p -> %p",
_destination, dest);
needEnd = JNI_TRUE;
} else if ((_useStencil == NO) != ([_mtlc.clip isShape] == NO)) {
// 1. When mode changes RECT -> SHAPE we must recreate encoder with
// stencilAttachment (todo: consider the case when current encoder already
// has stencil)
//
// 2. When mode changes SHAPE -> RECT it seems that we can use the same
// encoder with disabled stencil test, but [encoder
// setDepthStencilState:nil] causes crash, so we have to recreate encoder
// in such case
J2dTraceLn2(J2D_TRACE_VERBOSE,
"end common encoder because toggle stencil: %d -> %d",
(int)_useStencil, (int)[_mtlc.clip isShape]);
needEnd = JNI_TRUE;
}
}
if (needEnd)
[self endEncoder];
//
// 2. recreate encoder if necessary
//
jboolean forceUpdate = JNI_FALSE;
#ifdef ALWAYS_UPDATE_ENCODER_STATES
forceUpdate = JNI_TRUE;
#endif // ALWAYS_UPDATE_ENCODER_STATES
if (_encoder == nil) {
_destination = dest;
_useStencil = [_mtlc.clip isShape] && !_mtlc.clip.stencilMaskGenerationInProgress;
forceUpdate = JNI_TRUE;
MTLCommandBufferWrapper *cbw = [_mtlc getCommandBufferWrapper];
MTLRenderPassDescriptor *rpd =
[MTLRenderPassDescriptor renderPassDescriptor];
MTLRenderPassColorAttachmentDescriptor *ca = rpd.colorAttachments[0];
ca.texture = dest;
// TODO: Find out why we cannot use
// if (_mtlc.clip.stencilMaskGenerationInProgress == YES) {
// ca.loadAction = MTLLoadActionClear;
// ca.clearColor = MTLClearColorMake(0.0f, 0.0f,0.0f, 0.0f);
// }
// here to avoid creation of clearEncoder in beginShapeClip
ca.loadAction = MTLLoadActionLoad;
ca.storeAction = MTLStoreActionStore;
if (_useStencil && !renderOptions->isAA) {
// If you enable stencil testing or stencil writing, the
// MTLRenderPassDescriptor must include a stencil attachment.
rpd.stencilAttachment.loadAction = MTLLoadActionLoad;
rpd.stencilAttachment.storeAction = MTLStoreActionStore;
rpd.stencilAttachment.texture = _mtlc.clip.stencilTextureRef;
}
// J2dTraceLn1(J2D_TRACE_VERBOSE, "created render encoder to draw on
// tex=%p", dest);
_encoder = [[cbw getCommandBuffer] renderCommandEncoderWithDescriptor:rpd];
[_encoderStates reset:dest
isDstOpaque:renderOptions->dstFlags.isOpaque
isDstPremultiplied:YES
isAA:renderOptions->isAA
isText:renderOptions->isText
isLCD:renderOptions->isLCD];
}
//
// 3. update encoder states
//
[_encoderStates updateEncoder:_encoder
context:_mtlc
renderOptions:renderOptions
forceUpdate:forceUpdate];
return _encoder;
}
- (id<MTLBlitCommandEncoder> _Nonnull) createBlitEncoder {
[self endEncoder];
return [[[_mtlc getCommandBufferWrapper] getCommandBuffer] blitCommandEncoder];
}
- (void) endEncoder {
if (_encoder != nil) {
[_encoder endEncoding];
_encoder = nil;
_destination = nil;
}
}
@end

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLBlitLoops_h_Included
#define MTLBlitLoops_h_Included
#include "sun_java2d_metal_MTLBlitLoops.h"
#include "MTLSurfaceDataBase.h"
#include "MTLContext.h"
#define OFFSET_SRCTYPE sun_java2d_metal_MTLBlitLoops_OFFSET_SRCTYPE
#define OFFSET_HINT sun_java2d_metal_MTLBlitLoops_OFFSET_HINT
#define OFFSET_TEXTURE sun_java2d_metal_MTLBlitLoops_OFFSET_TEXTURE
#define OFFSET_RTT sun_java2d_metal_MTLBlitLoops_OFFSET_RTT
#define OFFSET_XFORM sun_java2d_metal_MTLBlitLoops_OFFSET_XFORM
#define OFFSET_ISOBLIT sun_java2d_metal_MTLBlitLoops_OFFSET_ISOBLIT
void MTLBlitLoops_IsoBlit(JNIEnv *env,
MTLContext *mtlc, jlong pSrcOps, jlong pDstOps,
jboolean xform, jint hint,
jboolean texture,
jint sx1, jint sy1,
jint sx2, jint sy2,
jdouble dx1, jdouble dy1,
jdouble dx2, jdouble dy2);
void MTLBlitLoops_Blit(JNIEnv *env,
MTLContext *mtlc, jlong pSrcOps, jlong pDstOps,
jboolean xform, jint hint,
jint srctype, jboolean texture,
jint sx1, jint sy1,
jint sx2, jint sy2,
jdouble dx1, jdouble dy1,
jdouble dx2, jdouble dy2);
void MTLBlitLoops_SurfaceToSwBlit(JNIEnv *env, MTLContext *mtlc,
jlong pSrcOps, jlong pDstOps, jint dsttype,
jint srcx, jint srcy,
jint dstx, jint dsty,
jint width, jint height);
void MTLBlitLoops_CopyArea(JNIEnv *env,
MTLContext *mtlc, BMTLSDOps *dstOps,
jint x, jint y,
jint width, jint height,
jint dx, jint dy);
void MTLBlitTex2Tex(MTLContext *mtlc, id<MTLTexture> src, id<MTLTexture> dest);
void drawTex2Tex(MTLContext *mtlc,
id<MTLTexture> src, id<MTLTexture> dst,
jboolean isSrcOpaque, jboolean isDstOpaque, jint hint,
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2);
#endif /* MTLBlitLoops_h_Included */

View file

@ -0,0 +1,822 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include <jni.h>
#include <jlong.h>
#include "SurfaceData.h"
#include "MTLBlitLoops.h"
#include "MTLRenderQueue.h"
#include "MTLSurfaceData.h"
#include "MTLUtils.h"
#include "GraphicsPrimitiveMgr.h"
#include <string.h> // memcpy
#include "IntArgbPre.h"
#import <Accelerate/Accelerate.h>
#ifdef DEBUG
#define TRACE_ISOBLIT
#define TRACE_BLIT
#endif //DEBUG
//#define DEBUG_ISOBLIT
//#define DEBUG_BLIT
typedef struct {
// Consider deleting this field, since it's always MTLPixelFormatBGRA8Unorm
jboolean hasAlpha;
jboolean isPremult;
NSString* swizzleKernel;
} MTLRasterFormatInfo;
/**
* This table contains the "pixel formats" for all system memory surfaces
* that Metal is capable of handling, indexed by the "PF_" constants defined
* in MTLLSurfaceData.java. These pixel formats contain information that is
* passed to Metal when copying from a system memory ("Sw") surface to
* an Metal surface
*/
MTLRasterFormatInfo RasterFormatInfos[] = {
{ 1, 0, nil }, /* 0 - IntArgb */ // Argb (in java notation)
{ 1, 1, nil }, /* 1 - IntArgbPre */
{ 0, 1, @"rgb_to_rgba" }, /* 2 - IntRgb */
{ 0, 1, @"xrgb_to_rgba" }, /* 3 - IntRgbx */
{ 0, 1, @"bgr_to_rgba" }, /* 4 - IntBgr */
{ 0, 1, @"xbgr_to_rgba" }, /* 5 - IntBgrx */
// TODO: support 2-byte formats
// { GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV,
// 2, 0, 1, }, /* 7 - Ushort555Rgb */
// { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1,
// 2, 0, 1, }, /* 8 - Ushort555Rgbx*/
// { GL_LUMINANCE, GL_UNSIGNED_BYTE,
// 1, 0, 1, }, /* 9 - ByteGray */
// { GL_LUMINANCE, GL_UNSIGNED_SHORT,
// 2, 0, 1, }, /*10 - UshortGray */
// { GL_BGR, GL_UNSIGNED_BYTE,
// 1, 0, 1, }, /*11 - ThreeByteBgr */
};
extern void J2dTraceImpl(int level, jboolean cr, const char *string, ...);
void fillTxQuad(
struct TxtVertex * txQuadVerts,
jint sx1, jint sy1, jint sx2, jint sy2, jint sw, jint sh,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2, jdouble dw, jdouble dh
) {
const float nsx1 = sx1/(float)sw;
const float nsy1 = sy1/(float)sh;
const float nsx2 = sx2/(float)sw;
const float nsy2 = sy2/(float)sh;
txQuadVerts[0].position[0] = dx1;
txQuadVerts[0].position[1] = dy1;
txQuadVerts[0].txtpos[0] = nsx1;
txQuadVerts[0].txtpos[1] = nsy1;
txQuadVerts[1].position[0] = dx2;
txQuadVerts[1].position[1] = dy1;
txQuadVerts[1].txtpos[0] = nsx2;
txQuadVerts[1].txtpos[1] = nsy1;
txQuadVerts[2].position[0] = dx2;
txQuadVerts[2].position[1] = dy2;
txQuadVerts[2].txtpos[0] = nsx2;
txQuadVerts[2].txtpos[1] = nsy2;
txQuadVerts[3].position[0] = dx2;
txQuadVerts[3].position[1] = dy2;
txQuadVerts[3].txtpos[0] = nsx2;
txQuadVerts[3].txtpos[1] = nsy2;
txQuadVerts[4].position[0] = dx1;
txQuadVerts[4].position[1] = dy2;
txQuadVerts[4].txtpos[0] = nsx1;
txQuadVerts[4].txtpos[1] = nsy2;
txQuadVerts[5].position[0] = dx1;
txQuadVerts[5].position[1] = dy1;
txQuadVerts[5].txtpos[0] = nsx1;
txQuadVerts[5].txtpos[1] = nsy1;
}
//#define TRACE_drawTex2Tex
void drawTex2Tex(MTLContext *mtlc,
id<MTLTexture> src, id<MTLTexture> dst,
jboolean isSrcOpaque, jboolean isDstOpaque, jint hint,
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
{
#ifdef TRACE_drawTex2Tex
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "drawTex2Tex: src tex=%p, dst tex=%p", src, dst);
J2dRlsTraceLn4(J2D_TRACE_VERBOSE, " sw=%d sh=%d dw=%d dh=%d", src.width, src.height, dst.width, dst.height);
J2dRlsTraceLn4(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d", sx1, sy1, sx2, sy2);
J2dRlsTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f", dx1, dy1, dx2, dy2);
#endif //TRACE_drawTex2Tex
id<MTLRenderCommandEncoder> encoder = [mtlc.encoderManager getTextureEncoder:dst
isSrcOpaque:isSrcOpaque
isDstOpaque:isDstOpaque
interpolation:hint
];
struct TxtVertex quadTxVerticesBuffer[6];
fillTxQuad(quadTxVerticesBuffer, sx1, sy1, sx2, sy2, src.width, src.height, dx1, dy1, dx2, dy2, dst.width, dst.height);
[encoder setVertexBytes:quadTxVerticesBuffer length:sizeof(quadTxVerticesBuffer) atIndex:MeshVertexBuffer];
[encoder setFragmentTexture:src atIndex: 0];
[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];
}
static void
replaceTextureRegion(MTLContext *mtlc, id<MTLTexture> dest, const SurfaceDataRasInfo *srcInfo,
const MTLRasterFormatInfo *rfi,
int dx1, int dy1, int dx2, int dy2) {
const int sw = srcInfo->bounds.x2 - srcInfo->bounds.x1;
const int sh = srcInfo->bounds.y2 - srcInfo->bounds.y1;
const int dw = dx2 - dx1;
const int dh = dy2 - dy1;
if (dw < sw || dh < sh) {
J2dTraceLn4(J2D_TRACE_ERROR, "replaceTextureRegion: dest size: (%d, %d) less than source size: (%d, %d)", dw, dh, sw, sh);
return;
}
const void *raster = srcInfo->rasBase;
raster += (NSUInteger)srcInfo->bounds.y1 * (NSUInteger)srcInfo->scanStride + (NSUInteger)srcInfo->bounds.x1 * (NSUInteger)srcInfo->pixelStride;
@autoreleasepool {
J2dTraceLn4(J2D_TRACE_VERBOSE, "replaceTextureRegion src (dw, dh) : [%d, %d] dest (dx1, dy1) =[%d, %d]",
dw, dh, dx1, dy1);
id<MTLBuffer> buff = [[mtlc.device newBufferWithLength:(sw * sh * srcInfo->pixelStride) options:MTLResourceStorageModeManaged] autorelease];
// copy src pixels inside src bounds to buff
for (int row = 0; row < sh; row++) {
memcpy(buff.contents + (row * sw * srcInfo->pixelStride), raster, sw * srcInfo->pixelStride);
raster += (NSUInteger)srcInfo->scanStride;
}
[buff didModifyRange:NSMakeRange(0, buff.length)];
if (rfi->swizzleKernel != nil) {
id <MTLBuffer> swizzled = [[mtlc.device newBufferWithLength:(sw * sh * srcInfo->pixelStride) options:MTLResourceStorageModeManaged] autorelease];
// this should be cheap, since data is already on GPU
id<MTLCommandBuffer> cb = [mtlc createCommandBuffer];
id<MTLComputeCommandEncoder> computeEncoder = [cb computeCommandEncoder];
id<MTLComputePipelineState> computePipelineState = [mtlc.pipelineStateStorage
getComputePipelineState:rfi->swizzleKernel];
[computeEncoder setComputePipelineState:computePipelineState];
[computeEncoder setBuffer:buff offset:0 atIndex:0];
[computeEncoder setBuffer:swizzled offset:0 atIndex:1];
NSUInteger threadGroupSize = computePipelineState.maxTotalThreadsPerThreadgroup;
if (threadGroupSize == 0) {
threadGroupSize = 1;
}
NSUInteger pixelCount = buff.length / srcInfo->pixelStride;
MTLSize threadsPerGroup = MTLSizeMake(threadGroupSize, 1, 1);
MTLSize threadGroups = MTLSizeMake((pixelCount + threadGroupSize - 1) / threadGroupSize,
1, 1);
[computeEncoder dispatchThreadgroups:threadGroups
threadsPerThreadgroup:threadsPerGroup];
[computeEncoder endEncoding];
[cb commit];
buff = swizzled;
}
id<MTLBlitCommandEncoder> blitEncoder = [mtlc.encoderManager createBlitEncoder];
[blitEncoder copyFromBuffer:buff
sourceOffset:0 sourceBytesPerRow:(sw * srcInfo->pixelStride)
sourceBytesPerImage:(sw * sh * srcInfo->pixelStride) sourceSize:MTLSizeMake(sw, sh, 1)
toTexture:dest
destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(dx1, dy1, 0)];
[blitEncoder endEncoding];
[mtlc.encoderManager endEncoder];
MTLCommandBufferWrapper * cbwrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
}
}
/**
* Inner loop used for copying a source system memory ("Sw") surface to a
* destination MTL "Surface". This method is invoked from
* MTLBlitLoops_Blit().
*/
static void
MTLBlitSwToTextureViaPooledTexture(
MTLContext *mtlc, SurfaceDataRasInfo *srcInfo, BMTLSDOps * bmtlsdOps,
MTLRasterFormatInfo *rfi, jint hint,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
{
int sw = srcInfo->bounds.x2 - srcInfo->bounds.x1;
int sh = srcInfo->bounds.y2 - srcInfo->bounds.y1;
sw = MIN(sw, MTL_GPU_FAMILY_MAC_TXT_SIZE);
sh = MIN(sh, MTL_GPU_FAMILY_MAC_TXT_SIZE);
id<MTLTexture> dest = bmtlsdOps->pTexture;
MTLPooledTextureHandle * texHandle = [mtlc.texturePool getTexture:sw height:sh format:MTLPixelFormatBGRA8Unorm];
if (texHandle == nil) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitSwToTextureViaPooledTexture: can't obtain temporary texture object from pool");
return;
}
[[mtlc getCommandBufferWrapper] registerPooledTexture:texHandle];
id<MTLTexture> texBuff = texHandle.texture;
replaceTextureRegion(mtlc, texBuff, srcInfo, rfi, 0, 0, sw, sh);
drawTex2Tex(mtlc, texBuff, dest, !rfi->hasAlpha, bmtlsdOps->isOpaque, hint,
0, 0, sw, sh, dx1, dy1, dx2, dy2);
}
static
jboolean isIntegerAndUnscaled(
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2
) {
const jdouble epsilon = 0.0001f;
// check that dx1,dy1 is integer
if (fabs(dx1 - (int)dx1) > epsilon || fabs(dy1 - (int)dy1) > epsilon) {
return JNI_FALSE;
}
// check that destSize equals srcSize
if (fabs(dx2 - dx1 - sx2 + sx1) > epsilon || fabs(dy2 - dy1 - sy2 + sy1) > epsilon) {
return JNI_FALSE;
}
return JNI_TRUE;
}
static
jboolean clipDestCoords(
jdouble *dx1, jdouble *dy1, jdouble *dx2, jdouble *dy2,
jint *sx1, jint *sy1, jint *sx2, jint *sy2,
jint destW, jint destH, const MTLScissorRect * clipRect
) {
// Trim destination rect by clip-rect (or dest.bounds)
const jint sw = *sx2 - *sx1;
const jint sh = *sy2 - *sy1;
const jdouble dw = *dx2 - *dx1;
const jdouble dh = *dy2 - *dy1;
jdouble dcx1 = 0;
jdouble dcx2 = destW;
jdouble dcy1 = 0;
jdouble dcy2 = destH;
if (clipRect != NULL) {
if (clipRect->x > dcx1)
dcx1 = clipRect->x;
const int maxX = clipRect->x + clipRect->width;
if (dcx2 > maxX)
dcx2 = maxX;
if (clipRect->y > dcy1)
dcy1 = clipRect->y;
const int maxY = clipRect->y + clipRect->height;
if (dcy2 > maxY)
dcy2 = maxY;
if (dcx1 >= dcx2) {
J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcx1=%1.2f, dcx2=%1.2f", dcx1, dcx2);
dcx1 = dcx2;
}
if (dcy1 >= dcy2) {
J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcy1=%1.2f, dcy2=%1.2f", dcy1, dcy2);
dcy1 = dcy2;
}
}
if (*dx2 <= dcx1 || *dx1 >= dcx2 || *dy2 <= dcy1 || *dy1 >= dcy2) {
J2dTraceLn(J2D_TRACE_INFO, "\tclipDestCoords: dest rect doesn't intersect clip area");
J2dTraceLn4(J2D_TRACE_INFO, "\tdx2=%1.4f <= dcx1=%1.4f || *dx1=%1.4f >= dcx2=%1.4f", *dx2, dcx1, *dx1, dcx2);
J2dTraceLn4(J2D_TRACE_INFO, "\t*dy2=%1.4f <= dcy1=%1.4f || *dy1=%1.4f >= dcy2=%1.4f", *dy2, dcy1, *dy1, dcy2);
return JNI_FALSE;
}
if (*dx1 < dcx1) {
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdx1=%1.2f, will be clipped to %1.2f | sx1+=%d", *dx1, dcx1, (jint)((dcx1 - *dx1) * (sw/dw)));
*sx1 += (jint)((dcx1 - *dx1) * (sw/dw));
*dx1 = dcx1;
}
if (*dx2 > dcx2) {
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdx2=%1.2f, will be clipped to %1.2f | sx2-=%d", *dx2, dcx2, (jint)((*dx2 - dcx2) * (sw/dw)));
*sx2 -= (jint)((*dx2 - dcx2) * (sw/dw));
*dx2 = dcx2;
}
if (*dy1 < dcy1) {
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdy1=%1.2f, will be clipped to %1.2f | sy1+=%d", *dy1, dcy1, (jint)((dcy1 - *dy1) * (sh/dh)));
*sy1 += (jint)((dcy1 - *dy1) * (sh/dh));
*dy1 = dcy1;
}
if (*dy2 > dcy2) {
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdy2=%1.2f, will be clipped to %1.2f | sy2-=%d", *dy2, dcy2, (jint)((*dy2 - dcy2) * (sh/dh)));
*sy2 -= (jint)((*dy2 - dcy2) * (sh/dh));
*dy2 = dcy2;
}
return JNI_TRUE;
}
/**
* General blit method for copying a native MTL surface to another MTL "Surface".
* Parameter texture == true forces to use 'texture' codepath (dest coordinates will always be integers).
* Parameter xform == true only when AffineTransform is used (invoked only from TransformBlit, dest coordinates will always be integers).
*/
void
MTLBlitLoops_IsoBlit(JNIEnv *env,
MTLContext *mtlc, jlong pSrcOps, jlong pDstOps,
jboolean xform, jint hint, jboolean texture,
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
{
BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps);
BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps);
RETURN_IF_NULL(mtlc);
RETURN_IF_NULL(srcOps);
RETURN_IF_NULL(dstOps);
// Verify if we use a valid MTLContext
MTLSDOps *dstMTLOps = (MTLSDOps *)dstOps->privOps;
RETURN_IF_TRUE(dstMTLOps->configInfo != NULL && mtlc != dstMTLOps->configInfo->context);
MTLSDOps *srcMTLOps = (MTLSDOps *)srcOps->privOps;
RETURN_IF_TRUE(srcMTLOps->configInfo != NULL && mtlc != srcMTLOps->configInfo->context);
id<MTLTexture> srcTex = srcOps->pTexture;
id<MTLTexture> dstTex = dstOps->pTexture;
if (srcTex == nil || srcTex == nil) {
J2dTraceLn2(J2D_TRACE_ERROR, "MTLBlitLoops_IsoBlit: surface is null (stex=%p, dtex=%p)", srcTex, dstTex);
return;
}
const jint sw = sx2 - sx1;
const jint sh = sy2 - sy1;
const jdouble dw = dx2 - dx1;
const jdouble dh = dy2 - dy1;
if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) {
J2dTraceLn4(J2D_TRACE_WARNING, "MTLBlitLoops_IsoBlit: invalid dimensions: sw=%d, sh%d, dw=%d, dh=%d", sw, sh, dw, dh);
return;
}
#ifdef DEBUG_ISOBLIT
if ((xform == JNI_TRUE) != (mtlc.useTransform == JNI_TRUE)) {
J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE,
"MTLBlitLoops_IsoBlit state error: xform=%d, mtlc.useTransform=%d, texture=%d",
xform, mtlc.useTransform, texture);
}
#endif // DEBUG_ISOBLIT
if (!xform) {
clipDestCoords(
&dx1, &dy1, &dx2, &dy2,
&sx1, &sy1, &sx2, &sy2,
dstTex.width, dstTex.height, texture ? NULL : [mtlc.clip getRect]
);
}
SurfaceDataBounds bounds;
bounds.x1 = sx1;
bounds.y1 = sy1;
bounds.x2 = sx2;
bounds.y2 = sy2;
SurfaceData_IntersectBoundsXYXY(&bounds, 0, 0, srcOps->width, srcOps->height);
if (bounds.x2 <= bounds.x1 || bounds.y2 <= bounds.y1) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLBlitLoops_IsoBlit: source rectangle doesn't intersect with source surface bounds");
J2dTraceLn6(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d sw=%d sh=%d", sx1, sy1, sx2, sy2, srcOps->width, srcOps->height);
J2dTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f", dx1, dy1, dx2, dy2);
return;
}
if (bounds.x1 != sx1) {
dx1 += (bounds.x1 - sx1) * (dw / sw);
sx1 = bounds.x1;
}
if (bounds.y1 != sy1) {
dy1 += (bounds.y1 - sy1) * (dh / sh);
sy1 = bounds.y1;
}
if (bounds.x2 != sx2) {
dx2 += (bounds.x2 - sx2) * (dw / sw);
sx2 = bounds.x2;
}
if (bounds.y2 != sy2) {
dy2 += (bounds.y2 - sy2) * (dh / sh);
sy2 = bounds.y2;
}
#ifdef TRACE_ISOBLIT
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE,
"MTLBlitLoops_IsoBlit [tx=%d, xf=%d, AC=%s]: src=%s, dst=%s | (%d, %d, %d, %d)->(%1.2f, %1.2f, %1.2f, %1.2f)",
texture, xform, [mtlc getCompositeDescription].cString,
getSurfaceDescription(srcOps).cString, getSurfaceDescription(dstOps).cString,
sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
#endif //TRACE_ISOBLIT
if (!texture && !xform
&& srcOps->isOpaque
&& isIntegerAndUnscaled(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)
&& (dstOps->isOpaque || !srcOps->isOpaque)
) {
#ifdef TRACE_ISOBLIT
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via blitEncoder]");
#endif //TRACE_ISOBLIT
id <MTLBlitCommandEncoder> blitEncoder = [mtlc.encoderManager createBlitEncoder];
[blitEncoder copyFromTexture:srcTex
sourceSlice:0
sourceLevel:0
sourceOrigin:MTLOriginMake(sx1, sy1, 0)
sourceSize:MTLSizeMake(sx2 - sx1, sy2 - sy1, 1)
toTexture:dstTex
destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(dx1, dy1, 0)];
[blitEncoder endEncoding];
return;
}
#ifdef TRACE_ISOBLIT
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via sampling]");
#endif //TRACE_ISOBLIT
drawTex2Tex(mtlc, srcTex, dstTex,
srcOps->isOpaque, dstOps->isOpaque,
hint, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
}
/**
* General blit method for copying a system memory ("Sw") surface to a native MTL surface.
* Parameter texture == true only in SwToTextureBlit (straight copy from sw to texture), dest coordinates will always be integers.
* Parameter xform == true only when AffineTransform is used (invoked only from TransformBlit, dest coordinates will always be integers).
*/
void
MTLBlitLoops_Blit(JNIEnv *env,
MTLContext *mtlc, jlong pSrcOps, jlong pDstOps,
jboolean xform, jint hint,
jint srctype, jboolean texture,
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
{
SurfaceDataOps *srcOps = (SurfaceDataOps *)jlong_to_ptr(pSrcOps);
BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps);
RETURN_IF_NULL(mtlc);
RETURN_IF_NULL(srcOps);
RETURN_IF_NULL(dstOps);
// Verify if we use a valid MTLContext
MTLSDOps *dstMTLOps = (MTLSDOps *)dstOps->privOps;
RETURN_IF_TRUE(dstMTLOps->configInfo != NULL && mtlc != dstMTLOps->configInfo->context);
id<MTLTexture> dest = dstOps->pTexture;
if (dest == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: dest is null");
return;
}
if (srctype < 0 || srctype >= sizeof(RasterFormatInfos)/ sizeof(MTLRasterFormatInfo)) {
J2dTraceLn1(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: source pixel format %d isn't supported", srctype);
return;
}
const jint sw = sx2 - sx1;
const jint sh = sy2 - sy1;
const jdouble dw = dx2 - dx1;
const jdouble dh = dy2 - dy1;
if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: invalid dimensions");
return;
}
#ifdef DEBUG_BLIT
if (
(xform == JNI_TRUE) != (mtlc.useTransform == JNI_TRUE)
|| (xform && texture)
) {
J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE,
"MTLBlitLoops_Blit state error: xform=%d, mtlc.useTransform=%d, texture=%d",
xform, mtlc.useTransform, texture);
}
if (texture) {
if (!isIntegerAndUnscaled(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)) {
J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE,
"MTLBlitLoops_Blit state error: texture=true, but src and dst dimensions aren't equal or dest coords aren't integers");
}
if (!dstOps->isOpaque && !RasterFormatInfos[srctype].hasAlpha) {
J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE,
"MTLBlitLoops_Blit state error: texture=true, but dest has alpha and source hasn't alpha, can't use texture-codepath");
}
}
#endif // DEBUG_BLIT
if (!xform) {
clipDestCoords(
&dx1, &dy1, &dx2, &dy2,
&sx1, &sy1, &sx2, &sy2,
dest.width, dest.height, texture ? NULL : [mtlc.clip getRect]
);
}
SurfaceDataRasInfo srcInfo;
srcInfo.bounds.x1 = sx1;
srcInfo.bounds.y1 = sy1;
srcInfo.bounds.x2 = sx2;
srcInfo.bounds.y2 = sy2;
// NOTE: This function will modify the contents of the bounds field to represent the maximum available raster data.
if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) != SD_SUCCESS) {
J2dTraceLn(J2D_TRACE_WARNING, "MTLBlitLoops_Blit: could not acquire lock");
return;
}
if (srcInfo.bounds.x2 > srcInfo.bounds.x1 && srcInfo.bounds.y2 > srcInfo.bounds.y1) {
srcOps->GetRasInfo(env, srcOps, &srcInfo);
if (srcInfo.rasBase) {
if (srcInfo.bounds.x1 != sx1) {
const int dx = srcInfo.bounds.x1 - sx1;
dx1 += dx * (dw / sw);
}
if (srcInfo.bounds.y1 != sy1) {
const int dy = srcInfo.bounds.y1 - sy1;
dy1 += dy * (dh / sh);
}
if (srcInfo.bounds.x2 != sx2) {
const int dx = srcInfo.bounds.x2 - sx2;
dx2 += dx * (dw / sw);
}
if (srcInfo.bounds.y2 != sy2) {
const int dy = srcInfo.bounds.y2 - sy2;
dy2 += dy * (dh / sh);
}
#ifdef TRACE_BLIT
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE,
"MTLBlitLoops_Blit [tx=%d, xf=%d, AC=%s]: bdst=%s, src=%p (%dx%d) O=%d premul=%d | (%d, %d, %d, %d)->(%1.2f, %1.2f, %1.2f, %1.2f)",
texture, xform, [mtlc getCompositeDescription].cString,
getSurfaceDescription(dstOps).cString, srcOps,
sx2 - sx1, sy2 - sy1,
RasterFormatInfos[srctype].hasAlpha ? 0 : 1, RasterFormatInfos[srctype].isPremult ? 1 : 0,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2);
#endif //TRACE_BLIT
MTLRasterFormatInfo rfi = RasterFormatInfos[srctype];
if (texture) {
replaceTextureRegion(mtlc, dest, &srcInfo, &rfi, (int) dx1, (int) dy1, (int) dx2, (int) dy2);
} else {
MTLBlitSwToTextureViaPooledTexture(mtlc, &srcInfo, dstOps, &rfi, hint, dx1, dy1, dx2, dy2);
}
}
SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
}
SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
}
void copyFromMTLBuffer(void *pDst, id<MTLBuffer> srcBuf, NSUInteger offset, NSUInteger len, BOOL convertFromArgbPre) {
char *pSrc = (char*)srcBuf.contents + offset;
if (convertFromArgbPre) {
NSUInteger pixelLen = len >> 2;
for (NSUInteger i = 0; i < pixelLen; i++) {
LoadIntArgbPreTo1IntArgb((jint*)pSrc, 0, i, ((jint*)pDst)[i]);
}
} else {
memcpy(pDst, pSrc, len);
}
}
/**
* Specialized blit method for copying a native MTL "Surface" (pbuffer,
* window, etc.) to a system memory ("Sw") surface.
*/
void
MTLBlitLoops_SurfaceToSwBlit(JNIEnv *env, MTLContext *mtlc,
jlong pSrcOps, jlong pDstOps, jint dsttype,
jint srcx, jint srcy, jint dstx, jint dsty,
jint width, jint height)
{
J2dTraceLn6(J2D_TRACE_VERBOSE, "MTLBlitLoops_SurfaceToSwBlit: sx=%d sy=%d w=%d h=%d dx=%d dy=%d", srcx, srcy, width, height, dstx, dsty);
BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps);
SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps);
SurfaceDataRasInfo srcInfo, dstInfo;
if (dsttype < 0 || dsttype >= sizeof(RasterFormatInfos)/ sizeof(MTLRasterFormatInfo)) {
J2dTraceLn1(J2D_TRACE_ERROR, "MTLBlitLoops_SurfaceToSwBlit: destination pixel format %d isn't supported", dsttype);
return;
}
if (width <= 0 || height <= 0) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_SurfaceToSwBlit: dimensions are non-positive");
return;
}
RETURN_IF_NULL(srcOps);
RETURN_IF_NULL(dstOps);
RETURN_IF_NULL(mtlc);
RETURN_IF_TRUE(width < 0);
RETURN_IF_TRUE(height < 0);
NSUInteger w = (NSUInteger)width;
NSUInteger h = (NSUInteger)height;
srcInfo.bounds.x1 = srcx;
srcInfo.bounds.y1 = srcy;
srcInfo.bounds.x2 = srcx + width;
srcInfo.bounds.y2 = srcy + height;
dstInfo.bounds.x1 = dstx;
dstInfo.bounds.y1 = dsty;
dstInfo.bounds.x2 = dstx + width;
dstInfo.bounds.y2 = dsty + height;
if (dstOps->Lock(env, dstOps, &dstInfo, SD_LOCK_WRITE) != SD_SUCCESS) {
J2dTraceLn(J2D_TRACE_WARNING,"MTLBlitLoops_SurfaceToSwBlit: could not acquire dst lock");
return;
}
SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds,
0, 0, srcOps->width, srcOps->height);
SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds,
srcx - dstx, srcy - dsty);
if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&
srcInfo.bounds.y2 > srcInfo.bounds.y1)
{
dstOps->GetRasInfo(env, dstOps, &dstInfo);
if (dstInfo.rasBase) {
void *pDst = dstInfo.rasBase;
srcx = srcInfo.bounds.x1;
srcy = srcInfo.bounds.y1;
dstx = dstInfo.bounds.x1;
dsty = dstInfo.bounds.y1;
width = srcInfo.bounds.x2 - srcInfo.bounds.x1;
height = srcInfo.bounds.y2 - srcInfo.bounds.y1;
pDst = PtrPixelsRow(pDst, dstx, dstInfo.pixelStride);
pDst = PtrPixelsRow(pDst, dsty, dstInfo.scanStride);
// Metal texture is (0,0) at left-top
srcx = srcOps->xOffset + srcx;
srcy = srcOps->yOffset + srcy;
NSUInteger byteLength = w * h * 4; // NOTE: assume that src format is MTLPixelFormatBGRA8Unorm
// Create MTLBuffer (or use static)
id<MTLBuffer> mtlbuf;
#ifdef USE_STATIC_BUFFER
// NOTE: theoretically we can use newBufferWithBytesNoCopy, but pDst must be allocated with special API
// mtlbuf = [mtlc.device
// newBufferWithBytesNoCopy:pDst
// length:(NSUInteger) srcLength
// options:MTLResourceCPUCacheModeDefaultCache
// deallocator:nil];
//
// see https://developer.apple.com/documentation/metal/mtldevice/1433382-newbufferwithbytesnocopy?language=objc
//
// The storage allocation of the returned new MTLBuffer object is the same as the pointer input value.
// The existing memory allocation must be covered by a single VM region, typically allocated with vm_allocate or mmap.
// Memory allocated by malloc is specifically disallowed.
static id<MTLBuffer> mtlIntermediateBuffer = nil; // need to reimplement with MTLBufferManager
if (mtlIntermediateBuffer == nil || mtlIntermediateBuffer.length < srcLength) {
if (mtlIntermediateBuffer != nil) {
[mtlIntermediateBuffer release];
}
mtlIntermediateBuffer = [mtlc.device newBufferWithLength:srcLength options:MTLResourceCPUCacheModeDefaultCache];
}
mtlbuf = mtlIntermediateBuffer;
#else // USE_STATIC_BUFFER
mtlbuf = [mtlc.device newBufferWithLength:byteLength options:MTLResourceStorageModeShared];
#endif // USE_STATIC_BUFFER
// Read from surface into MTLBuffer
// NOTE: using of separate blitCommandBuffer can produce errors (draw into surface (with general cmd-buf)
// can be unfinished when reading raster from blit cmd-buf).
// Consider to use [mtlc.encoderManager createBlitEncoder] and [mtlc commitCommandBuffer:JNI_TRUE];
J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLBlitLoops_SurfaceToSwBlit: source texture %p", srcOps->pTexture);
id<MTLCommandBuffer> cb = [mtlc createCommandBuffer];
id<MTLBlitCommandEncoder> blitEncoder = [cb blitCommandEncoder];
[blitEncoder copyFromTexture:srcOps->pTexture
sourceSlice:0
sourceLevel:0
sourceOrigin:MTLOriginMake(srcx, srcy, 0)
sourceSize:MTLSizeMake(w, h, 1)
toBuffer:mtlbuf
destinationOffset:0 /*offset already taken in: pDst = PtrPixelsRow(pDst, dstx, dstInfo.pixelStride)*/
destinationBytesPerRow:w*4
destinationBytesPerImage:byteLength];
[blitEncoder endEncoding];
// Commit and wait for reading complete
[cb commit];
[cb waitUntilCompleted];
// Perform conversion if necessary
BOOL convertFromPre = !RasterFormatInfos[dsttype].isPremult && !srcOps->isOpaque;
if ((dstInfo.scanStride == w * dstInfo.pixelStride) &&
(height == (dstInfo.bounds.y2 - dstInfo.bounds.y1))) {
// mtlbuf.contents have same dimensions as of pDst
copyFromMTLBuffer(pDst, mtlbuf, 0, byteLength, convertFromPre);
} else {
// mtlbuf.contents have smaller dimensions than pDst
// copy each row from mtlbuf.contents at appropriate position in pDst
// Note : pDst is already addjusted for offsets using PtrAddBytes above
NSUInteger rowSize = w * dstInfo.pixelStride;
for (int y = 0; y < height; y++) {
copyFromMTLBuffer(pDst, mtlbuf, y * rowSize, rowSize, convertFromPre);
pDst = PtrAddBytes(pDst, dstInfo.scanStride);
}
}
#ifndef USE_STATIC_BUFFER
[mtlbuf release];
#endif // USE_STATIC_BUFFER
}
SurfaceData_InvokeRelease(env, dstOps, &dstInfo);
}
SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);
}
void
MTLBlitLoops_CopyArea(JNIEnv *env,
MTLContext *mtlc, BMTLSDOps *dstOps,
jint x, jint y, jint width, jint height,
jint dx, jint dy)
{
#ifdef DEBUG
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_CopyArea: bdst=%p [tex=%p] %dx%d | src (%d, %d), %dx%d -> dst (%d, %d)",
dstOps, dstOps->pTexture, ((id<MTLTexture>)dstOps->pTexture).width, ((id<MTLTexture>)dstOps->pTexture).height, x, y, width, height, dx, dy);
#endif //DEBUG
jint texWidth = ((id<MTLTexture>)dstOps->pTexture).width;
jint texHeight = ((id<MTLTexture>)dstOps->pTexture).height;
SurfaceDataBounds srcBounds, dstBounds;
srcBounds.x1 = x;
srcBounds.y1 = y;
srcBounds.x2 = srcBounds.x1 + width;
srcBounds.y2 = srcBounds.y1 + height;
dstBounds.x1 = x + dx;
dstBounds.y1 = y + dy;
dstBounds.x2 = dstBounds.x1 + width;
dstBounds.y2 = dstBounds.y1 + height;
SurfaceData_IntersectBoundsXYXY(&srcBounds, 0, 0, texWidth, texHeight);
SurfaceData_IntersectBoundsXYXY(&dstBounds, 0, 0, texWidth, texHeight);
SurfaceData_IntersectBlitBounds(&dstBounds, &srcBounds, -dx, -dy);
int srcWidth = (srcBounds.x2 - srcBounds.x1);
int srcHeight = (srcBounds.y2 - srcBounds.y1);
if ((srcBounds.x1 < srcBounds.x2 && srcBounds.y1 < srcBounds.y2) &&
(dstBounds.x1 < dstBounds.x2 && dstBounds.y1 < dstBounds.y2))
{
@autoreleasepool {
id<MTLCommandBuffer> cb = [mtlc createCommandBuffer];
id<MTLBlitCommandEncoder> blitEncoder = [cb blitCommandEncoder];
// Create an intrermediate buffer
int totalBuffsize = srcWidth * srcHeight * 4;
id <MTLBuffer> buff = [[mtlc.device newBufferWithLength:totalBuffsize options:MTLResourceStorageModePrivate] autorelease];
[blitEncoder copyFromTexture:dstOps->pTexture
sourceSlice:0 sourceLevel:0 sourceOrigin:MTLOriginMake(srcBounds.x1, srcBounds.y1, 0) sourceSize:MTLSizeMake(srcWidth, srcHeight, 1)
toBuffer:buff destinationOffset:0 destinationBytesPerRow:(srcWidth * 4) destinationBytesPerImage:totalBuffsize];
[blitEncoder copyFromBuffer:buff
sourceOffset:0 sourceBytesPerRow:srcWidth*4 sourceBytesPerImage:totalBuffsize sourceSize:MTLSizeMake(srcWidth, srcHeight, 1)
toTexture:dstOps->pTexture destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(dstBounds.x1, dstBounds.y1, 0)];
[blitEncoder endEncoding];
[cb commit];
}
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLBufImgOps_h_Included
#define MTLBufImgOps_h_Included
#include "MTLContext.h"
@interface MTLRescaleOp : NSObject
- (id)init:(jboolean)isNonPremult factors:(unsigned char *)factors offsets:(unsigned char *)offsets;
- (jfloat *)getScaleFactors;
- (jfloat *)getOffsets;
- (NSString *)getDescription; // creates autorelease string
@property (readonly) jboolean isNonPremult;
@end
@interface MTLConvolveOp : NSObject
- (id)init:(jboolean)edgeZeroFill kernelWidth:(jint)kernelWidth
kernelHeight:(jint)kernelHeight
srcWidth:(jint)srcWidth
srcHeight:(jint)srcHeight
kernel:(unsigned char *)kernel
device:(id<MTLDevice>)device;
- (void) dealloc;
- (id<MTLBuffer>) getBuffer;
- (const float *) getImgEdge;
- (NSString *)getDescription; // creates autorelease string
@property (readonly) jboolean isEdgeZeroFill;
@property (readonly) int kernelSize;
@end
@interface MTLLookupOp : NSObject
- (id)init:(jboolean)nonPremult shortData:(jboolean)shortData
numBands:(jint)numBands
bandLength:(jint)bandLength
offset:(jint)offset
tableValues:(void *)tableValues
device:(id<MTLDevice>)device;
- (void) dealloc;
- (jfloat *)getOffset;
- (id<MTLTexture>) getLookupTexture;
- (NSString *)getDescription; // creates autorelease string
@property (readonly) jboolean isUseSrcAlpha;
@property (readonly) jboolean isNonPremult;
@end
#endif /* MTLBufImgOps_h_Included */

View file

@ -0,0 +1,223 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include <jlong.h>
#include "MTLBufImgOps.h"
#include "MTLContext.h"
#include "MTLRenderQueue.h"
#include "MTLSurfaceDataBase.h"
#include "GraphicsPrimitiveMgr.h"
@implementation MTLRescaleOp {
jboolean _isNonPremult;
jfloat _normScaleFactors[4];
jfloat _normOffsets[4];
}
-(jfloat *) getScaleFactors {
return _normScaleFactors;
}
-(jfloat *) getOffsets {
return _normOffsets;
}
- (id)init:(jboolean)isNonPremult factors:(unsigned char *)factors offsets:(unsigned char *)offsets {
self = [super init];
if (self) {
J2dTraceLn1(J2D_TRACE_INFO,"Created MTLRescaleOp: isNonPremult=%d", isNonPremult);
_isNonPremult = isNonPremult;
_normScaleFactors[0] = NEXT_FLOAT(factors);
_normScaleFactors[1] = NEXT_FLOAT(factors);
_normScaleFactors[2] = NEXT_FLOAT(factors);
_normScaleFactors[3] = NEXT_FLOAT(factors);
_normOffsets[0] = NEXT_FLOAT(offsets);
_normOffsets[1] = NEXT_FLOAT(offsets);
_normOffsets[2] = NEXT_FLOAT(offsets);
_normOffsets[3] = NEXT_FLOAT(offsets);
}
return self;
}
- (NSString *)getDescription {
return [NSString stringWithFormat:@"rescale: nonPremult=%d", _isNonPremult];
}
@end
@implementation MTLConvolveOp {
id<MTLBuffer> _buffer;
float _imgEdge[4];
int _kernelSize;
jboolean _isEdgeZeroFill;
}
- (id)init:(jboolean)edgeZeroFill kernelWidth:(jint)kernelWidth
kernelHeight:(jint)kernelHeight
srcWidth:(jint)srcWidth
srcHeight:(jint)srcHeight
kernel:(unsigned char *)kernel
device:(id<MTLDevice>)device {
self = [super init];
if (self) {
J2dTraceLn2(J2D_TRACE_INFO,"Created MTLConvolveOp: kernelW=%d kernelH=%d", kernelWidth, kernelHeight);
_isEdgeZeroFill = edgeZeroFill;
_kernelSize = kernelWidth * kernelHeight;
_buffer = [device newBufferWithLength:_kernelSize*sizeof(vector_float3) options:MTLResourceStorageModeShared];
float * kernelVals = [_buffer contents];
int kIndex = 0;
for (int i = -kernelHeight/2; i < kernelHeight/2+1; i++) {
for (int j = -kernelWidth/2; j < kernelWidth/2+1; j++) {
kernelVals[kIndex+0] = j/(float)srcWidth;
kernelVals[kIndex+1] = i/(float)srcHeight;
kernelVals[kIndex+2] = NEXT_FLOAT(kernel);
kIndex += 3;
}
}
_imgEdge[0] = (kernelWidth/2)/(float)srcWidth;
_imgEdge[1] = (kernelHeight/2)/(float)srcHeight;
_imgEdge[2] = 1 - _imgEdge[0];
_imgEdge[3] = 1 - _imgEdge[1];
}
return self;
}
- (void) dealloc {
[_buffer release];
[super dealloc];
}
- (id<MTLBuffer>) getBuffer {
return _buffer;
}
- (const float *) getImgEdge {
return _imgEdge;
}
- (NSString *)getDescription {
return [NSString stringWithFormat:@"convolve: isEdgeZeroFill=%d", _isEdgeZeroFill];
}
@end
@implementation MTLLookupOp {
float _offset[4];
jboolean _isUseSrcAlpha;
jboolean _isNonPremult;
id<MTLTexture> _lookupTex;
}
- (id)init:(jboolean)nonPremult shortData:(jboolean)shortData
numBands:(jint)numBands
bandLength:(jint)bandLength
offset:(jint)offset
tableValues:(void *)tableValues
device:(id<MTLDevice>)device {
self = [super init];
if (self) {
J2dTraceLn4(J2D_TRACE_INFO,"Created MTLLookupOp: short=%d num=%d len=%d off=%d",
shortData, numBands, bandLength, offset);
_isUseSrcAlpha = numBands != 4;
_isNonPremult = nonPremult;
_offset[0] = offset / 255.0f;
_offset[1] = _offset[0];
_offset[2] = _offset[0];
_offset[3] = _offset[0];
MTLTextureDescriptor *textureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatA8Unorm
width:(NSUInteger)256
height:(NSUInteger)4
mipmapped:NO];
_lookupTex = [device newTextureWithDescriptor:textureDescriptor];
void *bands[4];
for (int i = 0; i < 4; i++) {
bands[i] = NULL;
}
int bytesPerElem = (shortData ? 2 : 1);
if (numBands == 1) {
// replicate the single band for R/G/B; alpha band is unused
for (int i = 0; i < 3; i++) {
bands[i] = tableValues;
}
bands[3] = NULL;
} else if (numBands == 3) {
// user supplied band for each of R/G/B; alpha band is unused
for (int i = 0; i < 3; i++) {
bands[i] = PtrPixelsBand(tableValues, i, bandLength, bytesPerElem);
}
bands[3] = NULL;
} else if (numBands == 4) {
// user supplied band for each of R/G/B/A
for (int i = 0; i < 4; i++) {
bands[i] = PtrPixelsBand(tableValues, i, bandLength, bytesPerElem);
}
}
for (int i = 0; i < 4; i++) {
if (bands[i] == NULL)
continue;
MTLRegion region = {
{0, i, 0},
{bandLength, 1,1}
};
[_lookupTex replaceRegion:region
mipmapLevel:0
withBytes:bands[i]
bytesPerRow:bandLength*bytesPerElem];
}
}
return self;
}
- (void) dealloc {
[_lookupTex release];
[super dealloc];
}
- (jfloat *) getOffset {
return _offset;
}
- (id<MTLTexture>) getLookupTexture {
return _lookupTex;
}
- (NSString *)getDescription {
return [NSString stringWithFormat:@"lookup: offset=%f", _offset[0]];
}
@end

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#import <limits.h>
#ifndef MTLClip_h_Included
#define MTLClip_h_Included
#import <Metal/Metal.h>
#include <jni.h>
#include "MTLSurfaceDataBase.h"
enum Clip {
NO_CLIP,
RECT_CLIP,
SHAPE_CLIP
};
@class MTLContext;
@class MTLPipelineStatesStorage;
/**
* The MTLClip class represents clip mode (rect or stencil)
* */
@interface MTLClip : NSObject
@property (readonly) id<MTLTexture> stencilTextureRef;
@property (readonly) BOOL stencilMaskGenerationInProgress;
@property NSUInteger shapeX;
@property NSUInteger shapeY;
@property NSUInteger shapeWidth;
@property NSUInteger shapeHeight;
- (id)init;
- (BOOL)isEqual:(MTLClip *)other; // used to compare requested with cached
- (void)copyFrom:(MTLClip *)other; // used to save cached
- (BOOL)isShape;
- (BOOL)isRect;
// returns null when clipType != RECT_CLIP
- (const MTLScissorRect *) getRect;
- (void)reset;
- (void)resetStencilState;
- (void)setClipRectX1:(jint)x1 Y1:(jint)y1 X2:(jint)x2 Y2:(jint)y2;
- (void)beginShapeClip:(BMTLSDOps *)dstOps context:(MTLContext *)mtlc;
- (void)endShapeClip:(BMTLSDOps *)dstOps context:(MTLContext *)mtlc;
- (void)setScissorOrStencil:(id<MTLRenderCommandEncoder>)encoder
destWidth:(NSUInteger)dw
destHeight:(NSUInteger)dh
device:(id<MTLDevice>)device;
- (void)setMaskGenerationPipelineState:(id<MTLRenderCommandEncoder>)encoder
destWidth:(NSUInteger)dw
destHeight:(NSUInteger)dh
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage;
- (NSString *)getDescription __unused; // creates autorelease string
@end
#endif // MTLClip_h_Included

View file

@ -0,0 +1,372 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include "MTLClip.h"
#include "MTLContext.h"
#include "MTLStencilManager.h"
#include "common.h"
static MTLRenderPipelineDescriptor * templateStencilPipelineDesc = nil;
static void initTemplatePipelineDescriptors() {
if (templateStencilPipelineDesc != nil)
return;
MTLVertexDescriptor *vertDesc = [[MTLVertexDescriptor new] autorelease];
vertDesc.attributes[VertexAttributePosition].format = MTLVertexFormatFloat2;
vertDesc.attributes[VertexAttributePosition].offset = 0;
vertDesc.attributes[VertexAttributePosition].bufferIndex = MeshVertexBuffer;
vertDesc.layouts[MeshVertexBuffer].stride = sizeof(struct Vertex);
vertDesc.layouts[MeshVertexBuffer].stepRate = 1;
vertDesc.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;
templateStencilPipelineDesc = [MTLRenderPipelineDescriptor new];
templateStencilPipelineDesc.sampleCount = 1;
templateStencilPipelineDesc.vertexDescriptor = vertDesc;
templateStencilPipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatR8Uint; // A byte buffer format
templateStencilPipelineDesc.label = @"template_stencil";
}
@implementation MTLClip {
jint _clipType;
MTLScissorRect _clipRect;
MTLContext* _mtlc;
BMTLSDOps* _dstOps;
BOOL _stencilMaskGenerationInProgress;
BOOL _clipReady;
MTLOrigin _clipShapeOrigin;
MTLSize _clipShapeSize;
}
- (id)init {
self = [super init];
if (self) {
_clipType = NO_CLIP;
_mtlc = nil;
_dstOps = NULL;
_stencilMaskGenerationInProgress = NO;
_clipReady = NO;
}
return self;
}
- (BOOL)isEqual:(MTLClip *)other {
if (self == other)
return YES;
if (_stencilMaskGenerationInProgress == JNI_TRUE)
return other->_stencilMaskGenerationInProgress == JNI_TRUE;
if (_clipType != other->_clipType)
return NO;
if (_clipType == NO_CLIP)
return YES;
if (_clipType == RECT_CLIP) {
return _clipRect.x == other->_clipRect.x && _clipRect.y == other->_clipRect.y
&& _clipRect.width == other->_clipRect.width && _clipRect.height == other->_clipRect.height;
}
// NOTE: can compare stencil-data pointers here
return YES;
}
- (BOOL)isShape {
return _clipType == SHAPE_CLIP;
}
- (BOOL)isRect __unused {
return _clipType == RECT_CLIP;
}
- (const MTLScissorRect * _Nullable) getRect {
return _clipType == RECT_CLIP ? &_clipRect : NULL;
}
- (void)copyFrom:(MTLClip *)other {
_clipType = other->_clipType;
_stencilMaskGenerationInProgress = other->_stencilMaskGenerationInProgress;
_dstOps = other->_dstOps;
_mtlc = other->_mtlc;
if (other->_clipType == RECT_CLIP) {
_clipRect = other->_clipRect;
}
}
- (void)reset {
_clipType = NO_CLIP;
_stencilMaskGenerationInProgress = JNI_FALSE;
}
- (void)setClipRectX1:(jint)x1 Y1:(jint)y1 X2:(jint)x2 Y2:(jint)y2 {
if (_clipType == SHAPE_CLIP) {
_dstOps = NULL;
}
if (x1 >= x2 || y1 >= y2) {
J2dTraceLn4(J2D_TRACE_ERROR, "MTLClip.setClipRect: invalid rect: x1=%d y1=%d x2=%d y2=%d", x1, y1, x2, y2);
_clipType = NO_CLIP;
}
const jint width = x2 - x1;
const jint height = y2 - y1;
J2dTraceLn4(J2D_TRACE_INFO, "MTLClip.setClipRect: x=%d y=%d w=%d h=%d", x1, y1, width, height);
_clipRect.x = (NSUInteger)((x1 >= 0) ? x1 : 0);
_clipRect.y = (NSUInteger)((y1 >= 0) ? y1 : 0);
_clipRect.width = (NSUInteger)((width >= 0) ? width : 0);
_clipRect.height = (NSUInteger)((height >= 0) ? height : 0);
_clipType = RECT_CLIP;
}
- (void)beginShapeClip:(BMTLSDOps *)dstOps context:(MTLContext *)mtlc {
_stencilMaskGenerationInProgress = YES;
if ((dstOps == NULL) || (dstOps->pStencilData == NULL) || (dstOps->pStencilTexture == NULL)) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext_beginShapeClip: stencil render target or stencil texture is NULL");
return;
}
// Clear the stencil render buffer & stencil texture
@autoreleasepool {
if (dstOps->width <= 0 || dstOps->height <= 0) {
return;
}
_clipShapeSize = MTLSizeMake(0, 0, 1);
_clipShapeOrigin = MTLOriginMake(0, 0, 0);
MTLRenderPassDescriptor* clearPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
// set color buffer properties
clearPassDescriptor.colorAttachments[0].texture = dstOps->pStencilData;
clearPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
clearPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0f, 0.0f,0.0f, 0.0f);
id<MTLCommandBuffer> commandBuf = [mtlc createCommandBuffer];
id <MTLRenderCommandEncoder> clearEncoder = [commandBuf renderCommandEncoderWithDescriptor:clearPassDescriptor];
[clearEncoder endEncoding];
[commandBuf commit];
}
}
- (void)endShapeClip:(BMTLSDOps *)dstOps context:(MTLContext *)mtlc {
if ((dstOps == NULL) || (dstOps->pStencilData == NULL) || (dstOps->pStencilTexture == NULL)) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext_endShapeClip: stencil render target or stencil texture is NULL");
return;
}
// Complete the rendering to the stencil buffer ------------
[mtlc.encoderManager endEncoder];
MTLCommandBufferWrapper* cbWrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandBuffer = [cbWrapper getCommandBuffer];
[commandBuffer addCompletedHandler:^(id <MTLCommandBuffer> c) {
[cbWrapper release];
}];
[commandBuffer commit];
// Now the stencil data is ready, this needs to be used while rendering further
@autoreleasepool {
if (_clipShapeSize.width > 0 && _clipShapeSize.height > 0) {
id<MTLCommandBuffer> cb = [mtlc createCommandBuffer];
id<MTLBlitCommandEncoder> blitEncoder = [cb blitCommandEncoder];
[blitEncoder copyFromTexture:dstOps->pStencilData
sourceSlice:0
sourceLevel:0
sourceOrigin:_clipShapeOrigin
sourceSize:_clipShapeSize
toBuffer:dstOps->pStencilDataBuf
destinationOffset:0
destinationBytesPerRow:_clipShapeSize.width
destinationBytesPerImage:_clipShapeSize.width*_clipShapeSize.height];
[blitEncoder endEncoding];
[cb commit];
}
}
_stencilMaskGenerationInProgress = JNI_FALSE;
_mtlc = mtlc;
_dstOps = dstOps;
_clipType = SHAPE_CLIP;
_clipReady = NO;
}
- (void)setMaskGenerationPipelineState:(id<MTLRenderCommandEncoder>)encoder
destWidth:(NSUInteger)dw
destHeight:(NSUInteger)dh
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
{
initTemplatePipelineDescriptors();
// A PipelineState for rendering to a byte-buffered texture that will be used as a stencil
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:templateStencilPipelineDesc
vertexShaderId:@"vert_stencil"
fragmentShaderId:@"frag_stencil"];
[encoder setRenderPipelineState:pipelineState];
struct FrameUniforms uf; // color is ignored while writing to stencil buffer
memset(&uf, 0, sizeof(uf));
[encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
_clipRect.x = 0;
_clipRect.y = 0;
_clipRect.width = dw;
_clipRect.height = dh;
[encoder setScissorRect:_clipRect]; // just for insurance (to reset possible clip from previous drawing)
}
- (void)setScissorOrStencil:(id<MTLRenderCommandEncoder>)encoder
destWidth:(NSUInteger)dw
destHeight:(NSUInteger)dh
device:(id<MTLDevice>)device
{
if (_clipType == NO_CLIP || _clipType == SHAPE_CLIP) {
_clipRect.x = 0;
_clipRect.y = 0;
_clipRect.width = dw;
_clipRect.height = dh;
}
// Clamping clip rect to the destination area
MTLScissorRect rect = _clipRect;
if (rect.x > dw) {
rect.x = dw;
}
if (rect.y > dh) {
rect.y = dh;
}
if (rect.x + rect.width > dw) {
rect.width = dw - rect.x;
}
if (rect.y + rect.height > dh) {
rect.height = dh - rect.y;
}
[encoder setScissorRect:rect];
if (_clipType == NO_CLIP || _clipType == RECT_CLIP) {
// NOTE: It seems that we can use the same encoder (with disabled stencil test) when mode changes from SHAPE to RECT.
// But [encoder setDepthStencilState:nil] causes crash, so we have to recreate encoder in such case.
// So we can omit [encoder setDepthStencilState:nil] here.
return;
}
if (_clipType == SHAPE_CLIP) {
// Enable stencil test
[encoder setDepthStencilState:_mtlc.stencilManager.stencilState];
[encoder setStencilReferenceValue:0xFF];
}
}
- (NSString *)getDescription __unused {
if (_clipType == NO_CLIP) {
return @"NO_CLIP";
}
if (_clipType == RECT_CLIP) {
return [NSString stringWithFormat:@"RECT_CLIP [%lu,%lu - %lux%lu]", _clipRect.x, _clipRect.y, _clipRect.width, _clipRect.height];
}
return [NSString stringWithFormat:@"SHAPE_CLIP"];
}
- (id<MTLTexture>) stencilTextureRef {
if (_dstOps == NULL) return nil;
id <MTLTexture> _stencilTextureRef = _dstOps->pStencilTexture;
if (!_clipReady) {
@autoreleasepool {
MTLRenderPassDescriptor* clearPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
// set color buffer properties
clearPassDescriptor.stencilAttachment.texture = _stencilTextureRef;
clearPassDescriptor.stencilAttachment.clearStencil = 0;
clearPassDescriptor.stencilAttachment.loadAction = MTLLoadActionClear;
id<MTLCommandBuffer> commandBuf = [_mtlc createCommandBuffer];
id <MTLRenderCommandEncoder> clearEncoder = [commandBuf renderCommandEncoderWithDescriptor:clearPassDescriptor];
[clearEncoder endEncoding];
[commandBuf commit];
id <MTLCommandBuffer> cb = [_mtlc createCommandBuffer];
id <MTLBlitCommandEncoder> blitEncoder = [cb blitCommandEncoder];
id <MTLBuffer> _stencilDataBufRef = _dstOps->pStencilDataBuf;
[blitEncoder copyFromBuffer:_stencilDataBufRef
sourceOffset:0
sourceBytesPerRow:_clipShapeSize.width
sourceBytesPerImage:_clipShapeSize.width * _clipShapeSize.height
sourceSize:_clipShapeSize
toTexture:_stencilTextureRef
destinationSlice:0
destinationLevel:0
destinationOrigin:_clipShapeOrigin];
[blitEncoder endEncoding];
[cb commit];
_clipReady = YES;
}
}
return _stencilTextureRef;
}
- (NSUInteger)shapeX {
return _clipShapeOrigin.x;
}
- (void)setShapeX:(NSUInteger)shapeX {
_clipShapeOrigin.x = shapeX;
}
- (NSUInteger)shapeY {
return _clipShapeOrigin.y;
}
- (void)setShapeY:(NSUInteger)shapeY {
_clipShapeOrigin.y = shapeY;
}
- (NSUInteger)shapeWidth {
return _clipShapeSize.width;
}
- (void)setShapeWidth:(NSUInteger)shapeWidth {
_clipShapeSize.width = shapeWidth;
}
- (NSUInteger)shapeHeight {
return _clipShapeSize.height;
}
- (void)setShapeHeight:(NSUInteger)shapeHeight {
_clipShapeSize.height = shapeHeight;
}
@end

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLComposite_h_Included
#define MTLComposite_h_Included
#import <Metal/Metal.h>
#include <jni.h>
#define FLT_EPS (0.001f)
#define FLT_LT(x,y) ((x) < (y) - FLT_EPS)
#define FLT_GE(x,y) ((x) >= (y) - FLT_EPS)
#define FLT_LE(x,y) ((x) <= (y) + FLT_EPS)
#define FLT_GT(x,y) ((x) > (y) + FLT_EPS)
/**
* The MTLComposite class represents composite mode
* */
@interface MTLComposite : NSObject
- (id)init;
- (BOOL)isEqual:(MTLComposite *)other; // used to compare requested with cached
- (void)copyFrom:(MTLComposite *)other; // used to save cached
- (void)setRule:(jint)rule; // sets extraAlpha=1
- (void)setRule:(jint)rule extraAlpha:(jfloat)extraAlpha;
- (void)reset;
- (void)setXORComposite:(jint)color;
- (void)setAlphaComposite:(jint)rule;
- (jint)getCompositeState;
- (jint)getRule;
- (jint)getXorColor;
- (jfloat)getExtraAlpha;
- (NSString *)getDescription; // creates autorelease string
@end
#endif // MTLComposite_h_Included

View file

@ -0,0 +1,192 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include "MTLComposite.h"
#include "sun_java2d_SunGraphics2D.h"
#include "java_awt_AlphaComposite.h"
@implementation MTLComposite {
jint _compState;
jint _compositeRule;
jint _xorPixel;
jfloat _extraAlpha;
}
- (id)init {
self = [super init];
if (self) {
_compositeRule = -1;
_compState = -1;
_xorPixel = 0;
_extraAlpha = 1;
}
return self;
}
- (BOOL)isEqual:(MTLComposite *)other {
if (self == other)
return YES;
if (_compState == other->_compState) {
if (_compState == sun_java2d_SunGraphics2D_COMP_XOR) {
return _xorPixel == other->_xorPixel;
}
if (_compState == sun_java2d_SunGraphics2D_COMP_ALPHA) {
return _extraAlpha == other->_extraAlpha
&& _compositeRule == other->_compositeRule;
}
}
return NO;
}
- (void)copyFrom:(MTLComposite *)other {
_extraAlpha = other->_extraAlpha;
_compositeRule = other->_compositeRule;
_compState = other->_compState;
_xorPixel = other->_xorPixel;
}
- (void)setRule:(jint)rule {
_extraAlpha = 1.f;
_compositeRule = rule;
}
- (void)setRule:(jint)rule extraAlpha:(jfloat)extraAlpha {
_compState = sun_java2d_SunGraphics2D_COMP_ALPHA;
_extraAlpha = extraAlpha;
_compositeRule = rule;
}
- (void)reset {
_compState = sun_java2d_SunGraphics2D_COMP_ISCOPY;
_compositeRule = java_awt_AlphaComposite_SRC;
_extraAlpha = 1.f;
}
- (jint)getRule {
return _compositeRule;
}
- (NSString *)getDescription {
const char * result = "";
switch (_compositeRule) {
case java_awt_AlphaComposite_CLEAR:
{
result = "CLEAR";
}
break;
case java_awt_AlphaComposite_SRC:
{
result = "SRC";
}
break;
case java_awt_AlphaComposite_DST:
{
result = "DST";
}
break;
case java_awt_AlphaComposite_SRC_OVER:
{
result = "SRC_OVER";
}
break;
case java_awt_AlphaComposite_DST_OVER:
{
result = "DST_OVER";
}
break;
case java_awt_AlphaComposite_SRC_IN:
{
result = "SRC_IN";
}
break;
case java_awt_AlphaComposite_DST_IN:
{
result = "DST_IN";
}
break;
case java_awt_AlphaComposite_SRC_OUT:
{
result = "SRC_OUT";
}
break;
case java_awt_AlphaComposite_DST_OUT:
{
result = "DST_OUT";
}
break;
case java_awt_AlphaComposite_SRC_ATOP:
{
result = "SRC_ATOP";
}
break;
case java_awt_AlphaComposite_DST_ATOP:
{
result = "DST_ATOP";
}
break;
case java_awt_AlphaComposite_XOR:
{
result = "XOR";
}
break;
default:
result = "UNKNOWN";
break;
}
const double epsilon = 0.001f;
if (fabs(_extraAlpha - 1.f) > epsilon) {
return [NSString stringWithFormat:@"%s [%1.2f]", result, _extraAlpha];
}
return [NSString stringWithFormat:@"%s", result];
}
- (void)setAlphaComposite:(jint)rule {
_compState = sun_java2d_SunGraphics2D_COMP_ALPHA;
[self setRule:rule];
}
- (jint)getCompositeState {
return _compState;
}
-(void)setXORComposite:(jint)color {
_compState = sun_java2d_SunGraphics2D_COMP_XOR;
_xorPixel = color;
}
-(jint)getXorColor {
return _xorPixel;
}
- (jfloat)getExtraAlpha {
return _extraAlpha;
}
@end

View file

@ -0,0 +1,244 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLContext_h_Included
#define MTLContext_h_Included
#include "sun_java2d_pipe_BufferedContext.h"
#include "sun_java2d_metal_MTLContext_MTLContextCaps.h"
#import <Metal/Metal.h>
#include "MTLTexturePool.h"
#include "MTLPipelineStatesStorage.h"
#include "MTLTransform.h"
#include "MTLComposite.h"
#include "MTLPaints.h"
#include "MTLClip.h"
#include "EncoderManager.h"
#include "MTLSamplerManager.h"
@class MTLStencilManager;
// Constant from
// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
#define MTL_GPU_FAMILY_MAC_TXT_SIZE 16384
/**
* The MTLCommandBufferWrapper class contains command buffer and
* associated resources that will be released in completion handler
* */
@interface MTLCommandBufferWrapper : NSObject
- (id<MTLCommandBuffer>) getCommandBuffer;
- (void) onComplete; // invoked from completion handler in some pooled thread
- (void) registerPooledTexture:(MTLPooledTextureHandle *)handle;
@end
/**
* The MTLContext class contains cached state relevant to the native
* MTL context stored within the native ctxInfo field. Each Java-level
* MTLContext object is associated with a native-level MTLContext class.
* */
@interface MTLContext : NSObject
@property (readonly) MTLComposite * composite;
@property (readwrite, retain) MTLPaint * paint;
@property (readonly) MTLTransform * transform;
@property (readonly) MTLClip * clip;
@property jint textureFunction;
@property jboolean vertexCacheEnabled;
@property jboolean aaEnabled;
@property (readonly, strong) id<MTLDevice> device;
@property (strong) id<MTLCommandQueue> commandQueue;
@property (strong) id<MTLCommandQueue> blitCommandQueue;
@property (strong) id<MTLBuffer> vertexBuffer;
@property (readonly) EncoderManager * encoderManager;
@property (readonly) MTLSamplerManager * samplerManager;
@property (readonly) MTLStencilManager * stencilManager;
@property (strong)MTLPipelineStatesStorage* pipelineStateStorage;
@property (strong)MTLTexturePool* texturePool;
- (MTLCommandBufferWrapper *) getCommandBufferWrapper; // creates command buffer wrapper (when doesn't exist)
- (MTLCommandBufferWrapper *) pullCommandBufferWrapper; // returns current buffer wrapper with loosing object ownership
/**
* Fetches the MTLContext associated with the given destination surface,
* makes the context current for those surfaces, updates the destination
* viewport, and then returns a pointer to the MTLContext.
*/
+ (MTLContext*) setSurfacesEnv:(JNIEnv*)env src:(jlong)pSrc dst:(jlong)pDst;
- (id)initWithDevice:(id<MTLDevice>)d shadersLib:(NSString*)shadersLib;
- (void)dealloc;
/**
* Resets the current clip state (disables both scissor and depth tests).
*/
- (void)resetClip;
/**
* Sets the Metal scissor bounds to the provided rectangular clip bounds.
*/
- (void)setClipRectX1:(jint)x1 Y1:(jint)y1 X2:(jint)x2 Y2:(jint)y2;
- (const MTLScissorRect *)clipRect;
/**
* Sets up a complex (shape) clip using the Metal stencil buffer. This
* method prepares the stencil buffer so that the clip Region spans can
* be "rendered" into it. The stencil buffer is first cleared, then the
* stencil func is setup so that when we render the clip spans,
* nothing is rendered into the color buffer, but for each pixel that would
* be rendered, a 0xFF value is placed into that location in the stencil
* buffer. With stencil test enabled, pixels will only be rendered into the
* color buffer if the corresponding value at that (x,y) location in the
* stencil buffer is equal to 0xFF.
*/
- (void)beginShapeClip:(BMTLSDOps *)dstOps;
/**
* Finishes setting up the shape clip by resetting the stencil func
* so that future rendering operations will once again be encoded for the
* color buffer (while respecting the clip set up in the stencil buffer).
*/
- (void)endShapeClip:(BMTLSDOps *)dstOps;
/**
* Resets all Metal compositing state (disables blending and logic
* operations).
*/
- (void)resetComposite;
/**
* Initializes the Metal blending state. XOR mode is disabled and the
* appropriate blend functions are setup based on the AlphaComposite rule
* constant.
*/
- (void)setAlphaCompositeRule:(jint)rule extraAlpha:(jfloat)extraAlpha
flags:(jint)flags;
/**
* Returns autorelease string with composite description (for debugging only)
*/
- (NSString*)getCompositeDescription;
/**
* Returns autorelease string with paint description (for debugging only)
*/
- (NSString*)getPaintDescription;
/**
* Initializes the Metal logic op state to XOR mode. Blending is disabled
* before enabling logic op mode. The XOR pixel value will be applied
* later in the MTLContext_SetColor() method.
*/
- (void)setXorComposite:(jint)xorPixel;
- (jboolean)useXORComposite;
/**
* Resets the Metal transform state back to the identity matrix.
*/
- (void)resetTransform;
/**
* Initializes the Metal transform state by setting the modelview transform
* using the given matrix parameters.
*
* REMIND: it may be worthwhile to add serial id to AffineTransform, so we
* could do a quick check to see if the xform has changed since
* last time... a simple object compare won't suffice...
*/
- (void)setTransformM00:(jdouble) m00 M10:(jdouble) m10
M01:(jdouble) m01 M11:(jdouble) m11
M02:(jdouble) m02 M12:(jdouble) m12;
- (void)reset;
- (void)resetPaint;
- (void)setColorPaint:(int)pixel;
- (void)setGradientPaintUseMask:(jboolean)useMask
cyclic:(jboolean)cyclic
p0:(jdouble)p0
p1:(jdouble)p1
p3:(jdouble)p3
pixel1:(jint)pixel1
pixel2:(jint) pixel2;
- (void)setLinearGradientPaint:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jint)cycleMethod
numStops:(jint)numStops
p0:(jfloat)p0
p1:(jfloat)p1
p3:(jfloat)p3
fractions:(jfloat *)fractions
pixels:(jint *)pixels;
- (void)setRadialGradientPaint:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jboolean)cycleMethod
numStops:(jint)numStops
m00:(jfloat)m00
m01:(jfloat)m01
m02:(jfloat)m02
m10:(jfloat)m10
m11:(jfloat)m11
m12:(jfloat)m12
focusX:(jfloat)focusX
fractions:(void *)fractions
pixels:(void *)pixels;
- (void)setTexturePaint:(jboolean)useMask
pSrcOps:(jlong)pSrcOps
filter:(jboolean)filter
xp0:(jdouble)xp0
xp1:(jdouble)xp1
xp3:(jdouble)xp3
yp0:(jdouble)yp0
yp1:(jdouble)yp1
yp3:(jdouble)yp3;
// Sets current image conversion operation (instance of MTLConvolveOp, MTLRescaleOp, MTLLookupOp).
// Used only in MTLIsoBlit (to blit image with some conversion). Pattern of usage: enableOp -> IsoBlit -> disableOp.
// TODO: Need to remove it from MTLContext and pass it as an argument for IsoBlit (because it's more
// simple and clear)
-(void)setBufImgOp:(NSObject*)bufImgOp;
-(NSObject*)getBufImgOp;
- (id<MTLCommandBuffer>)createCommandBuffer;
- (id<MTLCommandBuffer>)createBlitCommandBuffer;
@end
/**
* See BufferedContext.java for more on these flags...
*/
#define MTLC_NO_CONTEXT_FLAGS \
sun_java2d_pipe_BufferedContext_NO_CONTEXT_FLAGS
#define MTLC_SRC_IS_OPAQUE \
sun_java2d_pipe_BufferedContext_SRC_IS_OPAQUE
#define MTLC_USE_MASK \
sun_java2d_pipe_BufferedContext_USE_MASK
#endif /* MTLContext_h_Included */

View file

@ -0,0 +1,492 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include <stdlib.h>
#include "sun_java2d_SunGraphics2D.h"
#include "jlong.h"
#import "MTLContext.h"
#include "MTLRenderQueue.h"
#import "MTLSamplerManager.h"
#import "MTLStencilManager.h"
extern jboolean MTLSD_InitMTLWindow(JNIEnv *env, MTLSDOps *mtlsdo);
static struct TxtVertex verts[PGRAM_VERTEX_COUNT] = {
{{-1.0, 1.0}, {0.0, 0.0}},
{{1.0, 1.0}, {1.0, 0.0}},
{{1.0, -1.0}, {1.0, 1.0}},
{{1.0, -1.0}, {1.0, 1.0}},
{{-1.0, -1.0}, {0.0, 1.0}},
{{-1.0, 1.0}, {0.0, 0.0}}
};
MTLTransform* tempTransform = nil;
@implementation MTLCommandBufferWrapper {
id<MTLCommandBuffer> _commandBuffer;
NSMutableArray * _pooledTextures;
NSLock* _lock;
}
- (id) initWithCommandBuffer:(id<MTLCommandBuffer>)cmdBuf {
self = [super init];
if (self) {
_commandBuffer = [cmdBuf retain];
_pooledTextures = [[NSMutableArray alloc] init];
_lock = [[NSLock alloc] init];
}
return self;
}
- (id<MTLCommandBuffer>) getCommandBuffer {
return _commandBuffer;
}
- (void) onComplete { // invoked from completion handler in some pooled thread
[_lock lock];
@try {
for (int c = 0; c < [_pooledTextures count]; ++c)
[[_pooledTextures objectAtIndex:c] releaseTexture];
[_pooledTextures removeAllObjects];
} @finally {
[_lock unlock];
}
}
- (void) registerPooledTexture:(MTLPooledTextureHandle *)handle {
[_lock lock];
@try {
[_pooledTextures addObject:handle];
} @finally {
[_lock unlock];
}
}
- (void) dealloc {
[self onComplete];
[_pooledTextures release];
_pooledTextures = nil;
[_commandBuffer release];
_commandBuffer = nil;
[_lock release];
_lock = nil;
[super dealloc];
}
@end
@implementation MTLContext {
MTLCommandBufferWrapper * _commandBufferWrapper;
MTLComposite * _composite;
MTLPaint * _paint;
MTLTransform * _transform;
MTLTransform * _tempTransform;
MTLClip * _clip;
NSObject* _bufImgOp; // TODO: pass as parameter of IsoBlit
EncoderManager * _encoderManager;
MTLSamplerManager * _samplerManager;
MTLStencilManager * _stencilManager;
}
@synthesize textureFunction,
vertexCacheEnabled, aaEnabled, device, pipelineStateStorage,
commandQueue, blitCommandQueue, vertexBuffer,
texturePool, paint=_paint, encoderManager=_encoderManager,
samplerManager=_samplerManager, stencilManager=_stencilManager;
extern void initSamplers(id<MTLDevice> device);
- (id)initWithDevice:(id<MTLDevice>)d shadersLib:(NSString*)shadersLib {
self = [super init];
if (self) {
// Initialization code here.
device = d;
pipelineStateStorage = [[MTLPipelineStatesStorage alloc] initWithDevice:device shaderLibPath:shadersLib];
if (pipelineStateStorage == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext.initWithDevice(): Failed to initialize MTLPipelineStatesStorage.");
return nil;
}
texturePool = [[MTLTexturePool alloc] initWithDevice:device];
vertexBuffer = [device newBufferWithBytes:verts
length:sizeof(verts)
options:MTLResourceCPUCacheModeDefaultCache];
_encoderManager = [[EncoderManager alloc] init];
[_encoderManager setContext:self];
_samplerManager = [[MTLSamplerManager alloc] initWithDevice:device];
_stencilManager = [[MTLStencilManager alloc] initWithDevice:device];
_composite = [[MTLComposite alloc] init];
_paint = [[MTLPaint alloc] init];
_transform = [[MTLTransform alloc] init];
_clip = [[MTLClip alloc] init];
_bufImgOp = nil;
_commandBufferWrapper = nil;
// Create command queue
commandQueue = [device newCommandQueue];
blitCommandQueue = [device newCommandQueue];
_tempTransform = [[MTLTransform alloc] init];
}
return self;
}
- (void)dealloc {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.dealloc");
// TODO : Check that texturePool is completely released.
// texturePool content is released in MTLCommandBufferWrapper.onComplete()
//self.texturePool = nil;
self.vertexBuffer = nil;
self.commandQueue = nil;
self.blitCommandQueue = nil;
self.pipelineStateStorage = nil;
if (_encoderManager != nil) {
[_encoderManager release];
_encoderManager = nil;
}
if (_samplerManager != nil) {
[_samplerManager release];
_samplerManager = nil;
}
if (_stencilManager != nil) {
[_stencilManager release];
_stencilManager = nil;
}
if (_composite != nil) {
[_composite release];
_composite = nil;
}
if (_paint != nil) {
[_paint release];
_paint = nil;
}
if (_transform != nil) {
[_transform release];
_transform = nil;
}
if (_tempTransform != nil) {
[_tempTransform release];
_tempTransform = nil;
}
if (_clip != nil) {
[_clip release];
_clip = nil;
}
[super dealloc];
}
- (void) reset {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLContext : reset");
// Add code for context state reset here
}
- (MTLCommandBufferWrapper *) getCommandBufferWrapper {
if (_commandBufferWrapper == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLContext : commandBuffer is NULL");
// NOTE: Command queues are thread-safe and allow multiple outstanding command buffers to be encoded simultaneously.
_commandBufferWrapper = [[MTLCommandBufferWrapper alloc] initWithCommandBuffer:[self.commandQueue commandBuffer]];// released in [layer blitTexture]
}
return _commandBufferWrapper;
}
- (MTLCommandBufferWrapper *) pullCommandBufferWrapper {
MTLCommandBufferWrapper * result = _commandBufferWrapper;
_commandBufferWrapper = nil;
return result;
}
+ (MTLContext*) setSurfacesEnv:(JNIEnv*)env src:(jlong)pSrc dst:(jlong)pDst {
BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrc);
BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDst);
MTLContext *mtlc = NULL;
if (srcOps == NULL || dstOps == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext_SetSurfaces: ops are null");
return NULL;
}
J2dTraceLn6(J2D_TRACE_VERBOSE, "MTLContext_SetSurfaces: bsrc=%p (tex=%p type=%d), bdst=%p (tex=%p type=%d)", srcOps, srcOps->pTexture, srcOps->drawableType, dstOps, dstOps->pTexture, dstOps->drawableType);
if (dstOps->drawableType == MTLSD_TEXTURE) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLContext_SetSurfaces: texture cannot be used as destination");
return NULL;
}
if (dstOps->drawableType == MTLSD_UNDEFINED) {
// initialize the surface as an MTLSD_WINDOW
if (!MTLSD_InitMTLWindow(env, dstOps)) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLContext_SetSurfaces: could not init MTL window");
return NULL;
}
}
// make the context current
MTLSDOps *dstMTLOps = (MTLSDOps *)dstOps->privOps;
mtlc = dstMTLOps->configInfo->context;
if (mtlc == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLContext_SetSurfaces: could not make context current");
return NULL;
}
return mtlc;
}
- (void)resetClip {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.resetClip");
[_clip reset];
}
- (void)setClipRectX1:(jint)x1 Y1:(jint)y1 X2:(jint)x2 Y2:(jint)y2 {
J2dTraceLn4(J2D_TRACE_INFO, "MTLContext.setClipRect: %d,%d - %d,%d", x1, y1, x2, y2);
[_clip setClipRectX1:x1 Y1:y1 X2:x2 Y2:y2];
}
- (void)beginShapeClip:(BMTLSDOps *)dstOps {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.beginShapeClip");
[_clip beginShapeClip:dstOps context:self];
// Store the current transform as we need to use identity transform
// for clip spans rendering
[_tempTransform copyFrom:_transform];
[self resetTransform];
}
- (void)endShapeClip:(BMTLSDOps *)dstOps {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.endShapeClip");
[_clip endShapeClip:dstOps context:self];
// Reset transform for further rendering
[_transform copyFrom:_tempTransform];
}
- (void)resetComposite {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLContext_ResetComposite");
[_composite reset];
}
- (void)setAlphaCompositeRule:(jint)rule extraAlpha:(jfloat)extraAlpha
flags:(jint)flags {
J2dTraceLn3(J2D_TRACE_INFO, "MTLContext_SetAlphaComposite: rule=%d, extraAlpha=%1.2f, flags=%d", rule, extraAlpha, flags);
[_composite setRule:rule extraAlpha:extraAlpha];
}
- (NSString*)getCompositeDescription {
return [_composite getDescription];
}
- (NSString*)getPaintDescription {
return [_paint getDescription];
}
- (void)setXorComposite:(jint)xp {
J2dTraceLn1(J2D_TRACE_INFO, "MTLContext.setXorComposite: xorPixel=%08x", xp);
[_composite setXORComposite:xp];
}
- (jboolean) useXORComposite {
return ([_composite getCompositeState] == sun_java2d_SunGraphics2D_COMP_XOR);
}
- (void)resetTransform {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_ResetTransform");
[_transform resetTransform];
}
- (void)setTransformM00:(jdouble) m00 M10:(jdouble) m10
M01:(jdouble) m01 M11:(jdouble) m11
M02:(jdouble) m02 M12:(jdouble) m12 {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_SetTransform");
[_transform setTransformM00:m00 M10:m10 M01:m01 M11:m11 M02:m02 M12:m12];
}
- (void)resetPaint {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.resetPaint");
self.paint = [[[MTLPaint alloc] init] autorelease];
}
- (void)setColorPaint:(int)pixel {
J2dTraceLn5(J2D_TRACE_INFO, "MTLContext.setColorPaint: pixel=%08x [r=%d g=%d b=%d a=%d]", pixel, (pixel >> 16) & (0xFF), (pixel >> 8) & 0xFF, (pixel) & 0xFF, (pixel >> 24) & 0xFF);
self.paint = [[[MTLColorPaint alloc] initWithColor:pixel] autorelease];
}
- (void)setGradientPaintUseMask:(jboolean)useMask
cyclic:(jboolean)cyclic
p0:(jdouble)p0
p1:(jdouble)p1
p3:(jdouble)p3
pixel1:(jint)pixel1
pixel2:(jint) pixel2
{
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.setGradientPaintUseMask");
self.paint = [[[MTLGradPaint alloc] initWithUseMask:useMask
cyclic:cyclic
p0:p0
p1:p1
p3:p3
pixel1:pixel1
pixel2:pixel2] autorelease];
}
- (void)setLinearGradientPaint:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jint)cycleMethod
// 0 - NO_CYCLE
// 1 - REFLECT
// 2 - REPEAT
numStops:(jint)numStops
p0:(jfloat)p0
p1:(jfloat)p1
p3:(jfloat)p3
fractions:(jfloat*)fractions
pixels:(jint*)pixels
{
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.setLinearGradientPaint");
self.paint = [[[MTLLinearGradPaint alloc] initWithUseMask:useMask
linear:linear
cycleMethod:cycleMethod
numStops:numStops
p0:p0
p1:p1
p3:p3
fractions:fractions
pixels:pixels] autorelease];
}
- (void)setRadialGradientPaint:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jboolean)cycleMethod
numStops:(jint)numStops
m00:(jfloat)m00
m01:(jfloat)m01
m02:(jfloat)m02
m10:(jfloat)m10
m11:(jfloat)m11
m12:(jfloat)m12
focusX:(jfloat)focusX
fractions:(void *)fractions
pixels:(void *)pixels
{
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.setRadialGradientPaint");
self.paint = [[[MTLRadialGradPaint alloc] initWithUseMask:useMask
linear:linear
cycleMethod:cycleMethod
numStops:numStops
m00:m00
m01:m01
m02:m02
m10:m10
m11:m11
m12:m12
focusX:focusX
fractions:fractions
pixels:pixels] autorelease];
}
- (void)setTexturePaint:(jboolean)useMask
pSrcOps:(jlong)pSrcOps
filter:(jboolean)filter
xp0:(jdouble)xp0
xp1:(jdouble)xp1
xp3:(jdouble)xp3
yp0:(jdouble)yp0
yp1:(jdouble)yp1
yp3:(jdouble)yp3
{
BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps);
if (srcOps == NULL || srcOps->pTexture == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext_setTexturePaint: texture paint - texture is null");
return;
}
J2dTraceLn1(J2D_TRACE_INFO, "MTLContext.setTexturePaint [tex=%p]", srcOps->pTexture);
self.paint = [[[MTLTexturePaint alloc] initWithUseMask:useMask
textureID:srcOps->pTexture
isOpaque:srcOps->isOpaque
filter:filter
xp0:xp0
xp1:xp1
xp3:xp3
yp0:yp0
yp1:yp1
yp3:yp3] autorelease];
}
- (id<MTLCommandBuffer>)createCommandBuffer {
return [self.commandQueue commandBuffer];
}
/*
* This should be exclusively used only for final blit
* and present of CAMetalDrawable in MTLLayer
*/
- (id<MTLCommandBuffer>)createBlitCommandBuffer {
return [self.blitCommandQueue commandBuffer];
}
-(void)setBufImgOp:(NSObject*)bufImgOp {
if (_bufImgOp != nil) {
[_bufImgOp release]; // context owns bufImgOp object
}
_bufImgOp = bufImgOp;
}
-(NSObject*)getBufImgOp {
return _bufImgOp;
}
@end

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2020, 2021, 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.
*/
#ifndef MTLGlyphCache_h_Included
#define MTLGlyphCache_h_Included
#ifdef __cplusplus
extern "C" {
#endif
#include "jni.h"
#include "fontscalerdefs.h"
#import <Metal/Metal.h>
typedef void (MTLFlushFunc)();
typedef struct _MTLCacheCellInfo MTLCacheCellInfo;
typedef struct {
CacheCellInfo *head;
CacheCellInfo *tail;
id<MTLTexture> texture;
jint width;
jint height;
jint cellWidth;
jint cellHeight;
MTLFlushFunc *Flush;
} MTLGlyphCacheInfo;
struct _MTLCacheCellInfo {
MTLGlyphCacheInfo *cacheInfo;
struct GlyphInfo *glyphInfo;
// next cell info in the cache's list
MTLCacheCellInfo *next;
// REMIND: find better name?
// next cell info in the glyph's cell list (next Glyph Cache Info)
MTLCacheCellInfo *nextGCI;
jint timesRendered;
jint x;
jint y;
// number of pixels from the left or right edge not considered touched
// by the glyph
jint leftOff;
jint rightOff;
jfloat tx1;
jfloat ty1;
jfloat tx2;
jfloat ty2;
};
MTLGlyphCacheInfo *
MTLGlyphCache_Init(jint width, jint height,
jint cellWidth, jint cellHeight,
MTLFlushFunc *func);
MTLCacheCellInfo *
MTLGlyphCache_AddGlyph(MTLGlyphCacheInfo *cache, struct GlyphInfo *glyph);
bool
MTLGlyphCache_IsCacheFull(MTLGlyphCacheInfo *cache, GlyphInfo *glyph);
void
MTLGlyphCache_Invalidate(MTLGlyphCacheInfo *cache);
void
MTLGlyphCache_AddCellInfo(struct GlyphInfo *glyph, MTLCacheCellInfo *cellInfo);
void
MTLGlyphCache_RemoveCellInfo(struct GlyphInfo *glyph, MTLCacheCellInfo *cellInfo);
MTLCacheCellInfo *
MTLGlyphCache_GetCellInfoForCache(struct GlyphInfo *glyph,
MTLGlyphCacheInfo *cache);
JNIEXPORT void
MTLGlyphCache_RemoveAllCellInfos(struct GlyphInfo *glyph);
void
MTLGlyphCache_Free(MTLGlyphCacheInfo *cache);
#ifdef __cplusplus
};
#endif
#endif /* MTLGlyphCache_h_Included */

View file

@ -0,0 +1,364 @@
/*
* Copyright (c) 2020, 2021, 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.
*/
#include <stdlib.h>
#include "jni.h"
#include "MTLGlyphCache.h"
#include "Trace.h"
/**
* When the cache is full, we will try to reuse the cache cells that have
* been used relatively less than the others (and we will save the cells that
* have been rendered more than the threshold defined here).
*/
#define TIMES_RENDERED_THRESHOLD 5
/**
* Creates a new GlyphCacheInfo structure, fills in the initial values, and
* then returns a pointer to the GlyphCacheInfo record.
*
* Note that this method only sets up a data structure describing a
* rectangular region of accelerated memory, containing "virtual" cells of
* the requested size. The cell information is added lazily to the linked
* list describing the cache as new glyphs are added. Platform specific
* glyph caching code is responsible for actually creating the accelerated
* memory surface that will contain the individual glyph images.
*
* Each glyph contains a reference to a list of cell infos - one per glyph
* cache. There may be multiple glyph caches (for example, one per graphics
* adapter), so if the glyph is cached on two devices its cell list will
* consists of two elements corresponding to different glyph caches.
*
* The platform-specific glyph caching code is supposed to use
* GetCellInfoForCache method for retrieving cache infos from the glyph's list.
*
* Note that if it is guaranteed that there will be only one global glyph
* cache then it one does not have to use AccelGlyphCache_GetCellInfoForCache
* for retrieving cell info for the glyph, but instead just use the struct's
* field directly.
*/
MTLGlyphCacheInfo *
MTLGlyphCache_Init(jint width, jint height,
jint cellWidth, jint cellHeight,
MTLFlushFunc *func)
{
MTLGlyphCacheInfo *gcinfo;
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_Init");
gcinfo = (MTLGlyphCacheInfo *)malloc(sizeof(MTLGlyphCacheInfo));
if (gcinfo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLGlyphCache_Init: could not allocate MTLGlyphCacheInfo");
return NULL;
}
gcinfo->head = NULL;
gcinfo->tail = NULL;
gcinfo->width = width;
gcinfo->height = height;
gcinfo->cellWidth = cellWidth;
gcinfo->cellHeight = cellHeight;
gcinfo->Flush = func;
return gcinfo;
}
/**
* Attempts to add the provided glyph to the specified cache. If the
* operation is successful, a pointer to the newly occupied cache cell is
* stored in the glyph's cellInfo field; otherwise, its cellInfo field is
* set to NULL, indicating that the glyph's original bits should be rendered
* instead. If the cache is full, the least-recently-used glyph is
* invalidated and its cache cell is reassigned to the new glyph being added.
*
* Note that this method only ensures that a rectangular region in the
* "virtual" glyph cache is available for the glyph image. Platform specific
* glyph caching code is responsible for actually caching the glyph image
* in the associated accelerated memory surface.
*
* Returns created cell info if it was successfully created and added to the
* cache and glyph's cell lists, NULL otherwise.
*/
MTLCacheCellInfo *
MTLGlyphCache_AddGlyph(MTLGlyphCacheInfo *cache, GlyphInfo *glyph)
{
MTLCacheCellInfo *cellinfo = NULL;
jint w = glyph->width;
jint h = glyph->height;
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_AddGlyph");
if ((glyph->width > cache->cellWidth) ||
(glyph->height > cache->cellHeight))
{
return NULL;
}
jint x, y;
if (cache->head == NULL) {
x = 0;
y = 0;
} else {
x = cache->tail->x + cache->cellWidth;
y = cache->tail->y;
if ((x + cache->cellWidth) > cache->width) {
x = 0;
y += cache->cellHeight;
}
}
// create new CacheCellInfo
cellinfo = (MTLCacheCellInfo *)malloc(sizeof(MTLCacheCellInfo));
if (cellinfo == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "could not allocate CellInfo");
return NULL;
}
cellinfo->cacheInfo = cache;
cellinfo->glyphInfo = glyph;
cellinfo->timesRendered = 0;
cellinfo->x = x;
cellinfo->y = y;
cellinfo->leftOff = 0;
cellinfo->rightOff = 0;
cellinfo->tx1 = (jfloat)cellinfo->x / cache->width;
cellinfo->ty1 = (jfloat)cellinfo->y / cache->height;
cellinfo->tx2 = cellinfo->tx1 + ((jfloat)w / cache->width);
cellinfo->ty2 = cellinfo->ty1 + ((jfloat)h / cache->height);
if (cache->head == NULL) {
// initialize the head cell
cache->head = cellinfo;
} else {
// update existing tail cell
cache->tail->next = cellinfo;
}
// add the new cell to the end of the list
cache->tail = cellinfo;
cellinfo->next = NULL;
cellinfo->nextGCI = NULL;
// add cache cell to the glyph's cells list
MTLGlyphCache_AddCellInfo(glyph, cellinfo);
return cellinfo;
}
bool
MTLGlyphCache_IsCacheFull(MTLGlyphCacheInfo *cache, GlyphInfo *glyph)
{
jint w = glyph->width;
jint h = glyph->height;
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_IsCacheFull");
jint x, y;
if (cache->head == NULL) {
return JNI_FALSE;
} else {
x = cache->tail->x + cache->cellWidth;
y = cache->tail->y;
if ((x + cache->cellWidth) > cache->width) {
x = 0;
y += cache->cellHeight;
if ((y + cache->cellHeight) > cache->height) {
return JNI_TRUE;
}
}
}
return JNI_FALSE;
}
/**
* Invalidates all cells in the cache. Note that this method does not
* attempt to compact the cache in any way; it just invalidates any cells
* that already exist.
*/
void
MTLGlyphCache_Invalidate(MTLGlyphCacheInfo *cache)
{
MTLCacheCellInfo *cellinfo;
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_Invalidate");
if (cache == NULL) {
return;
}
// flush any pending vertices that may be depending on the current
// glyph cache layout
if (cache->Flush != NULL) {
cache->Flush();
}
cellinfo = cache->head;
while (cellinfo != NULL) {
if (cellinfo->glyphInfo != NULL) {
// if the cell is occupied, notify the base glyph that its
// cached version for this cache is about to be invalidated
MTLGlyphCache_RemoveCellInfo(cellinfo->glyphInfo, cellinfo);
}
cellinfo = cellinfo->next;
}
}
/**
* Invalidates and frees all cells and the cache itself. The "cache" pointer
* becomes invalid after this function returns.
*/
void
MTLGlyphCache_Free(MTLGlyphCacheInfo *cache)
{
MTLCacheCellInfo *cellinfo;
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_Free");
if (cache == NULL) {
return;
}
// flush any pending vertices that may be depending on the current
// glyph cache
if (cache->Flush != NULL) {
cache->Flush();
}
while (cache->head != NULL) {
cellinfo = cache->head;
if (cellinfo->glyphInfo != NULL) {
// if the cell is occupied, notify the base glyph that its
// cached version for this cache is about to be invalidated
MTLGlyphCache_RemoveCellInfo(cellinfo->glyphInfo, cellinfo);
}
cache->head = cellinfo->next;
free(cellinfo);
}
free(cache);
}
/**
* Add cell info to the head of the glyph's list of cached cells.
*/
void
MTLGlyphCache_AddCellInfo(GlyphInfo *glyph, MTLCacheCellInfo *cellInfo)
{
// assert (glyph != NULL && cellInfo != NULL)
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_AddCellInfo");
J2dTraceLn2(J2D_TRACE_VERBOSE, " glyph 0x%x: adding cell 0x%x to the list",
glyph, cellInfo);
cellInfo->glyphInfo = glyph;
cellInfo->nextGCI = glyph->cellInfo;
glyph->cellInfo = cellInfo;
glyph->managed = MANAGED_GLYPH;
}
/**
* Removes cell info from the glyph's list of cached cells.
*/
void
MTLGlyphCache_RemoveCellInfo(GlyphInfo *glyph, MTLCacheCellInfo *cellInfo)
{
MTLCacheCellInfo *currCellInfo = glyph->cellInfo;
MTLCacheCellInfo *prevInfo = NULL;
// assert (glyph!= NULL && glyph->cellInfo != NULL && cellInfo != NULL)
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_RemoveCellInfo");
do {
if (currCellInfo == cellInfo) {
J2dTraceLn2(J2D_TRACE_VERBOSE,
" glyph 0x%x: removing cell 0x%x from glyph's list",
glyph, currCellInfo);
if (prevInfo == NULL) { // it's the head, chop-chop
glyph->cellInfo = currCellInfo->nextGCI;
} else {
prevInfo->nextGCI = currCellInfo->nextGCI;
}
currCellInfo->glyphInfo = NULL;
currCellInfo->nextGCI = NULL;
return;
}
prevInfo = currCellInfo;
currCellInfo = currCellInfo->nextGCI;
} while (currCellInfo != NULL);
J2dTraceLn2(J2D_TRACE_WARNING, "MTLGlyphCache_RemoveCellInfo: "\
"no cell 0x%x in glyph 0x%x's cell list",
cellInfo, glyph);
}
/**
* Removes cell info from the glyph's list of cached cells.
*/
JNIEXPORT void
MTLGlyphCache_RemoveAllCellInfos(GlyphInfo *glyph)
{
MTLCacheCellInfo *currCell, *prevCell;
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_RemoveAllCellInfos");
if (glyph == NULL || glyph->cellInfo == NULL) {
return;
}
// invalidate all of this glyph's accelerated cache cells
currCell = glyph->cellInfo;
do {
currCell->glyphInfo = NULL;
prevCell = currCell;
currCell = currCell->nextGCI;
prevCell->nextGCI = NULL;
} while (currCell != NULL);
glyph->cellInfo = NULL;
}
/**
* Returns cell info associated with particular cache from the glyph's list of
* cached cells.
*/
MTLCacheCellInfo *
MTLGlyphCache_GetCellInfoForCache(GlyphInfo *glyph, MTLGlyphCacheInfo *cache)
{
// assert (glyph != NULL && cache != NULL)
J2dTraceLn(J2D_TRACE_VERBOSE2, "MTLGlyphCache_GetCellInfoForCache");
if (glyph->cellInfo != NULL) {
MTLCacheCellInfo *cellInfo = glyph->cellInfo;
do {
if (cellInfo->cacheInfo == cache) {
J2dTraceLn3(J2D_TRACE_VERBOSE2,
" glyph 0x%x: found cell 0x%x for cache 0x%x",
glyph, cellInfo, cache);
return cellInfo;
}
cellInfo = cellInfo->nextGCI;
} while (cellInfo != NULL);
}
J2dTraceLn2(J2D_TRACE_VERBOSE2, " glyph 0x%x: no cell for cache 0x%x",
glyph, cache);
return NULL;
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLGraphicsConfig_h_Included
#define MTLGraphicsConfig_h_Included
#import "JNIUtilities.h"
#import "MTLSurfaceDataBase.h"
#import "MTLContext.h"
#import <Cocoa/Cocoa.h>
#import <Metal/Metal.h>
@interface MTLGraphicsConfigUtil : NSObject {}
+ (void) _getMTLConfigInfo: (NSMutableArray *)argValue;
@end
/**
* The MTLGraphicsConfigInfo structure contains information specific to a
* given MTLGraphicsConfig (pixel format).
* MTLContext *context;
* The context associated with this MTLGraphicsConfig.
*/
typedef struct _MTLGraphicsConfigInfo {
MTLContext *context;
} MTLGraphicsConfigInfo;
// From "Metal Feature Set Tables"
// There are 2 GPU families for mac - MTLGPUFamilyMac1 and MTLGPUFamilyMac2
// Both of them support "Maximum 2D texture width and height" of 16384 pixels
// Note : there is no API to get this value, hence hardcoding by reading from the table
#define MaxTextureSize 16384
#endif /* MTLGraphicsConfig_h_Included */

View file

@ -0,0 +1,229 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#import "sun_java2d_metal_MTLGraphicsConfig.h"
#import "MTLGraphicsConfig.h"
#import "MTLSurfaceData.h"
#import "ThreadUtilities.h"
#import "awt.h"
#pragma mark -
#pragma mark "--- Mac OS X specific methods for Metal pipeline ---"
/**
* Disposes all memory and resources associated with the given
* MTLGraphicsConfigInfo (including its native MTLContext data).
*/
void
MTLGC_DestroyMTLGraphicsConfig(jlong pConfigInfo)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLGC_DestroyMTLGraphicsConfig");
MTLGraphicsConfigInfo *mtlinfo =
(MTLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo);
if (mtlinfo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLGC_DestroyMTLGraphicsConfig: info is null");
return;
}
MTLContext *mtlc = (MTLContext*)mtlinfo->context;
if (mtlc != NULL) {
[mtlinfo->context release];
mtlinfo->context = nil;
}
free(mtlinfo);
}
#pragma mark -
#pragma mark "--- MTLGraphicsConfig methods ---"
JNIEXPORT jboolean JNICALL
Java_sun_java2d_metal_MTLGraphicsConfig_isMetalFrameworkAvailable
(JNIEnv *env, jclass mtlgc)
{
jboolean metalSupported = JNI_FALSE;
// It is guranteed that metal supported GPU is available macOS 10.14 onwards
if (@available(macOS 10.14, *)) {
metalSupported = JNI_TRUE;
}
J2dRlsTraceLn1(J2D_TRACE_INFO, "MTLGraphicsConfig_isMetalFrameworkAvailable : %d", metalSupported);
return metalSupported;
}
JNIEXPORT jboolean JNICALL
Java_sun_java2d_metal_MTLGraphicsConfig_tryLoadMetalLibrary
(JNIEnv *env, jclass mtlgc, jint displayID, jstring shadersLibName)
{
jboolean ret = JNI_FALSE;
JNI_COCOA_ENTER(env);
NSMutableArray * retArray = [NSMutableArray arrayWithCapacity:3];
[retArray addObject: [NSNumber numberWithInt: (int)displayID]];
char *str = JNU_GetStringPlatformChars(env, shadersLibName, 0);
[retArray addObject: [NSString stringWithUTF8String: str]];
[ThreadUtilities performOnMainThreadWaiting:YES block:^() {
[MTLGraphicsConfigUtil _tryLoadMetalLibrary: retArray];
}];
NSNumber * num = (NSNumber *)[retArray objectAtIndex: 0];
ret = (jboolean)[num boolValue];
JNU_ReleaseStringPlatformChars(env, shadersLibName, str);
JNI_COCOA_EXIT(env);
return ret;
}
/**
* Determines whether the MTL pipeline can be used for a given GraphicsConfig
* provided its screen number and visual ID. If the minimum requirements are
* met, the native MTLGraphicsConfigInfo structure is initialized for this
* GraphicsConfig with the necessary information (pixel format, etc.)
* and a pointer to this structure is returned as a jlong. If
* initialization fails at any point, zero is returned, indicating that MTL
* cannot be used for this GraphicsConfig (we should fallback on an existing
* 2D pipeline).
*/
JNIEXPORT jlong JNICALL
Java_sun_java2d_metal_MTLGraphicsConfig_getMTLConfigInfo
(JNIEnv *env, jclass mtlgc, jint displayID, jstring mtlShadersLib)
{
jlong ret = 0L;
JNI_COCOA_ENTER(env);
NSMutableArray * retArray = [NSMutableArray arrayWithCapacity:3];
[retArray addObject: [NSNumber numberWithInt: (int)displayID]];
char *str = JNU_GetStringPlatformChars(env, mtlShadersLib, 0);
[retArray addObject: [NSString stringWithUTF8String: str]];
[ThreadUtilities performOnMainThreadWaiting:YES block:^() {
[MTLGraphicsConfigUtil _getMTLConfigInfo: retArray];
}];
NSNumber * num = (NSNumber *)[retArray objectAtIndex: 0];
ret = (jlong)[num longValue];
JNU_ReleaseStringPlatformChars(env, mtlShadersLib, str);
JNI_COCOA_EXIT(env);
return ret;
}
@implementation MTLGraphicsConfigUtil
+ (void) _getMTLConfigInfo: (NSMutableArray *)argValue {
AWT_ASSERT_APPKIT_THREAD;
jint displayID = (jint)[(NSNumber *)[argValue objectAtIndex: 0] intValue];
NSString *mtlShadersLib = (NSString *)[argValue objectAtIndex: 1];
JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
[argValue removeAllObjects];
J2dRlsTraceLn(J2D_TRACE_INFO, "MTLGraphicsConfig_getMTLConfigInfo");
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSRect contentRect = NSMakeRect(0, 0, 64, 64);
NSWindow *window =
[[NSWindow alloc]
initWithContentRect: contentRect
styleMask: NSBorderlessWindowMask
backing: NSBackingStoreBuffered
defer: false];
if (window == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLGraphicsConfig_getMTLConfigInfo: NSWindow is NULL");
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
MTLContext *mtlc = [[MTLContext alloc] initWithDevice:CGDirectDisplayCopyCurrentMetalDevice(displayID)
shadersLib:mtlShadersLib];
if (mtlc == 0L) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLGC_InitMTLContext: could not initialze MTLContext.");
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
// create the MTLGraphicsConfigInfo record for this config
MTLGraphicsConfigInfo *mtlinfo = (MTLGraphicsConfigInfo *)malloc(sizeof(MTLGraphicsConfigInfo));
if (mtlinfo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLGraphicsConfig_getMTLConfigInfo: could not allocate memory for mtlinfo");
free(mtlc);
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
memset(mtlinfo, 0, sizeof(MTLGraphicsConfigInfo));
mtlinfo->context = mtlc;
[argValue addObject: [NSNumber numberWithLong:ptr_to_jlong(mtlinfo)]];
[pool drain];
}
+ (void) _tryLoadMetalLibrary: (NSMutableArray *)argValue {
AWT_ASSERT_APPKIT_THREAD;
jint displayID = (jint)[(NSNumber *)[argValue objectAtIndex: 0] intValue];
NSString *mtlShadersLib = (NSString *)[argValue objectAtIndex: 1];
JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
[argValue removeAllObjects];
J2dRlsTraceLn(J2D_TRACE_INFO, "MTLGraphicsConfigUtil_tryLoadMTLLibrary");
BOOL ret = FALSE;;
id<MTLDevice> device = CGDirectDisplayCopyCurrentMetalDevice(displayID);
if (device != nil) {
NSError *error = nil;
id<MTLLibrary> lib = [device newLibraryWithFile:mtlShadersLib error:&error];
if (lib != nil) {
ret = TRUE;
} else {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLGraphicsConfig_tryLoadMetalLibrary - Failed to load Metal shader library.");
}
} else {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLGraphicsConfig_tryLoadMetalLibrary - Failed to create MTLDevice.");
}
[argValue addObject: [NSNumber numberWithBool: ret]];
}
@end //GraphicsConfigUtil
JNIEXPORT jint JNICALL
Java_sun_java2d_metal_MTLGraphicsConfig_nativeGetMaxTextureSize
(JNIEnv *env, jclass mtlgc)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLGraphicsConfig_nativeGetMaxTextureSize");
return (jint)MaxTextureSize;
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLLayer_h_Included
#define MTLLayer_h_Included
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
#include <CoreVideo/CVDisplayLink.h>
#import "common.h"
@interface MTLLayer : CAMetalLayer
{
@private
jobject javaLayer;
// intermediate buffer, used the RQ lock to synchronize
MTLContext* ctx;
float bufferWidth;
float bufferHeight;
id<MTLTexture> buffer;
int nextDrawableCount;
int topInset;
int leftInset;
CVDisplayLinkRef displayLink;
}
@property (nonatomic) jobject javaLayer;
@property (readwrite, assign) MTLContext* ctx;
@property (readwrite, assign) float bufferWidth;
@property (readwrite, assign) float bufferHeight;
@property (readwrite, assign) id<MTLTexture> buffer;
@property (readwrite, assign) int nextDrawableCount;
@property (readwrite, assign) int topInset;
@property (readwrite, assign) int leftInset;
@property (readwrite, assign) CVDisplayLinkRef displayLink;
- (id) initWithJavaLayer:(jobject)layer;
- (void) blitTexture;
- (void) fillParallelogramCtxX:(jfloat)x
Y:(jfloat)y
DX1:(jfloat)dx1
DY1:(jfloat)dy1
DX2:(jfloat)dx2
DY2:(jfloat)dy2;
- (void) blitCallback;
- (void) display;
- (void) redraw;
- (void) startDisplayLink;
- (void) stopDisplayLink;
@end
#endif /* MTLLayer_h_Included */

View file

@ -0,0 +1,284 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#import "MTLGraphicsConfig.h"
#import "MTLLayer.h"
#import "ThreadUtilities.h"
#import "LWCToolkit.h"
#import "MTLSurfaceData.h"
#import "JNIUtilities.h"
@implementation MTLLayer
@synthesize javaLayer;
@synthesize ctx;
@synthesize bufferWidth;
@synthesize bufferHeight;
@synthesize buffer;
@synthesize topInset;
@synthesize leftInset;
@synthesize nextDrawableCount;
@synthesize displayLink;
- (id) initWithJavaLayer:(jobject)layer
{
AWT_ASSERT_APPKIT_THREAD;
// Initialize ourselves
self = [super init];
if (self == nil) return self;
self.javaLayer = layer;
self.contentsGravity = kCAGravityTopLeft;
//Disable CALayer's default animation
NSMutableDictionary * actions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
[NSNull null], @"anchorPoint",
[NSNull null], @"bounds",
[NSNull null], @"contents",
[NSNull null], @"contentsScale",
[NSNull null], @"onOrderIn",
[NSNull null], @"onOrderOut",
[NSNull null], @"position",
[NSNull null], @"sublayers",
nil];
self.actions = actions;
[actions release];
self.topInset = 0;
self.leftInset = 0;
self.framebufferOnly = NO;
self.nextDrawableCount = 0;
self.opaque = FALSE;
CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
CVDisplayLinkSetOutputCallback(displayLink, &displayLinkCallback, (__bridge void*)self);
return self;
}
- (void) blitTexture {
if (self.ctx == NULL || self.javaLayer == NULL || self.buffer == nil || self.ctx.device == nil) {
J2dTraceLn4(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: uninitialized (mtlc=%p, javaLayer=%p, buffer=%p, devide=%p)", self.ctx, self.javaLayer, self.buffer, ctx.device);
return;
}
if (self.nextDrawableCount != 0) {
return;
}
@autoreleasepool {
if ((self.buffer.width == 0) || (self.buffer.height == 0)) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: cannot create drawable of size 0");
return;
}
NSUInteger src_x = self.leftInset * self.contentsScale;
NSUInteger src_y = self.topInset * self.contentsScale;
NSUInteger src_w = self.buffer.width - src_x;
NSUInteger src_h = self.buffer.height - src_y;
if (src_h <= 0 || src_w <= 0) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: Invalid src width or height.");
return;
}
id<MTLCommandBuffer> commandBuf = [self.ctx createBlitCommandBuffer];
if (commandBuf == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: commandBuf is null");
return;
}
id<CAMetalDrawable> mtlDrawable = [self nextDrawable];
if (mtlDrawable == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: nextDrawable is null)");
return;
}
self.nextDrawableCount++;
id <MTLBlitCommandEncoder> blitEncoder = [commandBuf blitCommandEncoder];
[blitEncoder
copyFromTexture:self.buffer sourceSlice:0 sourceLevel:0
sourceOrigin:MTLOriginMake(src_x, src_y, 0)
sourceSize:MTLSizeMake(src_w, src_h, 1)
toTexture:mtlDrawable.texture destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(0, 0, 0)];
[blitEncoder endEncoding];
[commandBuf presentDrawable:mtlDrawable];
[commandBuf addCompletedHandler:^(id <MTLCommandBuffer> commandBuf) {
self.nextDrawableCount--;
}];
[commandBuf commit];
[self stopDisplayLink];
}
}
- (void) dealloc {
JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
(*env)->DeleteWeakGlobalRef(env, self.javaLayer);
self.javaLayer = nil;
[self stopDisplayLink];
CVDisplayLinkRelease(self.displayLink);
self.displayLink = nil;
[super dealloc];
}
- (void) blitCallback {
JNIEnv *env = [ThreadUtilities getJNIEnv];
DECLARE_CLASS(jc_JavaLayer, "sun/java2d/metal/MTLLayer");
DECLARE_METHOD(jm_drawInMTLContext, jc_JavaLayer, "drawInMTLContext", "()V");
jobject javaLayerLocalRef = (*env)->NewLocalRef(env, self.javaLayer);
if ((*env)->IsSameObject(env, javaLayerLocalRef, NULL)) {
return;
}
(*env)->CallVoidMethod(env, javaLayerLocalRef, jm_drawInMTLContext);
CHECK_EXCEPTION();
(*env)->DeleteLocalRef(env, javaLayerLocalRef);
}
- (void) display {
AWT_ASSERT_APPKIT_THREAD;
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer_display() called");
[self blitCallback];
[super display];
}
- (void) redraw {
AWT_ASSERT_APPKIT_THREAD;
[self setNeedsDisplay];
}
- (void) startDisplayLink {
if (!CVDisplayLinkIsRunning(self.displayLink))
CVDisplayLinkStart(self.displayLink);
}
- (void) stopDisplayLink {
if (CVDisplayLinkIsRunning(self.displayLink))
CVDisplayLinkStop(self.displayLink);
}
CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
{
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer_displayLinkCallback() called");
@autoreleasepool {
MTLLayer *layer = (__bridge MTLLayer *)displayLinkContext;
[layer performSelectorOnMainThread:@selector(redraw) withObject:nil waitUntilDone:NO];
}
return kCVReturnSuccess;
}
@end
/*
* Class: sun_java2d_metal_MTLLayer
* Method: nativeCreateLayer
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_sun_java2d_metal_MTLLayer_nativeCreateLayer
(JNIEnv *env, jobject obj)
{
__block MTLLayer *layer = nil;
JNI_COCOA_ENTER(env);
jobject javaLayer = (*env)->NewWeakGlobalRef(env, obj);
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
AWT_ASSERT_APPKIT_THREAD;
layer = [[MTLLayer alloc] initWithJavaLayer: javaLayer];
}];
JNI_COCOA_EXIT(env);
return ptr_to_jlong(layer);
}
// Must be called under the RQ lock.
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLLayer_validate
(JNIEnv *env, jclass cls, jlong layerPtr, jobject surfaceData)
{
MTLLayer *layer = OBJC(layerPtr);
if (surfaceData != NULL) {
BMTLSDOps *bmtlsdo = (BMTLSDOps*) SurfaceData_GetOps(env, surfaceData);
layer.bufferWidth = bmtlsdo->width;
layer.bufferHeight = bmtlsdo->width;
layer.buffer = bmtlsdo->pTexture;
layer.ctx = ((MTLSDOps *)bmtlsdo->privOps)->configInfo->context;
layer.device = layer.ctx.device;
layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
layer.drawableSize =
CGSizeMake(layer.buffer.width,
layer.buffer.height);
[layer startDisplayLink];
} else {
layer.ctx = NULL;
[layer stopDisplayLink];
}
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLLayer_nativeSetScale
(JNIEnv *env, jclass cls, jlong layerPtr, jdouble scale)
{
JNI_COCOA_ENTER(env);
MTLLayer *layer = jlong_to_ptr(layerPtr);
// We always call all setXX methods asynchronously, exception is only in
// this method where we need to change native texture size and layer's scale
// in one call on appkit, otherwise we'll get window's contents blinking,
// during screen-2-screen moving.
[ThreadUtilities performOnMainThreadWaiting:[NSThread isMainThread] block:^(){
layer.contentsScale = scale;
}];
JNI_COCOA_EXIT(env);
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLLayer_nativeSetInsets
(JNIEnv *env, jclass cls, jlong layerPtr, jint top, jint left)
{
MTLLayer *layer = jlong_to_ptr(layerPtr);
layer.topInset = top;
layer.leftInset = left;
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLLayer_blitTexture
(JNIEnv *env, jclass cls, jlong layerPtr)
{
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer_blitTexture");
MTLLayer *layer = jlong_to_ptr(layerPtr);
MTLContext * ctx = layer.ctx;
if (layer == NULL || ctx == NULL) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer_blit : Layer or Context is null");
return;
}
[layer blitTexture];
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLMaskBlit_h_Included
#define MTLMaskBlit_h_Included
#include "MTLContext.h"
void MTLMaskBlit_MaskBlit(JNIEnv *env, MTLContext *mtlc, BMTLSDOps * dstOps,
jint dstx, jint dsty,
jint width, jint height,
void *pPixels);
#endif /* MTLMaskBlit_h_Included */

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include <stdlib.h>
#include <jlong.h>
#include "MTLMaskBlit.h"
#include "MTLRenderQueue.h"
#include "MTLBlitLoops.h"
/**
* REMIND: This method assumes that the dimensions of the incoming pixel
* array are less than or equal to the cached blit texture tile;
* these are rather fragile assumptions, and should be cleaned up...
*/
void
MTLMaskBlit_MaskBlit(JNIEnv *env, MTLContext *mtlc, BMTLSDOps * dstOps,
jint dstx, jint dsty,
jint width, jint height,
void *pPixels)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLMaskBlit_MaskBlit");
if (width <= 0 || height <= 0) {
J2dTraceLn(J2D_TRACE_WARNING, "MTLMaskBlit_MaskBlit: invalid dimensions");
return;
}
RETURN_IF_NULL(pPixels);
RETURN_IF_NULL(mtlc);
MTLPooledTextureHandle * texHandle = [mtlc.texturePool
getTexture:width
height:height
format:MTLPixelFormatBGRA8Unorm];
if (texHandle == nil) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLMaskBlit_MaskBlit: can't obtain temporary texture object from pool");
return;
}
[[mtlc getCommandBufferWrapper] registerPooledTexture:texHandle];
id<MTLTexture> texBuff = texHandle.texture;
MTLRegion region = MTLRegionMake2D(0, 0, width, height);
[texBuff replaceRegion:region mipmapLevel:0 withBytes:pPixels bytesPerRow:4*width];
drawTex2Tex(mtlc, texBuff, dstOps->pTexture, JNI_FALSE, dstOps->isOpaque, 0,
0, 0, width, height, dstx, dsty, dstx + width, dsty + height);
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLMaskFill_h_Included
#define MTLMaskFill_h_Included
#include "MTLContext.h"
void MTLMaskFill_MaskFill(MTLContext *mtlc, BMTLSDOps * dstOps,
jint x, jint y, jint w, jint h,
jint maskoff, jint maskscan, jint masklen,
unsigned char *pMask);
#endif /* MTLMaskFill_h_Included */

View file

@ -0,0 +1,114 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include "sun_java2d_metal_MTLMaskFill.h"
#include "MTLMaskFill.h"
#include "MTLRenderQueue.h"
#include "MTLVertexCache.h"
/**
* In case of Metal we use shader for texture mapping.
*
* Descriptions of the many variables used in this method:
* x,y - upper left corner of the tile destination
* w,h - width/height of the mask tile
* x0 - placekeeper for the original destination x location
* tw,th - width/height of the actual texture tile in pixels
* sx1,sy1 - upper left corner of the mask tile source region
* sx2,sy2 - lower left corner of the mask tile source region
* sx,sy - "current" upper left corner of the mask tile region of interest
*/
void
MTLMaskFill_MaskFill(MTLContext *mtlc, BMTLSDOps * dstOps,
jint x, jint y, jint w, jint h,
jint maskoff, jint maskscan, jint masklen,
unsigned char *pMask)
{
J2dTraceLn5(J2D_TRACE_INFO, "MTLMaskFill_MaskFill (x=%d y=%d w=%d h=%d pMask=%p)", x, y, w, h, dstOps->pTexture);
jint tw, th, x0;
jint sx1, sy1, sx2, sy2;
jint sx, sy, sw, sh;
x0 = x;
tw = MTLVC_MASK_CACHE_TILE_WIDTH;
th = MTLVC_MASK_CACHE_TILE_HEIGHT;
sx1 = maskoff % maskscan;
sy1 = maskoff / maskscan;
sx2 = sx1 + w;
sy2 = sy1 + h;
for (sy = sy1; sy < sy2; sy += th, y += th) {
x = x0;
sh = ((sy + th) > sy2) ? (sy2 - sy) : th;
for (sx = sx1; sx < sx2; sx += tw, x += tw) {
sw = ((sx + tw) > sx2) ? (sx2 - sx) : tw;
MTLVertexCache_AddMaskQuad(mtlc,
sx, sy, x, y, sw, sh,
maskscan, pMask, dstOps);
}
}
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLMaskFill_maskFill
(JNIEnv *env, jobject self,
jint x, jint y, jint w, jint h,
jint maskoff, jint maskscan, jint masklen,
jbyteArray maskArray)
{
MTLContext *mtlc = MTLRenderQueue_GetCurrentContext();
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
unsigned char *mask;
J2dTraceLn(J2D_TRACE_ERROR, "MTLMaskFill_maskFill");
if (maskArray != NULL) {
mask = (unsigned char *)
(*env)->GetPrimitiveArrayCritical(env, maskArray, NULL);
} else {
mask = NULL;
}
MTLMaskFill_MaskFill(mtlc, dstOps,
x, y, w, h,
maskoff, maskscan, masklen, mask);
if (mtlc != NULL) {
RESET_PREVIOUS_OP();
[mtlc.encoderManager endEncoder];
MTLCommandBufferWrapper * cbwrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
}
if (mask != NULL) {
(*env)->ReleasePrimitiveArrayCritical(env, maskArray, mask, JNI_ABORT);
}
}

View file

@ -0,0 +1,142 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLPaints_h_Included
#define MTLPaints_h_Included
#import <Metal/Metal.h>
#include "RenderOptions.h"
#define sun_java2d_SunGraphics2D_PAINT_UNDEFINED -1
@class MTLContext;
@class MTLComposite;
@class MTLClip;
@class MTLPipelineStatesStorage;
/**
* The MTLPaint class represents paint mode (color, gradient etc.)
*/
@interface MTLPaint : NSObject
- (id)initWithState:(jint)state;
- (BOOL)isEqual:(MTLPaint *)other; // used to compare requested with cached
- (NSString *)getDescription;
// For the current paint mode and passed composite (and flags):
// 1. Selects vertex+fragment shader (and corresponding pipelineDesc) and set pipelineState
// 2. Prepares corresponding buffers of vertex and fragment shaders
- (void)setPipelineState:(id <MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage;
- (void)setXorModePipelineState:(id <MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage;
@end
@interface MTLColorPaint : MTLPaint
- (id)initWithColor:(jint)color;
@property (nonatomic, readonly) jint color;
@end
@interface MTLBaseGradPaint : MTLPaint
- (id)initWithState:(jint)state
mask:(jboolean)useMask
cyclic:(jboolean)cyclic;
@end
@interface MTLGradPaint : MTLBaseGradPaint
- (id)initWithUseMask:(jboolean)useMask
cyclic:(jboolean)cyclic
p0:(jdouble)p0
p1:(jdouble)p1
p3:(jdouble)p3
pixel1:(jint)pixel1
pixel2:(jint)pixel2;
@end
@interface MTLBaseMultiGradPaint : MTLBaseGradPaint
- (id)initWithState:(jint)state
mask:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jboolean)cycleMethod
numStops:(jint)numStops
fractions:(jfloat *)fractions
pixels:(jint *)pixels;
@end
@interface MTLLinearGradPaint : MTLBaseMultiGradPaint
- (id)initWithUseMask:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jboolean)cycleMethod
numStops:(jint)numStops
p0:(jfloat)p0
p1:(jfloat)p1
p3:(jfloat)p3
fractions:(jfloat *)fractions
pixels:(jint *)pixels;
@end
@interface MTLRadialGradPaint : MTLBaseMultiGradPaint
- (id)initWithUseMask:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jint)cycleMethod
numStops:(jint)numStops
m00:(jfloat)m00
m01:(jfloat)m01
m02:(jfloat)m02
m10:(jfloat)m10
m11:(jfloat)m11
m12:(jfloat)m12
focusX:(jfloat)focusX
fractions:(void *)fractions
pixels:(void *)pixels;
@end
@interface MTLTexturePaint : MTLPaint
- (id)initWithUseMask:(jboolean)useMask
textureID:(id <MTLTexture>)textureID
isOpaque:(jboolean)isOpaque
filter:(jboolean)filter
xp0:(jdouble)xp0
xp1:(jdouble)xp1
xp3:(jdouble)xp3
yp0:(jdouble)yp0
yp1:(jdouble)yp1
yp3:(jdouble)yp3;
@end
#endif /* MTLPaints_h_Included */

View file

@ -0,0 +1,993 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include "MTLPaints.h"
#include "MTLClip.h"
#include "common.h"
#include "sun_java2d_SunGraphics2D.h"
#include "sun_java2d_pipe_BufferedPaints.h"
#import "MTLComposite.h"
#import "MTLBufImgOps.h"
#include "MTLRenderQueue.h"
#define RGBA_TO_V4(c) \
{ \
(((c) >> 16) & (0xFF))/255.0f, \
(((c) >> 8) & 0xFF)/255.0f, \
((c) & 0xFF)/255.0f, \
(((c) >> 24) & 0xFF)/255.0f \
}
#define FLOAT_ARR_TO_V4(p) \
{ \
p[0], \
p[1], \
p[2], \
p[3] \
}
static MTLRenderPipelineDescriptor * templateRenderPipelineDesc = nil;
static MTLRenderPipelineDescriptor * templateTexturePipelineDesc = nil;
static MTLRenderPipelineDescriptor * templateAATexturePipelineDesc = nil;
static MTLRenderPipelineDescriptor * templateLCDPipelineDesc = nil;
static MTLRenderPipelineDescriptor * templateAAPipelineDesc = nil;
static void
setTxtUniforms(MTLContext *mtlc, int color, id <MTLRenderCommandEncoder> encoder, int interpolation, bool repeat,
jfloat extraAlpha, const SurfaceRasterFlags *srcFlags, const SurfaceRasterFlags *dstFlags, int mode);
static void initTemplatePipelineDescriptors() {
if (templateRenderPipelineDesc != nil && templateTexturePipelineDesc != nil &&
templateAATexturePipelineDesc != nil && templateLCDPipelineDesc != nil &&
templateAAPipelineDesc != nil)
return;
MTLVertexDescriptor *vertDesc = [[MTLVertexDescriptor new] autorelease];
vertDesc.attributes[VertexAttributePosition].format = MTLVertexFormatFloat2;
vertDesc.attributes[VertexAttributePosition].offset = 0;
vertDesc.attributes[VertexAttributePosition].bufferIndex = MeshVertexBuffer;
vertDesc.layouts[MeshVertexBuffer].stride = sizeof(struct Vertex);
vertDesc.layouts[MeshVertexBuffer].stepRate = 1;
vertDesc.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;
templateRenderPipelineDesc = [MTLRenderPipelineDescriptor new];
templateRenderPipelineDesc.sampleCount = 1;
templateRenderPipelineDesc.vertexDescriptor = vertDesc;
templateRenderPipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
templateRenderPipelineDesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
templateRenderPipelineDesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
templateRenderPipelineDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOne;
templateRenderPipelineDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne;
templateRenderPipelineDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
templateRenderPipelineDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
templateRenderPipelineDesc.label = @"template_render";
templateTexturePipelineDesc = [templateRenderPipelineDesc copy];
templateTexturePipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].format = MTLVertexFormatFloat2;
templateTexturePipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].offset = 2*sizeof(float);
templateTexturePipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].bufferIndex = MeshVertexBuffer;
templateTexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stride = sizeof(struct TxtVertex);
templateTexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepRate = 1;
templateTexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;
templateTexturePipelineDesc.label = @"template_texture";
templateAATexturePipelineDesc = [templateTexturePipelineDesc copy];
templateAATexturePipelineDesc.label = @"template_aa_texture";
templateLCDPipelineDesc = [MTLRenderPipelineDescriptor new];
templateLCDPipelineDesc.sampleCount = 1;
templateLCDPipelineDesc.vertexDescriptor = vertDesc;
templateLCDPipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
templateLCDPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].format = MTLVertexFormatFloat2;
templateLCDPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].offset = 2*sizeof(float);
templateLCDPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].bufferIndex = MeshVertexBuffer;
templateLCDPipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stride = sizeof(struct TxtVertex);
templateLCDPipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepRate = 1;
templateLCDPipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;
templateLCDPipelineDesc.label = @"template_lcd";
vertDesc = [[MTLVertexDescriptor new] autorelease];
vertDesc.attributes[VertexAttributePosition].format = MTLVertexFormatFloat2;
vertDesc.attributes[VertexAttributePosition].offset = 0;
vertDesc.attributes[VertexAttributePosition].bufferIndex = MeshVertexBuffer;
vertDesc.layouts[MeshVertexBuffer].stride = sizeof(struct AAVertex);
vertDesc.layouts[MeshVertexBuffer].stepRate = 1;
vertDesc.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;
templateAAPipelineDesc = [MTLRenderPipelineDescriptor new];
templateAAPipelineDesc.sampleCount = 1;
templateAAPipelineDesc.vertexDescriptor = vertDesc;
templateAAPipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
templateAAPipelineDesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
templateAAPipelineDesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
templateAAPipelineDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOne;
templateAAPipelineDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne;
templateAAPipelineDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
templateAAPipelineDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
templateAAPipelineDesc.colorAttachments[0].blendingEnabled = YES;
templateAAPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].format = MTLVertexFormatFloat2;
templateAAPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].offset = 2*sizeof(float);
templateAAPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].bufferIndex = MeshVertexBuffer;
templateAAPipelineDesc.vertexDescriptor.attributes[VertexAttributeITexPos].format = MTLVertexFormatFloat2;
templateAAPipelineDesc.vertexDescriptor.attributes[VertexAttributeITexPos].offset = 4*sizeof(float);
templateAAPipelineDesc.vertexDescriptor.attributes[VertexAttributeITexPos].bufferIndex = MeshVertexBuffer;
templateAAPipelineDesc.label = @"template_aa";
}
@implementation MTLColorPaint {
// color-mode
jint _color;
}
- (id)initWithColor:(jint)color {
self = [super initWithState:sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR];
if (self) {
_color = color;
}
return self;
}
- (jint)color {
return _color;
}
- (BOOL)isEqual:(MTLColorPaint *)other {
if (other == self)
return YES;
if (!other || ![[other class] isEqual:[self class]])
return NO;
return _color == other->_color;
}
- (NSUInteger)hash {
NSUInteger h = [super hash];
h = h*31 + _color;
return h;
}
- (NSString *)description {
return [NSString stringWithFormat:
@"[r=%d g=%d b=%d a=%d]",
(_color >> 16) & (0xFF),
(_color >> 8) & 0xFF,
(_color) & 0xFF,
(_color >> 24) & 0xFF];
}
- (void)setPipelineState:(id<MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
{
initTemplatePipelineDescriptors();
MTLRenderPipelineDescriptor *rpDesc = nil;
NSString *vertShader = @"vert_col";
NSString *fragShader = @"frag_col";
if (renderOptions->isTexture) {
vertShader = @"vert_txt";
fragShader = @"frag_txt";
rpDesc = [[templateTexturePipelineDesc copy] autorelease];
if (renderOptions->isAA) {
fragShader = @"aa_frag_txt";
rpDesc = [[templateAATexturePipelineDesc copy] autorelease];
}
if (renderOptions->isText) {
fragShader = @"frag_text";
}
if (renderOptions->isLCD) {
vertShader = @"vert_txt_lcd";
fragShader = @"lcd_color";
rpDesc = [[templateLCDPipelineDesc copy] autorelease];
}
setTxtUniforms(mtlc, _color, encoder,
renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha], &renderOptions->srcFlags,
&renderOptions->dstFlags, 1);
} else if (renderOptions->isAAShader) {
vertShader = @"vert_col_aa";
fragShader = @"frag_col_aa";
rpDesc = [[templateAAPipelineDesc copy] autorelease];
} else {
rpDesc = [[templateRenderPipelineDesc copy] autorelease];
}
struct FrameUniforms uf = {RGBA_TO_V4(_color)};
[encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
vertexShaderId:vertShader
fragmentShaderId:fragShader
composite:mtlc.composite
renderOptions:renderOptions
stencilNeeded:[mtlc.clip isShape]];
[encoder setRenderPipelineState:pipelineState];
}
- (void)setXorModePipelineState:(id<MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
{
initTemplatePipelineDescriptors();
NSString * vertShader = @"vert_col_xorMode";
NSString * fragShader = @"frag_col_xorMode";
MTLRenderPipelineDescriptor * rpDesc = nil;
jint xorColor = (jint) [mtlc.composite getXorColor];
// Calculate _color ^ xorColor for RGB components
// This color gets XORed with destination framebuffer pixel color
const int col = _color ^ xorColor;
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
if (renderOptions->isTexture) {
vertShader = @"vert_txt_xorMode";
fragShader = @"frag_txt_xorMode";
rpDesc = [[templateTexturePipelineDesc copy] autorelease];
setTxtUniforms(mtlc, col, encoder,
renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha],
&renderOptions->srcFlags, &renderOptions->dstFlags, 1);
[encoder setFragmentBytes:&xorColor length:sizeof(xorColor) atIndex:0];
[encoder setFragmentTexture:dstOps->pTexture atIndex:1];
} else {
struct FrameUniforms uf = {RGBA_TO_V4(col)};
rpDesc = [[templateRenderPipelineDesc copy] autorelease];
[encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
[encoder setFragmentTexture:dstOps->pTexture atIndex:0];
}
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
vertexShaderId:vertShader
fragmentShaderId:fragShader
composite:mtlc.composite
renderOptions:renderOptions
stencilNeeded:[mtlc.clip isShape]];
[encoder setRenderPipelineState:pipelineState];
}
@end
@implementation MTLBaseGradPaint {
jboolean _useMask;
@protected
jint _cyclic;
}
- (id)initWithState:(jint)state mask:(jboolean)useMask cyclic:(jboolean)cyclic {
self = [super initWithState:state];
if (self) {
_useMask = useMask;
_cyclic = cyclic;
}
return self;
}
- (BOOL)isEqual:(MTLBaseGradPaint *)other {
if (other == self)
return YES;
if (!other || ![[other class] isEqual:[self class]])
return NO;
return [super isEqual:self] && _cyclic == other->_cyclic && _useMask == other->_useMask;
}
- (NSUInteger)hash {
NSUInteger h = [super hash];
h = h*31 + _cyclic;
h = h*31 + _useMask;
return h;
}
@end
@implementation MTLGradPaint {
jdouble _p0;
jdouble _p1;
jdouble _p3;
jint _pixel1;
jint _pixel2;
}
- (id)initWithUseMask:(jboolean)useMask
cyclic:(jboolean)cyclic
p0:(jdouble)p0
p1:(jdouble)p1
p3:(jdouble)p3
pixel1:(jint)pixel1
pixel2:(jint)pixel2
{
self = [super initWithState:sun_java2d_SunGraphics2D_PAINT_GRADIENT
mask:useMask
cyclic:cyclic];
if (self) {
_p0 = p0;
_p1 = p1;
_p3 = p3;
_pixel1 = pixel1;
_pixel2 = pixel2;
}
return self;
}
- (BOOL)isEqual:(MTLGradPaint *)other {
if (other == self)
return YES;
if (!other || ![[other class] isEqual:[self class]])
return NO;
return [super isEqual:self] && _p0 == other->_p0 &&
_p1 == other->_p1 && _p3 == other->_p3 &&
_pixel1 == other->_pixel1 && _pixel2 == other->_pixel2;
}
- (NSUInteger)hash {
NSUInteger h = [super hash];
h = h*31 + [@(_p0) hash];
h = h*31 + [@(_p1) hash];;
h = h*31 + [@(_p3) hash];;
h = h*31 + _pixel1;
h = h*31 + _pixel2;
return h;
}
- (NSString *)description {
return [NSString stringWithFormat:@"gradient"];
}
- (void)setPipelineState:(id)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
{
initTemplatePipelineDescriptors();
MTLRenderPipelineDescriptor *rpDesc = nil;
NSString *vertShader = @"vert_grad";
NSString *fragShader = @"frag_grad";
struct GradFrameUniforms uf = {
{_p0, _p1, _p3},
RGBA_TO_V4(_pixel1),
RGBA_TO_V4(_pixel2),
_cyclic,
[mtlc.composite getExtraAlpha]
};
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:0];
if (renderOptions->isTexture) {
vertShader = @"vert_txt_grad";
fragShader = @"frag_txt_grad";
rpDesc = [[templateTexturePipelineDesc copy] autorelease];
} else {
rpDesc = [[templateRenderPipelineDesc copy] autorelease];
}
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
vertexShaderId:vertShader
fragmentShaderId:fragShader
composite:mtlc.composite
renderOptions:renderOptions
stencilNeeded:[mtlc.clip isShape]];
[encoder setRenderPipelineState:pipelineState];
}
- (void)setXorModePipelineState:(id)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
{
// This block is not reached in current implementation.
// Gradient paint XOR mode rendering uses a tile based rendering using a SW pipe (similar to OGL)
initTemplatePipelineDescriptors();
NSString* vertShader = @"vert_grad_xorMode";
NSString* fragShader = @"frag_grad_xorMode";
MTLRenderPipelineDescriptor *rpDesc = [[templateRenderPipelineDesc copy] autorelease];
jint xorColor = (jint) [mtlc.composite getXorColor];
struct GradFrameUniforms uf = {
{_p0, _p1, _p3},
RGBA_TO_V4(_pixel1 ^ xorColor),
RGBA_TO_V4(_pixel2 ^ xorColor),
_cyclic,
[mtlc.composite getExtraAlpha]
};
[encoder setFragmentBytes: &uf length:sizeof(uf) atIndex:0];
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
[encoder setFragmentTexture:dstOps->pTexture atIndex:0];
J2dTraceLn(J2D_TRACE_INFO, "MTLPaints - setXorModePipelineState -- PAINT_GRADIENT");
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
vertexShaderId:vertShader
fragmentShaderId:fragShader
composite:mtlc.composite
renderOptions:renderOptions
stencilNeeded:[mtlc.clip isShape]];
[encoder setRenderPipelineState:pipelineState];
}
@end
@implementation MTLBaseMultiGradPaint {
@protected
jboolean _linear;
jint _numFracts;
jfloat _fract[GRAD_MAX_FRACTIONS];
jint _pixel[GRAD_MAX_FRACTIONS];
}
- (id)initWithState:(jint)state
mask:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jboolean)cycleMethod
numStops:(jint)numStops
fractions:(jfloat *)fractions
pixels:(jint *)pixels
{
self = [super initWithState:state
mask:useMask
cyclic:cycleMethod];
if (self) {
_linear = linear;
memcpy(_fract, fractions,numStops*sizeof(jfloat));
memcpy(_pixel, pixels, numStops*sizeof(jint));
_numFracts = numStops;
}
return self;
}
- (BOOL)isEqual:(MTLBaseMultiGradPaint *)other {
if (other == self)
return YES;
if (!other || ![[other class] isEqual:[self class]])
return NO;
if (_numFracts != other->_numFracts || ![super isEqual:self])
return NO;
for (int i = 0; i < _numFracts; i++) {
if (_fract[i] != other->_fract[i]) return NO;
if (_pixel[i] != other->_pixel[i]) return NO;
}
return YES;
}
- (NSUInteger)hash {
NSUInteger h = [super hash];
h = h*31 + _numFracts;
for (int i = 0; i < _numFracts; i++) {
h = h*31 + [@(_fract[i]) hash];
h = h*31 + _pixel[i];
}
return h;
}
@end
@implementation MTLLinearGradPaint {
jdouble _p0;
jdouble _p1;
jdouble _p3;
}
- (void)setPipelineState:(id)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
{
initTemplatePipelineDescriptors();
MTLRenderPipelineDescriptor *rpDesc = nil;
NSString *vertShader = @"vert_grad";
NSString *fragShader = @"frag_lin_grad";
if (renderOptions->isTexture) {
vertShader = @"vert_txt_grad";
fragShader = @"frag_txt_lin_grad";
rpDesc = [[templateTexturePipelineDesc copy] autorelease];
} else {
rpDesc = [[templateRenderPipelineDesc copy] autorelease];
}
struct LinGradFrameUniforms uf = {
{_p0, _p1, _p3},
{},
{},
_numFracts,
_linear,
_cyclic,
[mtlc.composite getExtraAlpha]
};
memcpy(uf.fract, _fract, _numFracts*sizeof(jfloat));
for (int i = 0; i < _numFracts; i++) {
vector_float4 v = RGBA_TO_V4(_pixel[i]);
uf.color[i] = v;
}
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:0];
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
vertexShaderId:vertShader
fragmentShaderId:fragShader
composite:mtlc.composite
renderOptions:renderOptions
stencilNeeded:[mtlc.clip isShape]];
[encoder setRenderPipelineState:pipelineState];
}
- (id)initWithUseMask:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jboolean)cycleMethod
numStops:(jint)numStops
p0:(jfloat)p0
p1:(jfloat)p1
p3:(jfloat)p3
fractions:(jfloat *)fractions
pixels:(jint *)pixels
{
self = [super initWithState:sun_java2d_SunGraphics2D_PAINT_LIN_GRADIENT
mask:useMask
linear:linear
cycleMethod:cycleMethod
numStops:numStops
fractions:fractions
pixels:pixels];
if (self) {
_p0 = p0;
_p1 = p1;
_p3 = p3;
}
return self;
}
- (BOOL)isEqual:(MTLLinearGradPaint *)other {
if (other == self)
return YES;
if (!other || ![[other class] isEqual:[self class]] || ![super isEqual:other])
return NO;
return _p0 == other->_p0 && _p1 == other->_p1 && _p3 == other->_p3;
}
- (NSUInteger)hash {
NSUInteger h = [super hash];
h = h*31 + [@(_p0) hash];
h = h*31 + [@(_p1) hash];
h = h*31 + [@(_p3) hash];
return h;
}
- (NSString *)description {
return [NSString stringWithFormat:@"linear_gradient"];
}
@end
@implementation MTLRadialGradPaint {
jfloat _m00;
jfloat _m01;
jfloat _m02;
jfloat _m10;
jfloat _m11;
jfloat _m12;
jfloat _focusX;
}
- (id)initWithUseMask:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jint)cycleMethod
numStops:(jint)numStops
m00:(jfloat)m00
m01:(jfloat)m01
m02:(jfloat)m02
m10:(jfloat)m10
m11:(jfloat)m11
m12:(jfloat)m12
focusX:(jfloat)focusX
fractions:(void *)fractions
pixels:(void *)pixels
{
self = [super initWithState:sun_java2d_SunGraphics2D_PAINT_RAD_GRADIENT
mask:useMask
linear:linear
cycleMethod:cycleMethod
numStops:numStops
fractions:fractions
pixels:pixels];
if (self) {
_m00 = m00;
_m01 = m01;
_m02 = m02;
_m10 = m10;
_m11 = m11;
_m12 = m12;
_focusX = focusX;
}
return self;
}
- (BOOL)isEqual:(MTLRadialGradPaint *)other {
if (other == self)
return YES;
if (!other || ![[other class] isEqual:[self class]]
|| ![super isEqual:self])
return NO;
return _m00 == other->_m00 && _m01 == other->_m01 && _m02 == other->_m02 &&
_m10 == other->_m10 && _m11 == other->_m11 && _m12 == other->_m12 &&
_focusX == other->_focusX;
}
- (NSUInteger)hash {
NSUInteger h = [super hash];
h = h*31 + [@(_m00) hash];
h = h*31 + [@(_m01) hash];
h = h*31 + [@(_m02) hash];
h = h*31 + [@(_m10) hash];
h = h*31 + [@(_m11) hash];
h = h*31 + [@(_m12) hash];
return h;
}
- (NSString *)description {
return [NSString stringWithFormat:@"radial_gradient"];
}
- (void)setPipelineState:(id)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
{
initTemplatePipelineDescriptors();
MTLRenderPipelineDescriptor *rpDesc = nil;
NSString *vertShader = @"vert_grad";
NSString *fragShader = @"frag_rad_grad";
if (renderOptions->isTexture) {
vertShader = @"vert_txt_grad";
fragShader = @"frag_txt_rad_grad";
rpDesc = [[templateTexturePipelineDesc copy] autorelease];
} else {
rpDesc = [[templateRenderPipelineDesc copy] autorelease];
}
struct RadGradFrameUniforms uf = {
{},
{},
_numFracts,
_linear,
_cyclic,
{_m00, _m01, _m02},
{_m10, _m11, _m12},
{},
[mtlc.composite getExtraAlpha]
};
uf.precalc[0] = _focusX;
uf.precalc[1] = 1.0 - (_focusX * _focusX);
uf.precalc[2] = 1.0 / uf.precalc[1];
memcpy(uf.fract, _fract, _numFracts*sizeof(jfloat));
for (int i = 0; i < _numFracts; i++) {
vector_float4 v = RGBA_TO_V4(_pixel[i]);
uf.color[i] = v;
}
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:0];
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
vertexShaderId:vertShader
fragmentShaderId:fragShader
composite:mtlc.composite
renderOptions:renderOptions
stencilNeeded:[mtlc.clip isShape]];
[encoder setRenderPipelineState:pipelineState];
}
@end
@implementation MTLTexturePaint {
struct AnchorData _anchor;
id <MTLTexture> _paintTexture;
jboolean _isOpaque;
}
- (id)initWithUseMask:(jboolean)useMask
textureID:(id)textureId
isOpaque:(jboolean)isOpaque
filter:(jboolean)filter
xp0:(jdouble)xp0
xp1:(jdouble)xp1
xp3:(jdouble)xp3
yp0:(jdouble)yp0
yp1:(jdouble)yp1
yp3:(jdouble)yp3
{
self = [super initWithState:sun_java2d_SunGraphics2D_PAINT_TEXTURE];
if (self) {
_paintTexture = textureId;
_anchor.xParams[0] = xp0;
_anchor.xParams[1] = xp1;
_anchor.xParams[2] = xp3;
_anchor.yParams[0] = yp0;
_anchor.yParams[1] = yp1;
_anchor.yParams[2] = yp3;
_isOpaque = isOpaque;
}
return self;
}
- (BOOL)isEqual:(MTLTexturePaint *)other {
if (other == self)
return YES;
if (!other || ![[other class] isEqual:[self class]])
return NO;
return [_paintTexture isEqual:other->_paintTexture]
&& _anchor.xParams[0] == other->_anchor.xParams[0]
&& _anchor.xParams[1] == other->_anchor.xParams[1]
&& _anchor.xParams[2] == other->_anchor.xParams[2]
&& _anchor.yParams[0] == other->_anchor.yParams[0]
&& _anchor.yParams[1] == other->_anchor.yParams[1]
&& _anchor.yParams[2] == other->_anchor.yParams[2];
}
- (NSString *)description {
return [NSString stringWithFormat:@"texture_paint"];
}
- (void)setPipelineState:(id)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
{
initTemplatePipelineDescriptors();
MTLRenderPipelineDescriptor *rpDesc = nil;
NSString* vertShader = @"vert_tp";
NSString* fragShader = @"frag_tp";
[encoder setVertexBytes:&_anchor length:sizeof(_anchor) atIndex:FrameUniformBuffer];
if (renderOptions->isTexture) {
vertShader = @"vert_txt_tp";
fragShader = @"frag_txt_tp";
rpDesc = [[templateTexturePipelineDesc copy] autorelease];
[encoder setFragmentTexture:_paintTexture atIndex:1];
} else {
rpDesc = [[templateRenderPipelineDesc copy] autorelease];
[encoder setFragmentTexture:_paintTexture atIndex:0];
}
const SurfaceRasterFlags srcFlags = {_isOpaque, renderOptions->srcFlags.isPremultiplied};
setTxtUniforms(mtlc, 0, encoder,
renderOptions->interpolation, YES, [mtlc.composite getExtraAlpha],
&srcFlags, &renderOptions->dstFlags, 0);
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
vertexShaderId:vertShader
fragmentShaderId:fragShader
composite:mtlc.composite
renderOptions:renderOptions
stencilNeeded:[mtlc.clip isShape]];
[encoder setRenderPipelineState:pipelineState];
}
- (void)setXorModePipelineState:(id)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
{
initTemplatePipelineDescriptors();
// This block is not reached in current implementation.
// Texture paint XOR mode rendering uses a tile based rendering using a SW pipe (similar to OGL)
NSString* vertShader = @"vert_tp_xorMode";
NSString* fragShader = @"frag_tp_xorMode";
MTLRenderPipelineDescriptor *rpDesc = [[templateRenderPipelineDesc copy] autorelease];
jint xorColor = (jint) [mtlc.composite getXorColor];
[encoder setVertexBytes:&_anchor length:sizeof(_anchor) atIndex:FrameUniformBuffer];
[encoder setFragmentTexture:_paintTexture atIndex: 0];
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
[encoder setFragmentTexture:dstOps->pTexture atIndex:1];
[encoder setFragmentBytes:&xorColor length:sizeof(xorColor) atIndex: 0];
J2dTraceLn(J2D_TRACE_INFO, "MTLPaints - setXorModePipelineState -- PAINT_TEXTURE");
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
vertexShaderId:vertShader
fragmentShaderId:fragShader
composite:mtlc.composite
renderOptions:renderOptions
stencilNeeded:[mtlc.clip isShape]];
[encoder setRenderPipelineState:pipelineState];
}
@end
@implementation MTLPaint {
jint _paintState;
}
- (instancetype)init {
self = [super init];
if (self) {
_paintState = sun_java2d_SunGraphics2D_PAINT_UNDEFINED;
}
return self;
}
- (instancetype)initWithState:(jint)state {
self = [super init];
if (self) {
_paintState = state;
}
return self;
}
- (BOOL)isEqual:(MTLPaint *)other {
if (other == self)
return YES;
if (!other || ![other isKindOfClass:[self class]])
return NO;
return _paintState == other->_paintState;
}
- (NSUInteger)hash {
return _paintState;
}
- (NSString *)description {
return @"unknown-paint";
}
static void
setTxtUniforms(MTLContext *mtlc, int color, id <MTLRenderCommandEncoder> encoder, int interpolation, bool repeat,
jfloat extraAlpha, const SurfaceRasterFlags *srcFlags, const SurfaceRasterFlags *dstFlags, int mode) {
struct TxtFrameUniforms uf = {RGBA_TO_V4(color), mode, srcFlags->isOpaque, dstFlags->isOpaque, extraAlpha};
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
[mtlc.samplerManager setSamplerWithEncoder:encoder interpolation:interpolation repeat:repeat];
}
// For the current paint mode:
// 1. Selects vertex+fragment shaders (and corresponding pipelineDesc) and set pipelineState
// 2. Set vertex and fragment buffers
// Base implementation is used in drawTex2Tex
- (void)setPipelineState:(id <MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
{
initTemplatePipelineDescriptors();
// Called from drawTex2Tex used in flushBuffer and for buffered image ops
if (renderOptions->isTexture) {
NSString * vertShader = @"vert_txt";
NSString * fragShader = @"frag_txt";
MTLRenderPipelineDescriptor* rpDesc = [[templateTexturePipelineDesc copy] autorelease];
NSObject *bufImgOp = [mtlc getBufImgOp];
if (bufImgOp != nil) {
if ([bufImgOp isKindOfClass:[MTLRescaleOp class]]) {
MTLRescaleOp *rescaleOp = bufImgOp;
fragShader = @"frag_txt_op_rescale";
struct TxtFrameOpRescaleUniforms uf = {
RGBA_TO_V4(0), [mtlc.composite getExtraAlpha], renderOptions->srcFlags.isOpaque,
rescaleOp.isNonPremult,
FLOAT_ARR_TO_V4([rescaleOp getScaleFactors]), FLOAT_ARR_TO_V4([rescaleOp getOffsets])
};
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
[mtlc.samplerManager setSamplerWithEncoder:encoder interpolation:renderOptions->interpolation repeat:NO];
} else if ([bufImgOp isKindOfClass:[MTLConvolveOp class]]) {
MTLConvolveOp *convolveOp = bufImgOp;
fragShader = @"frag_txt_op_convolve";
struct TxtFrameOpConvolveUniforms uf = {
[mtlc.composite getExtraAlpha], renderOptions->srcFlags.isOpaque,
FLOAT_ARR_TO_V4([convolveOp getImgEdge]),
convolveOp.kernelSize, convolveOp.isEdgeZeroFill,
};
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
[mtlc.samplerManager setSamplerWithEncoder:encoder interpolation:renderOptions->interpolation repeat:NO];
[encoder setFragmentBuffer:[convolveOp getBuffer] offset:0 atIndex:2];
} else if ([bufImgOp isKindOfClass:[MTLLookupOp class]]) {
MTLLookupOp *lookupOp = bufImgOp;
fragShader = @"frag_txt_op_lookup";
struct TxtFrameOpLookupUniforms uf = {
[mtlc.composite getExtraAlpha], renderOptions->srcFlags.isOpaque,
FLOAT_ARR_TO_V4([lookupOp getOffset]), lookupOp.isUseSrcAlpha, lookupOp.isNonPremult,
};
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
[mtlc.samplerManager setSamplerWithEncoder:encoder interpolation:renderOptions->interpolation repeat:NO];
[encoder setFragmentTexture:[lookupOp getLookupTexture] atIndex:1];
}
} else {
setTxtUniforms(mtlc, 0, encoder,
renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha],
&renderOptions->srcFlags,
&renderOptions->dstFlags, 0);
}
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
vertexShaderId:vertShader
fragmentShaderId:fragShader
composite:mtlc.composite
renderOptions:renderOptions
stencilNeeded:[mtlc.clip isShape]];
[encoder setRenderPipelineState:pipelineState];
}
}
// For the current paint mode:
// 1. Selects vertex+fragment shaders (and corresponding pipelineDesc) and set pipelineState
// 2. Set vertex and fragment buffers
- (void)setXorModePipelineState:(id <MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
{
initTemplatePipelineDescriptors();
if (renderOptions->isTexture) {
jint xorColor = (jint) [mtlc.composite getXorColor];
NSString * vertShader = @"vert_txt_xorMode";
NSString * fragShader = @"frag_txt_xorMode";
MTLRenderPipelineDescriptor * rpDesc = [[templateTexturePipelineDesc copy] autorelease];
const int col = 0 ^ xorColor;
setTxtUniforms(mtlc, col, encoder,
renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha],
&renderOptions->srcFlags, &renderOptions->dstFlags, 0);
[encoder setFragmentBytes:&xorColor length:sizeof(xorColor) atIndex: 0];
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
[encoder setFragmentTexture:dstOps->pTexture atIndex:1];
setTxtUniforms(mtlc, 0, encoder,
renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha],
&renderOptions->srcFlags,
&renderOptions->dstFlags, 0);
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
vertexShaderId:vertShader
fragmentShaderId:fragShader
composite:mtlc.composite
renderOptions:renderOptions
stencilNeeded:[mtlc.clip isShape]];
[encoder setRenderPipelineState:pipelineState];
}
}
@end

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLPipelineStatesStorage_h_Included
#define MTLPipelineStatesStorage_h_Included
#import "MTLUtils.h"
#include "RenderOptions.h"
@class MTLComposite;
/**
* The MTLPipelineStatesStorage class used to obtain MTLRenderPipelineState
* */
@interface MTLPipelineStatesStorage : NSObject {
@private
id<MTLDevice> device;
id<MTLLibrary> library;
NSMutableDictionary<NSString*, id<MTLFunction>> * shaders;
NSMutableDictionary<NSString*, id<MTLComputePipelineState>> * computeStates;
}
@property (readwrite, assign) id<MTLDevice> device;
@property (readwrite, retain) id<MTLLibrary> library;
@property (readwrite, retain) NSMutableDictionary<NSString*, id<MTLFunction>> * shaders;
@property (readwrite, retain) NSMutableDictionary<NSString*, NSMutableDictionary *> * states;
- (id) initWithDevice:(id<MTLDevice>)device shaderLibPath:(NSString *)shadersLib;
- (id<MTLRenderPipelineState>) getPipelineState:(MTLRenderPipelineDescriptor *) pipelineDescriptor
vertexShaderId:(NSString *)vertexShaderId
fragmentShaderId:(NSString *)fragmentShaderId;
- (id<MTLRenderPipelineState>) getPipelineState:(MTLRenderPipelineDescriptor *) pipelineDescriptor
vertexShaderId:(NSString *)vertexShaderId
fragmentShaderId:(NSString *)fragmentShaderId
stencilNeeded:(bool)stencilNeeded;
- (id<MTLRenderPipelineState>) getPipelineState:(MTLRenderPipelineDescriptor *) pipelineDescriptor
vertexShaderId:(NSString *)vertexShaderId
fragmentShaderId:(NSString *)fragmentShaderId
composite:(MTLComposite*)composite
renderOptions:(const RenderOptions *)renderOptions
stencilNeeded:(bool)stencilNeeded;
- (id<MTLComputePipelineState>) getComputePipelineState:(NSString *)computeShaderId;
- (id<MTLFunction>) getShader:(NSString *)name;
@end
#endif // MTLPipelineStatesStorage_h_Included

View file

@ -0,0 +1,325 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#import "MTLPipelineStatesStorage.h"
#include "GraphicsPrimitiveMgr.h"
#import "MTLComposite.h"
#include "sun_java2d_SunGraphics2D.h"
extern const SurfaceRasterFlags defaultRasterFlags;
static void setBlendingFactors(
MTLRenderPipelineColorAttachmentDescriptor * cad,
MTLComposite* composite,
const RenderOptions * renderOptions);
@implementation MTLPipelineStatesStorage
@synthesize device;
@synthesize library;
@synthesize shaders;
@synthesize states;
- (id) initWithDevice:(id<MTLDevice>)dev shaderLibPath:(NSString *)shadersLib {
self = [super init];
if (self == nil) return self;
self.device = dev;
NSError *error = nil;
self.library = [dev newLibraryWithFile:shadersLib error:&error];
if (!self.library) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLPipelineStatesStorage.initWithDevice() - Failed to load Metal shader library.");
return nil;
}
self.shaders = [NSMutableDictionary dictionaryWithCapacity:10];
self.states = [NSMutableDictionary dictionaryWithCapacity:10];
computeStates = [[NSMutableDictionary dictionaryWithCapacity:10] retain] ;
return self;
}
- (NSPointerArray * ) getSubStates:(NSString *)vertexShaderId fragmentShader:(NSString *)fragmentShaderId {
NSMutableDictionary * vSubStates = states[vertexShaderId];
if (vSubStates == nil) {
@autoreleasepool {
vSubStates = [NSMutableDictionary dictionary];
[states setObject:vSubStates forKey:vertexShaderId];
}
}
NSPointerArray * sSubStates = vSubStates[fragmentShaderId];
if (sSubStates == nil) {
@autoreleasepool {
sSubStates = [NSPointerArray strongObjectsPointerArray];
[vSubStates setObject:sSubStates forKey:fragmentShaderId];
}
}
return sSubStates;
}
- (id<MTLRenderPipelineState>) getPipelineState:(MTLRenderPipelineDescriptor *) pipelineDescriptor
vertexShaderId:(NSString *)vertexShaderId
fragmentShaderId:(NSString *)fragmentShaderId
{
RenderOptions defaultOptions = {JNI_FALSE, JNI_FALSE, 0/*unused*/, {JNI_FALSE, JNI_TRUE}, {JNI_FALSE, JNI_TRUE}, JNI_FALSE, JNI_FALSE, JNI_FALSE};
return [self getPipelineState:pipelineDescriptor
vertexShaderId:vertexShaderId
fragmentShaderId:fragmentShaderId
composite:nil
renderOptions:&defaultOptions
stencilNeeded:NO];
}
- (id<MTLRenderPipelineState>) getPipelineState:(MTLRenderPipelineDescriptor *) pipelineDescriptor
vertexShaderId:(NSString *)vertexShaderId
fragmentShaderId:(NSString *)fragmentShaderId
stencilNeeded:(bool)stencilNeeded
{
RenderOptions defaultOptions = {JNI_FALSE, JNI_FALSE, 0/*unused*/, {JNI_FALSE, JNI_TRUE}, {JNI_FALSE, JNI_TRUE}, JNI_FALSE, JNI_FALSE, JNI_FALSE};
return [self getPipelineState:pipelineDescriptor
vertexShaderId:vertexShaderId
fragmentShaderId:fragmentShaderId
composite:nil
renderOptions:&defaultOptions
stencilNeeded:stencilNeeded];
}
// Base method to obtain MTLRenderPipelineState.
// NOTE: parameters compositeRule, srcFlags, dstFlags are used to set MTLRenderPipelineColorAttachmentDescriptor multipliers
- (id<MTLRenderPipelineState>) getPipelineState:(MTLRenderPipelineDescriptor *) pipelineDescriptor
vertexShaderId:(NSString *)vertexShaderId
fragmentShaderId:(NSString *)fragmentShaderId
composite:(MTLComposite*) composite
renderOptions:(const RenderOptions *)renderOptions
stencilNeeded:(bool)stencilNeeded;
{
jint compositeRule = composite != nil ? [composite getRule] : RULE_Src;
const jboolean useXorComposite = composite != nil && [composite getCompositeState] == sun_java2d_SunGraphics2D_COMP_XOR;
const jboolean useComposite = composite != nil && compositeRule >= 0
&& compositeRule < java_awt_AlphaComposite_MAX_RULE;
// Calculate index by flags and compositeRule
// TODO: reimplement, use map with convenient key (calculated by all arguments)
int subIndex = 0;
if (useXorComposite) {
// compositeRule value is already XOR_COMPOSITE_RULE
}
else {
if (useComposite) {
if (!renderOptions->srcFlags.isPremultiplied)
subIndex |= 1;
if (renderOptions->srcFlags.isOpaque)
subIndex |= 1 << 1;
if (!renderOptions->dstFlags.isPremultiplied)
subIndex |= 1 << 2;
if (renderOptions->dstFlags.isOpaque)
subIndex |= 1 << 3;
} else
compositeRule = RULE_Src;
}
if (stencilNeeded) {
subIndex |= 1 << 4;
}
if (renderOptions->isAA) {
subIndex |= 1 << 5;
}
if ((composite != nil && FLT_LT([composite getExtraAlpha], 1.0f))) {
subIndex |= 1 << 6;
}
int index = compositeRule*64 + subIndex;
NSPointerArray * subStates = [self getSubStates:vertexShaderId fragmentShader:fragmentShaderId];
if (index >= subStates.count) {
subStates.count = (NSUInteger) (index + 1);
}
id<MTLRenderPipelineState> result = [subStates pointerAtIndex:index];
if (result == nil) {
@autoreleasepool {
id <MTLFunction> vertexShader = [self getShader:vertexShaderId];
id <MTLFunction> fragmentShader = [self getShader:fragmentShaderId];
MTLRenderPipelineDescriptor *pipelineDesc = [[pipelineDescriptor copy] autorelease];
pipelineDesc.vertexFunction = vertexShader;
pipelineDesc.fragmentFunction = fragmentShader;
if (useXorComposite) {
/* The below configuration is the best performant implementation of XOR mode rendering.
It was found that it works ONLY for basic Colors and not for all RGB combinations.
Hence, a slow performant XOR mode rendering has been implemented by
disabling blending & committing after each draw call.
In XOR mode rendering, subsequent draw calls are rendered
by shader using already rendered framebuffer pixel value XORed
with current draw color and XOR color.
pipelineDesc.colorAttachments[0].blendingEnabled = YES;
pipelineDesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
pipelineDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOneMinusDestinationColor;
pipelineDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceColor;
*/
pipelineDesc.colorAttachments[0].blendingEnabled = NO;
} else if (useComposite ||
(composite != nil &&
FLT_LT([composite getExtraAlpha], 1.0f)))
{
setBlendingFactors(
pipelineDesc.colorAttachments[0],
composite,
renderOptions
);
}
if (stencilNeeded) {
pipelineDesc.stencilAttachmentPixelFormat = MTLPixelFormatStencil8;
} else {
// We continue to use same encoder when we move from shape clip
// to other opcodes. So we need to maintain apprppriate state
// for stencilAttachmentPixelFormat until we end the encoder
pipelineDesc.stencilAttachmentPixelFormat = MTLPixelFormatInvalid;
}
if (renderOptions->isAA) {
pipelineDesc.sampleCount = MTLAASampleCount;
pipelineDesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
pipelineDesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
pipelineDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOne;
pipelineDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne;
pipelineDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
pipelineDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
pipelineDesc.colorAttachments[0].blendingEnabled = YES;
}
NSError *error = nil;
result = [[self.device newRenderPipelineStateWithDescriptor:pipelineDesc error:&error] autorelease];
if (result == nil) {
NSLog(@"Failed to create pipeline state, error %@", error);
exit(0);
}
[subStates insertPointer:result atIndex:index];
}
}
return result;
}
- (id<MTLComputePipelineState>) getComputePipelineState:(NSString *)computeShaderId {
id<MTLComputePipelineState> result = computeStates[computeShaderId];
if (result == nil) {
id <MTLFunction> computeShader = [self getShader:computeShaderId];
@autoreleasepool {
NSError *error = nil;
result = (id <MTLComputePipelineState>) [[self.device newComputePipelineStateWithFunction:computeShader error:&error] autorelease];
if (result == nil) {
NSLog(@"Failed to create pipeline state, error %@", error);
exit(0);
}
computeStates[computeShaderId] = result;
}
}
return result;
}
- (id<MTLFunction>) getShader:(NSString *)name {
id<MTLFunction> result = [self.shaders valueForKey:name];
if (result == nil) {
result = [[self.library newFunctionWithName:name] autorelease];
[self.shaders setValue:result forKey:name];
}
return result;
}
- (void) dealloc {
[super dealloc];
[computeStates release];
}
@end
/**
* The MTLBlendRule structure encapsulates the two enumerated values that
* comprise a given Porter-Duff blending (compositing) rule. For example,
* the "SrcOver" rule can be represented by:
* rule.src = GL_ONE;
* rule.dst = GL_ONE_MINUS_SRC_ALPHA;
*
* GLenum src;
* The constant representing the source factor in this Porter-Duff rule.
*
* GLenum dst;
* The constant representing the destination factor in this Porter-Duff rule.
*/
struct MTLBlendRule {
MTLBlendFactor src;
MTLBlendFactor dst;
};
/**
* This table contains the standard blending rules (or Porter-Duff compositing
* factors) used in setBlendingFactors(), indexed by the rule constants from the
* AlphaComposite class.
*/
static struct MTLBlendRule StdBlendRules[] = {
{ MTLBlendFactorZero, MTLBlendFactorZero }, /* 0 - Nothing */
{ MTLBlendFactorZero, MTLBlendFactorZero }, /* 1 - RULE_Clear */
{ MTLBlendFactorOne, MTLBlendFactorZero }, /* 2 - RULE_Src */
{ MTLBlendFactorOne, MTLBlendFactorOneMinusSourceAlpha }, /* 3 - RULE_SrcOver */
{ MTLBlendFactorOneMinusDestinationAlpha, MTLBlendFactorOne }, /* 4 - RULE_DstOver */
{ MTLBlendFactorDestinationAlpha, MTLBlendFactorZero }, /* 5 - RULE_SrcIn */
{ MTLBlendFactorZero, MTLBlendFactorSourceAlpha }, /* 6 - RULE_DstIn */
{ MTLBlendFactorOneMinusDestinationAlpha, MTLBlendFactorZero }, /* 7 - RULE_SrcOut */
{ MTLBlendFactorZero, MTLBlendFactorOneMinusSourceAlpha }, /* 8 - RULE_DstOut */
{ MTLBlendFactorZero, MTLBlendFactorOne }, /* 9 - RULE_Dst */
{ MTLBlendFactorDestinationAlpha, MTLBlendFactorOneMinusSourceAlpha }, /*10 - RULE_SrcAtop */
{ MTLBlendFactorOneMinusDestinationAlpha, MTLBlendFactorSourceAlpha }, /*11 - RULE_DstAtop */
{ MTLBlendFactorOneMinusDestinationAlpha, MTLBlendFactorOneMinusSourceAlpha }, /*12 - RULE_AlphaXor*/
};
static void setBlendingFactors(
MTLRenderPipelineColorAttachmentDescriptor * cad,
MTLComposite* composite,
const RenderOptions * renderOptions
) {
const long compositeRule = composite != nil ? [composite getRule] : RULE_Src;
if ((compositeRule == RULE_Src || compositeRule == RULE_SrcOver) &&
(composite == nil || FLT_GE([composite getExtraAlpha], 1.0f)) &&
(renderOptions->srcFlags.isOpaque))
{
cad.blendingEnabled = NO;
return;
}
cad.blendingEnabled = YES;
cad.rgbBlendOperation = MTLBlendOperationAdd;
cad.alphaBlendOperation = MTLBlendOperationAdd;
cad.sourceAlphaBlendFactor = StdBlendRules[compositeRule].src;
cad.sourceRGBBlendFactor = StdBlendRules[compositeRule].src;
cad.destinationAlphaBlendFactor = StdBlendRules[compositeRule].dst;
cad.destinationRGBBlendFactor = StdBlendRules[compositeRule].dst;
}

View file

@ -0,0 +1,106 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLRenderQueue_h_Included
#define MTLRenderQueue_h_Included
#include "MTLContext.h"
#include "MTLSurfaceData.h"
#include "MTLVertexCache.h"
/*
* The following macros are used to pick values (of the specified type) off
* the queue.
*/
#define NEXT_VAL(buf, type) (((type *)((buf) += sizeof(type)))[-1])
#define NEXT_BYTE(buf) NEXT_VAL(buf, unsigned char)
#define NEXT_INT(buf) NEXT_VAL(buf, jint)
#define NEXT_FLOAT(buf) NEXT_VAL(buf, jfloat)
#define NEXT_BOOLEAN(buf) (jboolean)NEXT_INT(buf)
#define NEXT_LONG(buf) NEXT_VAL(buf, jlong)
#define NEXT_DOUBLE(buf) NEXT_VAL(buf, jdouble)
// Operations for CheckPreviousOp
enum {
MTL_OP_INIT,
MTL_OP_AA,
MTL_OP_SET_COLOR,
MTL_OP_RESET_PAINT,
MTL_OP_SYNC,
MTL_OP_SHAPE_CLIP_SPANS,
MTL_OP_MASK_OP,
MTL_OP_OTHER
};
/*
* These macros now simply delegate to the CheckPreviousOp() method.
*/
#define CHECK_PREVIOUS_OP(op) MTLRenderQueue_CheckPreviousOp(op)
#define RESET_PREVIOUS_OP() {mtlPreviousOp = MTL_OP_INIT;}
/*
* Increments a pointer (buf) by the given number of bytes.
*/
#define SKIP_BYTES(buf, numbytes) buf += (numbytes)
/*
* Extracts a value at the given offset from the provided packed value.
*/
#define EXTRACT_VAL(packedval, offset, mask) \
(((packedval) >> (offset)) & (mask))
#define EXTRACT_BYTE(packedval, offset) \
(unsigned char)EXTRACT_VAL(packedval, offset, 0xff)
#define EXTRACT_BOOLEAN(packedval, offset) \
(jboolean)EXTRACT_VAL(packedval, offset, 0x1)
/*
* The following macros allow the caller to return (or continue) if the
* provided value is NULL. (The strange else clause is included below to
* allow for a trailing ';' after RETURN/CONTINUE_IF_NULL() invocations.)
*/
#define ACT_IF_NULL(ACTION, value) \
if ((value) == NULL) { \
J2dTraceLn1(J2D_TRACE_ERROR, \
"%s is null", #value); \
ACTION; \
} else do { } while (0)
#define RETURN_IF_NULL(value) ACT_IF_NULL(return, value)
#define CONTINUE_IF_NULL(value) ACT_IF_NULL(continue, value)
#define ACT_IF_TRUE(ACTION, value) \
if ((value)) { \
J2dTraceLn1(J2D_TRACE_ERROR, \
"%s is false", #value);\
ACTION; \
} else do { } while (0)
#define RETURN_IF_TRUE(value) ACT_IF_TRUE(return, value)
MTLContext *MTLRenderQueue_GetCurrentContext();
BMTLSDOps *MTLRenderQueue_GetCurrentDestination();
void commitEncodedCommands();
extern jint mtlPreviousOp;
#endif /* MTLRenderQueue_h_Included */

View file

@ -0,0 +1,956 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include <stdlib.h>
#include "sun_java2d_pipe_BufferedOpCodes.h"
#include "jlong.h"
#include "MTLBlitLoops.h"
#include "MTLBufImgOps.h"
#include "MTLMaskBlit.h"
#include "MTLMaskFill.h"
#include "MTLPaints.h"
#include "MTLRenderQueue.h"
#include "MTLRenderer.h"
#include "MTLTextRenderer.h"
#import "ThreadUtilities.h"
/**
* References to the "current" context and destination surface.
*/
static MTLContext *mtlc = NULL;
static BMTLSDOps *dstOps = NULL;
jint mtlPreviousOp = MTL_OP_INIT;
/**
* The following methods are implemented in the windowing system (i.e. GLX
* and WGL) source files.
*/
extern void MTLGC_DestroyMTLGraphicsConfig(jlong pConfigInfo);
void MTLRenderQueue_CheckPreviousOp(jint op) {
if (mtlPreviousOp == op) {
// The op is the same as last time, so we can return immediately.
return;
}
if (op == MTL_OP_SET_COLOR) {
if (mtlPreviousOp != MTL_OP_MASK_OP) {
return; // SET_COLOR should not cause endEncoder
}
} else if (op == MTL_OP_MASK_OP) {
MTLVertexCache_EnableMaskCache(mtlc, dstOps);
mtlPreviousOp = op;
return;
}
J2dTraceLn1(J2D_TRACE_VERBOSE,
"MTLRenderQueue_CheckPreviousOp: new op=%d", op);
switch (mtlPreviousOp) {
case MTL_OP_INIT :
mtlPreviousOp = op;
return;
case MTL_OP_MASK_OP :
MTLVertexCache_DisableMaskCache(mtlc);
break;
}
if (mtlc != NULL) {
[mtlc.encoderManager endEncoder];
if (op == MTL_OP_RESET_PAINT || op == MTL_OP_SYNC || op == MTL_OP_SHAPE_CLIP_SPANS) {
MTLCommandBufferWrapper *cbwrapper = [mtlc pullCommandBufferWrapper];
id <MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
if (op == MTL_OP_SYNC || op == MTL_OP_SHAPE_CLIP_SPANS) {
[commandbuf waitUntilCompleted];
}
}
}
mtlPreviousOp = op;
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLRenderQueue_flushBuffer
(JNIEnv *env, jobject mtlrq,
jlong buf, jint limit)
{
unsigned char *b, *end;
J2dTraceLn1(J2D_TRACE_INFO,
"MTLRenderQueue_flushBuffer: limit=%d", limit);
b = (unsigned char *)jlong_to_ptr(buf);
if (b == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLRenderQueue_flushBuffer: cannot get direct buffer address");
return;
}
end = b + limit;
@autoreleasepool {
while (b < end) {
jint opcode = NEXT_INT(b);
J2dTraceLn2(J2D_TRACE_VERBOSE,
"MTLRenderQueue_flushBuffer: opcode=%d, rem=%d",
opcode, (end-b));
switch (opcode) {
// draw ops
case sun_java2d_pipe_BufferedOpCodes_DRAW_LINE:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
if ([mtlc useXORComposite]) {
commitEncodedCommands();
J2dTraceLn(J2D_TRACE_VERBOSE,
"DRAW_LINE in XOR mode - Force commit earlier draw calls before DRAW_LINE.");
}
jint x1 = NEXT_INT(b);
jint y1 = NEXT_INT(b);
jint x2 = NEXT_INT(b);
jint y2 = NEXT_INT(b);
MTLRenderer_DrawLine(mtlc, dstOps, x1, y1, x2, y2);
break;
}
case sun_java2d_pipe_BufferedOpCodes_DRAW_RECT:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
if ([mtlc useXORComposite]) {
commitEncodedCommands();
J2dTraceLn(J2D_TRACE_VERBOSE,
"DRAW_RECT in XOR mode - Force commit earlier draw calls before DRAW_RECT.");
}
jint x = NEXT_INT(b);
jint y = NEXT_INT(b);
jint w = NEXT_INT(b);
jint h = NEXT_INT(b);
MTLRenderer_DrawRect(mtlc, dstOps, x, y, w, h);
break;
}
case sun_java2d_pipe_BufferedOpCodes_DRAW_POLY:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jint nPoints = NEXT_INT(b);
jboolean isClosed = NEXT_BOOLEAN(b);
jint transX = NEXT_INT(b);
jint transY = NEXT_INT(b);
jint *xPoints = (jint *)b;
jint *yPoints = ((jint *)b) + nPoints;
if ([mtlc useXORComposite]) {
commitEncodedCommands();
J2dTraceLn(J2D_TRACE_VERBOSE,
"DRAW_POLY in XOR mode - Force commit earlier draw calls before DRAW_POLY.");
// draw separate (N-1) lines using N points
for(int point = 0; point < nPoints-1; point++) {
jint x1 = xPoints[point] + transX;
jint y1 = yPoints[point] + transY;
jint x2 = xPoints[point + 1] + transX;
jint y2 = yPoints[point + 1] + transY;
MTLRenderer_DrawLine(mtlc, dstOps, x1, y1, x2, y2);
}
if (isClosed) {
MTLRenderer_DrawLine(mtlc, dstOps, xPoints[0] + transX, yPoints[0] + transY,
xPoints[nPoints-1] + transX, yPoints[nPoints-1] + transY);
}
} else {
MTLRenderer_DrawPoly(mtlc, dstOps, nPoints, isClosed, transX, transY, xPoints, yPoints);
}
SKIP_BYTES(b, nPoints * BYTES_PER_POLY_POINT);
break;
}
case sun_java2d_pipe_BufferedOpCodes_DRAW_PIXEL:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
if ([mtlc useXORComposite]) {
commitEncodedCommands();
J2dTraceLn(J2D_TRACE_VERBOSE,
"DRAW_PIXEL in XOR mode - Force commit earlier draw calls before DRAW_PIXEL.");
}
jint x = NEXT_INT(b);
jint y = NEXT_INT(b);
CONTINUE_IF_NULL(mtlc);
MTLRenderer_DrawPixel(mtlc, dstOps, x, y);
break;
}
case sun_java2d_pipe_BufferedOpCodes_DRAW_SCANLINES:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
if ([mtlc useXORComposite]) {
commitEncodedCommands();
J2dTraceLn(J2D_TRACE_VERBOSE,
"DRAW_SCANLINES in XOR mode - Force commit earlier draw calls before "
"DRAW_SCANLINES.");
}
jint count = NEXT_INT(b);
MTLRenderer_DrawScanlines(mtlc, dstOps, count, (jint *)b);
SKIP_BYTES(b, count * BYTES_PER_SCANLINE);
break;
}
case sun_java2d_pipe_BufferedOpCodes_DRAW_PARALLELOGRAM:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
if ([mtlc useXORComposite]) {
commitEncodedCommands();
J2dTraceLn(J2D_TRACE_VERBOSE,
"DRAW_PARALLELOGRAM in XOR mode - Force commit earlier draw calls before "
"DRAW_PARALLELOGRAM.");
}
jfloat x11 = NEXT_FLOAT(b);
jfloat y11 = NEXT_FLOAT(b);
jfloat dx21 = NEXT_FLOAT(b);
jfloat dy21 = NEXT_FLOAT(b);
jfloat dx12 = NEXT_FLOAT(b);
jfloat dy12 = NEXT_FLOAT(b);
jfloat lwr21 = NEXT_FLOAT(b);
jfloat lwr12 = NEXT_FLOAT(b);
MTLRenderer_DrawParallelogram(mtlc, dstOps,
x11, y11,
dx21, dy21,
dx12, dy12,
lwr21, lwr12);
break;
}
case sun_java2d_pipe_BufferedOpCodes_DRAW_AAPARALLELOGRAM:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jfloat x11 = NEXT_FLOAT(b);
jfloat y11 = NEXT_FLOAT(b);
jfloat dx21 = NEXT_FLOAT(b);
jfloat dy21 = NEXT_FLOAT(b);
jfloat dx12 = NEXT_FLOAT(b);
jfloat dy12 = NEXT_FLOAT(b);
jfloat lwr21 = NEXT_FLOAT(b);
jfloat lwr12 = NEXT_FLOAT(b);
MTLRenderer_DrawAAParallelogram(mtlc, dstOps,
x11, y11,
dx21, dy21,
dx12, dy12,
lwr21, lwr12);
break;
}
// fill ops
case sun_java2d_pipe_BufferedOpCodes_FILL_RECT:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
if ([mtlc useXORComposite]) {
commitEncodedCommands();
J2dTraceLn(J2D_TRACE_VERBOSE,
"FILL_RECT in XOR mode - Force commit earlier draw calls before FILL_RECT.");
}
jint x = NEXT_INT(b);
jint y = NEXT_INT(b);
jint w = NEXT_INT(b);
jint h = NEXT_INT(b);
MTLRenderer_FillRect(mtlc, dstOps, x, y, w, h);
break;
}
case sun_java2d_pipe_BufferedOpCodes_FILL_SPANS:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
if ([mtlc useXORComposite]) {
commitEncodedCommands();
J2dTraceLn(J2D_TRACE_VERBOSE,
"FILL_SPANS in XOR mode - Force commit earlier draw calls before FILL_SPANS.");
}
jint count = NEXT_INT(b);
MTLRenderer_FillSpans(mtlc, dstOps, count, (jint *)b);
SKIP_BYTES(b, count * BYTES_PER_SPAN);
break;
}
case sun_java2d_pipe_BufferedOpCodes_FILL_PARALLELOGRAM:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
if ([mtlc useXORComposite]) {
commitEncodedCommands();
J2dTraceLn(J2D_TRACE_VERBOSE,
"FILL_PARALLELOGRAM in XOR mode - Force commit earlier draw calls before "
"FILL_PARALLELOGRAM.");
}
jfloat x11 = NEXT_FLOAT(b);
jfloat y11 = NEXT_FLOAT(b);
jfloat dx21 = NEXT_FLOAT(b);
jfloat dy21 = NEXT_FLOAT(b);
jfloat dx12 = NEXT_FLOAT(b);
jfloat dy12 = NEXT_FLOAT(b);
MTLRenderer_FillParallelogram(mtlc, dstOps,
x11, y11,
dx21, dy21,
dx12, dy12);
break;
}
case sun_java2d_pipe_BufferedOpCodes_FILL_AAPARALLELOGRAM:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jfloat x11 = NEXT_FLOAT(b);
jfloat y11 = NEXT_FLOAT(b);
jfloat dx21 = NEXT_FLOAT(b);
jfloat dy21 = NEXT_FLOAT(b);
jfloat dx12 = NEXT_FLOAT(b);
jfloat dy12 = NEXT_FLOAT(b);
MTLRenderer_FillAAParallelogram(mtlc, dstOps,
x11, y11,
dx21, dy21,
dx12, dy12);
break;
}
// text-related ops
case sun_java2d_pipe_BufferedOpCodes_DRAW_GLYPH_LIST:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
if ([mtlc useXORComposite]) {
commitEncodedCommands();
J2dTraceLn(J2D_TRACE_VERBOSE,
"DRAW_GLYPH_LIST in XOR mode - Force commit earlier draw calls before "
"DRAW_GLYPH_LIST.");
}
jint numGlyphs = NEXT_INT(b);
jint packedParams = NEXT_INT(b);
jfloat glyphListOrigX = NEXT_FLOAT(b);
jfloat glyphListOrigY = NEXT_FLOAT(b);
jboolean usePositions = EXTRACT_BOOLEAN(packedParams,
OFFSET_POSITIONS);
jboolean subPixPos = EXTRACT_BOOLEAN(packedParams,
OFFSET_SUBPIXPOS);
jboolean rgbOrder = EXTRACT_BOOLEAN(packedParams,
OFFSET_RGBORDER);
jint lcdContrast = EXTRACT_BYTE(packedParams,
OFFSET_CONTRAST);
unsigned char *images = b;
unsigned char *positions;
jint bytesPerGlyph;
if (usePositions) {
positions = (b + numGlyphs * BYTES_PER_GLYPH_IMAGE);
bytesPerGlyph = BYTES_PER_POSITIONED_GLYPH;
} else {
positions = NULL;
bytesPerGlyph = BYTES_PER_GLYPH_IMAGE;
}
MTLTR_DrawGlyphList(env, mtlc, dstOps,
numGlyphs, usePositions,
subPixPos, rgbOrder, lcdContrast,
glyphListOrigX, glyphListOrigY,
images, positions);
SKIP_BYTES(b, numGlyphs * bytesPerGlyph);
break;
}
// copy-related ops
case sun_java2d_pipe_BufferedOpCodes_COPY_AREA:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jint x = NEXT_INT(b);
jint y = NEXT_INT(b);
jint w = NEXT_INT(b);
jint h = NEXT_INT(b);
jint dx = NEXT_INT(b);
jint dy = NEXT_INT(b);
MTLBlitLoops_CopyArea(env, mtlc, dstOps,
x, y, w, h, dx, dy);
break;
}
case sun_java2d_pipe_BufferedOpCodes_BLIT:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jint packedParams = NEXT_INT(b);
jint sx1 = NEXT_INT(b);
jint sy1 = NEXT_INT(b);
jint sx2 = NEXT_INT(b);
jint sy2 = NEXT_INT(b);
jdouble dx1 = NEXT_DOUBLE(b);
jdouble dy1 = NEXT_DOUBLE(b);
jdouble dx2 = NEXT_DOUBLE(b);
jdouble dy2 = NEXT_DOUBLE(b);
jlong pSrc = NEXT_LONG(b);
jlong pDst = NEXT_LONG(b);
jint hint = EXTRACT_BYTE(packedParams, OFFSET_HINT);
jboolean texture = EXTRACT_BOOLEAN(packedParams,
OFFSET_TEXTURE);
jboolean xform = EXTRACT_BOOLEAN(packedParams,
OFFSET_XFORM);
jboolean isoblit = EXTRACT_BOOLEAN(packedParams,
OFFSET_ISOBLIT);
if (isoblit) {
MTLBlitLoops_IsoBlit(env, mtlc, pSrc, pDst,
xform, hint, texture,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2);
} else {
jint srctype = EXTRACT_BYTE(packedParams, OFFSET_SRCTYPE);
MTLBlitLoops_Blit(env, mtlc, pSrc, pDst,
xform, hint, srctype, texture,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2);
}
break;
}
case sun_java2d_pipe_BufferedOpCodes_SURFACE_TO_SW_BLIT:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jint sx = NEXT_INT(b);
jint sy = NEXT_INT(b);
jint dx = NEXT_INT(b);
jint dy = NEXT_INT(b);
jint w = NEXT_INT(b);
jint h = NEXT_INT(b);
jint dsttype = NEXT_INT(b);
jlong pSrc = NEXT_LONG(b);
jlong pDst = NEXT_LONG(b);
MTLBlitLoops_SurfaceToSwBlit(env, mtlc,
pSrc, pDst, dsttype,
sx, sy, dx, dy, w, h);
break;
}
case sun_java2d_pipe_BufferedOpCodes_MASK_FILL:
{
jint x = NEXT_INT(b);
jint y = NEXT_INT(b);
jint w = NEXT_INT(b);
jint h = NEXT_INT(b);
jint maskoff = NEXT_INT(b);
jint maskscan = NEXT_INT(b);
jint masklen = NEXT_INT(b);
unsigned char *pMask = (masklen > 0) ? b : NULL;
if (mtlc == nil)
return;
CHECK_PREVIOUS_OP(MTL_OP_MASK_OP);
MTLMaskFill_MaskFill(mtlc, dstOps, x, y, w, h,
maskoff, maskscan, masklen, pMask);
SKIP_BYTES(b, masklen);
break;
}
case sun_java2d_pipe_BufferedOpCodes_MASK_BLIT:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jint dstx = NEXT_INT(b);
jint dsty = NEXT_INT(b);
jint width = NEXT_INT(b);
jint height = NEXT_INT(b);
jint masklen = width * height * sizeof(jint);
MTLMaskBlit_MaskBlit(env, mtlc, dstOps,
dstx, dsty, width, height, b);
SKIP_BYTES(b, masklen);
break;
}
// state-related ops
case sun_java2d_pipe_BufferedOpCodes_SET_RECT_CLIP:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jint x1 = NEXT_INT(b);
jint y1 = NEXT_INT(b);
jint x2 = NEXT_INT(b);
jint y2 = NEXT_INT(b);
[mtlc setClipRectX1:x1 Y1:y1 X2:x2 Y2:y2];
break;
}
case sun_java2d_pipe_BufferedOpCodes_BEGIN_SHAPE_CLIP:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
[mtlc beginShapeClip:dstOps];
break;
}
case sun_java2d_pipe_BufferedOpCodes_SET_SHAPE_CLIP_SPANS:
{
CHECK_PREVIOUS_OP(MTL_OP_SHAPE_CLIP_SPANS);
// This results in creation of new render encoder with
// stencil buffer set as render target
jint count = NEXT_INT(b);
MTLRenderer_FillSpans(mtlc, dstOps, count, (jint *)b);
SKIP_BYTES(b, count * BYTES_PER_SPAN);
break;
}
case sun_java2d_pipe_BufferedOpCodes_END_SHAPE_CLIP:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
[mtlc endShapeClip:dstOps];
break;
}
case sun_java2d_pipe_BufferedOpCodes_RESET_CLIP:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
[mtlc resetClip];
break;
}
case sun_java2d_pipe_BufferedOpCodes_SET_ALPHA_COMPOSITE:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jint rule = NEXT_INT(b);
jfloat extraAlpha = NEXT_FLOAT(b);
jint flags = NEXT_INT(b);
[mtlc setAlphaCompositeRule:rule extraAlpha:extraAlpha flags:flags];
break;
}
case sun_java2d_pipe_BufferedOpCodes_SET_XOR_COMPOSITE:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jint xorPixel = NEXT_INT(b);
[mtlc setXorComposite:xorPixel];
break;
}
case sun_java2d_pipe_BufferedOpCodes_RESET_COMPOSITE:
{
/* TODO: check whether something needs to be done here if we are moving out of XOR composite
commitEncodedCommands();
MTLCommandBufferWrapper * cbwrapper = [mtlc pullCommandBufferWrapper];
[cbwrapper onComplete];
J2dTraceLn(J2D_TRACE_VERBOSE,
"RESET_COMPOSITE - Force commit earlier draw calls before RESET_COMPOSITE.");*/
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
[mtlc resetComposite];
break;
}
case sun_java2d_pipe_BufferedOpCodes_SET_TRANSFORM:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jdouble m00 = NEXT_DOUBLE(b);
jdouble m10 = NEXT_DOUBLE(b);
jdouble m01 = NEXT_DOUBLE(b);
jdouble m11 = NEXT_DOUBLE(b);
jdouble m02 = NEXT_DOUBLE(b);
jdouble m12 = NEXT_DOUBLE(b);
[mtlc setTransformM00:m00 M10:m10 M01:m01 M11:m11 M02:m02 M12:m12];
break;
}
case sun_java2d_pipe_BufferedOpCodes_RESET_TRANSFORM:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
[mtlc resetTransform];
break;
}
// context-related ops
case sun_java2d_pipe_BufferedOpCodes_SET_SURFACES:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jlong pSrc = NEXT_LONG(b);
jlong pDst = NEXT_LONG(b);
if (mtlc != NULL) {
[mtlc.encoderManager endEncoder];
MTLCommandBufferWrapper * cbwrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
}
mtlc = [MTLContext setSurfacesEnv:env src:pSrc dst:pDst];
dstOps = (BMTLSDOps *)jlong_to_ptr(pDst);
break;
}
case sun_java2d_pipe_BufferedOpCodes_SET_SCRATCH_SURFACE:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jlong pConfigInfo = NEXT_LONG(b);
MTLGraphicsConfigInfo *mtlInfo =
(MTLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo);
if (mtlInfo == NULL) {
} else {
MTLContext *newMtlc = mtlInfo->context;
if (newMtlc == NULL) {
} else {
if (mtlc != NULL) {
[mtlc.encoderManager endEncoder];
MTLCommandBufferWrapper * cbwrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
}
mtlc = newMtlc;
dstOps = NULL;
}
}
break;
}
case sun_java2d_pipe_BufferedOpCodes_FLUSH_SURFACE:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jlong pData = NEXT_LONG(b);
BMTLSDOps *mtlsdo = (BMTLSDOps *)jlong_to_ptr(pData);
if (mtlsdo != NULL) {
CONTINUE_IF_NULL(mtlc);
MTLTR_FreeGlyphCaches();
MTLSD_Delete(env, mtlsdo);
}
break;
}
case sun_java2d_pipe_BufferedOpCodes_DISPOSE_SURFACE:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jlong pData = NEXT_LONG(b);
BMTLSDOps *mtlsdo = (BMTLSDOps *)jlong_to_ptr(pData);
if (mtlsdo != NULL) {
CONTINUE_IF_NULL(mtlc);
MTLSD_Delete(env, mtlsdo);
if (mtlsdo->privOps != NULL) {
free(mtlsdo->privOps);
}
}
break;
}
case sun_java2d_pipe_BufferedOpCodes_DISPOSE_CONFIG:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jlong pConfigInfo = NEXT_LONG(b);
CONTINUE_IF_NULL(mtlc);
if (mtlc != NULL) {
[mtlc.encoderManager endEncoder];
}
MTLGC_DestroyMTLGraphicsConfig(pConfigInfo);
mtlc = NULL;
// dstOps = NULL;
break;
}
case sun_java2d_pipe_BufferedOpCodes_INVALIDATE_CONTEXT:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
// invalidate the references to the current context and
// destination surface that are maintained at the native level
if (mtlc != NULL) {
commitEncodedCommands();
RESET_PREVIOUS_OP();
[mtlc reset];
}
MTLTR_FreeGlyphCaches();
if (dstOps != NULL) {
MTLSD_Delete(env, dstOps);
}
mtlc = NULL;
dstOps = NULL;
break;
}
case sun_java2d_pipe_BufferedOpCodes_SYNC:
{
CHECK_PREVIOUS_OP(MTL_OP_SYNC);
break;
}
// special no-op (mainly used for achieving 8-byte alignment)
case sun_java2d_pipe_BufferedOpCodes_NOOP:
break;
// paint-related ops
case sun_java2d_pipe_BufferedOpCodes_RESET_PAINT:
{
CHECK_PREVIOUS_OP(MTL_OP_RESET_PAINT);
[mtlc resetPaint];
break;
}
case sun_java2d_pipe_BufferedOpCodes_SET_COLOR:
{
CHECK_PREVIOUS_OP(MTL_OP_SET_COLOR);
jint pixel = NEXT_INT(b);
[mtlc setColorPaint:pixel];
break;
}
case sun_java2d_pipe_BufferedOpCodes_SET_GRADIENT_PAINT:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jboolean useMask= NEXT_BOOLEAN(b);
jboolean cyclic = NEXT_BOOLEAN(b);
jdouble p0 = NEXT_DOUBLE(b);
jdouble p1 = NEXT_DOUBLE(b);
jdouble p3 = NEXT_DOUBLE(b);
jint pixel1 = NEXT_INT(b);
jint pixel2 = NEXT_INT(b);
[mtlc setGradientPaintUseMask:useMask
cyclic:cyclic
p0:p0
p1:p1
p3:p3
pixel1:pixel1
pixel2:pixel2];
break;
}
case sun_java2d_pipe_BufferedOpCodes_SET_LINEAR_GRADIENT_PAINT:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jboolean useMask = NEXT_BOOLEAN(b);
jboolean linear = NEXT_BOOLEAN(b);
jint cycleMethod = NEXT_INT(b);
jint numStops = NEXT_INT(b);
jfloat p0 = NEXT_FLOAT(b);
jfloat p1 = NEXT_FLOAT(b);
jfloat p3 = NEXT_FLOAT(b);
void *fractions, *pixels;
fractions = b; SKIP_BYTES(b, numStops * sizeof(jfloat));
pixels = b; SKIP_BYTES(b, numStops * sizeof(jint));
[mtlc setLinearGradientPaint:useMask
linear:linear
cycleMethod:cycleMethod
numStops:numStops
p0:p0
p1:p1
p3:p3
fractions:fractions
pixels:pixels];
break;
}
case sun_java2d_pipe_BufferedOpCodes_SET_RADIAL_GRADIENT_PAINT:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jboolean useMask = NEXT_BOOLEAN(b);
jboolean linear = NEXT_BOOLEAN(b);
jint numStops = NEXT_INT(b);
jint cycleMethod = NEXT_INT(b);
jfloat m00 = NEXT_FLOAT(b);
jfloat m01 = NEXT_FLOAT(b);
jfloat m02 = NEXT_FLOAT(b);
jfloat m10 = NEXT_FLOAT(b);
jfloat m11 = NEXT_FLOAT(b);
jfloat m12 = NEXT_FLOAT(b);
jfloat focusX = NEXT_FLOAT(b);
void *fractions, *pixels;
fractions = b; SKIP_BYTES(b, numStops * sizeof(jfloat));
pixels = b; SKIP_BYTES(b, numStops * sizeof(jint));
[mtlc setRadialGradientPaint:useMask
linear:linear
cycleMethod:cycleMethod
numStops:numStops
m00:m00
m01:m01
m02:m02
m10:m10
m11:m11
m12:m12
focusX:focusX
fractions:fractions
pixels:pixels];
break;
}
case sun_java2d_pipe_BufferedOpCodes_SET_TEXTURE_PAINT:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jboolean useMask= NEXT_BOOLEAN(b);
jboolean filter = NEXT_BOOLEAN(b);
jlong pSrc = NEXT_LONG(b);
jdouble xp0 = NEXT_DOUBLE(b);
jdouble xp1 = NEXT_DOUBLE(b);
jdouble xp3 = NEXT_DOUBLE(b);
jdouble yp0 = NEXT_DOUBLE(b);
jdouble yp1 = NEXT_DOUBLE(b);
jdouble yp3 = NEXT_DOUBLE(b);
[mtlc setTexturePaint:useMask
pSrcOps:pSrc
filter:filter
xp0:xp0
xp1:xp1
xp3:xp3
yp0:yp0
yp1:yp1
yp3:yp3];
break;
}
// BufferedImageOp-related ops
case sun_java2d_pipe_BufferedOpCodes_ENABLE_CONVOLVE_OP:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jlong pSrc = NEXT_LONG(b);
jboolean edgeZero = NEXT_BOOLEAN(b);
jint kernelWidth = NEXT_INT(b);
jint kernelHeight = NEXT_INT(b);
BMTLSDOps * bmtlsdOps = (BMTLSDOps *)pSrc;
MTLConvolveOp * convolveOp = [[MTLConvolveOp alloc] init:edgeZero
kernelWidth:kernelWidth
kernelHeight:kernelHeight
srcWidth:bmtlsdOps->width
srcHeight:bmtlsdOps->height
kernel:b
device:mtlc.device
];
[mtlc setBufImgOp:convolveOp];
SKIP_BYTES(b, kernelWidth * kernelHeight * sizeof(jfloat));
break;
}
case sun_java2d_pipe_BufferedOpCodes_DISABLE_CONVOLVE_OP:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
[mtlc setBufImgOp:NULL];
break;
}
case sun_java2d_pipe_BufferedOpCodes_ENABLE_RESCALE_OP:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jlong pSrc = NEXT_LONG(b);
jboolean nonPremult = NEXT_BOOLEAN(b);
jint numFactors = 4;
unsigned char *scaleFactors = b;
unsigned char *offsets = (b + numFactors * sizeof(jfloat));
MTLRescaleOp * rescaleOp =
[[MTLRescaleOp alloc] init:nonPremult factors:scaleFactors offsets:offsets];
[mtlc setBufImgOp:rescaleOp];
SKIP_BYTES(b, numFactors * sizeof(jfloat) * 2);
break;
}
case sun_java2d_pipe_BufferedOpCodes_DISABLE_RESCALE_OP:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
[mtlc setBufImgOp:NULL];
break;
}
case sun_java2d_pipe_BufferedOpCodes_ENABLE_LOOKUP_OP:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jlong pSrc = NEXT_LONG(b);
jboolean nonPremult = NEXT_BOOLEAN(b);
jboolean shortData = NEXT_BOOLEAN(b);
jint numBands = NEXT_INT(b);
jint bandLength = NEXT_INT(b);
jint offset = NEXT_INT(b);
jint bytesPerElem = shortData ? sizeof(jshort):sizeof(jbyte);
void *tableValues = b;
MTLLookupOp * lookupOp = [[MTLLookupOp alloc] init:nonPremult
shortData:shortData
numBands:numBands
bandLength:bandLength
offset:offset
tableValues:tableValues
device:mtlc.device];
[mtlc setBufImgOp:lookupOp];
SKIP_BYTES(b, numBands * bandLength * bytesPerElem);
break;
}
case sun_java2d_pipe_BufferedOpCodes_DISABLE_LOOKUP_OP:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
[mtlc setBufImgOp:NULL];
break;
}
default:
J2dRlsTraceLn1(J2D_TRACE_ERROR,
"MTLRenderQueue_flushBuffer: invalid opcode=%d", opcode);
return;
}
}
if (mtlc != NULL) {
if (mtlPreviousOp == MTL_OP_MASK_OP) {
MTLVertexCache_DisableMaskCache(mtlc);
}
[mtlc.encoderManager endEncoder];
MTLCommandBufferWrapper * cbwrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
if (dstOps != NULL) {
MTLSDOps *dstMTLOps = (MTLSDOps *)dstOps->privOps;
MTLLayer *layer = (MTLLayer*)dstMTLOps->layer;
if (layer != NULL) {
[layer startDisplayLink];
}
}
}
RESET_PREVIOUS_OP();
}
}
/**
* Returns a pointer to the "current" context, as set by the last SET_SURFACES
* or SET_SCRATCH_SURFACE operation.
*/
MTLContext *
MTLRenderQueue_GetCurrentContext()
{
return mtlc;
}
/**
* Returns a pointer to the "current" destination surface, as set by the last
* SET_SURFACES operation.
*/
BMTLSDOps *
MTLRenderQueue_GetCurrentDestination()
{
return dstOps;
}
/**
* commit earlier encoded commmands
* these would be rendered to the back-buffer - which is read in shader while rendering in XOR mode
*/
void commitEncodedCommands() {
[mtlc.encoderManager endEncoder];
MTLCommandBufferWrapper *cbwrapper = [mtlc pullCommandBufferWrapper];
id <MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
[commandbuf waitUntilCompleted];
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLRenderer_h_Included
#define MTLRenderer_h_Included
#include "sun_java2d_pipe_BufferedRenderPipe.h"
#include "MTLContext.h"
#include "MTLGraphicsConfig.h"
#import "MTLLayer.h"
#define BYTES_PER_POLY_POINT \
sun_java2d_pipe_BufferedRenderPipe_BYTES_PER_POLY_POINT
#define BYTES_PER_SCANLINE \
sun_java2d_pipe_BufferedRenderPipe_BYTES_PER_SCANLINE
#define BYTES_PER_SPAN \
sun_java2d_pipe_BufferedRenderPipe_BYTES_PER_SPAN
void MTLRenderer_DrawLine(MTLContext *mtlc, BMTLSDOps * dstOps,
jint x1, jint y1, jint x2, jint y2);
void MTLRenderer_DrawPixel(MTLContext *mtlc, BMTLSDOps * dstOps,
jint x, jint y);
void MTLRenderer_DrawRect(MTLContext *mtlc, BMTLSDOps * dstOps,
jint x, jint y, jint w, jint h);
void MTLRenderer_DrawPoly(MTLContext *mtlc, BMTLSDOps * dstOps,
jint nPoints, jint isClosed,
jint transX, jint transY,
jint *xPoints, jint *yPoints);
void MTLRenderer_DrawScanlines(MTLContext *mtlc, BMTLSDOps * dstOps,
jint count, jint *scanlines);
void MTLRenderer_DrawParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12,
jfloat lw21, jfloat lw12);
void MTLRenderer_DrawAAParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12,
jfloat lw21, jfloat lw12);
void MTLRenderer_FillRect(MTLContext *mtlc, BMTLSDOps * dstOps,
jint x, jint y, jint w, jint h);
void MTLRenderer_FillSpans(MTLContext *mtlc, BMTLSDOps * dstOps,
jint count, jint *spans);
void MTLRenderer_FillParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12);
void MTLRenderer_FillAAParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12);
#endif /* MTLRenderer_h_Included */

View file

@ -0,0 +1,961 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include <jlong.h>
#include <jni_util.h>
#include <math.h>
#include "sun_java2d_metal_MTLRenderer.h"
#include "MTLRenderer.h"
#include "MTLRenderQueue.h"
#include "MTLSurfaceData.h"
#include "MTLUtils.h"
#import "MTLLayer.h"
/**
* Note: Some of the methods in this file apply a "magic number"
* translation to line segments. It is same as what we have in
* OGLrenderer.
*
* The "magic numbers" you see here have been empirically derived
* after testing on a variety of graphics hardware in order to find some
* reasonable middle ground between the two specifications. The general
* approach is to apply a fractional translation to vertices so that they
* hit pixel centers and therefore touch the same pixels as in our other
* pipelines. Emphasis was placed on finding values so that MTL lines with
* a slope of +/- 1 hit all the same pixels as our other (software) loops.
* The stepping in other diagonal lines rendered with MTL may deviate
* slightly from those rendered with our software loops, but the most
* important thing is that these magic numbers ensure that all MTL lines
* hit the same endpoints as our software loops.
*
* If you find it necessary to change any of these magic numbers in the
* future, just be sure that you test the changes across a variety of
* hardware to ensure consistent rendering everywhere.
*/
void MTLRenderer_DrawLine(MTLContext *mtlc, BMTLSDOps * dstOps, jint x1, jint y1, jint x2, jint y2) {
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawLine: dest is null");
return;
}
J2dTraceLn5(J2D_TRACE_INFO, "MTLRenderer_DrawLine (x1=%d y1=%d x2=%d y2=%d), dst tex=%p", x1, y1, x2, y2, dstOps->pTexture);
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
if (mtlEncoder == nil)
return;
// DrawLine implementation same as in OGLRenderer.c
struct Vertex verts[2];
if (y1 == y2) {
// horizontal
float fx1 = (float)x1;
float fx2 = (float)x2;
float fy = ((float)y1) + 0.2f;
if (x1 > x2) {
float t = fx1; fx1 = fx2; fx2 = t;
}
verts[0].position[0] = fx1 + 0.2f;
verts[0].position[1] = fy;
verts[1].position[0] = fx2 + 1.2f;
verts[1].position[1] = fy;
} else if (x1 == x2) {
// vertical
float fx = ((float)x1) + 0.2f;
float fy1 = (float)y1;
float fy2 = (float)y2;
if (y1 > y2) {
float t = fy1; fy1 = fy2; fy2 = t;
}
verts[0].position[0] = fx;
verts[0].position[1] = fy1 + 0.2f;
verts[1].position[0] = fx;
verts[1].position[1] = fy2 + 1.2f;
} else {
// diagonal
float fx1 = (float)x1;
float fy1 = (float)y1;
float fx2 = (float)x2;
float fy2 = (float)y2;
if (x1 < x2) {
fx1 += 0.2f;
fx2 += 1.0f;
} else {
fx1 += 0.8f;
fx2 -= 0.2f;
}
if (y1 < y2) {
fy1 += 0.2f;
fy2 += 1.0f;
} else {
fy1 += 0.8f;
fy2 -= 0.2f;
}
verts[0].position[0] = fx1;
verts[0].position[1] = fy1;
verts[1].position[0] = fx2;
verts[1].position[1] = fy2;
}
[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:2];
}
void MTLRenderer_DrawPixel(MTLContext *mtlc, BMTLSDOps * dstOps, jint x, jint y) {
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawPixel: dest is null");
return;
}
id<MTLTexture> dest = dstOps->pTexture;
J2dTraceLn3(J2D_TRACE_INFO, "MTLRenderer_DrawPixel (x=%d y=%d), dst tex=%p", x, y, dest);
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
if (mtlEncoder == nil)
return;
// Translate each vertex by a fraction so
// that we hit pixel centers.
float fx = (float)x + 0.2f;
float fy = (float)y + 0.5f;
struct Vertex vert = {{fx, fy}};
[mtlEncoder setVertexBytes:&vert length:sizeof(vert) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypePoint vertexStart:0 vertexCount:1];
}
void MTLRenderer_DrawRect(MTLContext *mtlc, BMTLSDOps * dstOps, jint x, jint y, jint w, jint h) {
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawRect: dest is null");
return;
}
id<MTLTexture> dest = dstOps->pTexture;
J2dTraceLn5(J2D_TRACE_INFO, "MTLRenderer_DrawRect (x=%d y=%d w=%d h=%d), dst tex=%p", x, y, w, h, dest);
// TODO: use DrawParallelogram(x, y, w, h, lw=1, lh=1)
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
if (mtlEncoder == nil)
return;
// Translate each vertex by a fraction so
// that we hit pixel centers.
const int verticesCount = 5;
float fx = (float)x + 0.2f;
float fy = (float)y + 0.5f;
float fw = (float)w;
float fh = (float)h;
struct Vertex vertices[5] = {
{{fx, fy}},
{{fx + fw, fy}},
{{fx + fw, fy + fh}},
{{fx, fy + fh}},
{{fx, fy}},
};
[mtlEncoder setVertexBytes:vertices length:sizeof(vertices) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLineStrip vertexStart:0 vertexCount:verticesCount];
}
const int POLYLINE_BUF_SIZE = 64;
NS_INLINE void fillVertex(struct Vertex * vertex, int x, int y) {
vertex->position[0] = x;
vertex->position[1] = y;
}
void MTLRenderer_DrawPoly(MTLContext *mtlc, BMTLSDOps * dstOps,
jint nPoints, jint isClosed,
jint transX, jint transY,
jint *xPoints, jint *yPoints)
{
// Note that BufferedRenderPipe.drawPoly() has already rejected polys
// with nPoints<2, so we can be certain here that we have nPoints>=2.
if (xPoints == NULL || yPoints == NULL || nPoints < 2) { // just for insurance
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawPoly: points array is empty");
return;
}
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawPoly: dest is null");
return;
}
J2dTraceLn4(J2D_TRACE_INFO, "MTLRenderer_DrawPoly: %d points, transX=%d, transY=%d, dst tex=%p", nPoints, transX, transY, dstOps->pTexture);
__block struct {
struct Vertex verts[POLYLINE_BUF_SIZE];
} pointsChunk;
// We intend to submit draw commands in batches of POLYLINE_BUF_SIZE vertices at a time
// Subsequent batches need to be connected - so end point in one batch is repeated as first point in subsequent batch
// This inflates the total number of points by a factor of number of batches of size POLYLINE_BUF_SIZE
nPoints += (nPoints/POLYLINE_BUF_SIZE);
jint prevX = *(xPoints++);
jint prevY = *(yPoints++);
const jint firstX = prevX;
const jint firstY = prevY;
while (nPoints > 0) {
const bool isLastChunk = nPoints <= POLYLINE_BUF_SIZE;
__block int chunkSize = isLastChunk ? nPoints : POLYLINE_BUF_SIZE;
fillVertex(pointsChunk.verts, prevX + transX + 0.5f, prevY + transY + 0.5f);
J2dTraceLn2(J2D_TRACE_INFO, "MTLRenderer_DrawPoly: Point - (%1.2f, %1.2f)", prevX + transX + 0.5f, prevY + transY + 0.5f);
for (int i = 1; i < chunkSize; i++) {
prevX = *(xPoints++);
prevY = *(yPoints++);
fillVertex(pointsChunk.verts + i, prevX + transX + 0.5f, prevY + transY + 0.5f);
J2dTraceLn2(J2D_TRACE_INFO, "MTLRenderer_DrawPoly: Point - (%1.2f, %1.2f)", prevX + transX + 0.5f,prevY + transY + 0.5f);
}
bool drawCloseSegment = false;
if (isClosed && isLastChunk) {
if (chunkSize + 2 <= POLYLINE_BUF_SIZE) {
fillVertex(pointsChunk.verts + chunkSize, firstX + transX + 0.5f, firstY + transY + 0.5f);
J2dTraceLn2(J2D_TRACE_INFO, "MTLRenderer_DrawPoly: Point - (%1.2f, %1.2f)",firstX + transX + 0.5f, firstY + transY + 0.5f);
++chunkSize;
} else
drawCloseSegment = true;
}
nPoints -= chunkSize;
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
if (mtlEncoder == nil)
return;
[mtlEncoder setVertexBytes:pointsChunk.verts length:sizeof(pointsChunk.verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLineStrip vertexStart:0 vertexCount:chunkSize];
if (drawCloseSegment) {
struct Vertex vertices[2] = {
{{prevX + transX + 0.5f, prevY + transY + 0.5f}},
{{firstX + transX + 0.5f, firstY + transY + 0.5f}}
};
J2dTraceLn2(J2D_TRACE_INFO, "MTLRenderer_DrawPoly: last segment Point1 - (%1.2f, %1.2f)",prevX + transX + 0.5f, prevY + transY + 0.5f);
J2dTraceLn2(J2D_TRACE_INFO, "MTLRenderer_DrawPoly: last segment Point2 - (%1.2f, %1.2f)",firstX + transX + 0.5f, firstY + transY + 0.5f);
[mtlEncoder setVertexBytes:vertices length:sizeof(vertices) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:2];
}
}
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLRenderer_drawPoly
(JNIEnv *env, jobject mtlr,
jintArray xpointsArray, jintArray ypointsArray,
jint nPoints, jboolean isClosed,
jint transX, jint transY)
{
jint *xPoints, *yPoints;
J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_drawPoly");
xPoints = (jint *)
(*env)->GetPrimitiveArrayCritical(env, xpointsArray, NULL);
if (xPoints != NULL) {
yPoints = (jint *)
(*env)->GetPrimitiveArrayCritical(env, ypointsArray, NULL);
if (yPoints != NULL) {
MTLContext *mtlc = MTLRenderQueue_GetCurrentContext();
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
MTLRenderer_DrawPoly(mtlc, dstOps,
nPoints, isClosed,
transX, transY,
xPoints, yPoints);
if (mtlc != NULL) {
RESET_PREVIOUS_OP();
[mtlc.encoderManager endEncoder];
MTLCommandBufferWrapper * cbwrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
}
(*env)->ReleasePrimitiveArrayCritical(env, ypointsArray, yPoints,
JNI_ABORT);
}
(*env)->ReleasePrimitiveArrayCritical(env, xpointsArray, xPoints,
JNI_ABORT);
}
}
const int SCANLINE_MAX_VERTEX_SIZE = 4096;
const int VERTEX_STRUCT_SIZE = 8;
const int NUM_OF_VERTICES_PER_SCANLINE = 2;
void
MTLRenderer_DrawScanlines(MTLContext *mtlc, BMTLSDOps * dstOps,
jint scanlineCount, jint *scanlines)
{
J2dTraceLn2(J2D_TRACE_INFO, "MTLRenderer_DrawScanlines (scanlineCount=%d), dst tex=%p", scanlineCount, dstOps->pTexture);
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawScanlines: dest is null");
return;
}
RETURN_IF_NULL(scanlines);
int vertexSize = NUM_OF_VERTICES_PER_SCANLINE
* scanlineCount * VERTEX_STRUCT_SIZE;
J2dTraceLn1(J2D_TRACE_INFO, "MTLRenderer_DrawScanlines: Total vertex size : %d", vertexSize);
if (vertexSize == 0) return;
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
if (mtlEncoder == nil) return;
if (vertexSize <= SCANLINE_MAX_VERTEX_SIZE) {
struct Vertex verts[NUM_OF_VERTICES_PER_SCANLINE * scanlineCount];
for (int j = 0, i = 0; j < scanlineCount; j++) {
// Translate each vertex by a fraction so
// that we hit pixel centers.
float x1 = ((float)*(scanlines++)) + 0.2f;
float x2 = ((float)*(scanlines++)) + 1.2f;
float y = ((float)*(scanlines++)) + 0.5f;
struct Vertex v1 = {{x1, y}};
struct Vertex v2 = {{x2, y}};
verts[i++] = v1;
verts[i++] = v2;
}
[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0
vertexCount:NUM_OF_VERTICES_PER_SCANLINE * scanlineCount];
} else {
int remainingScanlineCount = vertexSize;
do {
if (remainingScanlineCount > SCANLINE_MAX_VERTEX_SIZE) {
struct Vertex verts[SCANLINE_MAX_VERTEX_SIZE/ VERTEX_STRUCT_SIZE];
for (int j = 0, i = 0; j < (SCANLINE_MAX_VERTEX_SIZE / (VERTEX_STRUCT_SIZE * 2)); j++) {
// Translate each vertex by a fraction so
// that we hit pixel centers.
float x1 = ((float)*(scanlines++)) + 0.2f;
float x2 = ((float)*(scanlines++)) + 1.2f;
float y = ((float)*(scanlines++)) + 0.5f;
struct Vertex v1 = {{x1, y}};
struct Vertex v2 = {{x2, y}};
verts[i++] = v1;
verts[i++] = v2;
}
[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0
vertexCount:(SCANLINE_MAX_VERTEX_SIZE / VERTEX_STRUCT_SIZE)];
remainingScanlineCount -= SCANLINE_MAX_VERTEX_SIZE;
} else {
struct Vertex verts[remainingScanlineCount / VERTEX_STRUCT_SIZE];
for (int j = 0, i = 0; j < (remainingScanlineCount / (VERTEX_STRUCT_SIZE * 2)); j++) {
// Translate each vertex by a fraction so
// that we hit pixel centers.
float x1 = ((float)*(scanlines++)) + 0.2f;
float x2 = ((float)*(scanlines++)) + 1.2f;
float y = ((float)*(scanlines++)) + 0.5f;
struct Vertex v1 = {{x1, y}};
struct Vertex v2 = {{x2, y}};
verts[i++] = v1;
verts[i++] = v2;
}
[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0
vertexCount:(remainingScanlineCount / VERTEX_STRUCT_SIZE)];
remainingScanlineCount -= remainingScanlineCount;
}
J2dTraceLn1(J2D_TRACE_INFO,
"MTLRenderer_DrawScanlines: Remaining vertex size %d", remainingScanlineCount);
} while (remainingScanlineCount != 0);
}
}
void
MTLRenderer_FillRect(MTLContext *mtlc, BMTLSDOps * dstOps, jint x, jint y, jint w, jint h)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_FillRect");
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillRect: current dest is null");
return;
}
struct Vertex verts[QUAD_VERTEX_COUNT] = {
{ {x, y}},
{ {x, y+h}},
{ {x+w, y}},
{ {x+w, y+h}
}};
id<MTLTexture> dest = dstOps->pTexture;
J2dTraceLn5(J2D_TRACE_INFO, "MTLRenderer_FillRect (x=%d y=%d w=%d h=%d), dst tex=%p", x, y, w, h, dest);
// Encode render command.
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
if (mtlEncoder == nil)
return;
[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount: QUAD_VERTEX_COUNT];
}
void MTLRenderer_FillSpans(MTLContext *mtlc, BMTLSDOps * dstOps, jint spanCount, jint *spans)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_FillSpans");
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillSpans: dest is null");
return;
}
// MTLRenderCommandEncoder setVertexBytes usage is recommended if the data is of 4KB.
// We use a buffer that closely matches the 4KB limit size
// This buffer is resued multiple times to encode draw calls of a triangle list
// NOTE : Due to nature of *spans data - it is not possible to use triangle strip.
// We use triangle list to draw spans
// Destination texture to which render commands are encoded
id<MTLTexture> dest = dstOps->pTexture;
id<MTLTexture> destAA = nil;
BOOL isDestOpaque = dstOps->isOpaque;
if (mtlc.clip.stencilMaskGenerationInProgress == JNI_TRUE) {
dest = dstOps->pStencilData;
isDestOpaque = NO;
}
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dest isDstOpaque:isDestOpaque];
if (mtlEncoder == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillSpans: mtlEncoder is nil");
return;
}
// This is the max no of vertices (of struct Vertex - 8 bytes) we can accomodate in 4KB
const int TOTAL_VERTICES_IN_BLOCK = 510;
struct Vertex vertexList[TOTAL_VERTICES_IN_BLOCK]; // a total of 170 triangles ==> 85 spans
jfloat shapeX1 = mtlc.clip.shapeX;
jfloat shapeY1 = mtlc.clip.shapeY;
jfloat shapeX2 = shapeX1 + mtlc.clip.shapeWidth;
jfloat shapeY2 = shapeY1 + mtlc.clip.shapeHeight;
int counter = 0;
for (int i = 0; i < spanCount; i++) {
jfloat x1 = *(spans++);
jfloat y1 = *(spans++);
jfloat x2 = *(spans++);
jfloat y2 = *(spans++);
if (mtlc.clip.stencilMaskGenerationInProgress == JNI_TRUE) {
if (shapeX1 > x1) shapeX1 = x1;
if (shapeY1 > y1) shapeY1 = y1;
if (shapeX2 < x2) shapeX2 = x2;
if (shapeY2 < y2) shapeY2 = y2;
}
struct Vertex verts[6] = {
{{x1, y1}},
{{x1, y2}},
{{x2, y1}},
{{x1, y2}},
{{x2, y1}},
{{x2, y2}
}};
memcpy(&vertexList[counter], &verts, sizeof(verts));
counter += 6;
// If vertexList buffer full
if (counter % TOTAL_VERTICES_IN_BLOCK == 0) {
[mtlEncoder setVertexBytes:vertexList length:sizeof(vertexList) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:TOTAL_VERTICES_IN_BLOCK];
counter = 0;
}
}
// Draw triangles using remaining vertices if any
if (counter != 0) {
[mtlEncoder setVertexBytes:vertexList length:sizeof(vertexList) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:counter];
}
if (mtlc.clip.stencilMaskGenerationInProgress == JNI_TRUE) {
if (shapeX1 < 0) shapeX1 = 0;
if (shapeY1 < 0) shapeY1 = 0;
if (shapeX1 > dest.width) shapeX1 = dest.width;
if (shapeY1 > dest.height) shapeY1 = dest.height;
if (shapeX2 < 0) shapeX2 = 0;
if (shapeY2 < 0) shapeY2 = 0;
if (shapeX2 > dest.width) shapeX2 = dest.width;
if (shapeY2 > dest.height) shapeY2 = dest.height;
mtlc.clip.shapeX = (NSUInteger) shapeX1;
mtlc.clip.shapeY = (NSUInteger) shapeY1;
mtlc.clip.shapeWidth = (NSUInteger) (shapeX2 - shapeX1);
mtlc.clip.shapeHeight = (NSUInteger) (shapeY2 - shapeY1);
}
}
void
MTLRenderer_FillParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12)
{
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillParallelogram: current dest is null");
return;
}
id<MTLTexture> dest = dstOps->pTexture;
J2dTraceLn7(J2D_TRACE_INFO,
"MTLRenderer_FillParallelogram"
"(x=%6.2f y=%6.2f "
"dx1=%6.2f dy1=%6.2f "
"dx2=%6.2f dy2=%6.2f dst tex=%p)",
fx11, fy11,
dx21, dy21,
dx12, dy12, dest);
struct Vertex verts[QUAD_VERTEX_COUNT] = {
{ {fx11, fy11}},
{ {fx11+dx21, fy11+dy21}},
{ {fx11+dx12, fy11+dy12}},
{ {fx11 + dx21 + dx12, fy11+ dy21 + dy12}
}};
// Encode render command.
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];;
if (mtlEncoder == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillParallelogram: error creating MTLRenderCommandEncoder.");
return;
}
[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount: QUAD_VERTEX_COUNT];
}
void
MTLRenderer_DrawParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12,
jfloat lwr21, jfloat lwr12)
{
// dx,dy for line width in the "21" and "12" directions.
jfloat ldx21 = dx21 * lwr21;
jfloat ldy21 = dy21 * lwr21;
jfloat ldx12 = dx12 * lwr12;
jfloat ldy12 = dy12 * lwr12;
// calculate origin of the outer parallelogram
jfloat ox11 = fx11 - (ldx21 + ldx12) / 2.0f;
jfloat oy11 = fy11 - (ldy21 + ldy12) / 2.0f;
J2dTraceLn8(J2D_TRACE_INFO,
"MTLRenderer_DrawParallelogram"
"(x=%6.2f y=%6.2f "
"dx1=%6.2f dy1=%6.2f lwr1=%6.2f "
"dx2=%6.2f dy2=%6.2f lwr2=%6.2f)",
fx11, fy11,
dx21, dy21, lwr21,
dx12, dy12, lwr12);
// Only need to generate 4 quads if the interior still
// has a hole in it (i.e. if the line width ratio was
// less than 1.0)
if (lwr21 < 1.0f && lwr12 < 1.0f) {
// Note: "TOP", "BOTTOM", "LEFT" and "RIGHT" here are
// relative to whether the dxNN variables are positive
// and negative. The math works fine regardless of
// their signs, but for conceptual simplicity the
// comments will refer to the sides as if the dxNN
// were all positive. "TOP" and "BOTTOM" segments
// are defined by the dxy21 deltas. "LEFT" and "RIGHT"
// segments are defined by the dxy12 deltas.
// Each segment includes its starting corner and comes
// to just short of the following corner. Thus, each
// corner is included just once and the only lengths
// needed are the original parallelogram delta lengths
// and the "line width deltas". The sides will cover
// the following relative territories:
//
// T T T T T R
// L R
// L R
// L R
// L R
// L B B B B B
// Every segment is drawn as a filled Parallelogram quad
// Each quad is encoded using two triangles
// For 4 segments - there are 8 triangles in total
// Each triangle has 3 vertices
const int TOTAL_VERTICES = 8 * 3;
struct Vertex vertexList[TOTAL_VERTICES];
int i = 0;
// TOP segment, to left side of RIGHT edge
// "width" of original pgram, "height" of hor. line size
fx11 = ox11;
fy11 = oy11;
fillVertex(vertexList + (i++), fx11, fy11);
fillVertex(vertexList + (i++), fx11 + dx21, fy11 + dy21);
fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);
fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);
fillVertex(vertexList + (i++), fx11 + ldx12, fy11 + ldy12);
fillVertex(vertexList + (i++), fx11, fy11);
// RIGHT segment, to top of BOTTOM edge
// "width" of vert. line size , "height" of original pgram
fx11 = ox11 + dx21;
fy11 = oy11 + dy21;
fillVertex(vertexList + (i++), fx11, fy11);
fillVertex(vertexList + (i++), fx11 + ldx21, fy11 + ldy21);
fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);
fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);
fillVertex(vertexList + (i++), fx11 + dx12, fy11 + dy12);
fillVertex(vertexList + (i++), fx11, fy11);
// BOTTOM segment, from right side of LEFT edge
// "width" of original pgram, "height" of hor. line size
fx11 = ox11 + dx12 + ldx21;
fy11 = oy11 + dy12 + ldy21;
fillVertex(vertexList + (i++), fx11, fy11);
fillVertex(vertexList + (i++), fx11 + dx21, fy11 + dy21);
fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);
fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);
fillVertex(vertexList + (i++), fx11 + ldx12, fy11 + ldy12);
fillVertex(vertexList + (i++), fx11, fy11);
// LEFT segment, from bottom of TOP edge
// "width" of vert. line size , "height" of inner pgram
fx11 = ox11 + ldx12;
fy11 = oy11 + ldy12;
fillVertex(vertexList + (i++), fx11, fy11);
fillVertex(vertexList + (i++), fx11 + ldx21, fy11 + ldy21);
fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);
fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);
fillVertex(vertexList + (i++), fx11 + dx12, fy11 + dy12);
fillVertex(vertexList + (i++), fx11, fy11);
// Encode render command.
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
if (mtlEncoder == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawParallelogram: error creating MTLRenderCommandEncoder.");
return;
}
[mtlEncoder setVertexBytes:vertexList length:sizeof(vertexList) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:TOTAL_VERTICES];
} else {
// The line width ratios were large enough to consume
// the entire hole in the middle of the parallelogram
// so we can just issue one large quad for the outer
// parallelogram.
dx21 += ldx21;
dy21 += ldy21;
dx12 += ldx12;
dy12 += ldy12;
MTLRenderer_FillParallelogram(mtlc, dstOps, ox11, oy11, dx21, dy21, dx12, dy12);
}
}
static struct AAVertex aaVertices[6];
static jint vertexCacheIndex = 0;
#define AA_ADD_VERTEX(OU, OV, IU, IV, DX, DY) \
do { \
struct AAVertex *v = &aaVertices[vertexCacheIndex++]; \
v->otxtpos[0] = OU; \
v->otxtpos[1] = OV; \
v->itxtpos[0] = IU; \
v->itxtpos[1] = IV; \
v->position[0]= DX; \
v->position[1] = DY; \
} while (0)
#define AA_ADD_TRIANGLES(ou11, ov11, iu11, iv11, ou21, ov21, iu21, iv21, ou22, ov22, iu22, iv22, ou12, ov12, iu12, iv12, DX1, DY1, DX2, DY2) \
do { \
AA_ADD_VERTEX(ou11, ov11, iu11, iv11, DX1, DY1); \
AA_ADD_VERTEX(ou21, ov21, iu21, iv21, DX2, DY1); \
AA_ADD_VERTEX(ou22, ov22, iu22, iv22, DX2, DY2); \
AA_ADD_VERTEX(ou22, ov22, iu22, iv22, DX2, DY2); \
AA_ADD_VERTEX(ou12, ov12, iu12, iv12, DX1, DY2); \
AA_ADD_VERTEX(ou11, ov11, iu11, iv11, DX1, DY1); \
} while (0)
#define ADJUST_PGRAM(V1, DV, V2) \
do { \
if ((DV) >= 0) { \
(V2) += (DV); \
} else { \
(V1) += (DV); \
} \
} while (0)
// Invert the following transform:
// DeltaT(0, 0) == (0, 0)
// DeltaT(1, 0) == (DX1, DY1)
// DeltaT(0, 1) == (DX2, DY2)
// DeltaT(1, 1) == (DX1+DX2, DY1+DY2)
// TM00 = DX1, TM01 = DX2, (TM02 = X11)
// TM10 = DY1, TM11 = DY2, (TM12 = Y11)
// Determinant = TM00*TM11 - TM01*TM10
// = DX1*DY2 - DX2*DY1
// Inverse is:
// IM00 = TM11/det, IM01 = -TM01/det
// IM10 = -TM10/det, IM11 = TM00/det
// IM02 = (TM01 * TM12 - TM11 * TM02) / det,
// IM12 = (TM10 * TM02 - TM00 * TM12) / det,
#define DECLARE_MATRIX(MAT) \
jfloat MAT ## 00, MAT ## 01, MAT ## 02, MAT ## 10, MAT ## 11, MAT ## 12
#define GET_INVERTED_MATRIX(MAT, X11, Y11, DX1, DY1, DX2, DY2, RET_CODE) \
do { \
jfloat det = DX1*DY2 - DX2*DY1; \
if (det == 0) { \
RET_CODE; \
} \
MAT ## 00 = DY2/det; \
MAT ## 01 = -DX2/det; \
MAT ## 10 = -DY1/det; \
MAT ## 11 = DX1/det; \
MAT ## 02 = (DX2 * Y11 - DY2 * X11) / det; \
MAT ## 12 = (DY1 * X11 - DX1 * Y11) / det; \
} while (0)
#define TRANSFORM(MAT, TX, TY, X, Y) \
do { \
TX = (X) * MAT ## 00 + (Y) * MAT ## 01 + MAT ## 02; \
TY = (X) * MAT ## 10 + (Y) * MAT ## 11 + MAT ## 12; \
} while (0)
void
MTLRenderer_FillAAParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12)
{
DECLARE_MATRIX(om);
// parameters for parallelogram bounding box
jfloat bx11, by11, bx22, by22;
// parameters for uv texture coordinates of parallelogram corners
jfloat ou11, ov11, ou12, ov12, ou21, ov21, ou22, ov22;
J2dTraceLn6(J2D_TRACE_INFO,
"MTLRenderer_FillAAParallelogram "
"(x=%6.2f y=%6.2f "
"dx1=%6.2f dy1=%6.2f "
"dx2=%6.2f dy2=%6.2f)",
fx11, fy11,
dx21, dy21,
dx12, dy12);
RETURN_IF_NULL(mtlc);
RETURN_IF_NULL(dstOps);
GET_INVERTED_MATRIX(om, fx11, fy11, dx21, dy21, dx12, dy12,
return);
bx11 = bx22 = fx11;
by11 = by22 = fy11;
ADJUST_PGRAM(bx11, dx21, bx22);
ADJUST_PGRAM(by11, dy21, by22);
ADJUST_PGRAM(bx11, dx12, bx22);
ADJUST_PGRAM(by11, dy12, by22);
bx11 = (jfloat) floor(bx11);
by11 = (jfloat) floor(by11);
bx22 = (jfloat) ceil(bx22);
by22 = (jfloat) ceil(by22);
TRANSFORM(om, ou11, ov11, bx11, by11);
TRANSFORM(om, ou21, ov21, bx22, by11);
TRANSFORM(om, ou12, ov12, bx11, by22);
TRANSFORM(om, ou22, ov22, bx22, by22);
id<MTLRenderCommandEncoder> encoder =
[mtlc.encoderManager getAAShaderRenderEncoder:dstOps];
AA_ADD_TRIANGLES(ou11, ov11, 5.f, 5.f, ou21, ov21, 6.f, 5.f, ou22, ov22, 6.f, 6.f, ou12, ov12, 5.f, 5.f, bx11, by11, bx22, by22);
[encoder setVertexBytes:aaVertices length:sizeof(aaVertices) atIndex:MeshVertexBuffer];
[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];
vertexCacheIndex = 0;
}
void
MTLRenderer_FillAAParallelogramInnerOuter(MTLContext *mtlc, MTLSDOps *dstOps,
jfloat ox11, jfloat oy11,
jfloat ox21, jfloat oy21,
jfloat ox12, jfloat oy12,
jfloat ix11, jfloat iy11,
jfloat ix21, jfloat iy21,
jfloat ix12, jfloat iy12)
{
DECLARE_MATRIX(om);
DECLARE_MATRIX(im);
// parameters for parallelogram bounding box
jfloat bx11, by11, bx22, by22;
// parameters for uv texture coordinates of outer parallelogram corners
jfloat ou11, ov11, ou12, ov12, ou21, ov21, ou22, ov22;
// parameters for uv texture coordinates of inner parallelogram corners
jfloat iu11, iv11, iu12, iv12, iu21, iv21, iu22, iv22;
RETURN_IF_NULL(mtlc);
RETURN_IF_NULL(dstOps);
GET_INVERTED_MATRIX(im, ix11, iy11, ix21, iy21, ix12, iy12,
// inner parallelogram is degenerate
// therefore it encloses no area
// fill outer
MTLRenderer_FillAAParallelogram(mtlc, dstOps,
ox11, oy11,
ox21, oy21,
ox12, oy12);
return);
GET_INVERTED_MATRIX(om, ox11, oy11, ox21, oy21, ox12, oy12,
return);
bx11 = bx22 = ox11;
by11 = by22 = oy11;
ADJUST_PGRAM(bx11, ox21, bx22);
ADJUST_PGRAM(by11, oy21, by22);
ADJUST_PGRAM(bx11, ox12, bx22);
ADJUST_PGRAM(by11, oy12, by22);
bx11 = (jfloat) floor(bx11);
by11 = (jfloat) floor(by11);
bx22 = (jfloat) ceil(bx22);
by22 = (jfloat) ceil(by22);
TRANSFORM(om, ou11, ov11, bx11, by11);
TRANSFORM(om, ou21, ov21, bx22, by11);
TRANSFORM(om, ou12, ov12, bx11, by22);
TRANSFORM(om, ou22, ov22, bx22, by22);
TRANSFORM(im, iu11, iv11, bx11, by11);
TRANSFORM(im, iu21, iv21, bx22, by11);
TRANSFORM(im, iu12, iv12, bx11, by22);
TRANSFORM(im, iu22, iv22, bx22, by22);
id<MTLRenderCommandEncoder> encoder =
[mtlc.encoderManager getAAShaderRenderEncoder:dstOps];
AA_ADD_TRIANGLES(ou11, ov11, iu11, iv11, ou21, ov21, iu21, iv21, ou22, ov22, iu22, iv22, ou12, ov12, iu12, iv12, bx11, by11, bx22, by22);
[encoder setVertexBytes:aaVertices length:sizeof(aaVertices) atIndex:MeshVertexBuffer];
[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];
vertexCacheIndex = 0;
}
void
MTLRenderer_DrawAAParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12,
jfloat lwr21, jfloat lwr12)
{
// dx,dy for line width in the "21" and "12" directions.
jfloat ldx21, ldy21, ldx12, ldy12;
// parameters for "outer" parallelogram
jfloat ofx11, ofy11, odx21, ody21, odx12, ody12;
// parameters for "inner" parallelogram
jfloat ifx11, ify11, idx21, idy21, idx12, idy12;
J2dTraceLn8(J2D_TRACE_INFO,
"MTLRenderer_DrawAAParallelogram "
"(x=%6.2f y=%6.2f "
"dx1=%6.2f dy1=%6.2f lwr1=%6.2f "
"dx2=%6.2f dy2=%6.2f lwr2=%6.2f)",
fx11, fy11,
dx21, dy21, lwr21,
dx12, dy12, lwr12);
RETURN_IF_NULL(mtlc);
RETURN_IF_NULL(dstOps);
// calculate true dx,dy for line widths from the "line width ratios"
ldx21 = dx21 * lwr21;
ldy21 = dy21 * lwr21;
ldx12 = dx12 * lwr12;
ldy12 = dy12 * lwr12;
// calculate coordinates of the outer parallelogram
ofx11 = fx11 - (ldx21 + ldx12) / 2.0f;
ofy11 = fy11 - (ldy21 + ldy12) / 2.0f;
odx21 = dx21 + ldx21;
ody21 = dy21 + ldy21;
odx12 = dx12 + ldx12;
ody12 = dy12 + ldy12;
// Only process the inner parallelogram if the line width ratio
// did not consume the entire interior of the parallelogram
// (i.e. if the width ratio was less than 1.0)
if (lwr21 < 1.0f && lwr12 < 1.0f) {
// calculate coordinates of the inner parallelogram
ifx11 = fx11 + (ldx21 + ldx12) / 2.0f;
ify11 = fy11 + (ldy21 + ldy12) / 2.0f;
idx21 = dx21 - ldx21;
idy21 = dy21 - ldy21;
idx12 = dx12 - ldx12;
idy12 = dy12 - ldy12;
MTLRenderer_FillAAParallelogramInnerOuter(mtlc, dstOps,
ofx11, ofy11,
odx21, ody21,
odx12, ody12,
ifx11, ify11,
idx21, idy21,
idx12, idy12);
} else {
MTLRenderer_FillAAParallelogram(mtlc, dstOps,
ofx11, ofy11,
odx21, ody21,
odx12, ody12);
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLSamplerManager_h_Included
#define MTLSamplerManager_h_Included
#import <Metal/Metal.h>
#include "RenderOptions.h"
@class MTLContex;
@interface MTLSamplerManager : NSObject
- (id _Nonnull)initWithDevice:(_Nonnull id<MTLDevice>) device;
- (void)dealloc;
- (void) setSamplerWithEncoder:(_Nonnull id<MTLRenderCommandEncoder>) encoder
interpolation:(int) interpolation
repeat:(bool) repeat;
@end
#endif // MTLSamplerManager_h_Included

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include "MTLSamplerManager.h"
#include "MTLContext.h"
#include "sun_java2d_SunGraphics2D.h"
#import "common.h"
@implementation MTLSamplerManager {
id<MTLSamplerState> _samplerNearestClamp;
id<MTLSamplerState> _samplerLinearClamp;
id<MTLSamplerState> _samplerNearestRepeat;
id<MTLSamplerState> _samplerLinearRepeat;
}
- (id _Nonnull)initWithDevice:(id<MTLDevice>) device {
self = [super init];
if (self) {
MTLSamplerDescriptor *samplerDescriptor = [[MTLSamplerDescriptor new] autorelease];
samplerDescriptor.rAddressMode = MTLSamplerAddressModeClampToEdge;
samplerDescriptor.sAddressMode = MTLSamplerAddressModeClampToEdge;
samplerDescriptor.tAddressMode = MTLSamplerAddressModeClampToEdge;
samplerDescriptor.minFilter = MTLSamplerMinMagFilterNearest;
samplerDescriptor.magFilter = MTLSamplerMinMagFilterNearest;
_samplerNearestClamp = [device newSamplerStateWithDescriptor:samplerDescriptor];
samplerDescriptor.minFilter = MTLSamplerMinMagFilterLinear;
samplerDescriptor.magFilter = MTLSamplerMinMagFilterLinear;
_samplerLinearClamp = [device newSamplerStateWithDescriptor:samplerDescriptor];
samplerDescriptor.rAddressMode = MTLSamplerAddressModeRepeat;
samplerDescriptor.sAddressMode = MTLSamplerAddressModeRepeat;
samplerDescriptor.tAddressMode = MTLSamplerAddressModeRepeat;
samplerDescriptor.minFilter = MTLSamplerMinMagFilterNearest;
samplerDescriptor.magFilter = MTLSamplerMinMagFilterNearest;
_samplerNearestRepeat = [device newSamplerStateWithDescriptor:samplerDescriptor];
samplerDescriptor.minFilter = MTLSamplerMinMagFilterLinear;
samplerDescriptor.magFilter = MTLSamplerMinMagFilterLinear;
_samplerLinearRepeat = [device newSamplerStateWithDescriptor:samplerDescriptor];
}
return self;
}
- (void) setSamplerWithEncoder:(id<MTLRenderCommandEncoder>) encoder
interpolation:(int) interpolation
repeat:(bool) repeat {
id<MTLSamplerState> sampler;
if (repeat) {
sampler = interpolation == INTERPOLATION_BILINEAR ? _samplerLinearRepeat : _samplerNearestRepeat;
} else {
sampler = interpolation == INTERPOLATION_BILINEAR ? _samplerLinearClamp : _samplerNearestClamp;
}
[encoder setFragmentSamplerState:sampler atIndex:0];
}
- (void)dealloc {
[_samplerNearestClamp release];
[_samplerLinearClamp release];
[_samplerNearestRepeat release];
[_samplerLinearRepeat release];
[super dealloc];
}
@end

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLStencilManager_h_Included
#define MTLStencilManager_h_Included
#import <Metal/Metal.h>
#include "RenderOptions.h"
@class MTLContex;
@interface MTLStencilManager : NSObject
- (id _Nonnull)initWithDevice:(_Nonnull id<MTLDevice>) device;
- (void)dealloc;
@property (readonly) _Nonnull id<MTLDepthStencilState> stencilState;
@end
#endif // MTLSamplerManager_h_Included

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include "MTLStencilManager.h"
//#include "MTLContext.h"
//#include "sun_java2d_SunGraphics2D.h"
//#import "common.h"
@implementation MTLStencilManager {
id<MTLDepthStencilState> _stencilState;
}
@synthesize stencilState = _stencilState;
- (id _Nonnull)initWithDevice:(id<MTLDevice>) device {
self = [super init];
if (self) {
MTLDepthStencilDescriptor* stencilDescriptor;
stencilDescriptor = [[MTLDepthStencilDescriptor new] autorelease];
stencilDescriptor.frontFaceStencil.stencilCompareFunction = MTLCompareFunctionEqual;
stencilDescriptor.frontFaceStencil.stencilFailureOperation = MTLStencilOperationKeep;
// TODO : backFaceStencil can be set to nil if all primitives are drawn as front-facing primitives
// currently, fill parallelogram uses back-facing primitive drawing - that needs to be changed.
// Once that part is changed, set backFaceStencil to nil
//stencilDescriptor.backFaceStencil = nil;
stencilDescriptor.backFaceStencil.stencilCompareFunction = MTLCompareFunctionEqual;
stencilDescriptor.backFaceStencil.stencilFailureOperation = MTLStencilOperationKeep;
_stencilState = [device newDepthStencilStateWithDescriptor:stencilDescriptor];
}
return self;
}
- (void)dealloc {
[_stencilState release];
[super dealloc];
}
@end

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLSurfaceData_h_Included
#define MTLSurfaceData_h_Included
#import "MTLSurfaceDataBase.h"
#import "MTLGraphicsConfig.h"
#import "AWTWindow.h"
#import "MTLLayer.h"
/**
* The MTLSDOps structure contains the MTL-specific information for a given
* MTLSurfaceData. It is referenced by the native MTLSDOps structure.
*/
typedef struct _MTLSDOps {
AWTView *peerData;
MTLLayer *layer;
jint argb[4]; // background clear color
MTLGraphicsConfigInfo *configInfo;
} MTLSDOps;
// debug-method
NSString * getSurfaceDescription(const BMTLSDOps * bmtlsdOps);
#endif /* MTLSurfaceData_h_Included */

View file

@ -0,0 +1,390 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#import <stdlib.h>
#import "sun_java2d_metal_MTLSurfaceData.h"
#import "jni_util.h"
#import "MTLRenderQueue.h"
#import "MTLGraphicsConfig.h"
#import "MTLSurfaceData.h"
#import "ThreadUtilities.h"
#include "jlong.h"
/**
* The following methods are implemented in the windowing system (i.e. GLX
* and WGL) source files.
*/
extern jlong MTLSD_GetNativeConfigInfo(BMTLSDOps *bmtlsdo);
extern jboolean MTLSD_InitMTLWindow(JNIEnv *env, BMTLSDOps *bmtlsdo);
extern void MTLSD_DestroyMTLSurface(JNIEnv *env, BMTLSDOps *bmtlsdo);
void MTLSD_SetNativeDimensions(JNIEnv *env, BMTLSDOps *bmtlsdo, jint w, jint h);
static jboolean MTLSurfaceData_initTexture(BMTLSDOps *bmtlsdo, jboolean isOpaque, jboolean rtt, jint width, jint height) {
@autoreleasepool {
if (bmtlsdo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLSurfaceData_initTexture: ops are null");
return JNI_FALSE;
}
if (width <= 0 || height <= 0) {
J2dRlsTraceLn2(J2D_TRACE_ERROR, "MTLSurfaceData_initTexture: texture dimensions is incorrect, w=%d, h=%d", width, height);
return JNI_FALSE;
}
MTLSDOps *mtlsdo = (MTLSDOps *)bmtlsdo->privOps;
if (mtlsdo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLSurfaceData_initTexture: MTLSDOps are null");
return JNI_FALSE;
}
if (mtlsdo->configInfo == NULL || mtlsdo->configInfo->context == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLSurfaceData_initTexture: MTLSDOps wasn't initialized (context is null)");
return JNI_FALSE;
}
MTLContext* ctx = mtlsdo->configInfo->context;
width = (width <= MaxTextureSize) ? width : 0;
height = (height <= MaxTextureSize) ? height : 0;
J2dTraceLn3(J2D_TRACE_VERBOSE, " desired texture dimensions: w=%d h=%d max=%d",
width, height, MaxTextureSize);
// if either dimension is 0, we cannot allocate a texture with the
// requested dimensions
if ((width == 0 || height == 0)) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLSurfaceData_initTexture: texture dimensions too large");
return JNI_FALSE;
}
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: MTLPixelFormatBGRA8Unorm width: width height: height mipmapped: NO];
textureDescriptor.usage = MTLTextureUsageUnknown;
textureDescriptor.storageMode = MTLStorageModePrivate;
bmtlsdo->pTexture = [ctx.device newTextureWithDescriptor: textureDescriptor];
MTLTextureDescriptor *stencilDataDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Uint width:width height:height mipmapped:NO];
stencilDataDescriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
stencilDataDescriptor.storageMode = MTLStorageModePrivate;
bmtlsdo->pStencilData = [ctx.device newTextureWithDescriptor:stencilDataDescriptor];
bmtlsdo->pAAStencilData = [ctx.device newTextureWithDescriptor:textureDescriptor];
bmtlsdo->pStencilDataBuf = [ctx.device newBufferWithLength:width*height options:MTLResourceStorageModePrivate];
bmtlsdo->pAAStencilDataBuf = [ctx.device newBufferWithLength:width*height*4 options:MTLResourceStorageModePrivate];
MTLTextureDescriptor *stencilTextureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatStencil8 width:width height:height mipmapped:NO];
stencilTextureDescriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
stencilTextureDescriptor.storageMode = MTLStorageModePrivate;
bmtlsdo->pStencilTexture = [ctx.device newTextureWithDescriptor:stencilTextureDescriptor];
bmtlsdo->isOpaque = isOpaque;
bmtlsdo->xOffset = 0;
bmtlsdo->yOffset = 0;
bmtlsdo->width = width;
bmtlsdo->height = height;
bmtlsdo->textureWidth = width;
bmtlsdo->textureHeight = height;
bmtlsdo->drawableType = rtt ? MTLSD_RT_TEXTURE : MTLSD_TEXTURE;
J2dTraceLn6(J2D_TRACE_VERBOSE, "MTLSurfaceData_initTexture: w=%d h=%d bp=%p [tex=%p] opaque=%d rtt=%d", width, height, bmtlsdo, bmtlsdo->pTexture, isOpaque, rtt);
return JNI_TRUE;
}
}
/**
* Initializes an MTL texture, using the given width and height as
* a guide.
*/
JNIEXPORT jboolean JNICALL
Java_sun_java2d_metal_MTLSurfaceData_initTexture(
JNIEnv *env, jobject mtlsd,
jlong pData, jboolean isOpaque,
jint width, jint height
) {
if (!MTLSurfaceData_initTexture((BMTLSDOps *)pData, isOpaque, JNI_FALSE, width, height))
return JNI_FALSE;
MTLSD_SetNativeDimensions(env, (BMTLSDOps *)pData, width, height);
return JNI_TRUE;
}
/**
* Initializes a framebuffer object, using the given width and height as
* a guide. See MTLSD_InitTextureObject() and MTLSD_initRTexture()
* for more information.
*/
JNIEXPORT jboolean JNICALL
Java_sun_java2d_metal_MTLSurfaceData_initRTexture
(JNIEnv *env, jobject mtlsd,
jlong pData, jboolean isOpaque,
jint width, jint height)
{
if (!MTLSurfaceData_initTexture((BMTLSDOps *)pData, isOpaque, JNI_TRUE, width, height))
return JNI_FALSE;
MTLSD_SetNativeDimensions(env, (BMTLSDOps *)pData, width, height);
return JNI_TRUE;
}
JNIEXPORT jlong JNICALL
Java_sun_java2d_metal_MTLSurfaceData_getMTLTexturePointer(JNIEnv *env, jobject mtlsd, jlong pData) {
if (pData == 0)
return 0;
return ptr_to_jlong(((BMTLSDOps *)pData)->pTexture);
}
/**
* Initializes nativeWidth/Height fields of the surfaceData object with
* passed arguments.
*/
void
MTLSD_SetNativeDimensions(JNIEnv *env, BMTLSDOps *mtlsdo,
jint width, jint height)
{
jobject sdObject;
sdObject = (*env)->NewLocalRef(env, mtlsdo->sdOps.sdObject);
if (sdObject == NULL) {
return;
}
JNU_SetFieldByName(env, NULL, sdObject, "nativeWidth", "I", width);
if (!((*env)->ExceptionOccurred(env))) {
JNU_SetFieldByName(env, NULL, sdObject, "nativeHeight", "I", height);
}
(*env)->DeleteLocalRef(env, sdObject);
}
/**
* Deletes native Metal resources associated with this surface.
*/
void
MTLSD_Delete(JNIEnv *env, BMTLSDOps *bmtlsdo)
{
J2dTraceLn3(J2D_TRACE_VERBOSE, "MTLSD_Delete: type=%d %p [tex=%p]", bmtlsdo->drawableType, bmtlsdo, bmtlsdo->pTexture);
if (bmtlsdo->drawableType == MTLSD_WINDOW) {
MTLSD_DestroyMTLSurface(env, bmtlsdo);
} else if (
bmtlsdo->drawableType == MTLSD_RT_TEXTURE
|| bmtlsdo->drawableType == MTLSD_TEXTURE
|| bmtlsdo->drawableType == MTLSD_FLIP_BACKBUFFER
) {
[(NSObject *)bmtlsdo->pTexture release];
[(NSObject *)bmtlsdo->pStencilTexture release];
[(NSObject *)bmtlsdo->pStencilData release];
[(NSObject *)bmtlsdo->pStencilDataBuf release];
[(NSObject *)bmtlsdo->pAAStencilData release];
[(NSObject *)bmtlsdo->pAAStencilDataBuf release];
bmtlsdo->pTexture = NULL;
bmtlsdo->drawableType = MTLSD_UNDEFINED;
}
}
/**
* This is the implementation of the general DisposeFunc defined in
* SurfaceData.h and used by the Disposer mechanism. It first flushes all
* native Metal resources and then frees any memory allocated within the
* native MTLSDOps structure.
*/
void
MTLSD_Dispose(JNIEnv *env, SurfaceDataOps *ops)
{
BMTLSDOps *bmtlsdo = (BMTLSDOps *)ops;
jobject graphicsConfig = bmtlsdo->graphicsConfig;
JNU_CallStaticMethodByName(env, NULL, "sun/java2d/metal/MTLSurfaceData",
"dispose",
"(JLsun/java2d/metal/MTLGraphicsConfig;)V",
ptr_to_jlong(ops), graphicsConfig);
(*env)->DeleteGlobalRef(env, graphicsConfig);
bmtlsdo->graphicsConfig = NULL;
}
/**
* This is the implementation of the general surface LockFunc defined in
* SurfaceData.h.
*/
jint
MTLSD_Lock(JNIEnv *env,
SurfaceDataOps *ops,
SurfaceDataRasInfo *pRasInfo,
jint lockflags)
{
JNU_ThrowInternalError(env, "MTLSD_Lock not implemented!");
return SD_FAILURE;
}
/**
* This is the implementation of the general GetRasInfoFunc defined in
* SurfaceData.h.
*/
void
MTLSD_GetRasInfo(JNIEnv *env,
SurfaceDataOps *ops,
SurfaceDataRasInfo *pRasInfo)
{
JNU_ThrowInternalError(env, "MTLSD_GetRasInfo not implemented!");
}
/**
* This is the implementation of the general surface UnlockFunc defined in
* SurfaceData.h.
*/
void
MTLSD_Unlock(JNIEnv *env,
SurfaceDataOps *ops,
SurfaceDataRasInfo *pRasInfo)
{
JNU_ThrowInternalError(env, "MTLSD_Unlock not implemented!");
}
/**
* This function disposes of any native windowing system resources associated
* with this surface.
*/
void
MTLSD_DestroyMTLSurface(JNIEnv *env, BMTLSDOps * bmtlsdo)
{
J2dTraceLn(J2D_TRACE_ERROR, "MTLSD_DestroyMTLSurface not implemented!");
JNI_COCOA_ENTER(env);
bmtlsdo->drawableType = MTLSD_UNDEFINED;
JNI_COCOA_EXIT(env);
}
/**
* This function initializes a native window surface and caches the window
* bounds in the given BMTLSDOps. Returns JNI_TRUE if the operation was
* successful; JNI_FALSE otherwise.
*/
jboolean
MTLSD_InitMTLWindow(JNIEnv *env, BMTLSDOps *bmtlsdo)
{
if (bmtlsdo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLSD_InitMTLWindow: ops are null");
return JNI_FALSE;
}
MTLSDOps *mtlsdo = (MTLSDOps *)bmtlsdo->privOps;
if (mtlsdo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLSD_InitMTLWindow: priv ops are null");
return JNI_FALSE;
}
AWTView *v = mtlsdo->peerData;
if (v == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLSD_InitMTLWindow: view is invalid");
return JNI_FALSE;
}
JNI_COCOA_ENTER(env);
NSRect surfaceBounds = [v bounds];
bmtlsdo->drawableType = MTLSD_WINDOW;
bmtlsdo->isOpaque = JNI_TRUE;
bmtlsdo->width = surfaceBounds.size.width;
bmtlsdo->height = surfaceBounds.size.height;
JNI_COCOA_EXIT(env);
J2dTraceLn2(J2D_TRACE_VERBOSE, " created window: w=%d h=%d", bmtlsdo->width, bmtlsdo->height);
return JNI_TRUE;
}
#pragma mark -
#pragma mark "--- MTLSurfaceData methods ---"
extern LockFunc MTLSD_Lock;
extern GetRasInfoFunc MTLSD_GetRasInfo;
extern UnlockFunc MTLSD_Unlock;
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLSurfaceData_initOps
(JNIEnv *env, jobject mtlsd, jobject gc,
jlong pConfigInfo, jlong pPeerData, jlong layerPtr,
jint xoff, jint yoff, jboolean isOpaque)
{
BMTLSDOps *bmtlsdo = (BMTLSDOps *)SurfaceData_InitOps(env, mtlsd, sizeof(BMTLSDOps));
MTLSDOps *mtlsdo = (MTLSDOps *)malloc(sizeof(MTLSDOps));
J2dTraceLn1(J2D_TRACE_INFO, "MTLSurfaceData_initOps p=%p", bmtlsdo);
J2dTraceLn1(J2D_TRACE_INFO, " pPeerData=%p", jlong_to_ptr(pPeerData));
J2dTraceLn1(J2D_TRACE_INFO, " layerPtr=%p", jlong_to_ptr(layerPtr));
J2dTraceLn2(J2D_TRACE_INFO, " xoff=%d, yoff=%d", (int)xoff, (int)yoff);
gc = (*env)->NewGlobalRef(env, gc);
if (gc == NULL) {
JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed.");
return;
}
if (mtlsdo == NULL) {
(*env)->DeleteGlobalRef(env, gc);
JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed.");
return;
}
// later the graphicsConfig will be used for deallocation of mtlsdo
bmtlsdo->privOps = mtlsdo;
bmtlsdo->graphicsConfig = gc;
bmtlsdo->sdOps.Lock = MTLSD_Lock;
bmtlsdo->sdOps.GetRasInfo = MTLSD_GetRasInfo;
bmtlsdo->sdOps.Unlock = MTLSD_Unlock;
bmtlsdo->sdOps.Dispose = MTLSD_Dispose;
bmtlsdo->drawableType = MTLSD_UNDEFINED;
bmtlsdo->xOffset = xoff;
bmtlsdo->yOffset = yoff;
bmtlsdo->isOpaque = isOpaque;
mtlsdo->peerData = (AWTView *)jlong_to_ptr(pPeerData);
mtlsdo->layer = (MTLLayer *)jlong_to_ptr(layerPtr);
mtlsdo->configInfo = (MTLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo);
if (mtlsdo->configInfo == NULL) {
free(mtlsdo);
JNU_ThrowNullPointerException(env, "Config info is null in initOps");
}
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLSurfaceData_clearWindow
(JNIEnv *env, jobject mtlsd)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLSurfaceData_clearWindow");
BMTLSDOps *bmtlsdo = (MTLSDOps*) SurfaceData_GetOps(env, mtlsd);
MTLSDOps *mtlsdo = (MTLSDOps*) bmtlsdo->privOps;
mtlsdo->peerData = NULL;
mtlsdo->layer = NULL;
}
NSString * getSurfaceDescription(const BMTLSDOps * bmtlsdOps) {
if (bmtlsdOps == NULL)
return @"NULL";
return [NSString stringWithFormat:@"%p [tex=%p, %dx%d, O=%d]", bmtlsdOps, bmtlsdOps->pTexture, bmtlsdOps->width, bmtlsdOps->height, bmtlsdOps->isOpaque];
}

View file

@ -0,0 +1,150 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLSurfaceDataBase_h_Included
#define MTLSurfaceDataBase_h_Included
#include "java_awt_image_AffineTransformOp.h"
#include "sun_java2d_metal_MTLSurfaceData.h"
#include "sun_java2d_pipe_hw_AccelSurface.h"
#include "SurfaceData.h"
#include "Trace.h"
/**
* The MTLSDOps structure describes a native Metal surface and contains all
* information pertaining to the native surface. Some information about
* the more important/different fields:
*
* void *privOps;
* Pointer to native-specific (Metal) SurfaceData info, such as the
* native Drawable handle and GraphicsConfig data.
*
* jint drawableType;
* The surface type; can be any one of the surface type constants defined
* below (MTLSD_WINDOW, MTLSD_TEXTURE, etc).
*
* jboolean isOpaque;
* If true, the surface should be treated as being fully opaque. If
* the underlying surface (e.g. pbuffer) has an alpha channel and isOpaque
* is true, then we should take appropriate action (i.e. call glColorMask()
* to disable writes into the alpha channel) to ensure that the surface
* remains fully opaque.
*
* jint x/yOffset
* The offset in pixels of the Metal viewport origin from the lower-left
* corner of the heavyweight drawable. For example, a top-level frame on
* Windows XP has lower-left insets of (4,4). The Metal viewport origin
* would typically begin at the lower-left corner of the client region (inside
* the frame decorations), but AWT/Swing will take the insets into account
* when rendering into that window. So in order to account for this, we
* need to adjust the Metal viewport origin by an x/yOffset of (-4,-4). On
* X11, top-level frames typically don't have this insets issue, so their
* x/yOffset would be (0,0) (the same applies to pbuffers).
*
* jint width/height;
* The cached surface bounds. For offscreen surface types (MTLSD_FBOBJECT,
* MTLSD_TEXTURE, etc.) these values must remain constant. Onscreen window
* surfaces (MTLSD_WINDOW, MTLSD_FLIP_BACKBUFFER, etc.) may have their
* bounds changed in response to a programmatic or user-initiated event, so
* these values represent the last known dimensions. To determine the true
* current bounds of this surface, query the native Drawable through the
* privOps field.
*
* void* pTexture;
* The texture object handle, as generated by MTLTextureDescriptor(). If this
* value is null, the texture has not yet been initialized.
*
* jint textureWidth/Height;
* The actual bounds of the texture object for this surface.
* The texture image that we care about has dimensions specified by the width
* and height fields in this MTLSDOps structure.
*/
typedef struct {
SurfaceDataOps sdOps;
void *privOps;
jobject graphicsConfig;
jint drawableType;
jboolean isOpaque;
jint xOffset;
jint yOffset;
jint width;
jint height;
void* pTexture;
void* pStencilData; // stencil data to be rendered to this buffer
void* pStencilDataBuf; // MTLBuffer with stencil data
void* pStencilTexture; // stencil texture byte buffer stencil mask used in main rendering
void* pAAStencilData; // stencil data for AA rendering
void* pAAStencilDataBuf; // MTLBuffer with AA stencil data
jint textureWidth;
jint textureHeight;
} BMTLSDOps;
#define MTLSD_UNDEFINED sun_java2d_pipe_hw_AccelSurface_UNDEFINED
#define MTLSD_WINDOW sun_java2d_pipe_hw_AccelSurface_WINDOW
#define MTLSD_TEXTURE sun_java2d_pipe_hw_AccelSurface_TEXTURE
#define MTLSD_FLIP_BACKBUFFER sun_java2d_pipe_hw_AccelSurface_FLIP_BACKBUFFER
#define MTLSD_RT_TEXTURE sun_java2d_pipe_hw_AccelSurface_RT_TEXTURE
/**
* These are shorthand names for the filtering method constants used by
* image transform methods.
*/
#define MTLSD_XFORM_DEFAULT 0
#define MTLSD_XFORM_NEAREST_NEIGHBOR \
java_awt_image_AffineTransformOp_TYPE_NEAREST_NEIGHBOR
#define MTLSD_XFORM_BILINEAR \
java_awt_image_AffineTransformOp_TYPE_BILINEAR
/**
* The SurfaceRasterFlags structure contains information about raster (of some MTLTexture):
*
* jboolean isOpaque;
* If true, indicates that this pixel format hasn't alpha component (and values of this component can contain garbage).
*
* jboolean isPremultiplied;
* If true, indicates that this pixel format contains color components that have been pre-multiplied by their
* corresponding alpha component.
*/
typedef struct {
jboolean isOpaque;
jboolean isPremultiplied;
} SurfaceRasterFlags;
/**
* Exported methods.
*/
jint MTLSD_Lock(JNIEnv *env,
SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo,
jint lockflags);
void MTLSD_GetRasInfo(JNIEnv *env,
SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo);
void MTLSD_Unlock(JNIEnv *env,
SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo);
void MTLSD_Dispose(JNIEnv *env, SurfaceDataOps *ops);
void MTLSD_Delete(JNIEnv *env, BMTLSDOps *mtlsdo);
jint MTLSD_NextPowerOfTwo(jint val, jint max);
#endif /* MTLSurfaceDataBase_h_Included */

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLTextRenderer_h_Included
#define MTLTextRenderer_h_Included
#include <jni.h>
#include <jlong.h>
#include "sun_java2d_pipe_BufferedTextPipe.h"
#include "MTLContext.h"
#include "MTLSurfaceData.h"
#define BYTES_PER_GLYPH_IMAGE \
sun_java2d_pipe_BufferedTextPipe_BYTES_PER_GLYPH_IMAGE
#define BYTES_PER_GLYPH_POSITION \
sun_java2d_pipe_BufferedTextPipe_BYTES_PER_GLYPH_POSITION
#define BYTES_PER_POSITIONED_GLYPH \
(BYTES_PER_GLYPH_IMAGE + BYTES_PER_GLYPH_POSITION)
#define OFFSET_CONTRAST sun_java2d_pipe_BufferedTextPipe_OFFSET_CONTRAST
#define OFFSET_RGBORDER sun_java2d_pipe_BufferedTextPipe_OFFSET_RGBORDER
#define OFFSET_SUBPIXPOS sun_java2d_pipe_BufferedTextPipe_OFFSET_SUBPIXPOS
#define OFFSET_POSITIONS sun_java2d_pipe_BufferedTextPipe_OFFSET_POSITIONS
void MTLTR_EnableGlyphVertexCache(MTLContext *mtlc, BMTLSDOps *dstOps);
void MTLTR_DisableGlyphVertexCache(MTLContext *mtlc);
id<MTLTexture> MTLTR_GetGlyphCacheTexture();
void MTLTR_FreeGlyphCaches();
void MTLTR_DrawGlyphList(JNIEnv *env, MTLContext *mtlc, BMTLSDOps *dstOps,
jint totalGlyphs, jboolean usePositions,
jboolean subPixPos, jboolean rgbOrder,
jint lcdContrast,
jfloat glyphListOrigX, jfloat glyphListOrigY,
unsigned char *images, unsigned char *positions);
#endif /* MTLTextRenderer_h_Included */

View file

@ -0,0 +1,777 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <jlong.h>
#include "sun_java2d_metal_MTLTextRenderer.h"
#include "SurfaceData.h"
#include "MTLContext.h"
#include "MTLRenderQueue.h"
#include "MTLTextRenderer.h"
#include "MTLVertexCache.h"
#include "MTLGlyphCache.h"
#include "MTLBlitLoops.h"
/**
* The following constants define the inner and outer bounds of the
* accelerated glyph cache.
*/
#define MTLTR_CACHE_WIDTH 512
#define MTLTR_CACHE_HEIGHT 512
#define MTLTR_CACHE_CELL_WIDTH 32
#define MTLTR_CACHE_CELL_HEIGHT 32
/**
* The current "glyph mode" state. This variable is used to track the
* codepath used to render a particular glyph. This variable is reset to
* MODE_NOT_INITED at the beginning of every call to MTLTR_DrawGlyphList().
* As each glyph is rendered, the glyphMode variable is updated to reflect
* the current mode, so if the current mode is the same as the mode used
* to render the previous glyph, we can avoid doing costly setup operations
* each time.
*/
typedef enum {
MODE_NOT_INITED,
MODE_USE_CACHE_GRAY,
MODE_USE_CACHE_LCD,
MODE_NO_CACHE_GRAY,
MODE_NO_CACHE_LCD,
MODE_NO_CACHE_COLOR
} GlyphMode;
static GlyphMode glyphMode = MODE_NOT_INITED;
/**
* There are two separate glyph caches: for AA and for LCD.
* Once one of them is initialized as either GRAY or LCD, it
* stays in that mode for the duration of the application. It should
* be safe to use this one glyph cache for all screens in a multimon
* environment, since the glyph cache texture is shared between all contexts,
* and (in theory) Metal drivers should be smart enough to manage that
* texture across all screens.
*/
static MTLGlyphCacheInfo *glyphCacheLCD = NULL;
static MTLGlyphCacheInfo *glyphCacheAA = NULL;
/**
* This value tracks the previous LCD rgbOrder setting, so if the rgbOrder
* value has changed since the last time, it indicates that we need to
* invalidate the cache, which may already store glyph images in the reverse
* order. Note that in most real world applications this value will not
* change over the course of the application, but tests like Font2DTest
* allow for changing the ordering at runtime, so we need to handle that case.
*/
static jboolean lastRGBOrder = JNI_TRUE;
/**
* This constant defines the size of the tile to use in the
* MTLTR_DrawLCDGlyphNoCache() method. See below for more on why we
* restrict this value to a particular size.
*/
#define MTLTR_NOCACHE_TILE_SIZE 32
static struct TxtVertex txtVertices[6];
static jint vertexCacheIndex = 0;
static id<MTLRenderCommandEncoder> lcdCacheEncoder = nil;
#define LCD_ADD_VERTEX(TX, TY, DX, DY, DZ) \
do { \
struct TxtVertex *v = &txtVertices[vertexCacheIndex++]; \
v->txtpos[0] = TX; \
v->txtpos[1] = TY; \
v->position[0]= DX; \
v->position[1] = DY; \
} while (0)
#define LCD_ADD_TRIANGLES(TX1, TY1, TX2, TY2, DX1, DY1, DX2, DY2) \
do { \
LCD_ADD_VERTEX(TX1, TY1, DX1, DY1, 0); \
LCD_ADD_VERTEX(TX2, TY1, DX2, DY1, 0); \
LCD_ADD_VERTEX(TX2, TY2, DX2, DY2, 0); \
LCD_ADD_VERTEX(TX2, TY2, DX2, DY2, 0); \
LCD_ADD_VERTEX(TX1, TY2, DX1, DY2, 0); \
LCD_ADD_VERTEX(TX1, TY1, DX1, DY1, 0); \
} while (0)
/**
* Initializes the one glyph cache (texture and data structure).
* If lcdCache is JNI_TRUE, the texture will contain RGB data,
* otherwise we will simply store the grayscale/monochrome glyph images
* as intensity values.
*/
static jboolean
MTLTR_InitGlyphCache(MTLContext *mtlc, jboolean lcdCache)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_InitGlyphCache");
// TODO : Need to verify RGB order in case of LCD
MTLPixelFormat pixelFormat =
lcdCache ? MTLPixelFormatBGRA8Unorm : MTLPixelFormatA8Unorm;
MTLGlyphCacheInfo *gcinfo;
// init glyph cache data structure
gcinfo = MTLGlyphCache_Init(MTLTR_CACHE_WIDTH,
MTLTR_CACHE_HEIGHT,
MTLTR_CACHE_CELL_WIDTH,
MTLTR_CACHE_CELL_HEIGHT,
MTLVertexCache_FlushGlyphVertexCache);
if (gcinfo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLTR_InitGlyphCache: could not init MTL glyph cache");
return JNI_FALSE;
}
MTLTextureDescriptor *textureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:pixelFormat
width:MTLTR_CACHE_WIDTH
height:MTLTR_CACHE_HEIGHT
mipmapped:NO];
gcinfo->texture = [mtlc.device newTextureWithDescriptor:textureDescriptor];
if (lcdCache) {
glyphCacheLCD = gcinfo;
} else {
glyphCacheAA = gcinfo;
}
return JNI_TRUE;
}
id<MTLTexture>
MTLTR_GetGlyphCacheTexture()
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_GetGlyphCacheTexture");
if (glyphCacheAA != NULL) {
return glyphCacheAA->texture;
}
return NULL;
}
/**
* Adds the given glyph to the glyph cache (texture and data structure)
* associated with the given MTLContext.
*/
static void
MTLTR_AddToGlyphCache(GlyphInfo *glyph, MTLContext *mtlc,
jboolean lcdCache)
{
MTLCacheCellInfo *ccinfo;
MTLGlyphCacheInfo *gcinfo;
jint w = glyph->width;
jint h = glyph->height;
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_AddToGlyphCache");
if (!lcdCache) {
gcinfo = glyphCacheAA;
} else {
gcinfo = glyphCacheLCD;
}
if ((gcinfo == NULL) || (glyph->image == NULL)) {
return;
}
bool isCacheFull = MTLGlyphCache_IsCacheFull(gcinfo, glyph);
if (isCacheFull) {
MTLGlyphCache_Free(gcinfo);
if (!lcdCache) {
MTLTR_InitGlyphCache(mtlc, JNI_FALSE);
gcinfo = glyphCacheAA;
} else {
MTLTR_InitGlyphCache(mtlc, JNI_TRUE);
gcinfo = glyphCacheLCD;
}
}
MTLGlyphCache_AddGlyph(gcinfo, glyph);
ccinfo = (MTLCacheCellInfo *) glyph->cellInfo;
if (ccinfo != NULL) {
// store glyph image in texture cell
MTLRegion region = {
{ccinfo->x, ccinfo->y, 0},
{w, h, 1}
};
if (!lcdCache) {
NSUInteger bytesPerRow = 1 * w;
[gcinfo->texture replaceRegion:region
mipmapLevel:0
withBytes:glyph->image
bytesPerRow:bytesPerRow];
} else {
unsigned int imageBytes = w * h * 4;
unsigned char imageData[imageBytes];
memset(&imageData, 0, sizeof(imageData));
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
imageData[(i * w * 4) + j * 4] = glyph->image[(i * w * 3) + j * 3];
imageData[(i * w * 4) + j * 4 + 1] = glyph->image[(i * w * 3) + j * 3 + 1];
imageData[(i * w * 4) + j * 4 + 2] = glyph->image[(i * w * 3) + j * 3 + 2];
imageData[(i * w * 4) + j * 4 + 3] = 0xFF;
}
}
NSUInteger bytesPerRow = 4 * w;
[gcinfo->texture replaceRegion:region
mipmapLevel:0
withBytes:imageData
bytesPerRow:bytesPerRow];
}
}
}
static jboolean
MTLTR_SetLCDContrast(MTLContext *mtlc,
jint contrast,
id<MTLRenderCommandEncoder> encoder)
{
if (![mtlc.paint isKindOfClass:[MTLColorPaint class]]) {
return JNI_FALSE;
}
MTLColorPaint* cPaint = (MTLColorPaint *) mtlc.paint;
// update the current color settings
double gamma = ((double)contrast) / 100.0;
double invgamma = 1.0/gamma;
jfloat radj, gadj, badj;
jfloat clr[4];
jint col = cPaint.color;
J2dTraceLn2(J2D_TRACE_INFO, "primary color %x, contrast %d", col, contrast);
J2dTraceLn2(J2D_TRACE_INFO, "gamma %f, invgamma %f", gamma, invgamma);
clr[0] = ((col >> 16) & 0xFF)/255.0f;
clr[1] = ((col >> 8) & 0xFF)/255.0f;
clr[2] = ((col) & 0xFF)/255.0f;
// gamma adjust the primary color
radj = (float)pow(clr[0], gamma);
gadj = (float)pow(clr[1], gamma);
badj = (float)pow(clr[2], gamma);
struct LCDFrameUniforms uf = {
{radj, gadj, badj},
{gamma, gamma, gamma},
{invgamma, invgamma, invgamma}};
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
return JNI_TRUE;
}
void
MTLTR_EnableGlyphVertexCache(MTLContext *mtlc, BMTLSDOps *dstOps)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_EnableGlyphVertexCache");
if (!MTLVertexCache_InitVertexCache()) {
return;
}
if (glyphCacheAA == NULL) {
if (!MTLTR_InitGlyphCache(mtlc, JNI_FALSE)) {
return;
}
}
MTLVertexCache_CreateSamplingEncoder(mtlc, dstOps);
}
void
MTLTR_DisableGlyphVertexCache(MTLContext *mtlc)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DisableGlyphVertexCache");
MTLVertexCache_FlushGlyphVertexCache();
MTLVertexCache_FreeVertexCache();
}
void MTLTR_FreeGlyphCaches() {
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_FreeGlyphCaches : freeing glyph caches.");
if (glyphCacheAA != NULL) {
[glyphCacheAA->texture release];
MTLGlyphCache_Free(glyphCacheAA);
glyphCacheAA = NULL;
}
if (glyphCacheLCD != NULL) {
[glyphCacheLCD->texture release];
MTLGlyphCache_Free(glyphCacheLCD);
glyphCacheLCD = NULL;
}
}
static jboolean
MTLTR_DrawGrayscaleGlyphViaCache(MTLContext *mtlc,
GlyphInfo *ginfo, jint x, jint y, BMTLSDOps *dstOps)
{
MTLCacheCellInfo *cell;
jfloat x1, y1, x2, y2;
if (glyphMode != MODE_USE_CACHE_GRAY) {
if (glyphMode == MODE_NO_CACHE_GRAY) {
MTLVertexCache_DisableMaskCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_LCD) {
[mtlc.encoderManager endEncoder];
lcdCacheEncoder = nil;
}
MTLTR_EnableGlyphVertexCache(mtlc, dstOps);
glyphMode = MODE_USE_CACHE_GRAY;
}
if (ginfo->cellInfo == NULL) {
// attempt to add glyph to accelerated glyph cache
MTLTR_AddToGlyphCache(ginfo, mtlc, JNI_FALSE);
if (ginfo->cellInfo == NULL) {
// we'll just no-op in the rare case that the cell is NULL
return JNI_TRUE;
}
}
cell = (MTLCacheCellInfo *) (ginfo->cellInfo);
cell->timesRendered++;
x1 = (jfloat)x;
y1 = (jfloat)y;
x2 = x1 + ginfo->width;
y2 = y1 + ginfo->height;
MTLVertexCache_AddGlyphQuad(mtlc,
cell->tx1, cell->ty1,
cell->tx2, cell->ty2,
x1, y1, x2, y2);
return JNI_TRUE;
}
static jboolean
MTLTR_DrawLCDGlyphViaCache(MTLContext *mtlc, BMTLSDOps *dstOps,
GlyphInfo *ginfo, jint x, jint y,
jboolean rgbOrder, jint contrast)
{
CacheCellInfo *cell;
jfloat tx1, ty1, tx2, ty2;
jint w = ginfo->width;
jint h = ginfo->height;
if (glyphMode != MODE_USE_CACHE_LCD) {
if (glyphMode == MODE_NO_CACHE_GRAY) {
MTLVertexCache_DisableMaskCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_GRAY) {
MTLTR_DisableGlyphVertexCache(mtlc);
}
if (glyphCacheLCD == NULL) {
if (!MTLTR_InitGlyphCache(mtlc, JNI_TRUE)) {
return JNI_FALSE;
}
}
if (lcdCacheEncoder == nil) {
lcdCacheEncoder = [mtlc.encoderManager getLCDEncoder:dstOps->pTexture isSrcOpaque:YES isDstOpaque:YES];
}
if (rgbOrder != lastRGBOrder) {
// need to invalidate the cache in this case; see comments
// for lastRGBOrder above
MTLGlyphCache_Invalidate(glyphCacheLCD);
lastRGBOrder = rgbOrder;
}
glyphMode = MODE_USE_CACHE_LCD;
}
if (ginfo->cellInfo == NULL) {
// attempt to add glyph to accelerated glyph cache
// TODO : Handle RGB order
MTLTR_AddToGlyphCache(ginfo, mtlc, JNI_TRUE);
if (ginfo->cellInfo == NULL) {
// we'll just no-op in the rare case that the cell is NULL
return JNI_TRUE;
}
}
cell = (CacheCellInfo *) (ginfo->cellInfo);
cell->timesRendered++;
MTLTR_SetLCDContrast(mtlc, contrast, lcdCacheEncoder);
tx1 = cell->tx1;
ty1 = cell->ty1;
tx2 = cell->tx2;
ty2 = cell->ty2;
J2dTraceLn4(J2D_TRACE_INFO, "tx1 %f, ty1 %f, tx2 %f, ty2 %f", tx1, ty1, tx2, ty2);
J2dTraceLn2(J2D_TRACE_INFO, "textureWidth %d textureHeight %d", dstOps->textureWidth, dstOps->textureHeight);
LCD_ADD_TRIANGLES(tx1, ty1, tx2, ty2, x, y, x+w, y+h);
[lcdCacheEncoder setVertexBytes:txtVertices length:sizeof(txtVertices) atIndex:MeshVertexBuffer];
[lcdCacheEncoder setFragmentTexture:glyphCacheLCD->texture atIndex:0];
[lcdCacheEncoder setFragmentTexture:dstOps->pTexture atIndex:1];
[lcdCacheEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];
vertexCacheIndex = 0;
return JNI_TRUE;
}
static jboolean
MTLTR_DrawGrayscaleGlyphNoCache(MTLContext *mtlc,
GlyphInfo *ginfo, jint x, jint y, BMTLSDOps *dstOps)
{
jint tw, th;
jint sx, sy, sw, sh;
jint x0;
jint w = ginfo->width;
jint h = ginfo->height;
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGrayscaleGlyphNoCache");
if (glyphMode != MODE_NO_CACHE_GRAY) {
if (glyphMode == MODE_USE_CACHE_GRAY) {
MTLTR_DisableGlyphVertexCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_LCD) {
[mtlc.encoderManager endEncoder];
lcdCacheEncoder = nil;
}
MTLVertexCache_EnableMaskCache(mtlc, dstOps);
glyphMode = MODE_NO_CACHE_GRAY;
}
x0 = x;
tw = MTLVC_MASK_CACHE_TILE_WIDTH;
th = MTLVC_MASK_CACHE_TILE_HEIGHT;
for (sy = 0; sy < h; sy += th, y += th) {
x = x0;
sh = ((sy + th) > h) ? (h - sy) : th;
for (sx = 0; sx < w; sx += tw, x += tw) {
sw = ((sx + tw) > w) ? (w - sx) : tw;
J2dTraceLn7(J2D_TRACE_INFO, "sx = %d sy = %d x = %d y = %d sw = %d sh = %d w = %d", sx, sy, x, y, sw, sh, w);
MTLVertexCache_AddMaskQuad(mtlc,
sx, sy, x, y, sw, sh,
w, ginfo->image,
dstOps);
}
}
return JNI_TRUE;
}
static jboolean
MTLTR_DrawLCDGlyphNoCache(MTLContext *mtlc, BMTLSDOps *dstOps,
GlyphInfo *ginfo, jint x, jint y,
jint rowBytesOffset,
jboolean rgbOrder, jint contrast)
{
jfloat tx1, ty1, tx2, ty2;
jint tw, th;
jint w = ginfo->width;
jint h = ginfo->height;
id<MTLTexture> blitTexture = nil;
J2dTraceLn2(J2D_TRACE_INFO, "MTLTR_DrawLCDGlyphNoCache x %d, y%d", x, y);
J2dTraceLn3(J2D_TRACE_INFO, "MTLTR_DrawLCDGlyphNoCache rowBytesOffset=%d, rgbOrder=%d, contrast=%d", rowBytesOffset, rgbOrder, contrast);
id<MTLRenderCommandEncoder> encoder = nil;
MTLTextureDescriptor *textureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:w
height:h
mipmapped:NO];
blitTexture = [mtlc.device newTextureWithDescriptor:textureDescriptor];
if (glyphMode != MODE_NO_CACHE_LCD) {
if (glyphMode == MODE_NO_CACHE_GRAY) {
MTLVertexCache_DisableMaskCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_GRAY) {
MTLTR_DisableGlyphVertexCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_LCD) {
[mtlc.encoderManager endEncoder];
lcdCacheEncoder = nil;
}
if (blitTexture == nil) {
J2dTraceLn(J2D_TRACE_ERROR, "can't obtain temporary texture object from pool");
return JNI_FALSE;
}
glyphMode = MODE_NO_CACHE_LCD;
}
encoder = [mtlc.encoderManager getLCDEncoder:dstOps->pTexture isSrcOpaque:YES isDstOpaque:YES];
MTLTR_SetLCDContrast(mtlc, contrast, encoder);
unsigned int imageBytes = w * h *4;
unsigned char imageData[imageBytes];
memset(&imageData, 0, sizeof(imageData));
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
imageData[(i * w * 4) + j * 4] = ginfo->image[((i * w * 3) + j * 3) + rowBytesOffset];
imageData[(i * w * 4) + j * 4 + 1] = ginfo->image[((i * w * 3) + j * 3 + 1) + rowBytesOffset];
imageData[(i * w * 4) + j * 4 + 2] = ginfo->image[((i * w * 3) + j * 3 + 2) + rowBytesOffset];
imageData[(i * w * 4) + j * 4 + 3] = 0xFF;
}
}
// copy LCD mask into glyph texture tile
MTLRegion region = MTLRegionMake2D(0, 0, w, h);
NSUInteger bytesPerRow = 4 * ginfo->width;
[blitTexture replaceRegion:region
mipmapLevel:0
withBytes:imageData
bytesPerRow:bytesPerRow];
tx1 = 0.0f;
ty1 = 0.0f;
tx2 = 1.0f;
ty2 = 1.0f;
J2dTraceLn3(J2D_TRACE_INFO, "xOffset %d yOffset %d, dstOps->height %d", dstOps->xOffset, dstOps->yOffset, dstOps->height);
J2dTraceLn4(J2D_TRACE_INFO, "tx1 %f, ty1 %f, tx2 %f, ty2 %f", tx1, ty1, tx2, ty2);
J2dTraceLn2(J2D_TRACE_INFO, "textureWidth %d textureHeight %d", dstOps->textureWidth, dstOps->textureHeight);
LCD_ADD_TRIANGLES(tx1, ty1, tx2, ty2, x, y, x+w, y+h);
[encoder setVertexBytes:txtVertices length:sizeof(txtVertices) atIndex:MeshVertexBuffer];
[encoder setFragmentTexture:blitTexture atIndex:0];
[encoder setFragmentTexture:dstOps->pTexture atIndex:1];
[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];
vertexCacheIndex = 0;
[mtlc.encoderManager endEncoder];
[blitTexture release];
MTLCommandBufferWrapper* cbwrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
[commandbuf waitUntilCompleted];
return JNI_TRUE;
}
// see DrawGlyphList.c for more on this macro...
#define FLOOR_ASSIGN(l, r) \
if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))
void
MTLTR_DrawGlyphList(JNIEnv *env, MTLContext *mtlc, BMTLSDOps *dstOps,
jint totalGlyphs, jboolean usePositions,
jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
jfloat glyphListOrigX, jfloat glyphListOrigY,
unsigned char *images, unsigned char *positions)
{
int glyphCounter;
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList");
RETURN_IF_NULL(mtlc);
RETURN_IF_NULL(dstOps);
RETURN_IF_NULL(images);
if (usePositions) {
RETURN_IF_NULL(positions);
}
glyphMode = MODE_NOT_INITED;
J2dTraceLn1(J2D_TRACE_INFO, "totalGlyphs = %d", totalGlyphs);
jboolean flushBeforeLCD = JNI_FALSE;
for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {
J2dTraceLn(J2D_TRACE_INFO, "Entered for loop for glyph list");
jint x, y;
jfloat glyphx, glyphy;
jboolean grayscale, ok;
GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));
if (ginfo == NULL) {
// this shouldn't happen, but if it does we'll just break out...
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLTR_DrawGlyphList: glyph info is null");
break;
}
grayscale = (ginfo->rowBytes == ginfo->width);
if (usePositions) {
jfloat posx = NEXT_FLOAT(positions);
jfloat posy = NEXT_FLOAT(positions);
glyphx = glyphListOrigX + posx + ginfo->topLeftX;
glyphy = glyphListOrigY + posy + ginfo->topLeftY;
FLOOR_ASSIGN(x, glyphx);
FLOOR_ASSIGN(y, glyphy);
} else {
glyphx = glyphListOrigX + ginfo->topLeftX;
glyphy = glyphListOrigY + ginfo->topLeftY;
FLOOR_ASSIGN(x, glyphx);
FLOOR_ASSIGN(y, glyphy);
glyphListOrigX += ginfo->advanceX;
glyphListOrigY += ginfo->advanceY;
}
if (ginfo->image == NULL) {
J2dTraceLn(J2D_TRACE_INFO, "Glyph image is null");
continue;
}
J2dTraceLn2(J2D_TRACE_INFO, "Glyph width = %d height = %d", ginfo->width, ginfo->height);
J2dTraceLn1(J2D_TRACE_INFO, "rowBytes = %d", ginfo->rowBytes);
if (grayscale) {
// grayscale or monochrome glyph data
if (ginfo->width <= MTLTR_CACHE_CELL_WIDTH &&
ginfo->height <= MTLTR_CACHE_CELL_HEIGHT)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList Grayscale cache");
ok = MTLTR_DrawGrayscaleGlyphViaCache(mtlc, ginfo, x, y, dstOps);
} else {
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList Grayscale no cache");
ok = MTLTR_DrawGrayscaleGlyphNoCache(mtlc, ginfo, x, y, dstOps);
}
} else {
if (!flushBeforeLCD) {
[mtlc.encoderManager endEncoder];
MTLCommandBufferWrapper* cbwrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
flushBeforeLCD = JNI_TRUE;
}
// LCD-optimized glyph data
jint rowBytesOffset = 0;
if (subPixPos) {
jint frac = (jint)((glyphx - x) * 3);
if (frac != 0) {
rowBytesOffset = 3 - frac;
x += 1;
}
}
if (rowBytesOffset == 0 &&
ginfo->width <= MTLTR_CACHE_CELL_WIDTH &&
ginfo->height <= MTLTR_CACHE_CELL_HEIGHT)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList LCD cache");
ok = MTLTR_DrawLCDGlyphViaCache(mtlc, dstOps,
ginfo, x, y,
rgbOrder, lcdContrast);
} else {
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList LCD no cache");
ok = MTLTR_DrawLCDGlyphNoCache(mtlc, dstOps,
ginfo, x, y,
rowBytesOffset,
rgbOrder, lcdContrast);
}
}
if (!ok) {
break;
}
}
/*
* Only in case of grayscale text drawing we need to flush
* cache. Still in case of LCD we are not using any intermediate
* cache.
*/
if (glyphMode == MODE_NO_CACHE_GRAY) {
MTLVertexCache_DisableMaskCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_GRAY) {
MTLTR_DisableGlyphVertexCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_LCD) {
[mtlc.encoderManager endEncoder];
lcdCacheEncoder = nil;
}
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLTextRenderer_drawGlyphList
(JNIEnv *env, jobject self,
jint numGlyphs, jboolean usePositions,
jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
jfloat glyphListOrigX, jfloat glyphListOrigY,
jlongArray imgArray, jfloatArray posArray)
{
unsigned char *images;
J2dTraceLn(J2D_TRACE_INFO, "MTLTextRenderer_drawGlyphList");
images = (unsigned char *)
(*env)->GetPrimitiveArrayCritical(env, imgArray, NULL);
if (images != NULL) {
MTLContext *mtlc = MTLRenderQueue_GetCurrentContext();
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
if (usePositions) {
unsigned char *positions = (unsigned char *)
(*env)->GetPrimitiveArrayCritical(env, posArray, NULL);
if (positions != NULL) {
MTLTR_DrawGlyphList(env, mtlc, dstOps,
numGlyphs, usePositions,
subPixPos, rgbOrder, lcdContrast,
glyphListOrigX, glyphListOrigY,
images, positions);
(*env)->ReleasePrimitiveArrayCritical(env, posArray,
positions, JNI_ABORT);
}
} else {
MTLTR_DrawGlyphList(env, mtlc, dstOps,
numGlyphs, usePositions,
subPixPos, rgbOrder, lcdContrast,
glyphListOrigX, glyphListOrigY,
images, NULL);
}
if (mtlc != NULL) {
RESET_PREVIOUS_OP();
[mtlc.encoderManager endEncoder];
MTLCommandBufferWrapper * cbwrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
}
(*env)->ReleasePrimitiveArrayCritical(env, imgArray,
images, JNI_ABORT);
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLTexturePool_h_Included
#define MTLTexturePool_h_Included
#include <time.h>
#import "MTLUtils.h"
@class MTLPoolCell;
@interface MTLTexturePoolItem : NSObject
@property (readwrite, retain) id<MTLTexture> texture;
@property (readwrite) bool isBusy;
@property (readwrite) time_t lastUsed;
@property (readwrite) bool isMultiSample;
@property (readwrite, assign) MTLTexturePoolItem* prev;
@property (readwrite, retain) MTLTexturePoolItem* next;
@property (readwrite, assign) MTLPoolCell* cell;
- (id) initWithTexture:(id<MTLTexture>)tex cell:(MTLPoolCell*)cell;
@end
@interface MTLPooledTextureHandle : NSObject
@property (readonly, assign) id<MTLTexture> texture;
@property (readonly) MTLRegion rect;
- (void) releaseTexture;
@end
// NOTE: owns all MTLTexture objects
@interface MTLTexturePool : NSObject
@property (readwrite, retain) id<MTLDevice> device;
- (id) initWithDevice:(id<MTLDevice>)device;
- (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format;
- (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format
isMultiSample:(bool)isMultiSample;
@end
@interface MTLPoolCell : NSObject
@property (readwrite, retain) MTLTexturePoolItem* available;
@property (readwrite, assign) MTLTexturePoolItem* availableTail;
@property (readwrite, retain) MTLTexturePoolItem* occupied;
- (MTLTexturePoolItem *)createItem:(id<MTLDevice>)dev
width:(int)width
height:(int)height
format:(MTLPixelFormat)format
isMultiSample:(bool)isMultiSample;
- (NSUInteger)cleanIfBefore:(time_t)lastUsedTimeToRemove;
- (void)releaseItem:(MTLTexturePoolItem *)item;
@end
#endif /* MTLTexturePool_h_Included */

View file

@ -0,0 +1,443 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#import "MTLTexturePool.h"
#import "Trace.h"
#define SCREEN_MEMORY_SIZE_4K (4096*2160*4) //~33,7 mb
#define MAX_POOL_MEMORY SCREEN_MEMORY_SIZE_4K/2
#define MAX_POOL_ITEM_LIFETIME_SEC 30
#define CELL_WIDTH_BITS 5 // ~ 32 pixel
#define CELL_HEIGHT_BITS 5 // ~ 32 pixel
@implementation MTLTexturePoolItem
@synthesize texture, isBusy, lastUsed, isMultiSample, next, cell;
- (id) initWithTexture:(id<MTLTexture>)tex cell:(MTLPoolCell*)c{
self = [super init];
if (self == nil) return self;
self.texture = tex;
isBusy = NO;
self.next = nil;
self.prev = nil;
self.cell = c;
return self;
}
- (void) dealloc {
[texture release];
[super dealloc];
}
@end
@implementation MTLPooledTextureHandle
{
MTLRegion _rect;
id<MTLTexture> _texture;
MTLTexturePoolItem * _poolItem;
}
@synthesize texture = _texture, rect = _rect;
- (id) initWithPoolItem:(id<MTLTexture>)texture rect:(MTLRegion)rectangle poolItem:(MTLTexturePoolItem *)poolItem {
self = [super init];
if (self == nil) return self;
_rect = rectangle;
_texture = texture;
_poolItem = poolItem;
return self;
}
- (void) releaseTexture {
[_poolItem.cell releaseItem:_poolItem];
}
@end
@implementation MTLPoolCell {
NSLock* _lock;
}
@synthesize available, availableTail, occupied;
- (instancetype)init {
self = [super init];
if (self) {
self.available = nil;
self.availableTail = nil;
self.occupied = nil;
_lock = [[NSLock alloc] init];
}
return self;
}
- (void)occupyItem:(MTLTexturePoolItem *)item {
if (item.isBusy) return;
[item retain];
if (item.prev == nil) {
self.available = item.next;
if (item.next) {
item.next.prev = nil;
} else {
self.availableTail = item.prev;
}
} else {
item.prev.next = item.next;
if (item.next) {
item.next.prev = item.prev;
} else {
self.availableTail = item.prev;
}
item.prev = nil;
}
if (occupied) occupied.prev = item;
item.next = occupied;
self.occupied = item;
[item release];
item.isBusy = YES;
}
- (void)releaseItem:(MTLTexturePoolItem *)item {
[_lock lock];
@try {
if (!item.isBusy) return;
[item retain];
if (item.prev == nil) {
self.occupied = item.next;
if (item.next) item.next.prev = nil;
} else {
item.prev.next = item.next;
if (item.next) item.next.prev = item.prev;
item.prev = nil;
}
if (self.available) {
self.available.prev = item;
} else {
self.availableTail = item;
}
item.next = self.available;
self.available = item;
item.isBusy = NO;
[item release];
} @finally {
[_lock unlock];
}
}
- (void)addOccupiedItem:(MTLTexturePoolItem *)item {
if (self.occupied) self.occupied.prev = item;
item.next = self.occupied;
item.isBusy = YES;
self.occupied = item;
}
- (void)removeAvailableItem:(MTLTexturePoolItem*)item {
[item retain];
if (item.prev == nil) {
self.available = item.next;
if (item.next) {
item.next.prev = nil;
item.next = nil;
} else {
self.availableTail = item.prev;
}
} else {
item.prev.next = item.next;
if (item.next) {
item.next.prev = item.prev;
item.next = nil;
} else {
self.availableTail = item.prev;
}
}
[item release];
}
- (void)removeAllItems {
MTLTexturePoolItem *cur = self.available;
while (cur != nil) {
cur = cur.next;
self.available = cur;
}
cur = self.occupied;
while (cur != nil) {
cur = cur.next;
self.occupied = cur;
}
self.availableTail = nil;
}
- (MTLTexturePoolItem *)createItem:(id<MTLDevice>)dev
width:(int)width
height:(int)height
format:(MTLPixelFormat)format
isMultiSample:(bool)isMultiSample
{
MTLTextureDescriptor *textureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format
width:(NSUInteger) width
height:(NSUInteger) height
mipmapped:NO];
if (isMultiSample) {
textureDescriptor.textureType = MTLTextureType2DMultisample;
textureDescriptor.sampleCount = MTLAASampleCount;
textureDescriptor.storageMode = MTLStorageModePrivate;
}
id <MTLTexture> tex = (id <MTLTexture>) [[dev newTextureWithDescriptor:textureDescriptor] autorelease];
MTLTexturePoolItem* item = [[[MTLTexturePoolItem alloc] initWithTexture:tex cell:self] autorelease];
item.isMultiSample = isMultiSample;
[_lock lock];
@try {
[self addOccupiedItem:item];
} @finally {
[_lock unlock];
}
return item;
}
- (NSUInteger)cleanIfBefore:(time_t)lastUsedTimeToRemove {
NSUInteger deallocMem = 0;
[_lock lock];
MTLTexturePoolItem *cur = availableTail;
@try {
while (cur != nil) {
MTLTexturePoolItem *prev = cur.prev;
if (lastUsedTimeToRemove <= 0 ||
cur.lastUsed < lastUsedTimeToRemove) {
#ifdef DEBUG
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE,
"MTLTexturePool: remove pool item: tex=%p, w=%d h=%d, elapsed=%d",
cur.texture, cur.texture.width, cur.texture.height,
time(NULL) - cur.lastUsed);
#endif //DEBUG
deallocMem += cur.texture.width * cur.texture.height * 4;
[self removeAvailableItem:cur];
} else {
if (lastUsedTimeToRemove > 0) break;
}
cur = prev;
}
} @finally {
[_lock unlock];
}
return deallocMem;
}
- (MTLTexturePoolItem *)occupyItem:(int)width height:(int)height format:(MTLPixelFormat)format
isMultiSample:(bool)isMultiSample {
int minDeltaArea = -1;
const int requestedPixels = width*height;
MTLTexturePoolItem *minDeltaTpi = nil;
[_lock lock];
@try {
for (MTLTexturePoolItem *cur = available; cur != nil; cur = cur.next) {
if (cur.texture.pixelFormat != format
|| cur.isMultiSample != isMultiSample) { // TODO: use swizzle when formats are not equal
continue;
}
if (cur.texture.width < width || cur.texture.height < height) {
continue;
}
const int deltaArea = (const int) (cur.texture.width * cur.texture.height - requestedPixels);
if (minDeltaArea < 0 || deltaArea < minDeltaArea) {
minDeltaArea = deltaArea;
minDeltaTpi = cur;
if (deltaArea == 0) {
// found exact match in current cell
break;
}
}
}
if (minDeltaTpi) {
[self occupyItem:minDeltaTpi];
}
} @finally {
[_lock unlock];
}
return minDeltaTpi;
}
- (void) dealloc {
[_lock lock];
@try {
[self removeAllItems];
} @finally {
[_lock unlock];
}
[_lock release];
[super dealloc];
}
@end
@implementation MTLTexturePool {
int _memoryTotalAllocated;
void ** _cells;
int _poolCellWidth;
int _poolCellHeight;
}
@synthesize device;
- (id) initWithDevice:(id<MTLDevice>)dev {
self = [super init];
if (self == nil) return self;
_memoryTotalAllocated = 0;
_poolCellWidth = 10;
_poolCellHeight = 10;
const int cellsCount = _poolCellWidth * _poolCellHeight;
_cells = (void **)malloc(cellsCount * sizeof(void*));
memset(_cells, 0, cellsCount * sizeof(void*));
self.device = dev;
return self;
}
- (void) dealloc {
for (int c = 0; c < _poolCellWidth * _poolCellHeight; ++c) {
MTLPoolCell * cell = _cells[c];
if (cell != NULL) {
[cell release];
}
}
free(_cells);
[super dealloc];
}
// NOTE: called from RQ-thread (on blit operations)
- (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format {
return [self getTexture:width height:height format:format isMultiSample:NO];
}
// NOTE: called from RQ-thread (on blit operations)
- (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format
isMultiSample:(bool)isMultiSample {
// 1. clean pool if necessary
const int requestedPixels = width*height;
const int requestedBytes = requestedPixels*4;
if (_memoryTotalAllocated + requestedBytes > MAX_POOL_MEMORY) {
[self cleanIfNecessary:0]; // release all free textures
} else if (_memoryTotalAllocated + requestedBytes > MAX_POOL_MEMORY/2) {
[self cleanIfNecessary:MAX_POOL_ITEM_LIFETIME_SEC]; // release only old free textures
}
// 2. find free item
const int cellX0 = width >> CELL_WIDTH_BITS;
const int cellY0 = height >> CELL_HEIGHT_BITS;
const int cellX1 = cellX0 + 1;
const int cellY1 = cellY0 + 1;
if (cellX1 > _poolCellWidth || cellY1 > _poolCellHeight) {
const int newCellWidth = cellX1 <= _poolCellWidth ? _poolCellWidth : cellX1;
const int newCellHeight = cellY1 <= _poolCellHeight ? _poolCellHeight : cellY1;
const int newCellsCount = newCellWidth*newCellHeight;
#ifdef DEBUG
J2dTraceLn2(J2D_TRACE_VERBOSE, "MTLTexturePool: resize: %d -> %d", _poolCellWidth * _poolCellHeight, newCellsCount);
#endif
void ** newcells = malloc(newCellsCount*sizeof(void*));
const int strideBytes = _poolCellWidth * sizeof(void*);
for (int cy = 0; cy < _poolCellHeight; ++cy) {
void ** dst = newcells + cy*newCellWidth;
void ** src = _cells + cy * _poolCellWidth;
memcpy(dst, src, strideBytes);
if (newCellWidth > _poolCellWidth)
memset(dst + _poolCellWidth, 0, (newCellWidth - _poolCellWidth) * sizeof(void*));
}
if (newCellHeight > _poolCellHeight) {
void ** dst = newcells + _poolCellHeight * newCellWidth;
memset(dst, 0, (newCellHeight - _poolCellHeight) * newCellWidth * sizeof(void*));
}
free(_cells);
_cells = newcells;
_poolCellWidth = newCellWidth;
_poolCellHeight = newCellHeight;
}
MTLTexturePoolItem * minDeltaTpi = nil;
int minDeltaArea = -1;
for (int cy = cellY0; cy < cellY1; ++cy) {
for (int cx = cellX0; cx < cellX1; ++cx) {
MTLPoolCell * cell = _cells[cy * _poolCellWidth + cx];
if (cell != NULL) {
MTLTexturePoolItem* tpi = [cell occupyItem:width height:height
format:format isMultiSample:isMultiSample];
if (!tpi) continue;
const int deltaArea = (const int) (tpi.texture.width * tpi.texture.height - requestedPixels);
if (minDeltaArea < 0 || deltaArea < minDeltaArea) {
minDeltaArea = deltaArea;
minDeltaTpi = tpi;
if (deltaArea == 0) {
// found exact match in current cell
break;
}
}
}
}
if (minDeltaTpi != nil) {
break;
}
}
if (minDeltaTpi == NULL) {
MTLPoolCell* cell = _cells[cellY0 * _poolCellWidth + cellX0];
if (cell == NULL) {
cell = [[MTLPoolCell alloc] init];
_cells[cellY0 * _poolCellWidth + cellX0] = cell;
}
minDeltaTpi = [cell createItem:device width:width height:height format:format isMultiSample:isMultiSample];
_memoryTotalAllocated += requestedBytes;
J2dTraceLn5(J2D_TRACE_VERBOSE, "MTLTexturePool: created pool item: tex=%p, w=%d h=%d, pf=%d | total memory = %d Kb", minDeltaTpi.texture, width, height, format, _memoryTotalAllocated/1024);
}
minDeltaTpi.isBusy = YES;
minDeltaTpi.lastUsed = time(NULL);
return [[[MTLPooledTextureHandle alloc] initWithPoolItem:minDeltaTpi.texture
rect:MTLRegionMake2D(0, 0,
minDeltaTpi.texture.width,
minDeltaTpi.texture.height)
poolItem:minDeltaTpi] autorelease];
}
- (void) cleanIfNecessary:(int)lastUsedTimeThreshold {
time_t lastUsedTimeToRemove =
lastUsedTimeThreshold > 0 ?
time(NULL) - lastUsedTimeThreshold :
lastUsedTimeThreshold;
for (int cy = 0; cy < _poolCellHeight; ++cy) {
for (int cx = 0; cx < _poolCellWidth; ++cx) {
MTLPoolCell * cell = _cells[cy * _poolCellWidth + cx];
if (cell != NULL) {
_memoryTotalAllocated -= [cell cleanIfBefore:lastUsedTimeToRemove];
}
}
}
}
@end

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLTransform_h_Included
#define MTLTransform_h_Included
#import <Metal/Metal.h>
#include <jni.h>
@interface MTLTransform : NSObject
- (id)init;
- (BOOL)isEqual:(MTLTransform *)other;
- (void)copyFrom:(MTLTransform *)other;
- (void)setTransformM00:(jdouble) m00 M10:(jdouble) m10
M01:(jdouble) m01 M11:(jdouble) m11
M02:(jdouble) m02 M12:(jdouble) m12;
- (void)resetTransform;
- (void)setVertexMatrix:(id<MTLRenderCommandEncoder>)encoder
destWidth:(NSUInteger)dw
destHeight:(NSUInteger)dh;
@end
#endif // MTLTransform_h_Included

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include "MTLTransform.h"
#include <jni.h>
#include <simd/simd.h>
#include "common.h"
@implementation MTLTransform {
jboolean _useTransform;
simd_float4x4 _transform4x4;
simd_float4x4 _normalize4x4; // just a buffer for setVertexMatrix
}
- (id)init {
self = [super init];
if (self) {
memset(&_normalize4x4, 0, sizeof(_normalize4x4));
_normalize4x4.columns[3][0] = -1.f;
_normalize4x4.columns[3][1] = 1.f;
_normalize4x4.columns[3][3] = 1.0;
_useTransform = JNI_FALSE;
}
return self;
}
- (BOOL)isEqual:(MTLTransform *)other {
if (self == other)
return YES;
return _useTransform == other->_useTransform
&& simd_equal(_transform4x4, other->_transform4x4);
}
- (void)copyFrom:(MTLTransform *)other {
_useTransform = other->_useTransform;
if (_useTransform) {
_transform4x4 = other->_transform4x4;
}
}
- (void)setTransformM00:(jdouble) m00 M10:(jdouble) m10
M01:(jdouble) m01 M11:(jdouble) m11
M02:(jdouble) m02 M12:(jdouble) m12 {
memset(&(_transform4x4), 0, sizeof(_transform4x4));
_transform4x4.columns[0][0] = m00;
_transform4x4.columns[0][1] = m10;
_transform4x4.columns[1][0] = m01;
_transform4x4.columns[1][1] = m11;
_transform4x4.columns[3][0] = m02;
_transform4x4.columns[3][1] = m12;
_transform4x4.columns[3][3] = 1.0;
_useTransform = JNI_TRUE;
}
- (void)resetTransform {
_useTransform = JNI_FALSE;
}
- (void)setVertexMatrix:(id<MTLRenderCommandEncoder>)encoder
destWidth:(NSUInteger)dw
destHeight:(NSUInteger)dh {
// update matrix for vertex shader
_normalize4x4.columns[0][0] = 2/(double)dw;
_normalize4x4.columns[1][1] = -2/(double)dh;
if (_useTransform) {
simd_float4x4 vertexMatrix = simd_mul(_normalize4x4, _transform4x4);
[encoder setVertexBytes:&(vertexMatrix) length:sizeof(vertexMatrix) atIndex:MatrixBuffer];
} else {
[encoder setVertexBytes:&(_normalize4x4) length:sizeof(_normalize4x4) atIndex:MatrixBuffer];
}
}
@end

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLUtils_h_Included
#define MTLUtils_h_Included
#import <Metal/Metal.h>
#define MTLAASampleCount 4
#endif /* MTLUtils_h_Included */

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include "MTLUtils.h"
#include <jni.h>
#include <simd/simd.h>
#import <ThreadUtilities.h>
#import <PropertiesUtilities.h>
#include "common.h"
#include "Trace.h"
extern void J2dTraceImpl(int level, jboolean cr, const char *string, ...);
void J2dTraceTraceVector(simd_float4 pt) {
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE, "[%lf %lf %lf %lf]", pt.x, pt.y, pt.z, pt.w);
}
void checkTransform(float * position, simd_float4x4 transform4x4) {
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE, "check transform: ");
simd_float4 fpt = simd_make_float4(position[0], position[1], position[2], 1.f);
simd_float4 fpt_trans = simd_mul(transform4x4, fpt);
J2dTraceTraceVector(fpt);
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE, " ===>>> ");
J2dTraceTraceVector(fpt_trans);
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, " ");
}
static void traceMatrix(simd_float4x4 * mtx) {
for (int row = 0; row < 4; ++row) {
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE, " [%lf %lf %lf %lf]",
mtx->columns[0][row], mtx->columns[1][row], mtx->columns[2][row], mtx->columns[3][row]);
}
}
void traceRaster(char * p, int width, int height, int stride) {
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
unsigned char pix0 = p[y*stride + x*4];
unsigned char pix1 = p[y*stride + x*4 + 1];
unsigned char pix2 = p[y*stride + x*4 + 2];
unsigned char pix3 = p[y*stride + x*4 + 3];
J2dTraceImpl(J2D_TRACE_INFO, JNI_FALSE,"[%u,%u,%u,%u], ", pix0, pix1, pix2, pix3);
}
J2dTraceImpl(J2D_TRACE_INFO, JNI_TRUE, "");
}
}
void tracePoints(jint nPoints, jint *xPoints, jint *yPoints) {
for (int i = 0; i < nPoints; i++)
J2dTraceImpl(J2D_TRACE_INFO, JNI_TRUE, "\t(%d, %d)", *(xPoints++), *(yPoints++));
}
jboolean isOptionEnabled(const char * option) {
JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
NSString * optionProp = [PropertiesUtilities
javaSystemPropertyForKey:[NSString stringWithUTF8String:option] withEnv:env];
NSString * lowerCaseProp = [optionProp localizedLowercaseString];
return [@"true" isEqual:lowerCaseProp];
}

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef MTLVertexCache_h_Included
#define MTLVertexCache_h_Included
#include "j2d_md.h"
#include "MTLContext.h"
#include "fontscalerdefs.h"
/**
* Constants that control the size of the vertex cache.
*/
#define MTLVC_MAX_INDEX 250
/**
* Constants that control the size of the texture tile cache used for
* mask operations.
*/
#define MTLVC_MASK_CACHE_TILE_WIDTH 32
#define MTLVC_MASK_CACHE_TILE_HEIGHT 32
#define MTLVC_MASK_CACHE_TILE_SIZE \
(MTLVC_MASK_CACHE_TILE_WIDTH * MTLVC_MASK_CACHE_TILE_HEIGHT)
#define MTLVC_MASK_CACHE_WIDTH_IN_TILES 8
#define MTLVC_MASK_CACHE_HEIGHT_IN_TILES 4
#define MTLVC_MASK_CACHE_WIDTH_IN_TEXELS \
(MTLVC_MASK_CACHE_TILE_WIDTH * MTLVC_MASK_CACHE_WIDTH_IN_TILES)
#define MTLVC_MASK_CACHE_HEIGHT_IN_TEXELS \
(MTLVC_MASK_CACHE_TILE_HEIGHT * MTLVC_MASK_CACHE_HEIGHT_IN_TILES)
/*
* We reserve one (fully opaque) tile in the upper-right corner for
* operations where the mask is null.
*/
#define MTLVC_MASK_CACHE_MAX_INDEX \
((MTLVC_MASK_CACHE_WIDTH_IN_TILES * MTLVC_MASK_CACHE_HEIGHT_IN_TILES) - 1)
#define MTLVC_MASK_CACHE_SPECIAL_TILE_X \
(MTLVC_MASK_CACHE_WIDTH_IN_TEXELS - MTLVC_MASK_CACHE_TILE_WIDTH)
#define MTLVC_MASK_CACHE_SPECIAL_TILE_Y \
(MTLVC_MASK_CACHE_HEIGHT_IN_TEXELS - MTLVC_MASK_CACHE_TILE_HEIGHT)
/**
* Exported methods.
*/
jboolean MTLVertexCache_InitVertexCache();
void MTLVertexCache_FlushVertexCache(MTLContext *mtlc);
void MTLVertexCache_FlushGlyphVertexCache();
void MTLVertexCache_FreeVertexCache();
void MTLVertexCache_EnableMaskCache(MTLContext *mtlc, BMTLSDOps *dstOps);
void MTLVertexCache_DisableMaskCache(MTLContext *mtlc);
void MTLVertexCache_AddMaskQuad(MTLContext *mtlc,
jint srcx, jint srcy,
jint dstx, jint dsty,
jint width, jint height,
jint maskscan, void *mask,
BMTLSDOps *dstOps);
void
MTLVertexCache_AddGlyphQuad(MTLContext *mtlc,
jfloat tx1, jfloat ty1, jfloat tx2, jfloat ty2,
jfloat dx1, jfloat dy1, jfloat dx2, jfloat dy2);
void MTLVertexCache_CreateSamplingEncoder(MTLContext *mtlc, BMTLSDOps *dstOps);
#endif /* MTLVertexCache_h_Included */

View file

@ -0,0 +1,316 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include <stdlib.h>
#include <string.h>
#include "sun_java2d_SunGraphics2D.h"
#include "MTLPaints.h"
#include "MTLVertexCache.h"
#include "MTLTexturePool.h"
#include "MTLTextRenderer.h"
#include "common.h"
typedef struct _J2DVertex {
float position[2];
float txtpos[2];
} J2DVertex;
static J2DVertex *vertexCache = NULL;
static jint vertexCacheIndex = 0;
static MTLPooledTextureHandle * maskCacheTex = NULL;
static jint maskCacheIndex = 0;
static id<MTLRenderCommandEncoder> encoder = NULL;
#define MTLVC_ADD_VERTEX(TX, TY, DX, DY, DZ) \
do { \
J2DVertex *v = &vertexCache[vertexCacheIndex++]; \
v->txtpos[0] = TX; \
v->txtpos[1] = TY; \
v->position[0]= DX; \
v->position[1] = DY; \
} while (0)
#define MTLVC_ADD_TRIANGLES(TX1, TY1, TX2, TY2, DX1, DY1, DX2, DY2) \
do { \
MTLVC_ADD_VERTEX(TX1, TY1, DX1, DY1, 0); \
MTLVC_ADD_VERTEX(TX2, TY1, DX2, DY1, 0); \
MTLVC_ADD_VERTEX(TX2, TY2, DX2, DY2, 0); \
MTLVC_ADD_VERTEX(TX2, TY2, DX2, DY2, 0); \
MTLVC_ADD_VERTEX(TX1, TY2, DX1, DY2, 0); \
MTLVC_ADD_VERTEX(TX1, TY1, DX1, DY1, 0); \
} while (0)
jboolean
MTLVertexCache_InitVertexCache()
{
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_InitVertexCache");
if (vertexCache == NULL) {
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_InitVertexCache : vertexCache == NULL");
vertexCache = (J2DVertex *)malloc(MTLVC_MAX_INDEX * sizeof(J2DVertex));
if (vertexCache == NULL) {
return JNI_FALSE;
}
}
return JNI_TRUE;
}
void
MTLVertexCache_FlushVertexCache(MTLContext *mtlc)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_FlushVertexCache");
if (vertexCacheIndex > 0) {
[encoder setVertexBytes: vertexCache length:vertexCacheIndex * sizeof(J2DVertex)
atIndex:MeshVertexBuffer];
[encoder setFragmentTexture:maskCacheTex.texture atIndex: 0];
J2dTraceLn1(J2D_TRACE_INFO,
"MTLVertexCache_FlushVertexCache : encode %d characters", (vertexCacheIndex / 6));
[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:vertexCacheIndex];
}
vertexCacheIndex = 0;
maskCacheIndex = 0;
if (maskCacheTex != nil) {
[[mtlc getCommandBufferWrapper] registerPooledTexture:maskCacheTex];
[maskCacheTex release];
maskCacheTex = nil;
}
}
void
MTLVertexCache_FlushGlyphVertexCache()
{
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_FlushGlyphVertexCache");
if (vertexCacheIndex > 0) {
[encoder setVertexBytes: vertexCache length:vertexCacheIndex * sizeof(J2DVertex)
atIndex:MeshVertexBuffer];
id<MTLTexture> glyphCacheTex = MTLTR_GetGlyphCacheTexture();
[encoder setFragmentTexture:glyphCacheTex atIndex: 0];
J2dTraceLn1(J2D_TRACE_INFO,
"MTLVertexCache_FlushGlyphVertexCache : encode %d characters", (vertexCacheIndex / 6));
[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:vertexCacheIndex];
}
vertexCacheIndex = 0;
}
void MTLVertexCache_FreeVertexCache()
{
free(vertexCache);
vertexCache = NULL;
}
static jboolean
MTLVertexCache_InitMaskCache(MTLContext *mtlc) {
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_InitMaskCache");
if (maskCacheTex == NULL) {
maskCacheTex = [mtlc.texturePool getTexture:MTLVC_MASK_CACHE_WIDTH_IN_TEXELS
height:MTLVC_MASK_CACHE_HEIGHT_IN_TEXELS
format:MTLPixelFormatA8Unorm];
[maskCacheTex retain];
if (maskCacheTex == nil) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLVertexCache_InitMaskCache: can't obtain temporary texture object from pool");
return JNI_FALSE;
}
}
// init special fully opaque tile in the upper-right corner of
// the mask cache texture
char tile[MTLVC_MASK_CACHE_TILE_SIZE];
memset(tile, 0xff, MTLVC_MASK_CACHE_TILE_SIZE);
jint texx = MTLVC_MASK_CACHE_TILE_WIDTH * (MTLVC_MASK_CACHE_WIDTH_IN_TILES - 1);
jint texy = MTLVC_MASK_CACHE_TILE_HEIGHT * (MTLVC_MASK_CACHE_HEIGHT_IN_TILES - 1);
NSUInteger bytesPerRow = 1 * MTLVC_MASK_CACHE_TILE_WIDTH;
MTLRegion region = {
{texx, texy, 0},
{MTLVC_MASK_CACHE_TILE_WIDTH, MTLVC_MASK_CACHE_TILE_HEIGHT, 1}
};
// do we really need this??
[maskCacheTex.texture replaceRegion:region
mipmapLevel:0
withBytes:tile
bytesPerRow:bytesPerRow];
return JNI_TRUE;
}
void
MTLVertexCache_EnableMaskCache(MTLContext *mtlc, BMTLSDOps *dstOps)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_EnableMaskCache");
if (!MTLVertexCache_InitVertexCache()) {
return;
}
if (maskCacheTex == NULL) {
if (!MTLVertexCache_InitMaskCache(mtlc)) {
return;
}
}
MTLVertexCache_CreateSamplingEncoder(mtlc, dstOps);
}
void
MTLVertexCache_DisableMaskCache(MTLContext *mtlc)
{
// TODO : Once we enable check_previous_op
// we will start using DisableMaskCache until then
// we are force flushing vertexcache.
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_DisableMaskCache");
MTLVertexCache_FlushVertexCache(mtlc);
maskCacheIndex = 0;
free(vertexCache);
vertexCache = NULL;
}
void
MTLVertexCache_CreateSamplingEncoder(MTLContext *mtlc, BMTLSDOps *dstOps) {
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_CreateSamplingEncoder");
encoder = [mtlc.encoderManager getTextEncoder:dstOps
isSrcOpaque:NO];
}
void
MTLVertexCache_AddMaskQuad(MTLContext *mtlc,
jint srcx, jint srcy,
jint dstx, jint dsty,
jint width, jint height,
jint maskscan, void *mask,
BMTLSDOps *dstOps)
{
jfloat tx1, ty1, tx2, ty2;
jfloat dx1, dy1, dx2, dy2;
J2dTraceLn1(J2D_TRACE_INFO, "MTLVertexCache_AddMaskQuad: %d",
maskCacheIndex);
if (maskCacheIndex >= MTLVC_MASK_CACHE_MAX_INDEX)
{
J2dTraceLn2(J2D_TRACE_INFO, "maskCacheIndex = %d, vertexCacheIndex = %d", maskCacheIndex, vertexCacheIndex);
MTLVertexCache_FlushVertexCache(mtlc);
MTLVertexCache_EnableMaskCache(mtlc, dstOps);
maskCacheIndex = 0;
}
if (mask != NULL) {
jint texx = MTLVC_MASK_CACHE_TILE_WIDTH *
(maskCacheIndex % MTLVC_MASK_CACHE_WIDTH_IN_TILES);
jint texy = MTLVC_MASK_CACHE_TILE_HEIGHT *
(maskCacheIndex / MTLVC_MASK_CACHE_WIDTH_IN_TILES);
J2dTraceLn5(J2D_TRACE_INFO, "texx = %d texy = %d width = %d height = %d maskscan = %d", texx, texy, width,
height, maskscan);
NSUInteger bytesPerRow = 1 * width;
NSUInteger slice = bytesPerRow * srcy + srcx;
MTLRegion region = {
{texx, texy, 0},
{width, height, 1}
};
// Whenever we have source stride bigger that destination stride
// we need to pick appropriate source subtexture. In repalceRegion
// we can give destination subtexturing properly but we can't
// subtexture from system memory glyph we have. So in such
// cases we are creating seperate tile and scan the source
// stride into destination using memcpy. In case of OpenGL we
// can update source pointers, in case of D3D we ar doing memcpy.
// We can use MTLBuffer and then copy source subtexture but that
// adds extra blitting logic.
// TODO : Research more and try removing memcpy logic.
if (maskscan <= width) {
int height_offset = bytesPerRow * srcy;
[maskCacheTex.texture replaceRegion:region
mipmapLevel:0
withBytes:mask + height_offset
bytesPerRow:bytesPerRow];
} else {
int dst_offset, src_offset;
int size = 1 * width * height;
char tile[size];
dst_offset = 0;
for (int i = srcy; i < srcy + height; i++) {
J2dTraceLn2(J2D_TRACE_INFO, "srcx = %d srcy = %d", srcx, srcy);
src_offset = maskscan * i + srcx;
J2dTraceLn2(J2D_TRACE_INFO, "src_offset = %d dst_offset = %d", src_offset, dst_offset);
memcpy(tile + dst_offset, mask + src_offset, width);
dst_offset = dst_offset + width;
}
[maskCacheTex.texture replaceRegion:region
mipmapLevel:0
withBytes:tile
bytesPerRow:bytesPerRow];
}
tx1 = ((jfloat) texx) / MTLVC_MASK_CACHE_WIDTH_IN_TEXELS;
ty1 = ((jfloat) texy) / MTLVC_MASK_CACHE_HEIGHT_IN_TEXELS;
} else {
tx1 = ((jfloat)MTLVC_MASK_CACHE_SPECIAL_TILE_X) /
MTLVC_MASK_CACHE_WIDTH_IN_TEXELS;
ty1 = ((jfloat)MTLVC_MASK_CACHE_SPECIAL_TILE_Y) /
MTLVC_MASK_CACHE_HEIGHT_IN_TEXELS;
}
maskCacheIndex++;
tx2 = tx1 + (((jfloat)width) / MTLVC_MASK_CACHE_WIDTH_IN_TEXELS);
ty2 = ty1 + (((jfloat)height) / MTLVC_MASK_CACHE_HEIGHT_IN_TEXELS);
dx1 = (jfloat)dstx;
dy1 = (jfloat)dsty;
dx2 = dx1 + width;
dy2 = dy1 + height;
J2dTraceLn8(J2D_TRACE_INFO, "tx1 = %f ty1 = %f tx2 = %f ty2 = %f dx1 = %f dy1 = %f dx2 = %f dy2 = %f", tx1, ty1, tx2, ty2, dx1, dy1, dx2, dy2);
MTLVC_ADD_TRIANGLES(tx1, ty1, tx2, ty2,
dx1, dy1, dx2, dy2);
}
void
MTLVertexCache_AddGlyphQuad(MTLContext *mtlc,
jfloat tx1, jfloat ty1, jfloat tx2, jfloat ty2,
jfloat dx1, jfloat dy1, jfloat dx2, jfloat dy2)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_AddGlyphQuad");
if (vertexCacheIndex >= MTLVC_MAX_INDEX)
{
J2dTraceLn2(J2D_TRACE_INFO, "maskCacheIndex = %d, vertexCacheIndex = %d", maskCacheIndex, vertexCacheIndex);
MTLVertexCache_FlushGlyphVertexCache();
}
MTLVC_ADD_TRIANGLES(tx1, ty1, tx2, ty2,
dx1, dy1, dx2, dy2);
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2020, 2021, 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.
*/
#ifndef __RENDEROPTIONS_H
#define __RENDEROPTIONS_H
#include <jni.h>
#include "MTLSurfaceDataBase.h"
// Utility struct to transfer rendering paramenters
typedef struct {
jboolean isTexture;
jboolean isAA;
int interpolation;
SurfaceRasterFlags srcFlags;
SurfaceRasterFlags dstFlags;
jboolean isText;
jboolean isLCD;
jboolean isAAShader;
} RenderOptions;
#endif //__RENDEROPTIONS_H

View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#ifndef COMMON_H
#define COMMON_H
#include <simd/simd.h>
#define PGRAM_VERTEX_COUNT 6
#define QUAD_VERTEX_COUNT 4
#define GRAD_MAX_FRACTIONS 12
enum GradCycleMethod {
GradNoCycle = 0,
GradReflect = 1,
GradRepeat = 2
};
enum VertexAttributes {
VertexAttributePosition = 0,
VertexAttributeTexPos = 1,
VertexAttributeITexPos = 2
};
enum BufferIndex {
MeshVertexBuffer = 0,
FrameUniformBuffer = 1,
MatrixBuffer = 2
};
struct FrameUniforms {
vector_float4 color;
};
struct TransformMatrix {
matrix_float4x4 transformMatrix;
};
struct GradFrameUniforms {
vector_float3 params;
vector_float4 color1;
vector_float4 color2;
int isCyclic;
float extraAlpha;
};
struct LinGradFrameUniforms {
vector_float3 params;
float fract[GRAD_MAX_FRACTIONS];
vector_float4 color[GRAD_MAX_FRACTIONS];
int numFracts;
int isLinear;
int cycleMethod;
float extraAlpha;
};
struct RadGradFrameUniforms {
float fract[GRAD_MAX_FRACTIONS];
vector_float4 color[GRAD_MAX_FRACTIONS];
int numFracts;
int isLinear;
int cycleMethod;
vector_float3 m0;
vector_float3 m1;
vector_float3 precalc;
float extraAlpha;
};
struct Vertex {
float position[2];
};
struct TxtVertex {
float position[2];
float txtpos[2];
};
struct AAVertex {
float position[2];
float otxtpos[2];
float itxtpos[2];
};
// These values are mapped from AffineTransformOp
#define INTERPOLATION_NEAREST_NEIGHBOR 1
#define INTERPOLATION_BILINEAR 2
// #define INTERPOLATION_BICUBIC 3
// NOTE: Metal samplers doesn't supports bicubic interpolation
// see table 2.7 from https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf
// (probably we need to implement separate fragment shader with bicubic interpolation)
struct TxtFrameUniforms {
vector_float4 color;
int mode; // NOTE: consider to use bit fields
int isSrcOpaque;
int isDstOpaque;
float extraAlpha;
};
struct TxtFrameOpRescaleUniforms {
vector_float4 color;
float extraAlpha;
int isSrcOpaque;
int isNonPremult;
vector_float4 normScaleFactors;
vector_float4 normOffsets;
};
struct TxtFrameOpConvolveUniforms {
float extraAlpha;
int isSrcOpaque;
vector_float4 imgEdge;
int kernelSize;
int isEdgeZeroFill;
};
struct TxtFrameOpLookupUniforms {
float extraAlpha;
int isSrcOpaque;
vector_float4 offset;
int isUseSrcAlpha;
int isNonPremult;
};
struct AnchorData
{
vector_float3 xParams;
vector_float3 yParams;
};
struct LCDFrameUniforms {
vector_float3 src_adj;
vector_float3 gamma;
vector_float3 invgamma;
};
#endif

View file

@ -0,0 +1,821 @@
/*
* Copyright (c) 2019, 2021, 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.
*/
#include <simd/simd.h>
#include <metal_stdlib>
#include "common.h"
using namespace metal;
struct VertexInput {
float2 position [[attribute(VertexAttributePosition)]];
};
struct TxtVertexInput {
float2 position [[attribute(VertexAttributePosition)]];
float2 texCoords [[attribute(VertexAttributeTexPos)]];
};
struct AAVertexInput {
float2 position [[attribute(VertexAttributePosition)]];
float2 oTexCoords [[attribute(VertexAttributeTexPos)]];
float2 iTexCoords [[attribute(VertexAttributeITexPos)]];
};
struct ColShaderInOut {
float4 position [[position]];
half4 color;
};
struct AAShaderInOut {
float4 position [[position]];
float2 outerTexCoords;
float2 innerTexCoords;
half4 color;
};
struct StencilShaderInOut {
float4 position [[position]];
char color;
};
struct TxtShaderInOut {
float4 position [[position]];
float2 texCoords;
float2 tpCoords;
};
struct LCDShaderInOut {
float4 position [[position]];
float2 orig_pos;
float2 texCoords;
};
struct GradShaderInOut {
float4 position [[position]];
float2 texCoords;
};
struct ColShaderInOut_XOR {
float4 position [[position]];
float2 orig_pos;
half4 color;
};
struct TxtShaderInOut_XOR {
float4 position [[position]];
float2 orig_pos;
float2 texCoords;
float2 tpCoords;
};
inline float fromLinear(float c)
{
if (isnan(c)) c = 0.0;
if (c > 1.0)
c = 1.0;
else if (c < 0.0)
c = 0.0;
else if (c < 0.0031308)
c = 12.92 * c;
else
c = 1.055 * powr(c, 1.0/2.4) - 0.055;
return c;
}
inline float3 fromLinear3(float3 c) {
//c.r = fromLinear(c.r);
//c.g = fromLinear(c.g);
//c.b = fromLinear(c.b);
// Use approximated calculations to match software rendering
c.rgb = 1.055 * pow(c.rgb, float3(0.416667)) - 0.055;
return c;
}
template <typename Uniforms> inline
float4 frag_single_grad(float a, Uniforms uniforms)
{
int fa = floor(a);
if (uniforms.isCyclic) {
if (fa%2) {
a = 1.0 + fa - a;
} else {
a = a - fa;
}
} else {
a = saturate(a);
}
return mix(uniforms.color1, uniforms.color2, a);
}
template <typename Uniforms> inline
float4 frag_multi_grad(float a, Uniforms uniforms)
{
if (uniforms.cycleMethod > GradNoCycle) {
int fa = floor(a);
a = a - fa;
if (uniforms.cycleMethod == GradReflect && fa%2) {
a = 1.0 - a;
}
} else {
a = saturate(a);
}
int n = 0;
for (;n < GRAD_MAX_FRACTIONS - 1; n++) {
if (a <= uniforms.fract[n + 1]) break;
}
a = (a - uniforms.fract[n]) / (uniforms.fract[n + 1] - uniforms.fract[n]);
float4 c = mix(uniforms.color[n], uniforms.color[n + 1], a);
if (uniforms.isLinear) {
c.rgb = fromLinear3(c.rgb);
}
return c;
}
vertex ColShaderInOut vert_col(VertexInput in [[stage_in]],
constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
ColShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.color = half4(uniforms.color.r, uniforms.color.g, uniforms.color.b, uniforms.color.a);
return out;
}
vertex AAShaderInOut vert_col_aa(AAVertexInput in [[stage_in]],
constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
AAShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.color = half4(uniforms.color.r, uniforms.color.g, uniforms.color.b, uniforms.color.a);
out.outerTexCoords = in.oTexCoords;
out.innerTexCoords = in.iTexCoords;
return out;
}
vertex StencilShaderInOut vert_stencil(VertexInput in [[stage_in]],
constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
StencilShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix * pos4;
out.color = 0xFF;
return out;
}
vertex GradShaderInOut vert_grad(VertexInput in [[stage_in]], constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
GradShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
return out;
}
vertex TxtShaderInOut vert_txt(TxtVertexInput in [[stage_in]], constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
TxtShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.texCoords = in.texCoords;
return out;
}
vertex LCDShaderInOut vert_txt_lcd(TxtVertexInput in [[stage_in]], constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
LCDShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.orig_pos = in.position;
out.texCoords = in.texCoords;
return out;
}
vertex TxtShaderInOut vert_txt_tp(TxtVertexInput in [[stage_in]], constant AnchorData& anchorData [[buffer(FrameUniformBuffer)]], constant TransformMatrix& transform [[buffer(MatrixBuffer)]])
{
TxtShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix * pos4;
// Compute texture coordinates here w.r.t. anchor rect of texture paint
out.tpCoords.x = (anchorData.xParams[0] * in.position.x) +
(anchorData.xParams[1] * in.position.y) +
(anchorData.xParams[2] * out.position.w);
out.tpCoords.y = (anchorData.yParams[0] * in.position.x) +
(anchorData.yParams[1] * in.position.y) +
(anchorData.yParams[2] * out.position.w);
out.texCoords = in.texCoords;
return out;
}
vertex GradShaderInOut vert_txt_grad(TxtVertexInput in [[stage_in]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
GradShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.texCoords = in.texCoords;
return out;
}
fragment half4 frag_col(ColShaderInOut in [[stage_in]]) {
return in.color;
}
fragment half4 frag_col_aa(AAShaderInOut in [[stage_in]]) {
float2 oleg1 = dfdx(in.outerTexCoords);
float2 oleg2 = dfdy(in.outerTexCoords);
// Calculate the bounds of the distorted pixel parallelogram.
float2 corner = in.outerTexCoords - (oleg1+oleg2)/2.0;
float2 omin = min(corner, corner+oleg1);
omin = min(omin, corner+oleg2);
omin = min(omin, corner+oleg1+oleg2);
float2 omax = max(corner, corner+oleg1);
omax = max(omax, corner+oleg2);
omax = max(omax, corner+oleg1+oleg2);
// Calculate the vectors for the "legs" of the pixel parallelogram
// for the inner parallelogram.
float2 ileg1 = dfdx(in.innerTexCoords);
float2 ileg2 = dfdy(in.innerTexCoords);
// Calculate the bounds of the distorted pixel parallelogram.
corner = in.innerTexCoords - (ileg1+ileg2)/2.0;
float2 imin = min(corner, corner+ileg1);
imin = min(imin, corner+ileg2);
imin = min(imin, corner+ileg1+ileg2);
float2 imax = max(corner, corner+ileg1);
imax = max(imax, corner+ileg2);
imax = max(imax, corner+ileg1+ileg2);
// Clamp the bounds of the parallelograms to the unit square to
// estimate the intersection of the pixel parallelogram with
// the unit square. The ratio of the 2 rectangle areas is a
// reasonable estimate of the proportion of coverage.
float2 o1 = clamp(omin, 0.0, 1.0);
float2 o2 = clamp(omax, 0.0, 1.0);
float oint = (o2.y-o1.y)*(o2.x-o1.x);
float oarea = (omax.y-omin.y)*(omax.x-omin.x);
float2 i1 = clamp(imin, 0.0, 1.0);
float2 i2 = clamp(imax, 0.0, 1.0);
float iint = (i2.y-i1.y)*(i2.x-i1.x);
float iarea = (imax.y-imin.y)*(imax.x-imin.x);
// Proportion of pixel in outer shape minus the proportion
// of pixel in the inner shape == the coverage of the pixel
// in the area between the two.
float coverage = oint/oarea - iint / iarea;
return (in.color * coverage);
}
fragment unsigned int frag_stencil(StencilShaderInOut in [[stage_in]]) {
return in.color;
}
// NOTE:
// 1. consider to make shaders without IF-conditions
// 2. we can pass interpolation mode via uniforms and select corresponding sampler in shader
// but it can cause performance problems (something like getTextureSampler(hint) will be invoked
// for every pixel)
fragment half4 frag_txt(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
constant TxtFrameUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
float srcA = uniforms.isSrcOpaque ? 1 : pixelColor.a;
if (uniforms.mode) {
float3 c = mix(pixelColor.rgb, uniforms.color.rgb, srcA);
return half4(c.r, c.g, c.b ,
(uniforms.isSrcOpaque) ?
uniforms.color.a : pixelColor.a*uniforms.color.a);
}
return half4(pixelColor.r,
pixelColor.g,
pixelColor.b, srcA)*uniforms.extraAlpha;
}
fragment half4 frag_text(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
constant TxtFrameUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
return half4(uniforms.color * pixelColor.a);
}
fragment half4 frag_txt_tp(TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
texture2d<float, access::sample> paintTexture [[texture(1)]],
constant TxtFrameUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 renderColor = renderTexture.sample(textureSampler, vert.texCoords);
float4 paintColor = paintTexture.sample(textureSampler, vert.tpCoords);
const float srcA = uniforms.isSrcOpaque ? 1 : paintColor.a;
return half4(paintColor.r*renderColor.a,
paintColor.g*renderColor.a,
paintColor.b*renderColor.a,
srcA*renderColor.a) * uniforms.extraAlpha;
}
fragment half4 frag_txt_grad(GradShaderInOut in [[stage_in]],
constant GradFrameUniforms& uniforms [[buffer(0)]],
texture2d<float, access::sample> renderTexture [[texture(0)]])
{
constexpr sampler textureSampler (address::repeat, mag_filter::nearest,
min_filter::nearest);
float4 renderColor = renderTexture.sample(textureSampler, in.texCoords);
float3 v = float3(in.position.x-0.5, in.position.y-0.5, 1);
float a = (dot(v,uniforms.params)-0.25)*2.0;
return half4(frag_single_grad(a, uniforms)*renderColor.a)*uniforms.extraAlpha;
}
fragment half4 frag_txt_lin_grad(GradShaderInOut in [[stage_in]],
constant LinGradFrameUniforms& uniforms [[buffer(0)]],
texture2d<float, access::sample> renderTexture [[texture(0)]])
{
constexpr sampler textureSampler (address::repeat, mag_filter::nearest,
min_filter::nearest);
float4 renderColor = renderTexture.sample(textureSampler, in.texCoords);
float3 v = float3(in.position.x, in.position.y, 1);
float a = dot(v,uniforms.params);
return half4(frag_multi_grad(a, uniforms)*renderColor.a)*uniforms.extraAlpha;
}
fragment half4 frag_txt_rad_grad(GradShaderInOut in [[stage_in]],
constant RadGradFrameUniforms& uniforms [[buffer(0)]],
texture2d<float, access::sample> renderTexture [[texture(0)]])
{
constexpr sampler textureSampler (address::repeat, mag_filter::nearest,
min_filter::nearest);
float4 renderColor = renderTexture.sample(textureSampler, in.texCoords);
float3 fragCoord = float3(in.position.x-0.5, in.position.y-0.5, 1);
float x = dot(fragCoord, uniforms.m0);
float y = dot(fragCoord, uniforms.m1);
float xfx = x - uniforms.precalc.x;
float a = (uniforms.precalc.x*xfx + sqrt(xfx*xfx + y*y*uniforms.precalc.y))*uniforms.precalc.z;
return half4(frag_multi_grad(a, uniforms)*renderColor.a)*uniforms.extraAlpha;
}
fragment half4 aa_frag_txt(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
texture2d<float, access::sample> stencilTexture [[texture(1)]],
constant TxtFrameUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
if (!is_null_texture(stencilTexture)) {
float4 stencil = stencilTexture.sample(textureSampler, vert.texCoords);
if (stencil.r == 0.0) {
discard_fragment();
}
}
return half4(pixelColor.r, pixelColor.g, pixelColor.b, pixelColor.a);
}
fragment half4 frag_txt_op_rescale(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> srcTex [[texture(0)]],
constant TxtFrameOpRescaleUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 srcColor = srcTex.sample(textureSampler, vert.texCoords);
const float srcA = uniforms.isSrcOpaque ? 1 : srcColor.a;
// TODO: check uniforms.isNonPremult and pre-multiply if necessary
return half4(srcColor.r*uniforms.normScaleFactors.r + uniforms.normOffsets.r,
srcColor.g*uniforms.normScaleFactors.g + uniforms.normOffsets.g,
srcColor.b*uniforms.normScaleFactors.b + uniforms.normOffsets.b, srcA)*uniforms.extraAlpha;
// NOTE: GL-shader multiplies result with glColor (in order to apply extra alpha), probably it's better to do the
// same here.
//
// GL-shader impl:
//" vec4 srcColor = texture%s(baseImage, gl_TexCoord[0].st);"
//" %s" // (placeholder for un-premult code: srcColor.rgb /= srcColor.a;)
//" vec4 result = (srcColor * scaleFactors) + offsets;" // rescale source value
//" %s" // (placeholder for re-premult code: result.rgb *= result.a;)
//" gl_FragColor = result * gl_Color;" // modulate with gl_Color in order to apply extra alpha
}
fragment half4 frag_txt_op_convolve(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> srcTex [[texture(0)]],
constant TxtFrameOpConvolveUniforms& uniforms [[buffer(1)]],
const device float * kernelVals [[buffer(2)]],
sampler textureSampler [[sampler(0)]]
) {
float4 sum = float4(0, 0, 0, 0);
if (vert.texCoords[0] < uniforms.imgEdge[0]
|| vert.texCoords[1] < uniforms.imgEdge[1]
|| vert.texCoords[0] > uniforms.imgEdge[2]
|| vert.texCoords[1] > uniforms.imgEdge[3]
) {
if (!uniforms.isEdgeZeroFill) {
sum = srcTex.sample(textureSampler, vert.texCoords);
}
}
for (int i = 0; i < uniforms.kernelSize; i++) {
float3 kern = float3(kernelVals[i*3], kernelVals[i*3 + 1], kernelVals[i*3 + 2]);
float2 pos = float2(vert.texCoords.x + kern.x, vert.texCoords.y + kern.y);
float4 pixCol = srcTex.sample(textureSampler, pos);
sum.r += kern.z * pixCol.r;
sum.g += kern.z * pixCol.g;
sum.b += kern.z * pixCol.b;
sum.a += kern.z * pixCol.a;
}
const float srcA = uniforms.isSrcOpaque ? 1 : sum.a;
return half4(sum.r, sum.g, sum.b, srcA)*uniforms.extraAlpha;
// NOTE: GL-shader multiplies result with glColor (in order to apply extra alpha), probably it's better to do the
// same here.
//
// GL-shader impl:
//" if (any(lessThan(gl_TexCoord[0].st, imgEdge.xy)) ||"
//" any(greaterThan(gl_TexCoord[0].st, imgEdge.zw)))"
//" {"
//" %s" // (placeholder for edge condition code)
//" } else {"
//" sum = vec4(0.0);"
//" for (i = 0; i < MAX_KERNEL_SIZE; i++) {"
//" sum +="
//" kernelVals[i].z *"
//" texture%s(baseImage,"
//" gl_TexCoord[0].st + kernelVals[i].xy);"
//" }"
//" }"
//""
//" gl_FragColor = sum * gl_Color;" // modulate with gl_Color in order to apply extra alpha
}
fragment half4 frag_txt_op_lookup(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> srcTex [[texture(0)]],
texture2d<float, access::sample> lookupTex [[texture(1)]],
constant TxtFrameOpLookupUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 srcColor = srcTex.sample(textureSampler, vert.texCoords);
float4 srcIndex = srcColor - uniforms.offset;
const float2 posR = float2(srcIndex.r, 0.125);
const float2 posG = float2(srcIndex.g, 0.375);
const float2 posB = float2(srcIndex.b, 0.625);
float4 lookupR = lookupTex.sample(textureSampler, posR);
float4 lookupG = lookupTex.sample(textureSampler, posG);
float4 lookupB = lookupTex.sample(textureSampler, posB);
const float srcA = uniforms.isSrcOpaque ? 1 : srcColor.a;
const float a = uniforms.isUseSrcAlpha ? srcA : lookupTex.sample(textureSampler, float2(srcIndex.a, 0.875)).a;
// TODO: check uniforms.isNonPremult and pre-multiply if necessary
return half4(lookupR.a, lookupG.a, lookupB.a, a)*uniforms.extraAlpha;
// NOTE: GL-shader multiplies result with glColor (in order to apply extra alpha), probably it's better to do the
// same here.
//
// GL-shader impl:
//" vec4 srcColor = texture%s(baseImage, gl_TexCoord[0].st);"
//" %s" // (placeholder for un-premult code)
//" vec4 srcIndex = srcColor - offset;" // subtract offset from original index
//
// // use source value as input to lookup table (note that
// // "v" texcoords are hardcoded to hit texel centers of
// // each row/band in texture)
//" vec4 result;"
//" result.r = texture2D(lookupTable, vec2(srcIndex.r, 0.125)).r;"
//" result.g = texture2D(lookupTable, vec2(srcIndex.g, 0.375)).r;"
//" result.b = texture2D(lookupTable, vec2(srcIndex.b, 0.625)).r;"
//" %s" // (placeholder for alpha store code)
//" %s" // (placeholder for re-premult code)
//" gl_FragColor = result * gl_Color;" // modulate with gl_Color in order to apply extra alpha
}
fragment half4 frag_grad(GradShaderInOut in [[stage_in]],
constant GradFrameUniforms& uniforms [[buffer(0)]]) {
float3 v = float3(in.position.x-0.5, in.position.y-0.5, 1);
float a = (dot(v,uniforms.params)-0.25)*2.0;
return half4(frag_single_grad(a, uniforms)) * uniforms.extraAlpha;
}
// LinGradFrameUniforms
fragment half4 frag_lin_grad(GradShaderInOut in [[stage_in]],
constant LinGradFrameUniforms& uniforms [[buffer(0)]]) {
float3 v = float3(in.position.x-0.5, in.position.y-0.5, 1);
float a = dot(v, uniforms.params);
return half4(frag_multi_grad(a, uniforms))*uniforms.extraAlpha;
}
fragment half4 frag_rad_grad(GradShaderInOut in [[stage_in]],
constant RadGradFrameUniforms& uniforms [[buffer(0)]]) {
float3 fragCoord = float3(in.position.x-0.5, in.position.y-0.5, 1);
float x = dot(fragCoord, uniforms.m0);
float y = dot(fragCoord, uniforms.m1);
float xfx = x - uniforms.precalc.x;
float a = (uniforms.precalc.x*xfx + sqrt(xfx*xfx + y*y*uniforms.precalc.y))*uniforms.precalc.z;
return half4(frag_multi_grad(a, uniforms))*uniforms.extraAlpha;
}
vertex TxtShaderInOut vert_tp(VertexInput in [[stage_in]],
constant AnchorData& anchorData [[buffer(FrameUniformBuffer)]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]])
{
TxtShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix * pos4;
// Compute texture coordinates here w.r.t. anchor rect of texture paint
out.texCoords.x = (anchorData.xParams[0] * in.position.x) +
(anchorData.xParams[1] * in.position.y) +
(anchorData.xParams[2] * out.position.w);
out.texCoords.y = (anchorData.yParams[0] * in.position.x) +
(anchorData.yParams[1] * in.position.y) +
(anchorData.yParams[2] * out.position.w);
return out;
}
fragment half4 frag_tp(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
constant TxtFrameUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]])
{
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
const float srcA = uniforms.isSrcOpaque ? 1 : pixelColor.a;
return half4(pixelColor.r, pixelColor.g, pixelColor.b, srcA) * uniforms.extraAlpha;
}
/* The variables involved in the equation can be expressed as follows:
*
* Cs = Color component of the source (foreground color) [0.0, 1.0]
* Cd = Color component of the destination (background color) [0.0, 1.0]
* Cr = Color component to be written to the destination [0.0, 1.0]
* Ag = Glyph alpha (aka intensity or coverage) [0.0, 1.0]
* Ga = Gamma adjustment in the range [1.0, 2.5]
* (^ means raised to the power)
*
* And here is the theoretical equation approximated by this shader:
*
* Cr = (Ag*(Cs^Ga) + (1-Ag)*(Cd^Ga)) ^ (1/Ga)
*/
fragment float4 lcd_color(
LCDShaderInOut vert [[stage_in]],
texture2d<float, access::sample> glyphTexture [[texture(0)]],
texture2d<float, access::sample> dstTexture [[texture(1)]],
constant LCDFrameUniforms& uniforms [[buffer(1)]])
{
float3 src_adj = uniforms.src_adj;
float3 gamma = uniforms.gamma;
float3 invgamma = uniforms.invgamma;
constexpr sampler glyphTextureSampler (address::repeat,
mag_filter::nearest,
min_filter::nearest);
// load the RGB value from the glyph image at the current texcoord
float3 glyph_clr = float3(glyphTexture.sample(glyphTextureSampler, vert.texCoords));
if (glyph_clr.r == 0.0f && glyph_clr.g == 0.0f && glyph_clr.b == 0.0f) {
// zero coverage, so skip this fragment
discard_fragment();
}
// load the RGB value from the corresponding destination pixel
uint2 texCoord = {(unsigned int)(vert.orig_pos.x), (unsigned int)(vert.orig_pos.y)};
float4 dst_clr = dstTexture.read(texCoord);
// gamma adjust the dest color
float3 dst_adj = pow(dst_clr.rgb, gamma);
// linearly interpolate the three color values
float3 result = mix(dst_adj, src_adj, glyph_clr);
// gamma re-adjust the resulting color (alpha is always set to 1.0)
return float4(pow(result.rgb, invgamma), 1.0);
}
// Compute shader to transfer clipping data to the texture used for manual clipping in
// aa_frag_txt shader
kernel void stencil2tex(const device uchar *imageBuffer [[buffer(0)]],
device uchar4 *outputBuffer [[buffer(1)]],
uint gid [[thread_position_in_grid]])
{
uchar p = imageBuffer[gid];
outputBuffer[gid] = uchar4(p, p, p, p);
}
// work item deals with 4 byte pixel
// assuming that data is aligned
kernel void rgb_to_rgba(const device uchar *imageBuffer [[buffer(0)]],
device uchar *outputBuffer [[buffer(1)]],
uint gid [[thread_position_in_grid]])
{
outputBuffer[4 * gid] = imageBuffer[4 * gid]; // r
outputBuffer[4 * gid + 1] = imageBuffer[4 * gid + 1]; // g
outputBuffer[4 * gid + 2] = imageBuffer[4 * gid + 2]; // b
outputBuffer[4 * gid + 3] = 255; // a
}
kernel void bgr_to_rgba(const device uchar *imageBuffer [[buffer(0)]],
device uchar *outputBuffer [[buffer(1)]],
uint gid [[thread_position_in_grid]])
{
outputBuffer[4 * gid] = imageBuffer[4 * gid + 2]; // r
outputBuffer[4 * gid + 1] = imageBuffer[4 * gid + 1]; // g
outputBuffer[4 * gid + 2] = imageBuffer[4 * gid]; // b
outputBuffer[4 * gid + 3] = 255; // a
}
kernel void xrgb_to_rgba(const device uchar *imageBuffer [[buffer(0)]],
device uchar *outputBuffer [[buffer(1)]],
uint gid [[thread_position_in_grid]])
{
outputBuffer[4 * gid] = imageBuffer[4 * gid + 1]; // r
outputBuffer[4 * gid + 1] = imageBuffer[4 * gid + 2]; // g
outputBuffer[4 * gid + 2] = imageBuffer[4 * gid + 3]; // b
outputBuffer[4 * gid + 3] = imageBuffer[4 * gid]; // a
}
kernel void xbgr_to_rgba(const device uchar *imageBuffer [[buffer(0)]],
device uchar *outputBuffer [[buffer(1)]],
uint gid [[thread_position_in_grid]])
{
outputBuffer[4 * gid] = imageBuffer[4 * gid + 3]; // r
outputBuffer[4 * gid + 1] = imageBuffer[4 * gid + 2]; // g
outputBuffer[4 * gid + 2] = imageBuffer[4 * gid + 1]; // b
outputBuffer[4 * gid + 3] = imageBuffer[4 * gid]; // a
}
// ----------------------------------------------------------------------------
// Shaders for rendering in XOR Mode
// ----------------------------------------------------------------------------
vertex ColShaderInOut_XOR vert_col_xorMode(VertexInput in [[stage_in]],
constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]])
{
ColShaderInOut_XOR out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.orig_pos = in.position;
out.color = half4(uniforms.color.r, uniforms.color.g, uniforms.color.b, uniforms.color.a);
return out;
}
fragment half4 frag_col_xorMode(ColShaderInOut_XOR in [[stage_in]],
texture2d<float, access::read> renderTexture [[texture(0)]])
{
uint2 texCoord = {(unsigned int)(in.orig_pos.x), (unsigned int)(in.orig_pos.y)};
float4 pixelColor = renderTexture.read(texCoord);
half4 color = in.color;
half4 c;
c.r = float( (unsigned char)(pixelColor.r * 255.0) ^ (unsigned char)(color.r * 255.0)) / 255.0f;
c.g = float( (unsigned char)(pixelColor.g * 255.0) ^ (unsigned char)(color.g * 255.0)) / 255.0f;
c.b = float( (unsigned char)(pixelColor.b * 255.0) ^ (unsigned char)(color.b * 255.0)) / 255.0f;
c.a = 1.0;
return c;
}
vertex TxtShaderInOut_XOR vert_txt_xorMode(
TxtVertexInput in [[stage_in]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]])
{
TxtShaderInOut_XOR out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.orig_pos = in.position;
out.texCoords = in.texCoords;
return out;
}
fragment half4 frag_txt_xorMode(
TxtShaderInOut_XOR vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
texture2d<float, access::read> backgroundTexture [[texture(1)]],
constant TxtFrameUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]])
{
uint2 texCoord = {(unsigned int)(vert.orig_pos.x), (unsigned int)(vert.orig_pos.y)};
float4 bgColor = backgroundTexture.read(texCoord);
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
float srcA = uniforms.isSrcOpaque ? 1 : pixelColor.a;
float4 c;
if (uniforms.mode) {
c = mix(pixelColor, uniforms.color, srcA);
} else {
c = float4(pixelColor.r,
pixelColor.g,
pixelColor.b, srcA)*uniforms.extraAlpha;
}
half4 ret;
ret.r = half( (unsigned char)(c.r * 255.0) ^ (unsigned char)(bgColor.r * 255.0)) / 255.0f;
ret.g = half( (unsigned char)(c.g * 255.0) ^ (unsigned char)(bgColor.g * 255.0)) / 255.0f;
ret.b = half( (unsigned char)(c.b * 255.0) ^ (unsigned char)(bgColor.b * 255.0)) / 255.0f;
ret.a = c.a + (1.0 - c.a) * bgColor.a;
return ret;
}
/*
// --------------------------------------------------------------------------------------
Currently, gradient paint and texture paint XOR mode rendering has been implemented
through tile based rendering (similar to OGL) that uses MTLBlitLoops_SurfaceToSwBlit method for
getting framebuffer tiles and render using a different render pipe (not MTLRenderer)
In metal, we can avoid tile based rendering and use below shaders.
NOTE: These two shaders are incomplete and need some tweak.
// --------------------------------------------------------------------------------------
fragment half4 frag_grad_xorMode(GradShaderInOut_XOR in [[stage_in]],
texture2d<float, access::read> renderTexture [[texture(0)]],
constant GradFrameUniforms& uniforms [[buffer(0)]]) {
uint2 texCoord = {(unsigned int)(in.orig_pos.x), (unsigned int)(in.orig_pos.y)};
float4 pixelColor = renderTexture.read(texCoord);
float3 v = float3(in.position.x, in.position.y, 1);
float a = (dot(v,uniforms.params)-0.25)*2.0;
float4 c = mix(uniforms.color1, uniforms.color2, a);
half4 ret;
ret.r = float( (unsigned char)(pixelColor.r * 255.0) ^ (unsigned char)(c.r * 255.0)) / 255.0f;
ret.g = float( (unsigned char)(pixelColor.g * 255.0) ^ (unsigned char)(c.g * 255.0)) / 255.0f;
ret.b = float( (unsigned char)(pixelColor.b * 255.0) ^ (unsigned char)(c.b * 255.0)) / 255.0f;
return half4(ret);
}
fragment half4 frag_tp_xorMode(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
texture2d<float, access::read> backgroundTexture [[texture(1)]],
constant int& xorColor[[buffer(0)]])
{
uint2 texCoord = {(unsigned int)(vert.orig_pos.x), (unsigned int)(vert.orig_pos.y)};
float4 bgColor = backgroundTexture.read(texCoord);
constexpr sampler textureSampler (address::repeat,
mag_filter::nearest,
min_filter::nearest);
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
pixelColor.r = float( (unsigned char)(pixelColor.r * 255.0) ^ ((xorColor >> 16) & 0xFF) ) / 255.0f;
pixelColor.g = float( (unsigned char)(pixelColor.g * 255.0) ^ ((xorColor >> 8) & 0xFF)) / 255.0f;
pixelColor.b = float( (unsigned char)(pixelColor.b * 255.0) ^ (xorColor & 0xFF)) / 255.0f;
pixelColor.a = 1.0;
half4 ret;
ret.r = half( (unsigned char)(pixelColor.r * 255.0) ^ (unsigned char)(bgColor.r * 255.0)) / 255.0f;
ret.g = half( (unsigned char)(pixelColor.g * 255.0) ^ (unsigned char)(bgColor.g * 255.0)) / 255.0f;
ret.b = half( (unsigned char)(pixelColor.b * 255.0) ^ (unsigned char)(bgColor.b * 255.0)) / 255.0f;
ret.a = 1.0;
return ret;
// This implementation defaults alpha to 1.0 as if source is opaque
//TODO : implement alpha component value if source is transparent
}
*/

View file

@ -485,6 +485,9 @@ extern struct _CompositeTypes {
#define PtrPixelsRow(p, y, scanStride) PtrAddBytes(p, \
((intptr_t) (y)) * (scanStride))
#define PtrPixelsBand(p, y, length, elemSize) PtrAddBytes(p, \
((intptr_t) (y)) * (length) * (elemSize))
/*
* The function to call with an array of NativePrimitive structures
* to register them with the Java GraphicsPrimitiveMgr.

View file

@ -0,0 +1,79 @@
#
# Copyright (c) 2019, 2021, 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.
#
# 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.
#
SOURCEPATH=src
CLASSES=build
DIST=dist
RESOURCES=resources
RENDERPERF_CLASSES = $(CLASSES)/renderperf/RenderPerfTest.class
RENDERPERFLCD_CLASSES = $(CLASSES)/renderperf/RenderPerfLCDTest.class
RENDERPERF_SOURCES = $(SOURCEPATH)/renderperf/RenderPerfTest.java
RENDERPERFLCD_SOURCES = $(SOURCEPATH)/renderperf/RenderPerfLCDTest.java
RENDERPERF_RESOURCES = $(CLASSES)/renderperf/images/duke.png
all: mkdirs $(DIST)/RenderPerfTest.jar $(DIST)/RenderPerfLCDTest.jar
run: mkdirs $(DIST)/RenderPerfTest.jar
java -jar $(DIST)/RenderPerfTest.jar
$(DIST)/RenderPerfTest.jar: \
$(RENDERPERF_CLASSES) $(RENDERPERF_RESOURCES) \
$(CLASSES)/renderperf.manifest
jar cvmf $(CLASSES)/renderperf.manifest $(DIST)/RenderPerfTest.jar -C $(CLASSES) .
$(DIST)/RenderPerfLCDTest.jar: \
$(RENDERPERFLCD_CLASSES) $(RENDERPERFLCD_RESOURCES) \
$(CLASSES)/renderperflcd.manifest
jar cvmf $(CLASSES)/renderperflcd.manifest $(DIST)/RenderPerfLCDTest.jar -C $(CLASSES) .
$(CLASSES)/renderperf/images/%: $(RESOURCES)/renderperf/images/%
cp -r $< $@
$(CLASSES)/renderperf.manifest:
echo "Main-Class: renderperf.RenderPerfTest" > $@
$(CLASSES)/renderperflcd.manifest:
echo "Main-Class: renderperf.RenderPerfLCDTest" > $@
$(DIST):
mkdir $(DIST)
$(CLASSES):
mkdir $(CLASSES)
mkdir -p $(CLASSES)/renderperf/images
mkdirs: $(DIST) $(CLASSES)
$(RENDERPERF_CLASSES): $(RENDERPERF_SOURCES)
javac -g:none -d $(CLASSES) -sourcepath $(SOURCEPATH) $<
$(RENDERPERFLCD_CLASSES): $(RENDERPERFLCD_SOURCES)
javac -g:none -d $(CLASSES) -sourcepath $(SOURCEPATH) $<
clean:
rm -rf $(CLASSES)
rm -rf $(DIST)

View file

@ -0,0 +1,34 @@
-----------------------------------------------------------------------
Introduction
-----------------------------------------------------------------------
RenderPerfTest is a set of on-screen rendering microbenchmarks to
analyze the performance of Java2D graphical primitives rendering
-----------------------------------------------------------------------
How To Compile
-----------------------------------------------------------------------
#> cd RenderPerfTest
The benchmark can be compiled by using either ant:
#> ant
or gnumake (assuming there's 'javac' in the path):
#> gnumake
The jar files will be generated into RenderPerfTest/dist directory.
-----------------------------------------------------------------------
How To Run RenderPerfTest
-----------------------------------------------------------------------
Run all tests
#> ant run
or
#> java -jar dist/RenderPerfTest.jar
Run particular test cases
#> java -jar dist/RenderPerfTest.jar WhiteTextGray ...

View file

@ -0,0 +1,86 @@
<!--
Copyright (c) 2019, 2021, 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.
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.
-->
<project name="RenderPerfTest" default="dist" basedir=".">
<description>
simple example build file
</description>
<!-- set global properties for this build -->
<property name="src" location="src"/>
<property name="build" location="build"/>
<property name="dist" location="dist"/>
<property name="resources" location="resources"/>
<target name="init">
<!-- Create the time stamp -->
<tstamp/>
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init"
description="compile the source " >
<!-- Compile the java code from ${src} into ${build} -->
<javac includeantruntime="false" debug="off" srcdir="${src}" destdir="${build}"/>
</target>
<target name="run" depends="dist"
description="run RenderPerfTest" >
<java jar="${dist}/RenderPerfTest.jar"
fork="true"
>
</java>
</target>
<target name="resources" depends="init"
description="copy resources into build dir" >
<!-- Copy the resource files from ${resources} into ${build}/ -->
<mkdir dir="${dist}"/>
<mkdir dir="${dist}/renderperf"/>
<mkdir dir="${build}/renderperf/images"/>
<copy todir="${build}/renderperf/images">
<fileset dir="resources/renderperf/images" />
</copy>
</target>
<target name="dist" depends="compile, resources"
description="generate the distribution" >
<!-- Create the distribution directory -->
<mkdir dir="${dist}"/>
<!-- Put everything in ${build} into the RenderPerfTest.jar file -->
<jar jarfile="${dist}/RenderPerfTest.jar" basedir="${build}">
<manifest>
<attribute name="Built-By" value="${user.name}"/>
<attribute name="Main-Class" value="renderperf.RenderPerfTest"/>
</manifest>
</jar>
</target>
<target name="clean"
description="clean up" >
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -0,0 +1,373 @@
/*
* Copyright (c) 2019, 2021, 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.
*
* 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 renderperf;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Robot;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class RenderPerfLCDTest {
private static HashSet<String> ignoredTests = new HashSet<>();
private final static int N = 1000;
private final static float WIDTH = 800;
private final static float HEIGHT = 800;
private final static float R = 25;
private final static int BW = 50;
private final static int BH = 50;
private final static int COUNT = 300;
private final static int DELAY = 10;
private final static int RESOLUTION = 5;
private final static int COLOR_TOLERANCE = 10;
private final static int MAX_MEASURE_TIME = 5000;
interface Configurable {
void configure(Graphics2D g2d);
}
interface Renderable {
void setup(Graphics2D g2d);
void render(Graphics2D g2d);
void update();
}
static class Particles {
private float[] bx;
private float[] by;
private float[] vx;
private float[] vy;
private float r;
private int n;
private float x0;
private float y0;
private float width;
private float height;
Particles(int n, float r, float x0, float y0, float width, float height) {
bx = new float[n];
by = new float[n];
vx = new float[n];
vy = new float[n];
this.n = n;
this.r = r;
this.x0 = x0;
this.y0 = y0;
this.width = width;
this.height = height;
for (int i = 0; i < n; i++) {
bx[i] = (float) (x0 + r + 0.1 + Math.random() * (width - 2 * r - 0.2 - x0));
by[i] = (float) (y0 + r + 0.1 + Math.random() * (height - 2 * r - 0.2 - y0));
vx[i] = 0.1f * (float) (Math.random() * 2 * r - r);
vy[i] = 0.1f * (float) (Math.random() * 2 * r - r);
}
}
void render(Graphics2D g2d, ParticleRenderer renderer) {
for (int i = 0; i < n; i++) {
renderer.render(g2d, i, bx, by, vx, vy);
}
}
void update() {
for (int i = 0; i < n; i++) {
bx[i] += vx[i];
if (bx[i] + r > width || bx[i] - r < x0) vx[i] = -vx[i];
by[i] += vy[i];
if (by[i] + r > height || by[i] - r < y0) vy[i] = -vy[i];
}
}
}
ParticleRenderable createPR(ParticleRenderer renderer) {
return new ParticleRenderable(renderer);
}
static class ParticleRenderable implements Renderable {
ParticleRenderer renderer;
Configurable configure;
ParticleRenderable(ParticleRenderer renderer, Configurable configure) {
this.renderer = renderer;
this.configure = configure;
}
ParticleRenderable(ParticleRenderer renderer) {
this(renderer, null);
}
@Override
public void setup(Graphics2D g2d) {
if (configure != null) configure.configure(g2d);
}
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, renderer);
}
@Override
public void update() {
balls.update();
}
public ParticleRenderable configure(Configurable configure) {
this.configure = configure;
return this;
}
}
interface ParticleRenderer {
void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy);
}
static class WhiteTextParticleRenderer implements ParticleRenderer {
float r;
WhiteTextParticleRenderer(float r) {
this.r = r;
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(Color.WHITE);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
setPaint(g2d, id);
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)(y[id] - r));
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)y[id]);
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)(y[id] + r));
}
}
static class TextParticleRenderer extends WhiteTextParticleRenderer {
Color[] colors;
float r;
TextParticleRenderer(int n, float r) {
super(r);
colors = new Color[n];
this.r = r;
for (int i = 0; i < n; i++) {
colors[i] = new Color((float) Math.random(),
(float) Math.random(), (float) Math.random());
}
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(colors[id % colors.length]);
}
}
static class PerfMeter {
private String name;
private int frame = 0;
private JPanel panel;
private long time;
private double execTime = 0;
private Color expColor = Color.RED;
AtomicBoolean waiting = new AtomicBoolean(false);
private double fps;
PerfMeter(String name) {
this.name = name;
}
PerfMeter exec(final Renderable renderable) throws Exception {
final CountDownLatch latch = new CountDownLatch(COUNT);
final CountDownLatch latchFrame = new CountDownLatch(1);
final long endTime = System.currentTimeMillis() + MAX_MEASURE_TIME;
final Frame f = new Frame();
f.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
latchFrame.countDown();
}
});
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
panel = new JPanel()
{
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
time = System.nanoTime();
Graphics2D g2d = (Graphics2D) g.create();
renderable.setup(g2d);
renderable.render(g2d);
g2d.setColor(expColor);
g.fillRect(0, 0, BW, BH);
}
};
panel.setPreferredSize(new Dimension((int)(WIDTH + BW), (int)(HEIGHT + BH)));
panel.setBackground(Color.BLACK);
f.add(panel);
//f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
});
Robot robot = new Robot();
Timer timer = new Timer(DELAY, e -> {
if (waiting.compareAndSet(false, true)) {
Color c = robot.getPixelColor(
panel.getTopLevelAncestor().getX() + panel.getTopLevelAncestor().getInsets().left + BW / 2,
panel.getTopLevelAncestor().getY() + panel.getTopLevelAncestor().getInsets().top + BW / 2);
if (isAlmostEqual(c, Color.BLUE)) {
expColor = Color.RED;
} else {
expColor = Color.BLUE;
}
renderable.update();
panel.getParent().repaint();
} else {
while (!isAlmostEqual(
robot.getPixelColor(
panel.getTopLevelAncestor().getX() + panel.getTopLevelAncestor().getInsets().left + BW/2,
panel.getTopLevelAncestor().getY() + panel.getTopLevelAncestor().getInsets().top + BH/2),
expColor))
{
try {
Thread.sleep(RESOLUTION);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
time = System.nanoTime() - time;
execTime += time;
frame++;
waiting.set(false);
}
if (System.currentTimeMillis() < endTime) {
latch.countDown();
} else {
while(latch.getCount() > 0) latch.countDown();
}
});
timer.start();
latch.await();
SwingUtilities.invokeAndWait(() -> {
timer.stop();
f.setVisible(false);
f.dispose();
});
latchFrame.await();
if (execTime != 0 && frame != 0) {
fps = 1e9 / (execTime / frame);
} else {
fps = 0;
}
return this;
}
private void report() {
System.err.println(name + " : " + String.format("%.2f FPS", fps));
}
private boolean isAlmostEqual(Color c1, Color c2) {
return Math.abs(c1.getRed() - c2.getRed()) < COLOR_TOLERANCE ||
Math.abs(c1.getGreen() - c2.getGreen()) < COLOR_TOLERANCE ||
Math.abs(c1.getBlue() - c2.getBlue()) < COLOR_TOLERANCE;
}
}
private static final Particles balls = new Particles(N, R, BW, BH, WIDTH, HEIGHT);
private static final ParticleRenderer textRenderer = new TextParticleRenderer(N, R);
private static final Configurable TextLCD = (Graphics2D g2d) ->
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
public void testTextBubblesLCD() throws Exception {
(new PerfMeter("TextLCD")).exec(createPR(textRenderer).configure(TextLCD)).report();
}
public static void main(String[] args)
throws InvocationTargetException, IllegalAccessException, NoSuchMethodException
{
RenderPerfLCDTest test = new RenderPerfLCDTest();
if (args.length > 0) {
for (String testCase : args) {
Method m = RenderPerfLCDTest.class.getDeclaredMethod(testCase);
m.invoke(test);
}
} else {
Method[] methods = RenderPerfLCDTest.class.getDeclaredMethods();
for (Method m : methods) {
if (m.getName().startsWith("test") && !ignoredTests.contains(m.getName())) {
m.invoke(test);
}
}
}
}
}

View file

@ -0,0 +1,986 @@
/*
* Copyright (c) 2019, 2021, 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.
*
* 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 renderperf;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.LinearGradientPaint;
import java.awt.RadialGradientPaint;
import java.awt.RenderingHints;
import java.awt.Robot;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferShort;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.WindowConstants;
public class RenderPerfTest {
private static HashSet<String> ignoredTests = new HashSet<>();
static {
ignoredTests.add("testWiredBoxAA");
}
private final static int N = 1000;
private final static float WIDTH = 800;
private final static float HEIGHT = 800;
private final static float R = 25;
private final static int BW = 50;
private final static int BH = 50;
private final static int COUNT = 300;
private final static int DELAY = 10;
private final static int RESOLUTION = 5;
private final static int COLOR_TOLERANCE = 10;
private final static int MAX_MEASURE_TIME = 5000;
interface Configurable {
void configure(Graphics2D g2d);
}
interface Renderable {
void setup(Graphics2D g2d);
void render(Graphics2D g2d);
void update();
}
static class Particles {
private float[] bx;
private float[] by;
private float[] vx;
private float[] vy;
private float r;
private int n;
private float x0;
private float y0;
private float width;
private float height;
Particles(int n, float r, float x0, float y0, float width, float height) {
bx = new float[n];
by = new float[n];
vx = new float[n];
vy = new float[n];
this.n = n;
this.r = r;
this.x0 = x0;
this.y0 = y0;
this.width = width;
this.height = height;
for (int i = 0; i < n; i++) {
bx[i] = (float) (x0 + r + 0.1 + Math.random() * (width - 2 * r - 0.2 - x0));
by[i] = (float) (y0 + r + 0.1 + Math.random() * (height - 2 * r - 0.2 - y0));
vx[i] = 0.1f * (float) (Math.random() * 2 * r - r);
vy[i] = 0.1f * (float) (Math.random() * 2 * r - r);
}
}
void render(Graphics2D g2d, ParticleRenderer renderer) {
for (int i = 0; i < n; i++) {
renderer.render(g2d, i, bx, by, vx, vy);
}
}
void update() {
for (int i = 0; i < n; i++) {
bx[i] += vx[i];
if (bx[i] + r > width || bx[i] - r < x0) vx[i] = -vx[i];
by[i] += vy[i];
if (by[i] + r > height || by[i] - r < y0) vy[i] = -vy[i];
}
}
}
ParticleRenderable createPR(ParticleRenderer renderer) {
return new ParticleRenderable(renderer);
}
static class ParticleRenderable implements Renderable {
ParticleRenderer renderer;
Configurable configure;
ParticleRenderable(ParticleRenderer renderer, Configurable configure) {
this.renderer = renderer;
this.configure = configure;
}
ParticleRenderable(ParticleRenderer renderer) {
this(renderer, null);
}
@Override
public void setup(Graphics2D g2d) {
if (configure != null) configure.configure(g2d);
}
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, renderer);
}
@Override
public void update() {
balls.update();
}
public ParticleRenderable configure(Configurable configure) {
this.configure = configure;
return this;
}
}
interface ParticleRenderer {
void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy);
}
static class FlatParticleRenderer implements ParticleRenderer {
Color[] colors;
float r;
FlatParticleRenderer(int n, float r) {
colors = new Color[n];
this.r = r;
for (int i = 0; i < n; i++) {
colors[i] = new Color((float) Math.random(),
(float) Math.random(), (float) Math.random());
}
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.fillOval((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r));
}
}
static class ClipFlatParticleRenderer extends FlatParticleRenderer {
ClipFlatParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
if ((id % 10) == 0) {
g2d.setColor(colors[id % colors.length]);
g2d.setClip(new Ellipse2D.Double((int) (x[id] - r), (int) (y[id] - r), (int) (2 * r), (int) (2 * r)));
g2d.fillRect((int) (x[id] - 2 * r), (int) (y[id] - 2 * r), (int) (4 * r), (int) (4 * r));
}
}
}
static class WhiteTextParticleRenderer implements ParticleRenderer {
float r;
WhiteTextParticleRenderer(float r) {
this.r = r;
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(Color.WHITE);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
setPaint(g2d, id);
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)(y[id] - r));
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)y[id]);
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)(y[id] + r));
}
}
static class TextParticleRenderer extends WhiteTextParticleRenderer {
Color[] colors;
float r;
TextParticleRenderer(int n, float r) {
super(r);
colors = new Color[n];
this.r = r;
for (int i = 0; i < n; i++) {
colors[i] = new Color((float) Math.random(),
(float) Math.random(), (float) Math.random());
}
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(colors[id % colors.length]);
}
}
static class LargeTextParticleRenderer extends TextParticleRenderer {
LargeTextParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
setPaint(g2d, id);
Font font = new Font("LucidaGrande", Font.PLAIN, 32);
g2d.setFont(font);
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)(y[id] - r));
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)y[id]);
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)(y[id] + r));
}
}
static class FlatOvalRotParticleRenderer extends FlatParticleRenderer {
FlatOvalRotParticleRenderer(int n, float r) {
super(n, r);
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(colors[id % colors.length]);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
setPaint(g2d, id);
if (Math.abs(vx[id] + vy[id]) > 0.001) {
AffineTransform t = (AffineTransform) g2d.getTransform().clone();
double l = vx[id] / Math.sqrt(vx[id] * vx[id] + vy[id] * vy[id]);
if (vy[id] < 0) {
l = -l;
}
g2d.translate(x[id], y[id]);
g2d.rotate(Math.acos(l));
g2d.fillOval(-(int)r, (int)(-0.5*r), (int) (2 * r), (int)r);
g2d.setTransform(t);
} else {
g2d.fillOval((int)(x[id] - r), (int)(y[id] - 0.5*r),
(int) (2 * r), (int) r);
}
}
}
static class LinGradOvalRotParticleRenderer extends FlatOvalRotParticleRenderer {
LinGradOvalRotParticleRenderer(int n, float r) {
super(n, r);
}
@Override
void setPaint(Graphics2D g2d, int id) {
Point2D start = new Point2D.Double(- r, - 0.5*r);
Point2D end = new Point2D.Double( 2 * r, r);
float[] dist = {0.0f, 1.0f};
Color[] cls = {colors[id %colors.length], colors[(colors.length - id) %colors.length]};
LinearGradientPaint p =
new LinearGradientPaint(start, end, dist, cls);
g2d.setPaint(p);
}
}
static class LinGrad3OvalRotParticleRenderer extends FlatOvalRotParticleRenderer {
LinGrad3OvalRotParticleRenderer(int n, float r) {
super(n, r);
}
@Override
void setPaint(Graphics2D g2d, int id) {
Point2D start = new Point2D.Double(- r, - 0.5*r);
Point2D end = new Point2D.Double( 2 * r, r);
float[] dist = {0.0f, 0.5f, 1.0f};
Color[] cls = {
colors[id %colors.length],
colors[(colors.length - id) %colors.length],
colors[(id*5) %colors.length]};
LinearGradientPaint p =
new LinearGradientPaint(start, end, dist, cls);
g2d.setPaint(p);
}
}
static class RadGrad3OvalRotParticleRenderer extends FlatOvalRotParticleRenderer {
RadGrad3OvalRotParticleRenderer(int n, float r) {
super(n, r);
}
@Override
void setPaint(Graphics2D g2d, int id) {
Point2D start = new Point2D.Double();
float[] dist = {0.0f, 0.5f, 1.0f};
Color[] cls = {
colors[id %colors.length],
colors[(colors.length - id) %colors.length],
colors[(id*5) %colors.length]};
RadialGradientPaint p =
new RadialGradientPaint(start, r, dist, cls);
g2d.setPaint(p);
}
}
static class FlatBoxParticleRenderer extends FlatParticleRenderer {
FlatBoxParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.fillRect((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r));
}
}
static class ClipFlatBoxParticleRenderer extends FlatParticleRenderer {
ClipFlatBoxParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
if ((id % 10) == 0) {
g2d.setColor(colors[id % colors.length]);
g2d.setClip((int) (x[id] - r), (int) (y[id] - r), (int) (2 * r), (int) (2 * r));
g2d.fillRect((int) (x[id] - 2 * r), (int) (y[id] - 2 * r), (int) (4 * r), (int) (4 * r));
}
}
}
static class ImgParticleRenderer extends FlatParticleRenderer {
BufferedImage dukeImg;
ImgParticleRenderer(int n, float r) {
super(n, r);
try {
dukeImg = ImageIO.read(
Objects.requireNonNull(
RenderPerfTest.class.getClassLoader().getResourceAsStream(
"renderperf/images/duke.png")));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.drawImage(dukeImg, (int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r), null);
}
}
static class FlatBoxRotParticleRenderer extends FlatParticleRenderer {
FlatBoxRotParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
if (Math.abs(vx[id] + vy[id]) > 0.001) {
AffineTransform t = (AffineTransform) g2d.getTransform().clone();
double l = vx[id] / Math.sqrt(vx[id] * vx[id] + vy[id] * vy[id]);
if (vy[id] < 0) {
l = -l;
}
g2d.translate(x[id], y[id]);
g2d.rotate(Math.acos(l));
g2d.fillRect(-(int)r, -(int)r, (int) (2 * r), (int) (2 * r));
g2d.setTransform(t);
} else {
g2d.fillRect((int)(x[id] - r), (int)(y[id] - r),
(int) (2 * r), (int) (2 * r));
}
}
}
static class WiredParticleRenderer extends FlatParticleRenderer {
WiredParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.drawOval((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r));
}
}
static class WiredBoxParticleRenderer extends FlatParticleRenderer {
WiredBoxParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.drawRect((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r));
}
}
static class SegParticleRenderer extends FlatParticleRenderer {
SegParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
double v = Math.sqrt(vx[id]*vx[id]+vy[id]*vy[id]);
float nvx = (float) (vx[id]/v);
float nvy = (float) (vy[id]/v);
g2d.setColor(colors[id % colors.length]);
g2d.drawLine((int)(x[id] - r*nvx), (int)(y[id] - r*nvy),
(int)(x[id] + 2*r*nvx), (int)(y[id] + 2*r*nvy));
}
}
static class WiredQuadParticleRenderer extends FlatParticleRenderer {
WiredQuadParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
if (id > 2 && (id % 3) == 0) {
g2d.setColor(colors[id % colors.length]);
g2d.draw(new QuadCurve2D.Float(x[id-3], y[id-3], x[id-2], y[id-2], x[id-1], y[id-1]));
}
}
}
static class FlatQuadParticleRenderer extends FlatParticleRenderer {
FlatQuadParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
if (id > 2 && (id % 3) == 0) {
g2d.setColor(colors[id % colors.length]);
g2d.fill(new QuadCurve2D.Float(x[id-3], y[id-3], x[id-2], y[id-2], x[id-1], y[id-1]));
}
}
}
static class BlitImageParticleRenderer extends FlatParticleRenderer {
BufferedImage image;
BlitImageParticleRenderer(int n, float r, BufferedImage img) {
super(n, r);
image = img;
fill(image);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.drawImage(image, (int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r), null);
}
private static void fill(final Image image) {
final Graphics2D graphics = (Graphics2D) image.getGraphics();
graphics.setComposite(AlphaComposite.Src);
for (int i = 0; i < image.getHeight(null); ++i) {
graphics.setColor(new Color(i, 0, 0));
graphics.fillRect(0, i, image.getWidth(null), 1);
}
graphics.dispose();
}
}
static class SwBlitImageParticleRenderer extends BlitImageParticleRenderer {
SwBlitImageParticleRenderer(int n, float r, final int type) {
super(n, r, makeUnmanagedBI(type));
}
private static BufferedImage makeUnmanagedBI(final int type) {
final BufferedImage bi = new BufferedImage(17, 33, type);
final DataBuffer db = bi.getRaster().getDataBuffer();
if (db instanceof DataBufferInt) {
((DataBufferInt) db).getData();
} else if (db instanceof DataBufferShort) {
((DataBufferShort) db).getData();
} else if (db instanceof DataBufferByte) {
((DataBufferByte) db).getData();
}
bi.setAccelerationPriority(0.0f);
return bi;
}
}
static class SurfaceBlitImageParticleRenderer extends BlitImageParticleRenderer {
SurfaceBlitImageParticleRenderer(int n, float r, final int type) {
super(n, r, makeManagedBI(type));
}
private static BufferedImage makeManagedBI(final int type) {
final BufferedImage bi = new BufferedImage(17, 33, type);
bi.setAccelerationPriority(1.0f);
return bi;
}
}
static class PerfMeter {
private String name;
private int frame = 0;
private JPanel panel;
private long time;
private double execTime = 0;
private Color expColor = Color.RED;
AtomicBoolean waiting = new AtomicBoolean(false);
private double fps;
PerfMeter(String name) {
this.name = name;
}
PerfMeter exec(final Renderable renderable) throws Exception {
final CountDownLatch latch = new CountDownLatch(COUNT);
final CountDownLatch latchFrame = new CountDownLatch(1);
final long endTime = System.currentTimeMillis() + MAX_MEASURE_TIME;
final JFrame f = new JFrame();
f.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
latchFrame.countDown();
}
});
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
panel = new JPanel()
{
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
time = System.nanoTime();
Graphics2D g2d = (Graphics2D) g.create();
renderable.setup(g2d);
renderable.render(g2d);
g2d.setColor(expColor);
g.fillRect(0, 0, BW, BH);
}
};
panel.setPreferredSize(new Dimension((int)(WIDTH + BW), (int)(HEIGHT + BH)));
panel.setBackground(Color.BLACK);
f.add(panel);
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
});
Robot robot = new Robot();
Timer timer = new Timer(DELAY, e -> {
if (waiting.compareAndSet(false, true)) {
Color c = robot.getPixelColor(
panel.getTopLevelAncestor().getX() + panel.getTopLevelAncestor().getInsets().left + BW / 2,
panel.getTopLevelAncestor().getY() + panel.getTopLevelAncestor().getInsets().top + BW / 2);
if (isAlmostEqual(c, Color.BLUE)) {
expColor = Color.RED;
} else {
expColor = Color.BLUE;
}
renderable.update();
panel.getParent().repaint();
} else {
while (!isAlmostEqual(
robot.getPixelColor(
panel.getTopLevelAncestor().getX() + panel.getTopLevelAncestor().getInsets().left + BW/2,
panel.getTopLevelAncestor().getY() + panel.getTopLevelAncestor().getInsets().top + BH/2),
expColor))
{
try {
Thread.sleep(RESOLUTION);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
time = System.nanoTime() - time;
execTime += time;
frame++;
waiting.set(false);
}
if (System.currentTimeMillis() < endTime) {
latch.countDown();
} else {
while(latch.getCount() > 0) latch.countDown();
}
});
timer.start();
latch.await();
SwingUtilities.invokeAndWait(() -> {
timer.stop();
f.setVisible(false);
f.dispose();
});
latchFrame.await();
if (execTime != 0 && frame != 0) {
fps = 1e9 / (execTime / frame);
} else {
fps = 0;
}
return this;
}
private void report() {
System.err.println(name + " : " + String.format("%.2f FPS", fps));
}
private boolean isAlmostEqual(Color c1, Color c2) {
return Math.abs(c1.getRed() - c2.getRed()) < COLOR_TOLERANCE ||
Math.abs(c1.getGreen() - c2.getGreen()) < COLOR_TOLERANCE ||
Math.abs(c1.getBlue() - c2.getBlue()) < COLOR_TOLERANCE;
}
}
private static final Particles balls = new Particles(N, R, BW, BH, WIDTH, HEIGHT);
private static final ParticleRenderer flatRenderer = new FlatParticleRenderer(N, R);
private static final ParticleRenderer clipFlatRenderer = new ClipFlatParticleRenderer(N, R);
private static final ParticleRenderer flatOvalRotRenderer = new FlatOvalRotParticleRenderer(N, R);
private static final ParticleRenderer flatBoxRenderer = new FlatBoxParticleRenderer(N, R);
private static final ParticleRenderer clipFlatBoxParticleRenderer = new ClipFlatBoxParticleRenderer(N, R);
private static final ParticleRenderer flatBoxRotRenderer = new FlatBoxRotParticleRenderer(N, R);
private static final ParticleRenderer linGradOvalRotRenderer = new LinGradOvalRotParticleRenderer(N, R);
private static final ParticleRenderer linGrad3OvalRotRenderer = new LinGrad3OvalRotParticleRenderer(N, R);
private static final ParticleRenderer radGrad3OvalRotRenderer = new RadGrad3OvalRotParticleRenderer(N, R);
private static final ParticleRenderer wiredRenderer = new WiredParticleRenderer(N, R);
private static final ParticleRenderer wiredBoxRenderer = new WiredBoxParticleRenderer(N, R);
private static final ParticleRenderer segRenderer = new SegParticleRenderer(N, R);
private static final ParticleRenderer flatQuadRenderer = new FlatQuadParticleRenderer(N, R);
private static final ParticleRenderer wiredQuadRenderer = new WiredQuadParticleRenderer(N, R);
private static final ParticleRenderer imgRenderer = new ImgParticleRenderer(N, R);
private static final ParticleRenderer textRenderer = new TextParticleRenderer(N, R);
private static final ParticleRenderer largeTextRenderer = new LargeTextParticleRenderer(N, R);
private static final ParticleRenderer whiteTextRenderer = new WhiteTextParticleRenderer(R);
private static final ParticleRenderer argbSwBlitImageRenderer = new SwBlitImageParticleRenderer(N, R, BufferedImage.TYPE_INT_ARGB);
private static final ParticleRenderer bgrSwBlitImageRenderer = new SwBlitImageParticleRenderer(N, R, BufferedImage.TYPE_INT_BGR);
private static final ParticleRenderer argbSurfaceBlitImageRenderer = new SurfaceBlitImageParticleRenderer(N, R, BufferedImage.TYPE_INT_ARGB);
private static final ParticleRenderer bgrSurfaceBlitImageRenderer = new SurfaceBlitImageParticleRenderer(N, R, BufferedImage.TYPE_INT_BGR);
private static final Configurable AA = (Graphics2D g2d) ->
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
private static final Configurable TextLCD = (Graphics2D g2d) ->
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
private static final Configurable TextAA = (Graphics2D g2d) ->
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
private static final Configurable XORMode = (Graphics2D g2d) ->
{g2d.setXORMode(Color.WHITE);};
private static final Configurable XORModeLCDText = (Graphics2D g2d) ->
{g2d.setXORMode(Color.WHITE);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);};
public void testFlatOval() throws Exception {
(new PerfMeter("FlatOval")).exec(createPR(flatRenderer)).report();
}
public void testFlatOvalAA() throws Exception {
(new PerfMeter("FlatOvalAA")).exec(createPR(flatRenderer).configure(AA)).report();
}
public void testClipFlatOval() throws Exception {
(new PerfMeter("ClipFlatOval")).exec(createPR(clipFlatRenderer)).report();
}
public void testClipFlatOvalAA() throws Exception {
(new PerfMeter("ClipFlatOvalAA")).exec(createPR(clipFlatRenderer).configure(AA)).report();
}
public void testFlatBox() throws Exception {
(new PerfMeter("FlatBox")).exec(createPR(flatBoxRenderer)).report();
}
public void testFlatBoxAA() throws Exception {
(new PerfMeter("FlatBoxAA")).exec(createPR(flatBoxRenderer).configure(AA)).report();
}
public void testClipFlatBox() throws Exception {
(new PerfMeter("ClipFlatBox")).exec(createPR(clipFlatBoxParticleRenderer)).report();
}
public void testClipFlatBoxAA() throws Exception {
(new PerfMeter("ClipFlatBoxAA")).exec(createPR(clipFlatBoxParticleRenderer).configure(AA)).report();
}
public void testImage() throws Exception {
(new PerfMeter("Image")).exec(createPR(imgRenderer)).report();
}
public void testImageAA() throws Exception {
(new PerfMeter("ImageAA")).exec(createPR(imgRenderer).configure(AA)).report();
}
public void testRotatedBox() throws Exception {
(new PerfMeter("RotatedBox")).exec(createPR(flatBoxRotRenderer)).report();
}
public void testRotatedBoxAA() throws Exception {
(new PerfMeter("RotatedBoxAA")).exec(createPR(flatBoxRotRenderer).configure(AA)).report();
}
public void testRotatedOval() throws Exception {
(new PerfMeter("RotatedOval")).exec(createPR(flatOvalRotRenderer)).report();
}
public void testRotatedOvalAA() throws Exception {
(new PerfMeter("RotatedOvalAA")).exec(createPR(flatOvalRotRenderer).configure(AA)).report();
}
public void testLinGrad3RotatedOval() throws Exception {
(new PerfMeter("LinGrad3RotatedOval")).exec(createPR(linGrad3OvalRotRenderer)).report();
}
public void testLinGrad3RotatedOvalAA() throws Exception {
(new PerfMeter("LinGrad3RotatedOvalAA")).exec(createPR(linGrad3OvalRotRenderer).configure(AA)).report();
}
public void testRadGrad3RotatedOval() throws Exception {
(new PerfMeter("RadGrad3RotatedOval")).exec(createPR(radGrad3OvalRotRenderer)).report();
}
public void testRadGrad3RotatedOvalAA() throws Exception {
(new PerfMeter("RadGrad3RotatedOvalAA")).exec(createPR(radGrad3OvalRotRenderer).configure(AA)).report();
}
public void testLinGradRotatedOval() throws Exception {
(new PerfMeter("LinGradRotatedOval")).exec(createPR(linGradOvalRotRenderer)).report();
}
public void testLinGradRotatedOvalAA() throws Exception {
(new PerfMeter("LinGradRotatedOvalAA")).exec(createPR(linGradOvalRotRenderer).configure(AA)).report();
}
public void testWiredBubbles() throws Exception {
(new PerfMeter("WiredBubbles")).exec(createPR(wiredRenderer)).report();
}
public void testWiredBubblesAA() throws Exception {
(new PerfMeter("WiredBubblesAA")).exec(createPR(wiredRenderer).configure(AA)).report();
}
public void testWiredBox() throws Exception {
(new PerfMeter("WiredBox")).exec(createPR(wiredBoxRenderer)).report();
}
public void testWiredBoxAA() throws Exception {
(new PerfMeter("WiredBoxAA")).exec(createPR(wiredBoxRenderer).configure(AA)).report();
}
public void testLines() throws Exception {
(new PerfMeter("Lines")).exec(createPR(segRenderer)).report();
}
public void testLinesAA() throws Exception {
(new PerfMeter("LinesAA")).exec(createPR(segRenderer).configure(AA)).report();
}
public void testFlatQuad() throws Exception {
(new PerfMeter("FlatQuad")).exec(createPR(flatQuadRenderer)).report();
}
public void testFlatQuadAA() throws Exception {
(new PerfMeter("FlatQuadAA")).exec(createPR(flatQuadRenderer).configure(AA)).report();
}
public void testWiredQuad() throws Exception {
(new PerfMeter("WiredQuad")).exec(createPR(wiredQuadRenderer)).report();
}
public void testWiredQuadAA() throws Exception {
(new PerfMeter("WiredQuadAA")).exec(createPR(wiredQuadRenderer).configure(AA)).report();
}
public void testTextNoAA() throws Exception {
(new PerfMeter("TextNoAA")).exec(createPR(textRenderer)).report();
}
public void testTextLCD() throws Exception {
(new PerfMeter("TextLCD")).exec(createPR(textRenderer).configure(TextLCD)).report();
}
public void testTextGray() throws Exception {
(new PerfMeter("TextGray")).exec(createPR(textRenderer).configure(TextAA)).report();
}
public void testLargeTextNoAA() throws Exception {
(new PerfMeter("LargeTextNoAA")).exec(createPR(largeTextRenderer)).report();
}
public void testLargeTextLCD() throws Exception {
(new PerfMeter("LargeTextLCD")).exec(createPR(largeTextRenderer).configure(TextLCD)).report();
}
public void testLargeTextGray() throws Exception {
(new PerfMeter("LargeTextGray")).exec(createPR(largeTextRenderer).configure(TextAA)).report();
}
public void testWhiteTextNoAA() throws Exception {
(new PerfMeter("WhiteTextNoAA")).exec(createPR(whiteTextRenderer)).report();
}
public void testWhiteTextLCD() throws Exception {
(new PerfMeter("WhiteTextLCD")).exec(createPR(whiteTextRenderer).configure(TextLCD)).report();
}
public void testWhiteTextGray() throws Exception {
(new PerfMeter("WhiteTextGray")).exec(createPR(whiteTextRenderer).configure(TextAA)).report();
}
public void testArgbSwBlitImage() throws Exception {
(new PerfMeter("ArgbSwBlitImage")).exec(createPR(argbSwBlitImageRenderer)).report();
}
public void testBgrSwBlitImage() throws Exception {
(new PerfMeter("BgrSwBlitImage")).exec(createPR(bgrSwBlitImageRenderer)).report();
}
public void testArgbSurfaceBlitImage() throws Exception {
(new PerfMeter("ArgbSurfaceBlitImageRenderer")).exec(createPR(argbSurfaceBlitImageRenderer)).report();
}
public void testBgrSurfaceBlitImage() throws Exception {
(new PerfMeter("BgrSurfaceBlitImage")).exec(createPR(bgrSurfaceBlitImageRenderer)).report();
}
public void testFlatOval_XOR() throws Exception {
(new PerfMeter("FlatOval_XOR")).exec(createPR(flatRenderer).configure(XORMode)).report();
}
public void testRotatedBox_XOR() throws Exception {
(new PerfMeter("RotatedBox_XOR")).exec(createPR(flatBoxRotRenderer).configure(XORMode)).report();
}
public void testLines_XOR() throws Exception {
(new PerfMeter("Lines_XOR")).exec(createPR(segRenderer).configure(XORMode)).report();
}
public void testImage_XOR() throws Exception {
(new PerfMeter("Image_XOR")).exec(createPR(imgRenderer).configure(XORMode)).report();
}
public void testTextNoAA_XOR() throws Exception {
(new PerfMeter("TextNoAA_XOR")).exec(createPR(textRenderer).configure(XORMode)).report();
}
public void testTextLCD_XOR() throws Exception {
(new PerfMeter("TextLCD_XOR")).exec(createPR(textRenderer).configure(XORModeLCDText)).report();
}
public static void main(String[] args)
throws InvocationTargetException, IllegalAccessException, NoSuchMethodException
{
RenderPerfTest test = new RenderPerfTest();
if (args.length > 0) {
for (String testCase : args) {
Method m = RenderPerfTest.class.getDeclaredMethod("test" + testCase);
m.invoke(test);
}
} else {
Method[] methods = RenderPerfTest.class.getDeclaredMethods();
for (Method m : methods) {
if (m.getName().startsWith("test") && !ignoredTests.contains(m.getName())) {
m.invoke(test);
}
}
}
}
}