Compare commits

...

40 Commits

Author SHA1 Message Date
Artem Bochkarev
d7040fc9af fix logging for MTLRenderer (disable by default) 2019-04-22 16:33:57 +07:00
Artem Bochkarev
97428befdc don't shift backbuffer (by title height)
because CAMetalLayer nextDrawable returns surface for the client area
2019-04-22 16:33:57 +07:00
Artem Bochkarev
3c97fc0994 use blitEncoder as much as possible 2019-04-22 15:12:34 +07:00
Artem Bochkarev
ba5352cd1d textures: implement MTLTexturePool and MTLBlitSwToSurfaceViaTexture
it is necessary for blit operations "memory -> MTLTexture" with sampling (MTLTexture.replaceRegion or MTLBlitEncoders can't do sampling)
2019-04-22 15:12:33 +07:00
Artem Bochkarev
7c5303d165 initial support AlphaComposite
implemented most frequent SrcOver
2019-04-22 15:12:33 +07:00
Artem Bochkarev
737282af47 remove MTLContext.renderSemaphore 2019-04-22 15:12:32 +07:00
Artem Bochkarev
c645e6d516 create new command buffer when schedule blitTexture 2019-04-22 15:12:31 +07:00
Artem Bochkarev
5e62f37e30 fixed texture format for RTexture 2019-04-22 15:12:31 +07:00
Artem Bochkarev
a22b6c76e0 refactoring: remove MTLContext.mtlEmptyCommandBuffer 2019-04-22 15:12:30 +07:00
Artem Bochkarev
e3b69a412c fixed usage of MTLLayer.blitTexture
invoke it at the end of each RQ.flush (only for layers that were modified by some 'drawing' opcode)
2019-04-22 15:12:29 +07:00
Artem Bochkarev
cc5e746558 fix logging for MTLContext 2019-04-22 15:12:29 +07:00
Artem Bochkarev
d5b78ddeb8 refactoring: rename MTLSurfaceData.initFBObject into initRTexture 2019-04-22 15:12:28 +07:00
Artem Bochkarev
66cc7dbe8c refactoring: move MTLContext_SetColor code from MTLPaints into MTLContext 2019-04-22 15:12:27 +07:00
Artem Bochkarev
e68ed092ea textures: minor fixes for logging
add for MTLContext_SetColor
make single line and more compact for blit operations
2019-04-15 17:53:20 +07:00
Artem Bochkarev
b3aafa946a textures: fix layer height 2019-04-15 17:09:36 +07:00
Artem Bochkarev
89f9020408 textures: support clip rect and color 2019-04-15 15:43:20 +07:00
Artem Bochkarev
600c2302bd textures: fixed coords for MTLBlitSwToSurface 2019-04-15 15:43:19 +07:00
Artem Bochkarev
bf1b49569c refactoring: call metal api from RQ-thread 2019-04-15 15:37:29 +07:00
Artem Bochkarev
2c5aa85c84 textures: remove mtlFrameBuffer from MTLContext
pass buffer via java-peer MTLLayer.validate
2019-04-15 15:35:02 +07:00
Artem Bochkarev
d5bdb9ee89 refactoring: remove MTLSD_Flush, MTLRenderer_BeginFrame
because unnecessary and have unclear logic
2019-04-15 15:28:13 +07:00
Artem Bochkarev
3203bbb4aa textures: refactoring (remove MTLContext.mtlCurrentBuffer, code cleanup) 2019-04-10 14:53:00 +07:00
Artem Bochkarev
80c9f7c103 textures: support transformations 2019-04-03 22:18:38 +07:00
Alexey Ushakov
e5fe46f243 Updated build include dir 2019-03-21 19:26:50 +03:00
Alexey Ushakov
7c8923fdfb MetalPerf: Added LinearGradientPaint test 2019-03-19 22:29:53 +03:00
Alexey Ushakov
b9bc959ab1 MetalPerf: Added perf tests with rotated shapes 2019-03-19 19:14:10 +03:00
Artem Bochkarev
c25756ad98 textures: initial blit-operations support 2019-03-18 16:25:46 +03:00
Alexey Ushakov
d05b9a0f35 Refactoring:
Removed redundant MTLGraphicsConfigBase class
Removed redundant MTLSurfaceDataBase class
Removed redundant MTLLayer.blitTexture method
Removed redundant MTLSD_XX functions
Improved logging by passing native file and line number
Replaced FBOBJECT (from OGL) with RT_TEXTURE

Added native semaphore to access shared command queue
Corrected logging for "notimpl" primitives
2019-03-18 16:25:45 +03:00
Alexey Ushakov
5cb66def9d test execution option for "not implemented" logging 2019-03-18 16:25:44 +03:00
Alexey Ushakov
9eaaca1598 Logging for not implemented primitives 2019-03-18 16:25:43 +03:00
Alexey Ushakov
2dccfc2fc6 jdk cmake: remove deleted files 2019-03-18 16:25:42 +03:00
Alexey Ushakov
d4c16e4cc8 Metal refactoring
Copyright update
2019-03-18 16:25:41 +03:00
Alexey Ushakov
ac8cd67dd9 MetalPerfTest: Added wired box, oval, line perf and some more tests 2019-03-18 16:25:40 +03:00
Artem Bochkarev
28f2429359 MTLRenderer: initial implementation of line primitives drawing 2019-03-18 16:25:39 +03:00
Alexey Ushakov
f578a41e61 Removed unnecessary MTLCtxInfo 2019-03-18 16:25:39 +03:00
Alexey Ushakov
ee55ef1524 Added cmake projects for hotspot and jdk 2019-03-18 16:25:38 +03:00
Alexey Ushakov
9055bb22c5 Improved rendering performance including flat shape rendering 2019-03-18 16:25:05 +03:00
Alexey Ushakov
e306de9239 Improved performance on MetalPerfTest
- fixed flushNow logic to get updates instantly
  - replaced temporary MTLBuffer creation for parallelogram with
    direct bytes sending
2019-03-18 16:22:53 +03:00
Alexey Ushakov
4d5b05c180 JRE-745 Improve java2d rendering performance on macOS by using Metal framework
Metal initial commit
2019-03-18 16:22:40 +03:00
Alexey Ushakov
142faacf67 JRE-646 Provide performance logging of graphics primitives
Enhanced logging (example: -Dsun.java2d.trace=log,ptime,td=3000000,verbose)
 Filter primitives by name and detect LCD/Grayscale text rendering (JRE-679)
 Example: -Dsun.java2d.trace=log,ptime,name:OGLDrawGlyphsGray,verbose

 (cherry picked from commit 4fac53062d)
2019-03-18 16:09:44 +03:00
Alexey Ushakov
93d1ca5a56 Added gradle project 2019-03-18 16:09:12 +03:00
99 changed files with 16997 additions and 68 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
# common for all OS
set(CMAKE_CXX_STANDARD 98)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GNU_SOURCE -D_REENTRANT -DVM_LITTLE_ENDIAN -D_LP64 -DTARGET_ARCH_x86 ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DINCLUDE_SUFFIX_CPU=_x86 -DAMD64 -DHOTSPOT_LIB_ARCH='amd64' -DCOMPILER1 -DCOMPILER2")
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin" OR ${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTARGET_COMPILER_gcc")
endif ()
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DLINUX -DTARGET_OS_FAMILY_linux -DTARGET_COMPILER_gcc -D_GNU_SOURCE")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLINUX -DTARGET_OS_FAMILY_linux -DTARGET_COMPILER_gcc -D_GNU_SOURCE")
endif ()
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_ALLBSD_SOURCE -DTARGET_OS_FAMILY_bsd")
endif ()
if ("${CMAKE_SYSTEM_NAME}" MATCHES "CYGWIN") #not shure about TARGET_COMPILER
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTARGET_COMPILER_visCPP -DWIN64 -D_WINDOWS -DTARGET_OS_FAMILY_windows")
endif ()
add_custom_target(configure
COMMAND bash configure
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/../../../)
add_custom_target(build_images
COMMAND make COMPILER_WARNINGS_FATAL=false images
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/../../../
DEPENDS ${SOURCE_FILES})

View File

@@ -0,0 +1,129 @@
apply plugin: 'java'
import org.gradle.internal.os.OperatingSystem
repositories {
mavenCentral()
}
def test_jvm = {
if (project.hasProperty('jbsdkhome')) {
file(jbsdkhome + (OperatingSystem.current().isWindows()?"/bin/java.exe" : "/bin/java")).absolutePath
} else {
if (OperatingSystem.current().isMacOsX()) {
file('../../../build/macosx-x86_64-server-release/images/jdk-bundle/jdk-13.jdk/Contents/Home/bin/java').absolutePath
} else if (OperatingSystem.current().isLinux()) {
file('../../../build/linux-x86_64-server-release/images/jdk/bin/java').absolutePath
} else {
file('../../../build/windows-x86_64-server-release/images/j2sdk-image/bin/java.exe').absolutePath
}
}
}
dependencies {
testCompile('junit:junit:4.12'){
exclude group: 'org.hamcrest'
}
testCompile 'org.hamcrest:hamcrest-library:1.3'
testCompile 'net.java.dev.jna:jna:4.4.0'
testCompile 'com.twelvemonkeys.imageio:imageio-tiff:3.3.2'
testCompile 'org.apache.commons:commons-lang3:3.0'
}
def jdk_modules = ["java.base", "java.logging", "java.prefs",
"java.se.ee", "java.sql", "java.datatransfer",
"java.management", "java.rmi", "java.security.jgss",
"java.sql.rowset", "java.desktop", "java.management.rmi",
"java.scripting", "java.security.sasl", "java.transaction",
"java.instrument", "java.naming", "java.se",
"java.smartcardio", "java.xml.crypto"]
def jdk_class_dirs = []
jdk_modules.collect(jdk_class_dirs) {
new File("../../../src/" + it + "/share/classes")
}
if (OperatingSystem.current().isMacOsX())
jdk_modules.collect(jdk_class_dirs) {
"../../../src/" + it + "/macosx/classes"
}
else if (OperatingSystem.current().isLinux()) {
jdk_modules.collect(jdk_class_dirs) {
"../../../src/" + it + "/solaris/classes"
}
jdk_modules.collect(jdk_class_dirs) {
"../../../src/" + it + "/unix/classes"
}
} else
jdk_modules.collect(jdk_class_dirs) {
"../../../src/" + it + "/windows/classes"
}
sourceSets.main.java.srcDirs = jdk_class_dirs
sourceSets {
test {
java {
srcDir "../../../test/jdk/jbu"
}
}
}
test.dependsOn.clear()
test.dependsOn tasks.compileTestJava
test {
systemProperty "jb.java2d.metal", "true"
systemProperty "testdata", file('../../../test/jdk/jbu/testdata').absolutePath
// Generate golden images for DroidFontTest and MixedTextTest
// systemProperty "gentestdata", ""
// Enable Java2D logging (https://confluence.jetbrains.com/display/JRE/Java2D+Rendering+Logging)
// systemProperty "sun.java2d.trace", "log"
// systemProperty "sun.java2d.trace", "log,pimpl"
outputs.upToDateWhen { false }
executable = test_jvm()
// Enable async/dtrace profiler
jvmArgs "-XX:+PreserveFramePointer"
// Enable native J2D logging (only in debug build)
// Can be turned on for J2D by adding "#define DEBUG 1" into jdk/src/share/native/sun/java2d/Trace.h
// environment 'J2D_TRACE_LEVEL', '4'
}
def buildDir = project.buildscript.sourceFile.parentFile.parentFile.parentFile.parentFile
def make_cmd = "make"
if (OperatingSystem.current().isWindows()) {
def cyg_make_cmd = new File("c:/cygwin64/bin/make.exe")
if (cyg_make_cmd.exists()) make_cmd = cyg_make_cmd.absolutePath
}
def test_run = false
task make_images << {
if (!test_run) {
def pb = new ProcessBuilder().command(make_cmd.toString(), "-C", buildDir.absolutePath, "images")
def proc = pb.redirectErrorStream(true).start()
proc.inputStream.eachLine { println it }
assert proc.waitFor() == 0
}
}
task make_clean << {
def pb = new ProcessBuilder().command(make_cmd.toString(), "-C", buildDir.absolutePath, "clean")
def proc = pb.redirectErrorStream(true).start()
proc.inputStream.eachLine {println it}
assert proc.waitFor() == 0
}
task run_test << {
test_run = true
}
tasks.cleanTest.dependsOn tasks.run_test
classes.dependsOn.clear()
classes.dependsOn tasks.make_images
tasks.cleanClasses.dependsOn tasks.make_clean

File diff suppressed because it is too large Load Diff

View File

@@ -246,6 +246,8 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \
LIBS_macosx := -lmlib_image \
-framework Cocoa \
-framework OpenGL \
-framework Metal \
-framework MetalKit \
-framework JavaNativeFoundation \
-framework JavaRuntimeSupport \
-framework ApplicationServices \
@@ -823,6 +825,8 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false)
-framework ApplicationServices \
-framework Foundation \
-framework Cocoa \
-framework Metal \
-framework MetalKit \
-framework JavaNativeFoundation
else ifeq ($(call isTargetOs, windows), true)
LIBSPLASHSCREEN_LIBS += kernel32.lib user32.lib gdi32.lib delayimp.lib $(WIN_JAVA_LIB) jvm.lib
@@ -885,6 +889,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 \
@@ -920,6 +925,8 @@ ifeq ($(call isTargetOs, macosx), true)
-framework AudioToolbox \
-framework Carbon \
-framework Cocoa \
-framework Metal \
-framework MetalKit \
-framework Security \
-framework ExceptionHandling \
-framework JavaNativeFoundation \
@@ -943,6 +950,11 @@ endif
################################################################################
ifeq ($(call isTargetOs, macosx), true)
XCODE_PATH := $(shell /usr/bin/xcode-select -p)
CompileMetalShaders :
$(XCODE_PATH)/Platforms/MacOSX.platform/usr/bin/metal -O2 -std=osx-metal1.1 -o $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libosxui/shaders.air $(TOPDIR)/src/java.desktop/macosx/native/libawt_lwawt/awt/shaders.metal
$(XCODE_PATH)/Platforms/MacOSX.platform/usr/bin/metal-ar r $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libosxui/shaders.metal-ar $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libosxui/shaders.air
$(XCODE_PATH)/Platforms/MacOSX.platform/usr/bin/metallib -o $(INSTALL_LIBRARIES_HERE)/shaders.metallib $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libosxui/shaders.metal-ar
$(eval $(call SetupJdkLibrary, BUILD_LIBOSXUI, \
NAME := osxui, \
@@ -958,6 +970,9 @@ ifeq ($(call isTargetOs, macosx), true)
-L$(INSTALL_LIBRARIES_HERE), \
LIBS := -lawt -losxapp -lawt_lwawt \
-framework Cocoa \
-framework Metal \
-framework MetalKit \
-framework OpenGL \
-framework Carbon \
-framework ApplicationServices \
-framework JavaNativeFoundation \
@@ -966,6 +981,7 @@ ifeq ($(call isTargetOs, macosx), true)
))
TARGETS += $(BUILD_LIBOSXUI)
$(BUILD_LIBOSXUI): CompileMetalShaders
$(BUILD_LIBOSXUI): $(BUILD_LIBAWT)

View File

@@ -1162,6 +1162,7 @@ sun.java2d.d3d
sun.java2d.jules
sun.java2d.loops
sun.java2d.opengl
sun.java2d.metal
sun.java2d.pipe
sun.java2d.pipe.hw
sun.java2d.pisces

View File

@@ -34,6 +34,7 @@ import java.awt.image.ColorModel;
import sun.java2d.SurfaceData;
import sun.java2d.opengl.CGLLayer;
import sun.lwawt.LWGraphicsConfig;
import sun.lwawt.macosx.CFRetainedResource;
import sun.lwawt.macosx.CPlatformView;
public abstract class CGraphicsConfig extends GraphicsConfiguration
@@ -87,7 +88,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

@@ -36,6 +36,8 @@ import java.awt.geom.Rectangle2D;
import java.util.Objects;
import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.macos.MacOSFlags;
import sun.java2d.metal.MTLGraphicsConfig;
import sun.java2d.opengl.CGLGraphicsConfig;
public final class CGraphicsDevice extends GraphicsDevice
@@ -60,7 +62,9 @@ public final class CGraphicsDevice extends GraphicsDevice
public CGraphicsDevice(final int displayID) {
this.displayID = displayID;
config = CGLGraphicsConfig.getConfig(this, displayID, 0);
config = MacOSFlags.isMetalEnabled() ?
MTLGraphicsConfig.getConfig(this, displayID, 0) :
CGLGraphicsConfig.getConfig(this, displayID, 0);
}
/**

View File

@@ -27,6 +27,8 @@ package sun.java2d;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.macos.MacOSFlags;
import sun.java2d.metal.MTLVolatileSurfaceManager;
import sun.java2d.opengl.CGLVolatileSurfaceManager;
/**
@@ -49,6 +51,7 @@ public class MacosxSurfaceManagerFactory extends SurfaceManagerFactory {
public VolatileSurfaceManager createVolatileManager(SunVolatileImage vImg,
Object context)
{
return new CGLVolatileSurfaceManager(vImg, context);
return MacOSFlags.isMetalEnabled() ? new MTLVolatileSurfaceManager(vImg, context) :
new CGLVolatileSurfaceManager(vImg, context);
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.macos;
import java.security.PrivilegedAction;
public class MacOSFlags {
/**
* Description of command-line flags. All flags with [true|false]
* values
* metalEnabled: usage: "-Djb.java2d.metal=[true|false]"
*/
private static boolean metalEnabled;
static {
initJavaFlags();
initNativeFlags();
}
private static native boolean initNativeFlags();
private static boolean getBooleanProp(String p, boolean defaultVal) {
String propString = System.getProperty(p);
boolean 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 = true;
} else if (propString.equals("false") ||
propString.equals("f") ||
propString.equals("False") ||
propString.equals("F"))
{
returnVal = false;
}
}
return returnVal;
}
private static boolean getPropertySet(String p) {
String propString = System.getProperty(p);
return (propString != null) ? true : false;
}
private static void initJavaFlags() {
java.security.AccessController.doPrivileged(
(PrivilegedAction<Object>) () -> {
metalEnabled = getBooleanProp("jb.java2d.metal", false);
return null;
});
}
public static boolean isMetalEnabled() {
return metalEnabled;
}
}

View File

@@ -0,0 +1,945 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.SurfaceData;
import sun.java2d.loops.*;
import sun.java2d.pipe.Region;
import sun.java2d.pipe.RenderBuffer;
import sun.java2d.pipe.RenderQueue;
import sun.java2d.pipe.hw.AccelSurface;
import java.awt.*;
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 MTLSwToSurfaceBlit(SurfaceType.ThreeByteBgr,
MTLSurfaceData.PF_3BYTE_BGR),
new MTLSwToSurfaceBlit(SurfaceType.Ushort565Rgb,
MTLSurfaceData.PF_USHORT_565_RGB),
new MTLSwToSurfaceBlit(SurfaceType.Ushort555Rgb,
MTLSurfaceData.PF_USHORT_555_RGB),
new MTLSwToSurfaceBlit(SurfaceType.Ushort555Rgbx,
MTLSurfaceData.PF_USHORT_555_RGBX),
new MTLSwToSurfaceBlit(SurfaceType.ByteGray,
MTLSurfaceData.PF_BYTE_GRAY),
new MTLSwToSurfaceBlit(SurfaceType.UshortGray,
MTLSurfaceData.PF_USHORT_GRAY),
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.ThreeByteBgr,
MTLSurfaceData.PF_3BYTE_BGR),
new MTLSwToSurfaceScale(SurfaceType.Ushort565Rgb,
MTLSurfaceData.PF_USHORT_565_RGB),
new MTLSwToSurfaceScale(SurfaceType.Ushort555Rgb,
MTLSurfaceData.PF_USHORT_555_RGB),
new MTLSwToSurfaceScale(SurfaceType.Ushort555Rgbx,
MTLSurfaceData.PF_USHORT_555_RGBX),
new MTLSwToSurfaceScale(SurfaceType.ByteGray,
MTLSurfaceData.PF_BYTE_GRAY),
new MTLSwToSurfaceScale(SurfaceType.UshortGray,
MTLSurfaceData.PF_USHORT_GRAY),
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),
new MTLSwToSurfaceTransform(SurfaceType.ThreeByteBgr,
MTLSurfaceData.PF_3BYTE_BGR),
new MTLSwToSurfaceTransform(SurfaceType.Ushort565Rgb,
MTLSurfaceData.PF_USHORT_565_RGB),
new MTLSwToSurfaceTransform(SurfaceType.Ushort555Rgb,
MTLSurfaceData.PF_USHORT_555_RGB),
new MTLSwToSurfaceTransform(SurfaceType.Ushort555Rgbx,
MTLSurfaceData.PF_USHORT_555_RGBX),
new MTLSwToSurfaceTransform(SurfaceType.ByteGray,
MTLSurfaceData.PF_BYTE_GRAY),
new MTLSwToSurfaceTransform(SurfaceType.UshortGray,
MTLSurfaceData.PF_USHORT_GRAY),
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 MTLSwToTextureBlit(SurfaceType.ThreeByteBgr,
MTLSurfaceData.PF_3BYTE_BGR),
new MTLSwToTextureBlit(SurfaceType.Ushort565Rgb,
MTLSurfaceData.PF_USHORT_565_RGB),
new MTLSwToTextureBlit(SurfaceType.Ushort555Rgb,
MTLSurfaceData.PF_USHORT_555_RGB),
new MTLSwToTextureBlit(SurfaceType.Ushort555Rgbx,
MTLSurfaceData.PF_USHORT_555_RGBX),
new MTLSwToTextureBlit(SurfaceType.ByteGray,
MTLSurfaceData.PF_BYTE_GRAY),
new MTLSwToTextureBlit(SurfaceType.UshortGray,
MTLSurfaceData.PF_USHORT_GRAY),
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 oglDst = (MTLSurfaceData)dstData;
if (texture) {
// make sure we have a current context before uploading
// the sysmem data to the texture object
MTLGraphicsConfig gc = oglDst.getMTLGraphicsConfig();
MTLContext.setScratchSurface(gc);
} else {
MTLContext.validateContext(oglDst, oglDst,
clip, comp, xform, null, null,
ctxflags);
}
int packedParams = createPackedParams(false, texture,
false, 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 oglSrc = (MTLSurfaceData)srcData;
MTLSurfaceData oglDst = (MTLSurfaceData)dstData;
int srctype = oglSrc.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 = oglDst;
} 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 = oglDst;
} else {
srcCtxData = oglSrc;
}
}
MTLContext.validateContext(srcCtxData, oglDst,
clip, comp, xform, null, null,
ctxflags);
if (biop != null) {
MTLBufImgOps.enableBufImgOp(rq, oglSrc, srcImg, biop);
}
int packedParams = createPackedParams(true, texture,
rtt, 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 && oglDst.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 OpenGL 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 OpenGL 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 OpenGL 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 OpenGL 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 OpenGL.
*/
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,112 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.loops.CompositeType;
import sun.java2d.pipe.BufferedBufImgOps;
import java.awt.image.*;
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,222 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.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.*;
/**
* Note that the RenderQueue lock must be acquired before calling any of
* the methods in this class.
*/
public class MTLContext extends BufferedContext {
private final MTLGraphicsConfig config;
public MTLContext(RenderQueue rq, MTLGraphicsConfig config) {
super(rq);
this.config = config;
}
/**
* 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 pbuffer
* 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 RenderQueue getRenderQueue() {
return MTLRenderQueue.getInstance();
}
/**
* Returns a string representing adapter id (vendor, renderer, version).
* Must be called on the rendering thread.
*
* @return an id string for the adapter
*/
public static final native String getMTLIdString();
@Override
public void saveState() {
// assert rq.lock.isHeldByCurrentThread();
// reset all attributes of this and current contexts
invalidateContext();
invalidateCurrentContext();
setScratchSurface(config);
// save the state on the native level
rq.ensureCapacity(4);
buf.putInt(SAVE_STATE);
rq.flushNow();
}
@Override
public void restoreState() {
// assert rq.lock.isHeldByCurrentThread();
// reset all attributes of this and current contexts
invalidateContext();
invalidateCurrentContext();
setScratchSurface(config);
// restore the state on the native level
rq.ensureCapacity(4);
buf.putInt(RESTORE_STATE);
rq.flushNow();
}
public static class MTLContextCaps extends ContextCapabilities {
/**
* This cap will only be set if the fbobject system property has been
* enabled and we are able to create an FBO with depth buffer.
*/
@Native
public static final int CAPS_EXT_FBOBJECT =
(CAPS_RT_TEXTURE_ALPHA | CAPS_RT_TEXTURE_OPAQUE);
/** 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
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);
/** Indicates the presence of the GL_ARB_texture_rectangle extension. */
@Native
static final int CAPS_EXT_TEXRECT = (FIRST_PRIVATE_CAP << 4);
/** Indicates the presence of the GL_NV_texture_barrier extension. */
@Native
static final int CAPS_EXT_TEXBARRIER = (FIRST_PRIVATE_CAP << 5);
public MTLContextCaps(int caps, String adapterId) {
super(caps, adapterId);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
if ((caps & CAPS_EXT_FBOBJECT) != 0) {
sb.append("CAPS_EXT_FBOBJECT|");
}
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|");
}
if ((caps & CAPS_EXT_TEXRECT) != 0) {
sb.append("CAPS_EXT_TEXRECT|");
}
if ((caps & CAPS_EXT_TEXBARRIER) != 0) {
sb.append("CAPS_EXT_TEXBARRIER|");
}
return sb.toString();
}
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.loops.SurfaceType;
import sun.java2d.loops.TransformBlit;
import sun.java2d.pipe.DrawImage;
import java.awt.*;
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,439 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.awt.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 sun.lwawt.macosx.CPlatformView;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.io.File;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import static sun.java2d.opengl.OGLSurfaceData.TEXTURE;
import static sun.java2d.pipe.hw.AccelSurface.RT_TEXTURE;
import static sun.java2d.pipe.hw.ContextCapabilities.*;
public final class MTLGraphicsConfig extends CGraphicsConfig
implements AccelGraphicsConfig, SurfaceManager.ProxiedGraphicsConfig
{
//private static final int kOpenGLSwapInterval =
// RuntimeOptions.getCurrentOptions().OpenGLSwapInterval;
private static final int kOpenGLSwapInterval = 0; // TODO
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 int pixfmt;
private BufferCapabilities bufferCaps;
private long pConfigInfo;
private ContextCapabilities mtlCaps;
private MTLContext context;
private final Object disposerReferent = new Object();
private final int maxTextureSize;
private static native boolean initMTL();
private static native long getMTLConfigInfo(int displayID, String mtlShadersLib);
/**
* Returns GL_MAX_TEXTURE_SIZE from the shared opengl context. Must be
* called under OGLRQ lock, because this method change current context.
*
* @return GL_MAX_TEXTURE_SIZE
*/
private static native int nativeGetMaxTextureSize();
private static final HashMap<Long, Integer> pGCRefCounts = new HashMap<>();
static {
mtlAvailable = initMTL();
}
private MTLGraphicsConfig(CGraphicsDevice device, int pixfmt,
long configInfo, int maxTextureSize,
ContextCapabilities mtlCaps) {
super(device);
this.pixfmt = pixfmt;
this.pConfigInfo = configInfo;
this.mtlCaps = mtlCaps;
this.maxTextureSize = maxTextureSize;
context = new MTLContext(MTLRenderQueue.getInstance(), this);
refPConfigInfo(pConfigInfo);
// 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, int pixfmt)
{
if (!mtlAvailable) {
return null;
}
long cfginfo = 0;
int textureSize = 0;
final String[] ids = new String[1];
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
// getCGLConfigInfo() 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();
// 7160609: GL still fails to create a square texture of this
// size. Half should be safe enough.
// Explicitly not support a texture more than 2^14, see 8010999.
textureSize = textureSize <= 16384 ? textureSize / 2 : 8192;
MTLContext.setScratchSurface(cfginfo);
rq.flushAndInvokeNow(() -> {
ids[0] = MTLContext.getMTLIdString();
});
}
} finally {
rq.unlock();
}
if (cfginfo == 0) {
return null;
}
ContextCapabilities caps = new MTLContext.MTLContextCaps(
CAPS_PS30 | CAPS_PS20 | CAPS_RT_PLAIN_ALPHA |
CAPS_RT_TEXTURE_ALPHA | CAPS_RT_TEXTURE_OPAQUE |
CAPS_MULTITEXTURE | CAPS_TEXNONPOW2 | CAPS_TEXNONSQUARE,
ids[0]);
return new MTLGraphicsConfig(device, pixfmt, cfginfo, textureSize, caps);
}
static void refPConfigInfo(long pConfigInfo) {
synchronized (pGCRefCounts) {
Integer count = pGCRefCounts.get(pConfigInfo);
if (count == null) {
count = 1;
}
else {
count++;
}
pGCRefCounts.put(pConfigInfo, count);
}
}
static void deRefPConfigInfo(long pConfigInfo) {
synchronized (pGCRefCounts) {
Integer count = pGCRefCounts.get(pConfigInfo);
if (count != null) {
count--;
pGCRefCounts.put(pConfigInfo, count);
if (count == 0) {
MTLRenderQueue.disposeGraphicsConfig(pConfigInfo);
pGCRefCounts.remove(pConfigInfo);
}
}
}
}
/**
* 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) {
deRefPConfigInfo(pCfgInfo);
pCfgInfo = 0;
}
}
}
// TODO: CGraphicsConfig doesn't implement displayChanged() yet
//@Override
public synchronized void displayChanged() {
//super.displayChanged();
// the context could hold a reference to a MTLSurfaceData, which in
// turn has a reference back to this MTLGraphicsConfig, so in order
// for this instance to be disposed we need to break the connection
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
MTLContext.invalidateCurrentContext();
} finally {
rq.unlock();
}
}
@Override
public String toString() {
return ("MTLGraphicsConfig[" + getDevice().getIDstring() +
",pixfmt="+pixfmt+"]");
}
@Override
public SurfaceData createSurfaceData(CPlatformView pView) {
return MTLSurfaceData.createData(pView);
}
@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) {
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,124 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.NullSurfaceData;
import sun.java2d.SurfaceData;
import sun.lwawt.LWWindowPeer;
import sun.lwawt.macosx.CFRetainedResource;
import java.awt.*;
public class MTLLayer extends CFRetainedResource {
private native long nativeCreateLayer();
private static native void nativeSetScale(long layerPtr, double scale);
private static native void validate(long layerPtr, MTLSurfaceData cglsd);
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 drawInCGLContext callback)
MTLGraphicsConfig gc = (MTLGraphicsConfig)getGraphicsConfiguration();
surfaceData = gc.createSurfaceData(this);
setScale(gc.getDevice().getScaleFactor());
// 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 cglsd) {
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
execute(ptr -> validate(ptr, cglsd));
} finally {
rq.unlock();
}
}
@Override
public void dispose() {
// break the connection between the layer and the buffer
validate(null);
super.dispose();
}
private void setScale(final int _scale) {
if (scale != _scale) {
scale = _scale;
execute(ptr -> nativeSetScale(ptr, scale));
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.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.*;
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 oglDst = (MTLSurfaceData)dstData;
MTLContext.validateContext(oglDst, oglDst,
clip, comp, null, null, null,
MTLContext.NO_CONTEXT_FLAGS);
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.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.*;
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,208 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.loops.CompositeType;
import java.awt.*;
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 (or the
* GL_ARB_texture_non_power_of_two extension is present)
* - the texture image can be (or is already) cached in an OpenGL
* texture object
*/
@Override
boolean isPaintValid(SunGraphics2D sg2d) {
TexturePaint paint = (TexturePaint)sg2d.paint;
MTLSurfaceData dstData = (MTLSurfaceData)sg2d.surfaceData;
BufferedImage bi = paint.getImage();
// see if texture-non-pow2 extension is available
if (!dstData.isTexNonPow2Available()) {
int imgw = bi.getWidth();
int imgh = bi.getHeight();
// verify that the texture image dimensions are pow2
if ((imgw & (imgw - 1)) != 0 || (imgh & (imgh - 1)) != 0) {
return false;
}
}
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
// OpenGL 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 oglData = (MTLSurfaceData)srcData;
if (oglData.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) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.awt.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;
/**
* OGL-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 OGL pipeline, but only if the OGL 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 OGL 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 OGL 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,223 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.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.*;
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,885 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.awt.SunHints;
import sun.awt.image.PixelConverter;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.SurfaceDataProxy;
import sun.java2d.loops.CompositeType;
import sun.java2d.loops.GraphicsPrimitive;
import sun.java2d.loops.MaskFill;
import sun.java2d.loops.SurfaceType;
import sun.java2d.pipe.ParallelogramPipe;
import sun.java2d.pipe.PixelToParallelogramConverter;
import sun.java2d.pipe.RenderBuffer;
import sun.java2d.pipe.TextPipe;
import sun.java2d.pipe.hw.AccelSurface;
import sun.lwawt.macosx.CPlatformView;
import java.awt.*;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_LCD_SHADER;
import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_TEXRECT;
import static sun.java2d.pipe.BufferedOpCodes.FLUSH_SURFACE;
import static sun.java2d.pipe.BufferedOpCodes.SWAP_BUFFERS;
import static sun.java2d.pipe.hw.ContextCapabilities.*;
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;
/** This will be true if the fbobject system property has been enabled. */
private static boolean isFBObjectEnabled;
/** This will be true if the lcdshader system property has been enabled.*/
private static boolean isLCDShaderEnabled;
/** This will be true if the biopshader system property has been enabled.*/
private static boolean isBIOpShaderEnabled;
/** This will be true if the gradshader system property has been enabled.*/
private static boolean isGradShaderEnabled;
static {
if (!GraphicsEnvironment.isHeadless()) {
// fbobject currently enabled by default; use "false" to disable
String fbo = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"java2d.metal.fbobject"));
isFBObjectEnabled = !"false".equals(fbo);
// lcdshader currently enabled by default; use "false" to disable
String lcd = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"java2d.metal.lcdshader"));
isLCDShaderEnabled = !"false".equals(lcd);
// biopshader currently enabled by default; use "false" to disable
String biop = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"java2d.metal.biopshader"));
isBIOpShaderEnabled = !"false".equals(biop);
// gradshader currently enabled by default; use "false" to disable
String grad = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"java2d.metal.gradshader"));
isGradShaderEnabled = !"false".equals(grad);
MTLRenderQueue rq = MTLRenderQueue.getInstance();
mtlImagePipe = new MTLDrawImage();
mtlTextPipe = new MTLTextRenderer(rq);
mtlRenderPipe = new MTLRenderer(rq);
if (GraphicsPrimitive.tracingEnabled()) {
mtlTextPipe = mtlTextPipe.traceWrap();
//The wrapped mtlRenderPipe will wrap the AA pipe as well...
//mtlAAPgramPipe = mtlRenderPipe.traceWrap();
}
mtlAAPgramPipe = mtlRenderPipe.getAAParallelogramPipe();
mtlTxRenderPipe =
new PixelToParallelogramConverter(mtlRenderPipe,
mtlRenderPipe,
1.0, 0.25, true);
MTLBlitLoops.register();
MTLMaskFill.register();
MTLMaskBlit.register();
}
}
protected final int scale;
protected final int width;
protected final int height;
protected CPlatformView pView;
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 OpenGL
* surface type constant (e.g. TEXTURE -> MTLTexture).
*/
private static SurfaceType getCustomSurfaceType(int oglType) {
switch (oglType) {
case TEXTURE:
return MTLTexture;
case RT_TEXTURE:
return MTLSurfaceRTT;
default:
return MTLSurface;
}
}
static void swapBuffers(long window) {
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(12, 4);
buf.putInt(SWAP_BUFFERS);
buf.putLong(window);
rq.flushNow();
} finally {
rq.unlock();
}
}
native void validate(int xoff, int yoff, int width, int height, boolean isOpaque);
private native void initOps(long pConfigInfo, long pPeerData, long layerPtr,
int xoff, int yoff, boolean isOpaque);
protected MTLSurfaceData(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;
}
protected MTLSurfaceData(CPlatformView pView, MTLGraphicsConfig gc,
ColorModel cm, int type, int width, int height)
{
this(gc, cm, type, width, height);
this.pView = pView;
this.graphicsConfig = gc;
long pConfigInfo = gc.getNativeConfigInfo();
long pPeerData = 0L;
boolean isOpaque = true;
if (pView != null) {
pPeerData = pView.getAWTView();
isOpaque = pView.isOpaque();
}
MTLGraphicsConfig.refPConfigInfo(pConfigInfo);
initOps(pConfigInfo, pPeerData, 0, 0, 0, isOpaque);
}
protected MTLSurfaceData(MTLLayer layer, MTLGraphicsConfig gc,
ColorModel cm, int type, int width, int height)
{
this(gc, cm, type, width, height);
this.graphicsConfig = gc;
long pConfigInfo = gc.getNativeConfigInfo();
long layerPtr = 0L;
boolean isOpaque = true;
if (layer != null) {
layerPtr = layer.getPointer();
isOpaque = layer.isOpaque();
}
MTLGraphicsConfig.refPConfigInfo(pConfigInfo);
initOps(pConfigInfo, 0, layerPtr, 0, 0, isOpaque);
}
@Override //SurfaceData
public GraphicsConfiguration getDeviceConfiguration() {
return graphicsConfig;
}
/**
* Creates a SurfaceData object representing the primary (front) buffer of
* an on-screen Window.
*/
public static MTLWindowSurfaceData createData(CPlatformView pView) {
MTLGraphicsConfig gc = getGC(pView);
return new MTLWindowSurfaceData(pView, gc);
}
/**
* 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 = getGC(layer);
Rectangle r = layer.getBounds();
return new MTLLayerSurfaceData(layer, gc, r.width, r.height);
}
/**
* Creates a SurfaceData object representing the back buffer of a
* double-buffered on-screen Window.
*/
public static MTLOffScreenSurfaceData createData(CPlatformView pView,
Image image, int type) {
MTLGraphicsConfig gc = getGC(pView);
Rectangle r = pView.getBounds();
if (type == FLIP_BACKBUFFER) {
return new MTLOffScreenSurfaceData(pView, gc, r.width, r.height,
image, gc.getColorModel(), FLIP_BACKBUFFER);
} else {
return new MTLVSyncOffScreenSurfaceData(pView, gc, r.width,
r.height, image, gc.getColorModel(), type);
}
}
/**
* Creates a SurfaceData object representing an off-screen buffer (either a
* FBO or Texture).
*/
public static MTLOffScreenSurfaceData createData(MTLGraphicsConfig gc,
int width, int height, ColorModel cm, Image image, int type) {
return new MTLOffScreenSurfaceData(null, gc, width, height, image, cm,
type);
}
public static MTLGraphicsConfig getGC(CPlatformView pView) {
if (pView != null) {
return (MTLGraphicsConfig)pView.getGraphicsConfiguration();
} else {
// REMIND: this should rarely (never?) happen, but what if
// default config is not CGL?
GraphicsEnvironment env = GraphicsEnvironment
.getLocalGraphicsEnvironment();
GraphicsDevice gd = env.getDefaultScreenDevice();
return (MTLGraphicsConfig) gd.getDefaultConfiguration();
}
}
public static MTLGraphicsConfig getGC(MTLLayer layer) {
return (MTLGraphicsConfig)layer.getGraphicsConfiguration();
}
public void validate() {
// Overridden in MTLWindowSurfaceData below
}
@Override
public double getDefaultScaleX() {
return scale;
}
@Override
public double getDefaultScaleY() {
return scale;
}
protected native void clearWindow();
protected native boolean initTexture(long pData,
boolean isOpaque, boolean texNonPow2,
boolean texRect,
int width, int height);
protected native boolean initRTexture(long pData,
boolean isOpaque, boolean texNonPow2,
boolean texRect,
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, isTexNonPow2Available(),
isTexRectAvailable(),
width, height);
break;
case RT_TEXTURE:
success = initRTexture(getNativeOps(),
isOpaque, isTexNonPow2Available(),
isTexRectAvailable(),
width, height);
break;
case FLIP_BACKBUFFER:
success = initFlipBackbuffer(getNativeOps());
break;
default:
break;
}
if (!success) {
throw new OutOfMemoryError("can't create offscreen surface");
}
}
/**
* Initializes the appropriate OpenGL offscreen surface based on the value
* of the type parameter. If the surface creation fails for any reason,
* an OutOfMemoryError will be thrown.
*/
protected void initSurface(final int width, final int height) {
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
switch (type) {
case TEXTURE:
case RT_TEXTURE:
// need to make sure the context is current before
// creating the texture or fbobject
MTLContext.setScratchSurface(graphicsConfig);
break;
default:
break;
}
rq.flushAndInvokeNow(new Runnable() {
public void run() {
initSurfaceNow(width, height);
}
});
} finally {
rq.unlock();
}
}
/**
* Returns the MTLContext for the GraphicsConfig associated with this
* surface.
*/
public final MTLContext getContext() {
return graphicsConfig.getContext();
}
/**
* Returns the 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 OGL text rendering code
* and remove the above restrictions, but that would require significantly
* more code just to support a few uncommon cases.
*/
public boolean canRenderLCDText(SunGraphics2D sg2d) {
return
graphicsConfig.isCapPresent(CAPS_EXT_LCD_SHADER) &&
sg2d.surfaceData.getTransparency() == Transparency.OPAQUE &&
sg2d.paintState <= SunGraphics2D.PAINT_OPAQUECOLOR &&
(sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY ||
(sg2d.compositeState <= SunGraphics2D.COMP_ALPHA && canHandleComposite(sg2d.composite)));
}
private boolean canHandleComposite(Composite c) {
if (c instanceof AlphaComposite) {
AlphaComposite ac = (AlphaComposite)c;
return ac.getRule() == AlphaComposite.SRC_OVER && ac.getAlpha() >= 1f;
}
return false;
}
public void validatePipe(SunGraphics2D sg2d) {
TextPipe textpipe;
boolean validated = false;
// MTLTextRenderer handles both AA and non-AA text, but
// only works with the following modes:
// (Note: For LCD text we only enter this code path if
// canRenderLCDText() has already validated that the mode is
// CompositeType.SrcNoEa (opaque color), which will be subsumed
// by the CompositeType.SrcNoEa (any color) test below.)
if (/* CompositeType.SrcNoEa (any color) */
(sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY &&
sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) ||
/* CompositeType.SrcOver (any color) */
(sg2d.compositeState == SunGraphics2D.COMP_ALPHA &&
sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR &&
(((AlphaComposite)sg2d.composite).getRule() ==
AlphaComposite.SRC_OVER)) ||
/* CompositeType.Xor (any color) */
(sg2d.compositeState == SunGraphics2D.COMP_XOR &&
sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR))
{
textpipe = mtlTextPipe;
} else {
// do this to initialize textpipe correctly; we will attempt
// to override the non-text pipes below
super.validatePipe(sg2d);
textpipe = sg2d.textpipe;
validated = true;
}
PixelToParallelogramConverter txPipe = null;
MTLRenderer nonTxPipe = null;
if (sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) {
if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) {
if (sg2d.compositeState <= SunGraphics2D.COMP_XOR) {
txPipe = mtlTxRenderPipe;
nonTxPipe = mtlRenderPipe;
}
} else if (sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) {
if (MTLPaints.isValid(sg2d)) {
txPipe = mtlTxRenderPipe;
nonTxPipe = mtlRenderPipe;
}
// custom paints handled by super.validatePipe() below
}
} else {
if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) {
if (graphicsConfig.isCapPresent(CAPS_PS30) &&
(sg2d.imageComp == CompositeType.SrcOverNoEa ||
sg2d.imageComp == CompositeType.SrcOver))
{
if (!validated) {
super.validatePipe(sg2d);
validated = true;
}
PixelToParallelogramConverter aaConverter =
new PixelToParallelogramConverter(sg2d.shapepipe,
mtlAAPgramPipe,
1.0/8.0, 0.499,
false);
sg2d.drawpipe = aaConverter;
sg2d.fillpipe = aaConverter;
sg2d.shapepipe = aaConverter;
} else if (sg2d.compositeState == SunGraphics2D.COMP_XOR) {
// install the solid pipes when AA and XOR are both enabled
txPipe = mtlTxRenderPipe;
nonTxPipe = mtlRenderPipe;
}
}
// other cases handled by super.validatePipe() below
}
if (txPipe != null) {
if (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) {
sg2d.drawpipe = txPipe;
sg2d.fillpipe = txPipe;
} else if (sg2d.strokeState != SunGraphics2D.STROKE_THIN) {
sg2d.drawpipe = txPipe;
sg2d.fillpipe = nonTxPipe;
} else {
sg2d.drawpipe = nonTxPipe;
sg2d.fillpipe = nonTxPipe;
}
// Note that we use the transforming pipe here because it
// will examine the shape and possibly perform an optimized
// operation if it can be simplified. The simplifications
// will be valid for all STROKE and TRANSFORM types.
sg2d.shapepipe = txPipe;
} else {
if (!validated) {
super.validatePipe(sg2d);
}
}
// install the text pipe based on our earlier decision
sg2d.textpipe = textpipe;
// always override the image pipe with the specialized OGL pipe
sg2d.imagepipe = mtlImagePipe;
}
@Override
protected MaskFill getMaskFill(SunGraphics2D sg2d) {
if (sg2d.paintState > SunGraphics2D.PAINT_ALPHACOLOR) {
/*
* We can only accelerate non-Color MaskFill operations if
* all of the following conditions hold true:
* - there is an implementation for the given paintState
* - the current Paint can be accelerated for this destination
* - multitexturing is available (since we need to modulate
* the alpha mask texture with the paint texture)
*
* In all other cases, we return null, in which case the
* validation code will choose a more general software-based loop.
*/
if (!MTLPaints.isValid(sg2d) ||
!graphicsConfig.isCapPresent(CAPS_MULTITEXTURE))
{
return null;
}
}
return super.getMaskFill(sg2d);
}
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();
}
}
/**
* Returns true if OpenGL textures can have non-power-of-two dimensions
* when using the basic GL_TEXTURE_2D target.
*/
boolean isTexNonPow2Available() {
return graphicsConfig.isCapPresent(CAPS_TEXNONPOW2);
}
/**
* Returns true if OpenGL textures can have non-power-of-two dimensions
* when using the GL_TEXTURE_RECTANGLE_ARB target (only available when the
* GL_ARB_texture_rectangle extension is present).
*/
boolean isTexRectAvailable() {
return graphicsConfig.isCapPresent(CAPS_EXT_TEXRECT);
}
/**
* Returns true if the surface is an on-screen window surface or
* a FBO texture attached to an on-screen CALayer.
*
* Needed by Mac OS X port.
*/
public boolean isOnScreen() {
return getType() == WINDOW;
}
private native int getTextureTarget(long pData);
private native int getTextureID(long pData);
/**
* If this surface is backed by a texture object, returns the target
* for that texture (either GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB).
* Otherwise, this method will return zero.
*/
public final int getTextureTarget() {
return getTextureTarget(getNativeOps());
}
/**
* If this surface is backed by a texture object, returns the texture ID
* for that texture.
* Otherwise, this method will return zero.
*/
public final int getTextureID() {
return getTextureID(getNativeOps());
}
/**
* Returns native resource of specified {@code resType} associated with
* this surface.
*
* Specifically, for {@code 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 getTextureID();
}
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();
}
}
public static class MTLWindowSurfaceData extends MTLSurfaceData {
public MTLWindowSurfaceData(CPlatformView pView,
MTLGraphicsConfig gc) {
super(pView, gc, gc.getColorModel(), WINDOW, 0, 0);
}
@Override
public SurfaceData getReplacement() {
return pView.getSurfaceData();
}
@Override
public Rectangle getBounds() {
Rectangle r = pView.getBounds();
return new Rectangle(0, 0, r.width, r.height);
}
/**
* Returns destination Component associated with this SurfaceData.
*/
@Override
public Object getDestination() {
return pView.getDestination();
}
public void validate() {
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
rq.flushAndInvokeNow(() -> {
Rectangle peerBounds = pView.getBounds();
validate(0, 0, peerBounds.width, peerBounds.height, pView.isOpaque());
});
} finally {
rq.unlock();
}
}
@Override
public void invalidate() {
super.invalidate();
clearWindow();
}
}
/**
* 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 MTLLayer layer;
public 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 Rectangle getBounds() {
return new Rectangle(width, height);
}
@Override
public Object getDestination() {
return layer.getDestination();
}
@Override
public int getTransparency() {
return layer.getTransparency();
}
@Override
public void invalidate() {
super.invalidate();
clearWindow();
}
}
/**
* A surface which implements a v-synced flip back-buffer with COPIED
* FlipContents.
*
* This surface serves as a back-buffer to the outside world, while it is
* actually an offscreen surface. When the BufferStrategy this surface
* belongs to is showed, it is first copied to the real private
* FLIP_BACKBUFFER, which is then flipped.
*/
public static class MTLVSyncOffScreenSurfaceData extends
MTLOffScreenSurfaceData {
private MTLOffScreenSurfaceData flipSurface;
public MTLVSyncOffScreenSurfaceData(CPlatformView pView,
MTLGraphicsConfig gc, int width, int height, Image image,
ColorModel cm, int type) {
super(pView, gc, width, height, image, cm, type);
flipSurface = MTLSurfaceData.createData(pView, image,
FLIP_BACKBUFFER);
}
public SurfaceData getFlipSurface() {
return flipSurface;
}
@Override
public void flush() {
flipSurface.flush();
super.flush();
}
}
public static class MTLOffScreenSurfaceData extends MTLSurfaceData {
private Image offscreenImage;
public MTLOffScreenSurfaceData(CPlatformView pView,
MTLGraphicsConfig gc, int width, int height, Image image,
ColorModel cm, int type) {
super(pView, gc, cm, type, width, height);
offscreenImage = image;
initSurface(this.width, this.height);
}
@Override
public SurfaceData getReplacement() {
return restoreContents(offscreenImage);
}
@Override
public Rectangle getBounds() {
if (type == FLIP_BACKBUFFER) {
Rectangle r = pView.getBounds();
return new Rectangle(0, 0, r.width, r.height);
} else {
return new Rectangle(width, height);
}
}
/**
* Returns destination Image associated with this SurfaceData.
*/
@Override
public Object getDestination() {
return offscreenImage;
}
}
// additional cleanup
private static native void destroyCGLContext(long ctx);
public static void destroyOGLContext(long ctx) {
if (ctx != 0L) {
destroyCGLContext(ctx);
}
}
public static void dispose(long pData, long pConfigInfo) {
MTLGraphicsConfig.deRefPConfigInfo(pConfigInfo);
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.SurfaceData;
import sun.java2d.SurfaceDataProxy;
import sun.java2d.loops.CompositeType;
import java.awt.*;
/**
* The proxy class contains the logic for when to replace a
* SurfaceData with a cached OGL 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 oglgc;
int transparency;
public MTLSurfaceDataProxy(MTLGraphicsConfig oglgc, int transparency) {
this.oglgc = oglgc;
this.transparency = transparency;
}
@Override
public SurfaceData validateSurfaceData(SurfaceData srcData,
SurfaceData cachedData,
int w, int h)
{
if (cachedData == null) {
try {
cachedData = oglgc.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, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.font.GlyphList;
import sun.java2d.SunGraphics2D;
import sun.java2d.loops.GraphicsPrimitive;
import sun.java2d.pipe.BufferedTextPipe;
import sun.java2d.pipe.RenderQueue;
import java.awt.*;
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 oglDst = (MTLSurfaceData)sg2d.surfaceData;
MTLContext.validateContext(oglDst, oglDst,
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,326 @@
/*
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.pipe.Region;
import java.awt.*;
/**
* This class contains a number of static utility methods that may be
* called (via reflection) by a third-party library in order
* to interoperate with the metal-based Java 2D pipeline.
*
*/
class MTLUtilities {
/**
* These OGL-specific surface type constants are the same as those
* defined in the MTLSurfaceData class and are duplicated here so that
* clients of this API can access them more easily via reflection.
*/
public static final int UNDEFINED = MTLSurfaceData.UNDEFINED;
public static final int WINDOW = MTLSurfaceData.WINDOW;
public static final int TEXTURE = MTLSurfaceData.TEXTURE;
public static final int FLIP_BACKBUFFER = MTLSurfaceData.FLIP_BACKBUFFER;
public static final int RT_TEXTURE = MTLSurfaceData.RT_TEXTURE;
private MTLUtilities() {
}
/**
* Returns true if the current thread is the OGL QueueFlusher thread.
*/
public static boolean isQueueFlusherThread() {
return MTLRenderQueue.isQueueFlusherThread();
}
/**
* Invokes the given Runnable on the MTL QueueFlusher thread with the
* MTL context corresponding to the given Graphics object made
* current. It is legal for MTL code executed in the given
* Runnable to change the current MTL context; it will be reset
* once the Runnable completes. No guarantees are made as to the
* state of the MTL context of the Graphics object; for
*
* In order to avoid deadlock, it is important that the given Runnable
* does not attempt to acquire the AWT lock, as that will be handled
* automatically as part of the {@code rq.flushAndInvokeNow()} step.
*
* @param g the Graphics object for the corresponding destination surface;
* if null, the step making a context current to the destination surface
* will be skipped
* @param r the action to be performed on the QFT; cannot be null
* @return true if the operation completed successfully, or false if
* there was any problem making a context current to the surface
* associated with the given Graphics object
*/
public static boolean invokeWithMTLContextCurrent(Graphics g, Runnable r) {
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
if (g != null) {
if (!(g instanceof SunGraphics2D)) {
return false;
}
SurfaceData sData = ((SunGraphics2D)g).surfaceData;
if (!(sData instanceof MTLSurfaceData)) {
return false;
}
// make a context current to the destination surface
MTLContext.validateContext((MTLSurfaceData)sData);
}
// invoke the given runnable on the QFT
rq.flushAndInvokeNow(r);
// invalidate the current context so that the next time we render
// with Java 2D, the context state will be completely revalidated
MTLContext.invalidateCurrentContext();
} finally {
rq.unlock();
}
return true;
}
/**
* Invokes the given Runnable on the MTL QueueFlusher thread with the
* "shared" MTL context (corresponding to the given
* GraphicsConfiguration object) made current. This method is typically
* used when the Runnable needs a current context to complete its
* operation, but does not require that the context be made current to
* a particular surface. For example, an application may call this
* method so that the given Runnable can query the OpenGL capabilities
* of the given GraphicsConfiguration, without making a context current
* to a dummy surface (or similar hacky techniques).
*
* In order to avoid deadlock, it is important that the given Runnable
* does not attempt to acquire the AWT lock, as that will be handled
* automatically as part of the {@code rq.flushAndInvokeNow()} step.
*
* @param config the GraphicsConfiguration object whose "shared"
* context will be made current during this operation; if this value is
* null or if MTL is not enabled for the GraphicsConfiguration, this
* method will return false
* @param r the action to be performed on the QFT; cannot be null
* @return true if the operation completed successfully, or false if
* there was any problem making the shared context current
*/
public static boolean
invokeWithMTLSharedContextCurrent(GraphicsConfiguration config,
Runnable r)
{
if (!(config instanceof MTLGraphicsConfig)) {
return false;
}
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
// make the "shared" context current for the given GraphicsConfig
MTLContext.setScratchSurface((MTLGraphicsConfig)config);
// invoke the given runnable on the QFT
rq.flushAndInvokeNow(r);
// invalidate the current context so that the next time we render
// with Java 2D, the context state will be completely revalidated
MTLContext.invalidateCurrentContext();
} finally {
rq.unlock();
}
return true;
}
/**
* Returns the Rectangle describing the MTL viewport on the
* Java 2D surface associated with the given Graphics object and
* component width and height. When a third-party library is
* performing MTL rendering directly into the visible region of
* the associated surface, this viewport helps the application
* position the MTL output correctly on that surface.
*
* Note that the x/y values in the returned Rectangle object represent
* the lower-left corner of the viewport region, relative to the
* lower-left corner of the given surface.
*
* @param g the Graphics object for the corresponding destination surface;
* cannot be null
* @param componentWidth width of the component to be painted
* @param componentHeight height of the component to be painted
* @return a Rectangle describing the MTL viewport for the given
* destination surface and component dimensions, or null if the given
* Graphics object is invalid
*/
public static Rectangle getMTLViewport(Graphics g,
int componentWidth,
int componentHeight)
{
if (!(g instanceof SunGraphics2D)) {
return null;
}
SunGraphics2D sg2d = (SunGraphics2D)g;
SurfaceData sData = sg2d.surfaceData;
// this is the upper-left origin of the region to be painted,
// relative to the upper-left origin of the surface
// (in Java2D coordinates)
int x0 = sg2d.transX;
int y0 = sg2d.transY;
// this is the lower-left origin of the region to be painted,
// relative to the lower-left origin of the surface
// (in OpenGL coordinates)
Rectangle surfaceBounds = sData.getBounds();
int x1 = x0;
int y1 = surfaceBounds.height - (y0 + componentHeight);
return new Rectangle(x1, y1, componentWidth, componentHeight);
}
/**
* Returns the Rectangle describing the MTL scissor box on the
* Java 2D surface associated with the given Graphics object. When a
* third-party library is performing MTL rendering directly
* into the visible region of the associated surface, this scissor box
* must be set to avoid drawing over existing rendering results.
*
* Note that the x/y values in the returned Rectangle object represent
* the lower-left corner of the scissor region, relative to the
* lower-left corner of the given surface.
*
* @param g the Graphics object for the corresponding destination surface;
* cannot be null
* @return a Rectangle describing the MTL scissor box for the given
* Graphics object and corresponding destination surface, or null if the
* given Graphics object is invalid or the clip region is non-rectangular
*/
public static Rectangle getOGLScissorBox(Graphics g) {
if (!(g instanceof SunGraphics2D)) {
return null;
}
SunGraphics2D sg2d = (SunGraphics2D)g;
SurfaceData sData = sg2d.surfaceData;
Region r = sg2d.getCompClip();
if (!r.isRectangular()) {
// caller probably doesn't know how to handle shape clip
// appropriately, so just return null (Swing currently never
// sets a shape clip, but that could change in the future)
return null;
}
// this is the upper-left origin of the scissor box relative to the
// upper-left origin of the surface (in Java 2D coordinates)
int x0 = r.getLoX();
int y0 = r.getLoY();
// this is the width and height of the scissor region
int w = r.getWidth();
int h = r.getHeight();
// this is the lower-left origin of the scissor box relative to the
// lower-left origin of the surface (in OpenGL coordinates)
Rectangle surfaceBounds = sData.getBounds();
int x1 = x0;
int y1 = surfaceBounds.height - (y0 + h);
return new Rectangle(x1, y1, w, h);
}
/**
* Returns an Object identifier for the Java 2D surface associated with
* the given Graphics object. This identifier may be used to determine
* whether the surface has changed since the last invocation of this
* operation, and thereby whether the MTL state corresponding to the
* old surface must be destroyed and recreated.
*
* @param g the Graphics object for the corresponding destination surface;
* cannot be null
* @return an identifier for the surface associated with the given
* Graphics object, or null if the given Graphics object is invalid
*/
public static Object getMTLSurfaceIdentifier(Graphics g) {
if (!(g instanceof SunGraphics2D)) {
return null;
}
return ((SunGraphics2D)g).surfaceData;
}
/**
* Returns one of the MTL-specific surface type constants (defined in
* this class), which describes the surface associated with the given
* Graphics object.
*
* @param g the Graphics object for the corresponding destination surface;
* cannot be null
* @return a constant that describes the surface associated with the
* given Graphics object; if the given Graphics object is invalid (i.e.
* is not associated with an OpenGL surface) this method will return
* {@code MTLUtilities.UNDEFINED}
*/
public static int getMTLSurfaceType(Graphics g) {
if (!(g instanceof SunGraphics2D)) {
return UNDEFINED;
}
SurfaceData sData = ((SunGraphics2D)g).surfaceData;
if (!(sData instanceof MTLSurfaceData)) {
return UNDEFINED;
}
return ((MTLSurfaceData)sData).getType();
}
/**
* Returns the MTL texture target constant (either GL_TEXTURE_2D
* or GL_TEXTURE_RECTANGLE_ARB) for the surface associated with the
* given Graphics object. This method is only useful for those surface
* types that are backed by an MTL texture, namely {@code TEXTURE},
* {@code RT_TEXTURE}, and (on Windows only) {@code PBUFFER}.
*
* @param g the Graphics object for the corresponding destination surface;
* cannot be null
* @return the texture target constant for the surface associated with the
* given Graphics object; if the given Graphics object is invalid (i.e.
* is not associated with an MTL surface), or the associated surface
* is not backed by an OpenGL texture, this method will return zero.
*/
public static int getMTLTextureType(Graphics g) {
if (!(g instanceof SunGraphics2D)) {
return 0;
}
SurfaceData sData = ((SunGraphics2D)g).surfaceData;
if (!(sData instanceof MTLSurfaceData)) {
return 0;
}
return ((MTLSurfaceData)sData).getTextureTarget();
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.awt.AWTAccessor;
import sun.awt.AWTAccessor.ComponentAccessor;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.BackBufferCapsProvider;
import sun.java2d.SurfaceData;
import sun.java2d.opengl.OGLSurfaceData;
import sun.java2d.pipe.hw.ExtendedBufferCapabilities;
import java.awt.*;
import java.awt.image.ColorModel;
import java.awt.peer.ComponentPeer;
import static java.awt.BufferCapabilities.FlipContents.COPIED;
import static sun.java2d.opengl.OGLContext.OGLContextCaps.CAPS_EXT_FBOBJECT;
import static sun.java2d.pipe.hw.ExtendedBufferCapabilities.VSyncType.VSYNC_ON;
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 under the
* following conditions:
* - the image is not bitmask AND the GraphicsConfig supports the FBO
* extension
*/
int transparency = vImg.getTransparency();
MTLGraphicsConfig gc = (MTLGraphicsConfig) vImg.getGraphicsConfig();
accelerationEnabled = true;
//gc.isCapPresent(CAPS_EXT_FBOBJECT)
//&& transparency != Transparency.BITMASK;
}
protected boolean isAccelerationEnabled() {
return accelerationEnabled;
}
/**
* Create a FBO-based SurfaceData object (or init the backbuffer
* of an existing window if this is a double buffered GraphicsConfig)
*/
protected SurfaceData initAcceleratedSurface() {
SurfaceData sData = null;
Component comp = vImg.getComponent();
final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
final ComponentPeer peer = (comp != null) ? acc.getPeer(comp) : null;
try {
boolean createVSynced = false;
boolean forceback = false;
if (context instanceof Boolean) {
forceback = ((Boolean)context).booleanValue();
if (forceback && peer instanceof BackBufferCapsProvider) {
BackBufferCapsProvider provider =
(BackBufferCapsProvider)peer;
BufferCapabilities caps = provider.getBackBufferCaps();
if (caps instanceof ExtendedBufferCapabilities) {
ExtendedBufferCapabilities ebc =
(ExtendedBufferCapabilities)caps;
if (ebc.getVSync() == VSYNC_ON &&
ebc.getFlipContents() == COPIED)
{
createVSynced = true;
forceback = false;
}
}
}
}
if (forceback) {
// peer must be non-null in this case
// TODO: modify parameter to delegate
// sData = MTLSurfaceData.createData(peer, vImg, FLIP_BACKBUFFER);
} else {
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 == OGLSurfaceData.UNDEFINED) {
type = OGLSurfaceData.FBOBJECT;
}
if (createVSynced) {
// TODO: modify parameter to delegate
// sData = MTLSurfaceData.createData(peer, vImg, type);
} else {
sData = MTLSurfaceData.createData(gc,
vImg.getWidth(),
vImg.getHeight(),
cm, vImg, type);
}
}
} catch (NullPointerException ex) {
sData = null;
} catch (OutOfMemoryError er) {
sData = null;
}
return sData;
}
@Override
protected boolean isConfigValid(GraphicsConfiguration gc) {
return ((gc == null) || (gc == vImg.getGraphicsConfig()));
}
@Override
public void initContents() {
if (vImg.getForcedAccelSurfaceType() != OGLSurfaceData.TEXTURE) {
super.initContents();
}
}
}

View File

@@ -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 sun.lwawt.macosx.CPlatformView;
import static sun.java2d.opengl.OGLContext.OGLContextCaps.CAPS_DOUBLEBUFFERED;
@@ -264,8 +265,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

@@ -249,7 +249,7 @@ public abstract class CGLSurfaceData extends OGLSurfaceData {
}
@Override
boolean isOnScreen() {
public boolean isOnScreen() {
return true;
}

View File

@@ -54,9 +54,12 @@ import sun.awt.image.SunVolatileImage;
import sun.awt.image.ToolkitImage;
import sun.java2d.SunGraphics2D;
import sun.java2d.macos.MacOSFlags;
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;
import javax.swing.JComponent;
@@ -1418,7 +1421,8 @@ public abstract class LWComponentPeer<T extends Component, D extends JComponent>
}
protected static final void flushOnscreenGraphics(){
final OGLRenderQueue rq = OGLRenderQueue.getInstance();
RenderQueue rq = MacOSFlags.isMetalEnabled() ?
MTLRenderQueue.getInstance() : OGLRenderQueue.getInstance();
rq.lock();
try {
rq.flushNow();

View File

@@ -33,6 +33,9 @@ import java.util.concurrent.atomic.AtomicReference;
import sun.awt.CGraphicsConfig;
import sun.awt.CGraphicsEnvironment;
import sun.java2d.macos.MacOSFlags;
import sun.java2d.metal.MTLLayer;
import sun.java2d.metal.MTLSurfaceData;
import sun.lwawt.LWWindowPeer;
import sun.java2d.SurfaceData;
@@ -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() {
@@ -59,7 +62,7 @@ public class CPlatformView extends CFRetainedResource {
initializeBase(peer, responder);
if (!LWCToolkit.getSunAwtDisableCALayers()) {
this.windowLayer = createCGLayer();
this.windowLayer = MacOSFlags.isMetalEnabled()? createMTLLayer() : createCGLayer();
}
setPtr(nativeCreateView(0, 0, 0, 0, getWindowLayerPtr()));
}
@@ -68,6 +71,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;
@@ -107,7 +115,10 @@ public class CPlatformView extends CFRetainedResource {
// ----------------------------------------------------------------------
public SurfaceData replaceSurfaceData() {
if (!LWCToolkit.getSunAwtDisableCALayers()) {
surfaceData = windowLayer.replaceSurfaceData();
surfaceData = (MacOSFlags.isMetalEnabled()) ?
((MTLLayer)windowLayer).replaceSurfaceData() :
((CGLLayer)windowLayer).replaceSurfaceData()
;
} else {
if (surfaceData == null) {
CGraphicsConfig graphicsConfig = (CGraphicsConfig)getGraphicsConfiguration();
@@ -121,7 +132,11 @@ public class CPlatformView extends CFRetainedResource {
private void validateSurface() {
if (surfaceData != null) {
((CGLSurfaceData)surfaceData).validate();
if (MacOSFlags.isMetalEnabled()) {
((MTLSurfaceData) surfaceData).validate();
} else {
((CGLSurfaceData) surfaceData).validate();
}
}
}
@@ -143,7 +158,9 @@ public class CPlatformView extends CFRetainedResource {
public long getWindowLayerPtr() {
if (!LWCToolkit.getSunAwtDisableCALayers()) {
return windowLayer.getPointer();
return MacOSFlags.isMetalEnabled() ?
((MTLLayer)windowLayer).getPointer() :
((CGLLayer)windowLayer).getPointer();
} else {
return 0;
}

View File

@@ -62,6 +62,7 @@ import sun.awt.AWTAccessor;
import sun.awt.AWTAccessor.ComponentAccessor;
import sun.awt.AWTAccessor.WindowAccessor;
import sun.java2d.SurfaceData;
import sun.java2d.metal.MTLSurfaceData;
import sun.java2d.opengl.CGLSurfaceData;
import sun.lwawt.LWLightweightFramePeer;
import sun.lwawt.LWToolkit;
@@ -1056,6 +1057,8 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
SurfaceData surfaceData = getSurfaceData();
if (surfaceData instanceof CGLSurfaceData) {
((CGLSurfaceData)surfaceData).validate();
} else if (surfaceData instanceof MTLSurfaceData) {
((MTLSurfaceData)surfaceData).validate();
}
}

View File

@@ -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

@@ -108,6 +108,8 @@ import sun.awt.LightweightFrame;
import sun.awt.SunToolkit;
import sun.awt.datatransfer.DataTransferer;
import sun.awt.util.ThreadGroupUtils;
import sun.java2d.macos.MacOSFlags;
import sun.java2d.metal.MTLRenderQueue;
import sun.java2d.opengl.OGLRenderQueue;
import sun.lwawt.LWComponentPeer;
import sun.lwawt.LWCursorManager;
@@ -476,7 +478,11 @@ public final class LWCToolkit extends LWToolkit {
@Override
public void sync() {
// flush the OGL pipeline (this is a no-op if OGL is not enabled)
OGLRenderQueue.sync();
if (MacOSFlags.isMetalEnabled()) {
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

@@ -24,7 +24,8 @@
*/
#import <Cocoa/Cocoa.h>
#import <Metal/Metal.h>
#import <MetalKit/MetalKit.h>
#import "CDragSource.h"
#import "CDropTarget.h"

View File

@@ -37,6 +37,8 @@
#import <Carbon/Carbon.h>
#import <JavaNativeFoundation/JavaNativeFoundation.h>
jboolean metalEnabled = JNI_FALSE;
@interface AWTView()
@property (retain) CDropTarget *_dropTarget;
@property (retain) CDragSource *_dragSource;
@@ -52,6 +54,8 @@
//#define IM_DEBUG TRUE
//#define EXTRA_DEBUG
#define METAL_DEBUG
static BOOL shouldUsePressAndHold() {
static int shouldUsePressAndHold = -1;
if (shouldUsePressAndHold != -1) return shouldUsePressAndHold;
@@ -1498,3 +1502,19 @@ JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPlatformView_nativeIsViewUnder
return underMouse;
}
jboolean GetStaticBoolean(JNIEnv *env, jclass fClass, const char *fieldName)
{
jfieldID fieldID = (*env)->GetStaticFieldID(env, fClass, fieldName, "Z");
return (*env)->GetStaticBooleanField(env, fClass, fieldID);
}
JNIEXPORT void JNICALL
Java_sun_java2d_macos_MacOSFlags_initNativeFlags(JNIEnv *env,
jclass flagsClass)
{
metalEnabled = GetStaticBoolean(env, flagsClass, "metalEnabled");
#ifdef METAL_DEBUG
fprintf(stderr, "metalEnabled=%d\n", metalEnabled);
#endif
}

View File

@@ -1171,6 +1171,8 @@ JNF_COCOA_ENTER(env);
JNF_COCOA_EXIT(env);
}
extern jboolean metalEnabled;
/*
* Class: sun_lwawt_macosx_CPlatformWindow
* Method: nativeGetNSWindowInsets
@@ -1197,6 +1199,10 @@ JNF_COCOA_ENTER(env);
jint left = (jint)(contentRect.origin.x - frame.origin.x);
jint bottom = (jint)(contentRect.origin.y - frame.origin.y);
jint right = (jint)(frame.size.width - (contentRect.size.width + left));
if (metalEnabled == JNI_TRUE) {
bottom -= top;
top = 0;
}
static JNF_CLASS_CACHE(jc_Insets, "java/awt/Insets");
static JNF_CTOR_CACHE(jc_Insets_ctor, jc_Insets, "(IIII)V");

View File

@@ -26,6 +26,8 @@
#import "LWCToolkit.h"
#import "ThreadUtilities.h"
#include "GeomUtilities.h"
#import <Metal/Metal.h>
#import <MetalKit/MetalKit.h>
#import <JavaNativeFoundation/JavaNativeFoundation.h>
@@ -383,4 +385,4 @@ JNF_COCOA_ENTER(env);
JNF_COCOA_EXIT(env);
return ret;
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef COMMON_H
#define COMMON_H
#include <simd/SIMD.h>
#define PGRAM_VERTEX_COUNT 6
enum VertexAttributes {
VertexAttributePosition = 0,
VertexAttributeTexPos = 1
};
enum BufferIndex {
MeshVertexBuffer = 0,
FrameUniformBuffer = 1,
};
struct FrameUniforms {
vector_float4 color;
};
struct FrameUniformsTransform {
matrix_float4x4 transformMatrix;
};
struct Vertex {
float position[3];
};
struct TxtVertex {
float position[3];
float txtpos[2];
};
#endif

View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <simd/simd.h>
#include <metal_stdlib>
#include "common.h"
using namespace metal;
struct VertexInput {
float3 position [[attribute(VertexAttributePosition)]];
};
struct TxtVertexInput {
float3 position [[attribute(VertexAttributePosition)]];
float2 texCoords [[attribute(VertexAttributeTexPos)]];
};
struct ColShaderInOut {
float4 position [[position]];
half4 color;
};
struct TxtShaderInOut {
float4 position [[position]];
float2 texCoords;
};
vertex ColShaderInOut vert_col(VertexInput in [[stage_in]],
constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]]) {
ColShaderInOut out;
out.position = float4(in.position, 1.0);
out.color = half4(uniforms.color.r, uniforms.color.g, uniforms.color.b, uniforms.color.a);
return out;
}
vertex TxtShaderInOut vert_txt(TxtVertexInput in [[stage_in]],
constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]]) {
TxtShaderInOut out;
out.position = float4(in.position, 1.0);
out.texCoords = in.texCoords;
return out;
}
vertex TxtShaderInOut vert_txt_matrix(TxtVertexInput in [[stage_in]],
constant FrameUniformsTransform& uniforms [[buffer(FrameUniformBuffer)]]) {
TxtShaderInOut out;
float4 pos4 = float4(in.position, 1.0);
out.position = simd::operator*(uniforms.transformMatrix, pos4);
out.position[2] = 0;
out.position[3] = 1.f;
out.texCoords = in.texCoords;
return out;
}
fragment half4 frag_col(ColShaderInOut in [[stage_in]]) {
return in.color;
}
fragment half4 frag_txt(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]]
)
{
constexpr sampler textureSampler (mag_filter::linear,
min_filter::linear);
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
return half4(pixelColor.r, pixelColor.g, pixelColor.b , pixelColor.a);
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#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, jboolean rtt,
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);
#endif /* MTLBlitLoops_h_Included */

View File

@@ -0,0 +1,506 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#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 <stdlib.h> // malloc
#include <string.h> // memcpy
#include "IntArgbPre.h"
extern MTLPixelFormat PixelFormats[];
extern void J2dTraceImpl(int level, jboolean cr, const char *string, ...);
void _fillTxQuad(
struct TxtVertex * txQuadVerts, jboolean normalizeDst,
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;
if (normalizeDst) {
dx1 = (2.0*dx1/dw) - 1.0;
dy1 = 2.0*(1.0 - dy1/dh) - 1.0;
dx2 = (2.0*dx2/dw) - 1.0;
dy2 = 2.0*(1.0 - dy2/dh) - 1.0;
}
txQuadVerts[0].position[0] = dx1;
txQuadVerts[0].position[1] = dy1;
txQuadVerts[0].position[2] = 0;
txQuadVerts[0].txtpos[0] = nsx1;
txQuadVerts[0].txtpos[1] = nsy1;
txQuadVerts[1].position[0] = dx2;
txQuadVerts[1].position[1] = dy1;
txQuadVerts[1].position[2] = 0;
txQuadVerts[1].txtpos[0] = nsx2;
txQuadVerts[1].txtpos[1] = nsy1;
txQuadVerts[2].position[0] = dx2;
txQuadVerts[2].position[1] = dy2;
txQuadVerts[2].position[2] = 0;
txQuadVerts[2].txtpos[0] = nsx2;
txQuadVerts[2].txtpos[1] = nsy2;
txQuadVerts[3].position[0] = dx2;
txQuadVerts[3].position[1] = dy2;
txQuadVerts[3].position[2] = 0;
txQuadVerts[3].txtpos[0] = nsx2;
txQuadVerts[3].txtpos[1] = nsy2;
txQuadVerts[4].position[0] = dx1;
txQuadVerts[4].position[1] = dy2;
txQuadVerts[4].position[2] = 0;
txQuadVerts[4].txtpos[0] = nsx1;
txQuadVerts[4].txtpos[1] = nsy2;
txQuadVerts[5].position[0] = dx1;
txQuadVerts[5].position[1] = dy1;
txQuadVerts[5].position[2] = 0;
txQuadVerts[5].txtpos[0] = nsx1;
txQuadVerts[5].txtpos[1] = nsy1;
}
//
// DEBUG funcs, will be removed later
//
void _traceRaster(SurfaceDataRasInfo *srcInfo, int width, int height) {
char * p = srcInfo->rasBase;
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
char pix0 = p[y*srcInfo->scanStride + x*4];
char pix1 = p[y*srcInfo->scanStride + x*4 + 1];
char pix2 = p[y*srcInfo->scanStride + x*4 + 2];
char pix3 = p[y*srcInfo->scanStride + x*4 + 3];
J2dTrace4(J2D_TRACE_INFO, "[%d,%d,%d,%d], ", pix0, pix1, pix2, pix3);
}
J2dTraceLn(J2D_TRACE_INFO, "");
}
}
/**
* Inner loop used for copying a source MTL "Surface" (window, pbuffer,
* etc.) to a destination OpenGL "Surface". Note that the same surface can
* be used as both the source and destination, as is the case in a copyArea()
* operation. This method is invoked from MTLBlitLoops_IsoBlit() as well as
* MTLBlitLoops_CopyArea().
*
* The standard glCopyPixels() mechanism is used to copy the source region
* into the destination region. If the regions have different dimensions,
* the source will be scaled into the destination as appropriate (only
* nearest neighbor filtering will be applied for simple scale operations).
*/
static void
MTLBlitSurfaceToSurface(MTLContext *mtlc, BMTLSDOps *srcOps, BMTLSDOps *dstOps,
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
{
//TODO
J2dTraceNotImplPrimitive("MTLBlitSurfaceToSurface");
}
static void _drawTex2Tex(MTLContext *mtlc,
id<MTLTexture> src, id<MTLTexture> dst,
jboolean rtt, jint hint,
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
{
if (mtlc == NULL || src == nil || dst == nil)
return;
// J2dTraceLn2(J2D_TRACE_VERBOSE, "_drawTex2Tex: src tex=%p, dst tex=%p", src, dst);
// J2dTraceLn4(J2D_TRACE_VERBOSE, " sw=%d sh=%d dw=%d dh=%d", src.width, src.height, dst.width, dst.height);
// J2dTraceLn4(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d", sx1, sy1, sx2, sy2);
// J2dTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f", dx1, dy1, dx2, dy2);
id<MTLRenderCommandEncoder> encoder = MTLContext_CreateSamplingEncoder(mtlc, dst);
const jboolean normalize = !mtlc->useTransform;
struct TxtVertex quadTxVerticesBuffer[6];
_fillTxQuad(quadTxVerticesBuffer, normalize, 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];
[encoder endEncoding];
}
/**
* Inner loop used for copying a source MTL "Texture" to a destination
* MTL "Surface". This method is invoked from MTLBlitLoops_IsoBlit().
*
* This method will copy, scale, or transform the source texture into the
* destination depending on the transform state, as established in
* and MTLContext_SetTransform(). If the source texture is
* transformed in any way when rendered into the destination, the filtering
* method applied is determined by the hint parameter (can be GL_NEAREST or
* GL_LINEAR).
*/
static void
MTLBlitTextureToSurface(MTLContext *mtlc,
BMTLSDOps *srcOps, BMTLSDOps *dstOps,
jboolean rtt, jint hint,
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
{
id<MTLTexture> srcTex = srcOps->pTexture;
#ifdef DEBUG
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_IsoBlit [via sampling]: bsrc=%p [tex=%p], bdst=%p [tex=%p] | s (%dx%d) -> d (%dx%d) | src (%d, %d, %d, %d) -> dst (%1.2f, %1.2f, %1.2f, %1.2f)", srcOps, srcOps->pTexture, dstOps, dstOps->pTexture, srcTex.width, srcTex.height, dstOps->width, dstOps->height, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
#endif //DEBUG
_drawTex2Tex(mtlc, srcOps->pTexture, dstOps->pTexture, rtt, hint, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
}
/**
* Inner loop used for copying a source system memory ("Sw") surface to a
* destination MTL "Surface". This method is invoked from
* MTLBlitLoops_Blit().
*
* The standard glDrawPixels() mechanism is used to copy the source region
* into the destination region. If the regions have different
* dimensions, the source will be scaled into the destination
* as appropriate (only nearest neighbor filtering will be applied for simple
* scale operations).
*/
static void
MTLBlitSwToSurfaceViaTexture(MTLContext *ctx, SurfaceDataRasInfo *srcInfo, BMTLSDOps * bmtlsdOps,
MTPixelFormat *pf,
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
{
if (bmtlsdOps == NULL || bmtlsdOps->pTexture == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitSwToSurfaceViaTexture: dest is null");
return;
}
const int sw = sx2 - sx1;
const int sh = sy2 - sy1;
id<MTLTexture> dest = bmtlsdOps->pTexture;
#ifdef DEBUG
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_Blit [via pooled texture]: bdst=%p [tex=%p], sw=%d, sh=%d | src (%d, %d, %d, %d) -> dst (%1.2f, %1.2f, %1.2f, %1.2f)", bmtlsdOps, dest, sw, sh, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
#endif //DEBUG
id<MTLTexture> texBuff = [ctx->mtlTexturePool getTexture:sw height:sh format:MTLPixelFormatBGRA8Unorm];
if (texBuff == nil) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitSwToSurfaceViaTexture: can't obtain temporary texture object from pool");
return;
}
MTLRegion region = MTLRegionMake2D(0, 0, sw, sh);
[texBuff replaceRegion:region mipmapLevel:0 withBytes:srcInfo->rasBase bytesPerRow:srcInfo->scanStride]; // texBuff is locked for current frame
_drawTex2Tex(ctx, texBuff, dest, 0, 0, 0, 0, sw, sh, dx1, dy1, dx2, dy2);
}
/**
* Inner loop used for copying a source system memory ("Sw") surface or
* MTL "Surface" to a destination OpenGL "Surface", using an MTL texture
* tile as an intermediate surface. This method is invoked from
* MTLBlitLoops_Blit() for "Sw" surfaces and MTLBlitLoops_IsoBlit() for
* "Surface" surfaces.
*
* This method is used to transform the source surface into the destination.
* Pixel rectangles cannot be arbitrarily transformed (without the
* GL_EXT_pixel_transform extension, which is not supported on most modern
* hardware). However, texture mapped quads do respect the GL_MODELVIEW
* transform matrix, so we use textures here to perform the transform
* operation. This method uses a tile-based approach in which a small
* subregion of the source surface is copied into a cached texture tile. The
* texture tile is then mapped into the appropriate location in the
* destination surface.
*
* REMIND: this only works well using GL_NEAREST for the filtering mode
* (GL_LINEAR causes visible stitching problems between tiles,
* but this can be fixed by making use of texture borders)
*/
static void
MTLBlitToSurfaceViaTexture(MTLContext *mtlc, SurfaceDataRasInfo *srcInfo,
MTPixelFormat *pf, MTLSDOps *srcOps,
jboolean swsurface, jint hint,
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
{
//TODO
J2dTraceNotImplPrimitive("MTLBlitToSurfaceViaTexture");
}
/**
* Inner loop used for copying a source system memory ("Sw") surface to a
* destination OpenGL "Texture". This method is invoked from
* MTLBlitLoops_Blit().
*
* The source surface is effectively loaded into the MTL texture object,
* which must have already been initialized by MTLSD_initTexture(). Note
* that this method is only capable of copying the source surface into the
* destination surface (i.e. no scaling or general transform is allowed).
* This restriction should not be an issue as this method is only used
* currently to cache a static system memory image into an MTL texture in
* a hidden-acceleration situation.
*/
static void
MTLBlitSwToTexture(SurfaceDataRasInfo *srcInfo, MTPixelFormat *pf,
MTLSDOps *dstOps,
jint dx1, jint dy1, jint dx2, jint dy2)
{
//TODO
J2dTraceNotImplPrimitive("MTLBlitSwToTexture");
}
/**
* General blit method for copying a native MTL surface (of type "Surface"
* or "Texture") to another MTL "Surface". If texture is JNI_TRUE, this
* method will invoke the Texture->Surface inner loop; otherwise, one of the
* Surface->Surface inner loops will be invoked, depending on the transform
* state.
*
* REMIND: we can trick these blit methods into doing XOR simply by passing
* in the (pixel ^ xorpixel) as the pixel value and preceding the
* blit with a fillrect...
*/
void
MTLBlitLoops_IsoBlit(JNIEnv *env,
MTLContext *mtlc, jlong pSrcOps, jlong pDstOps,
jboolean xform, jint hint,
jboolean texture, jboolean rtt,
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(srcOps);
RETURN_IF_NULL(dstOps);
id<MTLTexture> srcTex = srcOps->pTexture;
id<MTLTexture> dstTex = dstOps->pTexture;
if (mtlc == NULL || 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;
}
SurfaceDataRasInfo srcInfo;
srcInfo.bounds.x1 = sx1;
srcInfo.bounds.y1 = sy1;
srcInfo.bounds.x2 = sx2;
srcInfo.bounds.y2 = sy2;
SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds, 0, 0, srcOps->width, srcOps->height);
if (srcInfo.bounds.x2 <= srcInfo.bounds.x1 || srcInfo.bounds.y2 <= srcInfo.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 (srcInfo.bounds.x1 != sx1) {
dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw);
sx1 = srcInfo.bounds.x1;
}
if (srcInfo.bounds.y1 != sy1) {
dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh);
sy1 = srcInfo.bounds.y1;
}
if (srcInfo.bounds.x2 != sx2) {
dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw);
sx2 = srcInfo.bounds.x2;
}
if (srcInfo.bounds.y2 != sy2) {
dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh);
sy2 = srcInfo.bounds.y2;
}
const jboolean useBlitEncoder =
MTLContext_IsBlendingDisabled(mtlc)
&& fabs(dx2 - dx1 - sx2 + sx1) < 0.001f && fabs(dy2 - dy1 - sy2 + sy1) < 0.001f // dimensions are equal (TODO: check that dx1,dy1 is integer)
&& !mtlc->useTransform; // TODO: check whether transform is simple translate (and use blitEncoder in this case)
if (useBlitEncoder) {
#ifdef DEBUG
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_IsoBlit [via blitEncoder]: bdst=%p [tex=%p] %dx%d | src (%d, %d, %d, %d) -> dst (%1.2f, %1.2f, %1.2f, %1.2f)", dstOps, dstTex, dstTex.width, dstTex.height, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
#endif //DEBUG
id <MTLBlitCommandEncoder> blitEncoder = MTLContext_CreateBlitEncoder(mtlc);
[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];
} else {
// TODO: support other flags
MTLBlitTextureToSurface(mtlc, srcOps, dstOps, rtt, hint, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
}
}
/**
* General blit method for copying a system memory ("Sw") surface to a native
* MTL surface (of type "Surface" or "Texture"). If texture is JNI_TRUE,
* this method will invoke the Sw->Texture inner loop; otherwise, one of the
* Sw->Surface inner loops will be invoked, depending on the transform state.
*/
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)
{
RETURN_IF_NULL(jlong_to_ptr(pSrcOps));
RETURN_IF_NULL(jlong_to_ptr(pDstOps));
SurfaceDataOps *srcOps = (SurfaceDataOps *)jlong_to_ptr(pSrcOps);
BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps);
SurfaceDataRasInfo srcInfo;
MTLPixelFormat pf = MTLPixelFormatBGRA8Unorm;//PixelFormats[srctype];
if (dstOps == NULL || dstOps->pTexture == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: dest is null");
return;
}
id<MTLTexture> dest = dstOps->pTexture;
if (dx1 < 0) {
sx1 += dx1;
dx1 = 0;
}
if (dx2 > dest.width) {
sx2 -= dx2 - dest.width;
dx2 = dest.width;
}
if (dy1 < 0) {
sy1 += dy1;
dy1 = 0;
}
if (dy2 > dest.height) {
sy2 -= dy2 - dest.height;
dy2 = dest.height;
}
jint sw = sx2 - sx1;
jint sh = sy2 - sy1;
jdouble dw = dx2 - dx1;
jdouble dh = dy2 - dy1;
if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0 || srctype < 0) {
J2dTraceLn(J2D_TRACE_WARNING, "MTLBlitLoops_Blit: invalid dimensions or srctype");
return;
}
srcInfo.bounds.x1 = sx1;
srcInfo.bounds.y1 = sy1;
srcInfo.bounds.x2 = sx2;
srcInfo.bounds.y2 = sy2;
if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) != SD_SUCCESS) {
J2dTraceLn(J2D_TRACE_WARNING, "MTLBlitLoops_Blit: could not acquire lock");
return;
}
J2dTraceLn5(J2D_TRACE_VERBOSE, "MTLBlitLoops_Blit: pf=%d texture=%d srctype=%d xform=%d hint=%d", pf, texture, srctype, xform, hint);
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) {
dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw);
sx1 = srcInfo.bounds.x1;
}
if (srcInfo.bounds.y1 != sy1) {
dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh);
sy1 = srcInfo.bounds.y1;
}
if (srcInfo.bounds.x2 != sx2) {
dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw);
sx2 = srcInfo.bounds.x2;
}
if (srcInfo.bounds.y2 != sy2) {
dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh);
sy2 = srcInfo.bounds.y2;
}
const jboolean useReplaceRegion =
MTLContext_IsBlendingDisabled(mtlc)
&& fabs(dx2 - dx1 - sx2 + sx1) < 0.001f && fabs(dy2 - dy1 - sy2 + sy1) < 0.001f // dimensions are equal (TODO: check that dx1,dy1 is integer)
&& !mtlc->useTransform; // TODO: check whether transform is simple translate (and use replaceRegion in this case)
if (useReplaceRegion) {
MTLRegion region = MTLRegionMake2D(dx1, dy1, dx2 - dx1, dy2 - dy1);
#ifdef DEBUG
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_Blit [replaceRegion]: bdst=%p [tex=%p] %dx%d | src (%d, %d, %d, %d) -> dst (%1.2f, %1.2f, %1.2f, %1.2f)", dstOps, dest, dest.width, dest.height, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
#endif //DEBUG
[dest replaceRegion:region mipmapLevel:0 withBytes:srcInfo.rasBase bytesPerRow:srcInfo.scanStride]; // executed at CPU (sync), TODO: lock dest for current frame
} else {
MTLBlitSwToSurfaceViaTexture(mtlc, &srcInfo, dstOps, &pf, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
}
}
SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
}
SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
}
/**
* 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)
{
//TODO
J2dTraceNotImplPrimitive("MTLBlitLoops_SurfaceToSwBlit");
}
void
MTLBlitLoops_CopyArea(JNIEnv *env,
MTLContext *mtlc, BMTLSDOps *dstOps,
jint x, jint y, jint width, jint height,
jint dx, jint dy)
{
//TODO
J2dTraceNotImplPrimitive("MTLBlitLoops_CopyArea");
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLBufImgOps_h_Included
#define MTLBufImgOps_h_Included
#include "MTLContext.h"
void MTLBufImgOps_EnableConvolveOp(MTLContext *mtlc, jlong pSrcOps,
jboolean edgeZeroFill,
jint kernelWidth, jint KernelHeight,
unsigned char *kernelVals);
void MTLBufImgOps_DisableConvolveOp(MTLContext *mtlc);
void MTLBufImgOps_EnableRescaleOp(MTLContext *mtlc, jlong pSrcOps,
jboolean nonPremult,
unsigned char *scaleFactors,
unsigned char *offsets);
void MTLBufImgOps_DisableRescaleOp(MTLContext *mtlc);
void MTLBufImgOps_EnableLookupOp(MTLContext *mtlc, jlong pSrcOps,
jboolean nonPremult, jboolean shortData,
jint numBands, jint bandLength, jint offset,
void *tableValues);
void MTLBufImgOps_DisableLookupOp(MTLContext *mtlc);
#endif /* MTLBufImgOps_h_Included */

View File

@@ -0,0 +1,378 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <jlong.h>
#include "MTLBufImgOps.h"
#include "MTLContext.h"
#include "MTLRenderQueue.h"
#include "MTLSurfaceDataBase.h"
#include "GraphicsPrimitiveMgr.h"
/** Evaluates to true if the given bit is set on the local flags variable. */
#define IS_SET(flagbit) \
(((flags) & (flagbit)) != 0)
/**************************** ConvolveOp support ****************************/
/**
* The ConvolveOp shader is fairly straightforward. For each texel in
* the source texture, the shader samples the MxN texels in the surrounding
* area, multiplies each by its corresponding kernel value, and then sums
* them all together to produce a single color result. Finally, the
* resulting value is multiplied by the current OpenGL color, which contains
* the extra alpha value.
*
* Note that this shader source code includes some "holes" marked by "%s".
* This allows us to build different shader programs (e.g. one for
* 3x3, one for 5x5, and so on) simply by filling in these "holes" with
* a call to sprintf(). See the MTLBufImgOps_CreateConvolveProgram() method
* for more details.
*
* REMIND: Currently this shader (and the supporting code in the
* EnableConvolveOp() method) only supports 3x3 and 5x5 filters.
* Early shader-level hardware did not support non-constant sized
* arrays but modern hardware should support them (although I
* don't know of any simple way to find out, other than to compile
* the shader at runtime and see if the drivers complain).
*/
static const char *convolveShaderSource =
// maximum size supported by this shader
"const int MAX_KERNEL_SIZE = %s;"
// image to be convolved
"uniform sampler%s baseImage;"
// image edge limits:
// imgEdge.xy = imgMin.xy (anything < will be treated as edge case)
// imgEdge.zw = imgMax.xy (anything > will be treated as edge case)
"uniform vec4 imgEdge;"
// value for each location in the convolution kernel:
// kernelVals[i].x = offsetX[i]
// kernelVals[i].y = offsetY[i]
// kernelVals[i].z = kernel[i]
"uniform vec3 kernelVals[MAX_KERNEL_SIZE];"
""
"void main(void)"
"{"
" int i;"
" vec4 sum;"
""
" if (any(lessThan(gl_TexCoord[0].st, imgEdge.xy)) ||"
" any(greaterThan(gl_TexCoord[0].st, imgEdge.zw)))"
" {"
// (placeholder for edge condition code)
" %s"
" } 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);"
" }"
" }"
""
// modulate with gl_Color in order to apply extra alpha
" gl_FragColor = sum * gl_Color;"
"}";
/**
* Flags that can be bitwise-or'ed together to control how the shader
* source code is generated.
*/
#define CONVOLVE_RECT (1 << 0)
#define CONVOLVE_EDGE_ZERO_FILL (1 << 1)
#define CONVOLVE_5X5 (1 << 2)
/**
* The handles to the ConvolveOp fragment program objects. The index to
* the array should be a bitwise-or'ing of the CONVOLVE_* flags defined
* above. Note that most applications will likely need to initialize one
* or two of these elements, so the array is usually sparsely populated.
*/
static GLhandleARB convolvePrograms[8];
/**
* The maximum kernel size supported by the ConvolveOp shader.
*/
#define MAX_KERNEL_SIZE 25
/**
* Compiles and links the ConvolveOp shader program. If successful, this
* function returns a handle to the newly created shader program; otherwise
* returns 0.
*/
static GLhandleARB
MTLBufImgOps_CreateConvolveProgram(jint flags)
{
//TODO
J2dTraceNotImplPrimitive("MTLBufImgOps_CreateConvolveProgram");
return NULL;
}
void
MTLBufImgOps_EnableConvolveOp(MTLContext *mtlc, jlong pSrcOps,
jboolean edgeZeroFill,
jint kernelWidth, jint kernelHeight,
unsigned char *kernel)
{
//TODO
J2dTraceNotImplPrimitive("MTLBufImgOps_EnableConvolveOp");
}
void
MTLBufImgOps_DisableConvolveOp(MTLContext *mtlc)
{
//TODO
J2dTraceNotImplPrimitive("MTLBufImgOps_EnableConvolveOp");
J2dTraceLn(J2D_TRACE_INFO, "MTLBufImgOps_DisableConvolveOp");
}
/**************************** RescaleOp support *****************************/
/**
* The RescaleOp shader is one of the simplest possible. Each fragment
* from the source image is multiplied by the user's scale factor and added
* to the user's offset value (these are component-wise operations).
* Finally, the resulting value is multiplied by the current OpenGL color,
* which contains the extra alpha value.
*
* The RescaleOp spec says that the operation is performed regardless of
* whether the source data is premultiplied or non-premultiplied. This is
* a problem for the OpenGL pipeline in that a non-premultiplied
* BufferedImage will have already been converted into premultiplied
* when uploaded to an OpenGL texture. Therefore, we have a special mode
* called RESCALE_NON_PREMULT (used only for source images that were
* originally non-premultiplied) that un-premultiplies the source color
* prior to the rescale operation, then re-premultiplies the resulting
* color before returning from the fragment shader.
*
* Note that this shader source code includes some "holes" marked by "%s".
* This allows us to build different shader programs (e.g. one for
* GL_TEXTURE_2D targets, one for GL_TEXTURE_RECTANGLE_ARB targets, and so on)
* simply by filling in these "holes" with a call to sprintf(). See the
* MTLBufImgOps_CreateRescaleProgram() method for more details.
*/
static const char *rescaleShaderSource =
// image to be rescaled
"uniform sampler%s baseImage;"
// vector containing scale factors
"uniform vec4 scaleFactors;"
// vector containing offsets
"uniform vec4 offsets;"
""
"void main(void)"
"{"
" vec4 srcColor = texture%s(baseImage, gl_TexCoord[0].st);"
// (placeholder for un-premult code)
" %s"
// rescale source value
" vec4 result = (srcColor * scaleFactors) + offsets;"
// (placeholder for re-premult code)
" %s"
// modulate with gl_Color in order to apply extra alpha
" gl_FragColor = result * gl_Color;"
"}";
/**
* Flags that can be bitwise-or'ed together to control how the shader
* source code is generated.
*/
#define RESCALE_RECT (1 << 0)
#define RESCALE_NON_PREMULT (1 << 1)
/**
* The handles to the RescaleOp fragment program objects. The index to
* the array should be a bitwise-or'ing of the RESCALE_* flags defined
* above. Note that most applications will likely need to initialize one
* or two of these elements, so the array is usually sparsely populated.
*/
static GLhandleARB rescalePrograms[4];
/**
* Compiles and links the RescaleOp shader program. If successful, this
* function returns a handle to the newly created shader program; otherwise
* returns 0.
*/
static GLhandleARB
MTLBufImgOps_CreateRescaleProgram(jint flags)
{
//TODO
J2dTraceNotImplPrimitive("MTLBufImgOps_CreateRescaleProgram");
return NULL;
}
void
MTLBufImgOps_EnableRescaleOp(MTLContext *mtlc, jlong pSrcOps,
jboolean nonPremult,
unsigned char *scaleFactors,
unsigned char *offsets)
{
//TODO
J2dTraceNotImplPrimitive("MTLBufImgOps_EnableRescaleOp");
}
void
MTLBufImgOps_DisableRescaleOp(MTLContext *mtlc)
{
//TODO
J2dTraceNotImplPrimitive("MTLBufImgOps_DisableRescaleOp");
J2dTraceLn(J2D_TRACE_INFO, "MTLBufImgOps_DisableRescaleOp");
RETURN_IF_NULL(mtlc);
}
/**************************** LookupOp support ******************************/
/**
* The LookupOp shader takes a fragment color (from the source texture) as
* input, subtracts the optional user offset value, and then uses the
* resulting value to index into the lookup table texture to provide
* a new color result. Finally, the resulting value is multiplied by
* the current OpenGL color, which contains the extra alpha value.
*
* The lookup step requires 3 texture accesses (or 4, when alpha is included),
* which is somewhat unfortunate because it's not ideal from a performance
* standpoint, but that sort of thing is getting faster with newer hardware.
* In the 3-band case, we could consider using a three-dimensional texture
* and performing the lookup with a single texture access step. We already
* use this approach in the LCD text shader, and it works well, but for the
* purposes of this LookupOp shader, it's probably overkill. Also, there's
* a difference in that the LCD text shader only needs to populate the 3D LUT
* once, but here we would need to populate it on every invocation, which
* would likely be a waste of VRAM and CPU/GPU cycles.
*
* The LUT texture is currently hardcoded as 4 rows/bands, each containing
* 256 elements. This means that we currently only support user-provided
* tables with no more than 256 elements in each band (this is checked at
* at the Java level). If the user provides a table with less than 256
* elements per band, our shader will still work fine, but if elements are
* accessed with an index >= the size of the LUT, then the shader will simply
* produce undefined values. Typically the user would provide an offset
* value that would prevent this from happening, but it's worth pointing out
* this fact because the software LookupOp implementation would usually
* throw an ArrayIndexOutOfBoundsException in this scenario (although it is
* not something demanded by the spec).
*
* The LookupOp spec says that the operation is performed regardless of
* whether the source data is premultiplied or non-premultiplied. This is
* a problem for the OpenGL pipeline in that a non-premultiplied
* BufferedImage will have already been converted into premultiplied
* when uploaded to an OpenGL texture. Therefore, we have a special mode
* called LOOKUP_NON_PREMULT (used only for source images that were
* originally non-premultiplied) that un-premultiplies the source color
* prior to the lookup operation, then re-premultiplies the resulting
* color before returning from the fragment shader.
*
* Note that this shader source code includes some "holes" marked by "%s".
* This allows us to build different shader programs (e.g. one for
* GL_TEXTURE_2D targets, one for GL_TEXTURE_RECTANGLE_ARB targets, and so on)
* simply by filling in these "holes" with a call to sprintf(). See the
* MTLBufImgOps_CreateLookupProgram() method for more details.
*/
static const char *lookupShaderSource =
// source image (bound to texture unit 0)
"uniform sampler%s baseImage;"
// lookup table (bound to texture unit 1)
"uniform sampler2D lookupTable;"
// offset subtracted from source index prior to lookup step
"uniform vec4 offset;"
""
"void main(void)"
"{"
" vec4 srcColor = texture%s(baseImage, gl_TexCoord[0].st);"
// (placeholder for un-premult code)
" %s"
// subtract offset from original index
" vec4 srcIndex = srcColor - offset;"
// 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;"
// (placeholder for alpha store code)
" %s"
// (placeholder for re-premult code)
" %s"
// modulate with gl_Color in order to apply extra alpha
" gl_FragColor = result * gl_Color;"
"}";
/**
* Flags that can be bitwise-or'ed together to control how the shader
* source code is generated.
*/
#define LOOKUP_RECT (1 << 0)
#define LOOKUP_USE_SRC_ALPHA (1 << 1)
#define LOOKUP_NON_PREMULT (1 << 2)
/**
* The handles to the LookupOp fragment program objects. The index to
* the array should be a bitwise-or'ing of the LOOKUP_* flags defined
* above. Note that most applications will likely need to initialize one
* or two of these elements, so the array is usually sparsely populated.
*/
static GLhandleARB lookupPrograms[8];
/**
* The handle to the lookup table texture object used by the shader.
*/
static GLuint lutTextureID = 0;
/**
* Compiles and links the LookupOp shader program. If successful, this
* function returns a handle to the newly created shader program; otherwise
* returns 0.
*/
static GLhandleARB
MTLBufImgOps_CreateLookupProgram(jint flags)
{
//TODO
J2dTraceNotImplPrimitive("MTLBufImgOps_CreateLookupProgram");
return NULL;
}
void
MTLBufImgOps_EnableLookupOp(MTLContext *mtlc, jlong pSrcOps,
jboolean nonPremult, jboolean shortData,
jint numBands, jint bandLength, jint offset,
void *tableValues)
{
//TODO
J2dTraceNotImplPrimitive("MTLBufImgOps_EnableLookupOp");
}
void
MTLBufImgOps_DisableLookupOp(MTLContext *mtlc)
{
//TODO
J2dTraceNotImplPrimitive("MTLBufImgOps_DisableLookupOp");
J2dTraceLn(J2D_TRACE_INFO, "MTLBufImgOps_DisableLookupOp");
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,157 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLContext_h_Included
#define MTLContext_h_Included
#include <simd/simd.h>
#include "sun_java2d_pipe_BufferedContext.h"
#include "sun_java2d_metal_MTLContext.h"
#include "sun_java2d_metal_MTLContext_MTLContextCaps.h"
#import <Metal/Metal.h>
#include "j2d_md.h"
#include "MTLSurfaceDataBase.h"
#include "MTLTexturePool.h"
/**
* 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.
*/
typedef struct {
jint src;
jint dst;
} MTLBlendRule;
/**
* The MTLContext structure contains cached state relevant to the native
* OpenGL context stored within the native ctxInfo field. Each Java-level
* MTLContext object is associated with a native-level MTLContext structure.
* The caps field is a bitfield that expresses the capabilities of the
* GraphicsConfig associated with this context (see MTLContext.java for
* the definitions of each capability bit). The other fields are
* simply cached values of various elements of the context state, typically
* used in the MTLContext.set*() methods.
*
* Note that the textureFunction field is implicitly set to zero when the
* MTLContext is created. The acceptable values (e.g. GL_MODULATE,
* GL_REPLACE) for this field are never zero, which means we will always
* validate the texture function state upon the first call to the
* MTLC_UPDATE_TEXTURE_FUNCTION() macro.
*/
typedef struct {
jint compState;
jfloat extraAlpha;
jint alphaCompositeRule;
jint xorPixel;
jint pixel;
jubyte r;
jubyte g;
jubyte b;
jubyte a;
jint paintState;
jboolean useMask;
jboolean useTransform;
simd_float4x4 transform4x4;
jint blitTextureID;
jint textureFunction;
jboolean vertexCacheEnabled;
id<MTLDevice> mtlDevice;
id<MTLLibrary> mtlLibrary;
id<MTLRenderPipelineState> mtlPipelineState;
id<MTLRenderPipelineState> mtlBlitPipelineState;
id<MTLRenderPipelineState> mtlBlitMatrixPipelineState;
id<MTLRenderPipelineState> mtlBlitSrcOverPipelineState;
id<MTLRenderPipelineState> mtlBlitMatrixSrcOverPipelineState; // TODO: implement PipelineStateFactory
id<MTLCommandQueue> mtlCommandQueue;
id<MTLCommandBuffer> mtlCommandBuffer;
id<MTLBuffer> mtlVertexBuffer;
jint mtlColor;
MTLScissorRect mtlClipRect;
jboolean useClip;
MTLTexturePool* mtlTexturePool;
} MTLContext;
/**
* 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
/**
* See MTLContext.java for more on these flags.
*/
/**
* Exported methods.
*/
MTLContext *MTLContext_SetSurfaces(JNIEnv *env, jlong pSrc, jlong pDst);
void MTLContext_ResetClip(MTLContext *mtlc);
void MTLContext_SetRectClip(MTLContext *mtlc, BMTLSDOps *dstOps,
jint x1, jint y1, jint x2, jint y2);
void MTLContext_BeginShapeClip(MTLContext *mtlc);
void MTLContext_EndShapeClip(MTLContext *mtlc, BMTLSDOps *dstOps);
void MTLContext_SetExtraAlpha(jfloat ea);
void MTLContext_ResetComposite(MTLContext *mtlc);
void MTLContext_SetAlphaComposite(MTLContext *mtlc,
jint rule, jfloat extraAlpha, jint flags);
void MTLContext_SetXorComposite(MTLContext *mtlc, jint xorPixel);
jboolean MTLContext_IsBlendingDisabled(MTLContext *mtlc);
void MTLContext_ResetTransform(MTLContext *mtlc);
void MTLContext_SetTransform(MTLContext *mtlc,
jdouble m00, jdouble m10,
jdouble m01, jdouble m11,
jdouble m02, jdouble m12);
jboolean MTLContext_InitBlitTileTexture(MTLContext *mtlc);
jint MTLContext_CreateBlitTexture(jint internalFormat, jint pixelFormat,
jint width, jint height);
void MTLContext_DestroyContextResources(MTLContext *mtlc);
void MTLContext_SetColor(MTLContext *ctx, int r, int g, int b, int a);
void MTLContext_SetColorInt(MTLContext *ctx, int pixel);
id<MTLRenderCommandEncoder> MTLContext_CreateRenderEncoder(MTLContext *mtlc, id<MTLTexture> dest);
id<MTLRenderCommandEncoder> MTLContext_CreateSamplingEncoder(MTLContext *mtlc, id<MTLTexture> dest);
id<MTLBlitCommandEncoder> MTLContext_CreateBlitEncoder(MTLContext *mtlc);
#endif /* MTLContext_h_Included */

View File

@@ -0,0 +1,604 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <stdlib.h>
#include <string.h>
#include "sun_java2d_SunGraphics2D.h"
#include "jlong.h"
#include "jni_util.h"
#include "MTLContext.h"
#include "MTLRenderQueue.h"
#include "MTLSurfaceDataBase.h"
#include "GraphicsPrimitiveMgr.h"
#include "Region.h"
#include "jvm.h"
extern jboolean MTLSD_InitMTLWindow(JNIEnv *env, MTLSDOps *mtlsdo);
extern MTLContext *MTLSD_MakeMTLContextCurrent(JNIEnv *env,
MTLSDOps *srcOps,
MTLSDOps *dstOps);
/**
* This table contains the standard blending rules (or Porter-Duff compositing
* factors) used in glBlendFunc(), indexed by the rule constants from the
* AlphaComposite class.
*/
MTLBlendRule MTStdBlendRules[] = {
};
/** Evaluates to "front" or "back", depending on the value of buf. */
//#define MTLC_ACTIVE_BUFFER_NAME(buf) \
// (buf == GL_FRONT || buf == GL_COLOR_ATTACHMENT0_EXT) ? "front" : "back"
/**
* Initializes the viewport and projection matrix, effectively positioning
* the origin at the top-left corner of the surface. This allows Java 2D
* coordinates to be passed directly to OpenGL, which is typically based on
* a bottom-right coordinate system. This method also sets the appropriate
* read and draw buffers.
*/
static void
MTLContext_SetViewport(BMTLSDOps *srcOps,BMTLSDOps *dstOps)
{
//TODO
J2dTraceNotImplPrimitive("MTLContext_SetViewport");
jint width = dstOps->width;
jint height = dstOps->height;
}
/**
* Initializes the alpha channel of the current surface so that it contains
* fully opaque alpha values.
*/
static void
MTLContext_InitAlphaChannel()
{
//TODO
J2dTraceNotImplPrimitive("MTLContext_InitAlphaChannel");
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_InitAlphaChannel");
}
/**
* 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 *
MTLContext_SetSurfaces(JNIEnv *env, jlong pSrc, 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 OGLSD_WINDOW
if (!MTLSD_InitMTLWindow(env, dstOps)) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLContext_SetSurfaces: could not init OGL window");
return NULL;
}
}
// make the context current
MTLSDOps *dstCGLOps = (MTLSDOps *)dstOps->privOps;
mtlc = dstCGLOps->configInfo->context;
if (mtlc == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLContext_SetSurfaces: could not make context current");
return NULL;
}
// update the viewport
MTLContext_SetViewport(srcOps, dstOps);
// perform additional one-time initialization, if necessary
if (dstOps->needsInit) {
if (dstOps->isOpaque) {
// in this case we are treating the destination as opaque, but
// to do so, first we need to ensure that the alpha channel
// is filled with fully opaque values (see 6319663)
MTLContext_InitAlphaChannel();
}
dstOps->needsInit = JNI_FALSE;
}
return mtlc;
}
/**
* Resets the current clip state (disables both scissor and depth tests).
*/
void
MTLContext_ResetClip(MTLContext *mtlc)
{
//TODO
J2dTraceNotImplPrimitive("MTLContext_ResetClip");
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_ResetClip");
mtlc->useClip = JNI_FALSE;
}
/**
* Sets the Metal scissor bounds to the provided rectangular clip bounds.
*/
void
MTLContext_SetRectClip(MTLContext *mtlc, BMTLSDOps *dstOps,
jint x1, jint y1, jint x2, jint y2)
{
//TODO
J2dTraceNotImplPrimitive("MTLContext_SetRectClip");
jint width = x2 - x1;
jint height = y2 - y1;
J2dTraceLn4(J2D_TRACE_INFO, "MTLContext_SetRectClip: x=%d y=%d w=%d h=%d", x1, y1, width, height);
mtlc->mtlClipRect.x = x1;
mtlc->mtlClipRect.y = y1;
mtlc->mtlClipRect.width = width;
mtlc->mtlClipRect.height = height;
mtlc->useClip = JNI_TRUE;
}
/**
* Sets up a complex (shape) clip using the OpenGL depth buffer. This
* method prepares the depth buffer so that the clip Region spans can
* be "rendered" into it. The depth buffer is first cleared, then the
* depth 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 non-zero value is placed into that location in the depth
* buffer. With depth test enabled, pixels will only be rendered into the
* color buffer if the corresponding value at that (x,y) location in the
* depth buffer differs from the incoming depth value.
*/
void
MTLContext_BeginShapeClip(MTLContext *mtlc)
{
//TODO
J2dTraceNotImplPrimitive("MTLContext_BeginShapeClip");
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_BeginShapeClip");
}
/**
* Finishes setting up the shape clip by resetting the depth func
* so that future rendering operations will once again be written into the
* color buffer (while respecting the clip set up in the depth buffer).
*/
void
MTLContext_EndShapeClip(MTLContext *mtlc, BMTLSDOps *dstOps)
{
//TODO
J2dTraceNotImplPrimitive("MTLContext_EndShapeClip");
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_EndShapeClip");
}
/**
* Initializes the OpenGL state responsible for applying extra alpha. This
* step is only necessary for any operation that uses glDrawPixels() or
* glCopyPixels() with a non-1.0f extra alpha value. Since the source is
* always premultiplied, we apply the extra alpha value to both alpha and
* color components using GL_*_SCALE.
*/
void
MTLContext_SetExtraAlpha(jfloat ea)
{
//TODO
J2dTraceNotImplPrimitive("MTLContext_SetExtraAlpha");
J2dTraceLn1(J2D_TRACE_INFO, "MTLContext_SetExtraAlpha: ea=%f", ea);
}
/**
* Resets all OpenGL compositing state (disables blending and logic
* operations).
*/
void
MTLContext_ResetComposite(MTLContext *mtlc)
{
//TODO
J2dTraceNotImplPrimitive("MTLContext_ResetComposite");
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_ResetComposite");
}
/**
* Initializes the OpenGL blending state. XOR mode is disabled and the
* appropriate blend functions are setup based on the AlphaComposite rule
* constant.
*/
void MTLContext_SetAlphaComposite(MTLContext *mtlc, jint rule, jfloat extraAlpha, jint flags) {
J2dTracePrimitive("MTLContext_SetAlphaComposite");
J2dTraceLn3(J2D_TRACE_INFO, "MTLContext_SetAlphaComposite: rule=%d, extraAlpha=%1.2f, flags=%d", rule, extraAlpha, flags);
mtlc->extraAlpha = extraAlpha;
mtlc->alphaCompositeRule = rule;
}
jboolean MTLContext_IsBlendingDisabled(MTLContext *mtlc) {
// TODO: hold case mtlc->alphaCompositeRule == RULE_SrcOver && sun_java2d_pipe_BufferedContext_SRC_IS_OPAQUE
return mtlc->alphaCompositeRule == RULE_Src && (mtlc->extraAlpha - 1.0f < 0.001f);
}
/**
* Initializes the OpenGL 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
MTLContext_SetXorComposite(MTLContext *mtlc, jint xorPixel)
{
//TODO
J2dTraceNotImplPrimitive("MTLContext_SetXorComposite");
J2dTraceLn1(J2D_TRACE_INFO,
"MTLContext_SetXorComposite: xorPixel=%08x", xorPixel);
}
/**
* Resets the OpenGL transform state back to the identity matrix.
*/
void
MTLContext_ResetTransform(MTLContext *mtlc)
{
J2dTracePrimitive("MTLContext_ResetTransform");
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_ResetTransform");
mtlc->useTransform = JNI_FALSE;
}
static void _traceMatrix(simd_float4x4 * mtx) {
for (int row = 0; row < 4; ++row) {
J2dTraceLn4(J2D_TRACE_VERBOSE, " [%lf %lf %lf %lf]",
mtx->columns[0][row], mtx->columns[1][row], mtx->columns[2][row], mtx->columns[3][row]);
}
}
static void _setTransform(MTLContext *mtlc, id<MTLTexture> dest,
jdouble m00, jdouble m10,
jdouble m01, jdouble m11,
jdouble m02, jdouble m12
) {
if (dest == nil) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLContext_SetTransformMetal: current dest is null");
return;
}
const double dw = dest.width;
const double dh = dest.height;
simd_float4x4 transform;
memset(&transform, 0, sizeof(transform));
transform.columns[0][0] = m00;
transform.columns[0][1] = m10;
transform.columns[1][0] = m01;
transform.columns[1][1] = m11;
transform.columns[3][0] = m02;
transform.columns[3][1] = m12;
transform.columns[3][3] = 1.0;
transform.columns[4][4] = 1.0;
simd_float4x4 normalize;
memset(&normalize, 0, sizeof(normalize));
normalize.columns[0][0] = 2/dw;
normalize.columns[1][1] = -2/dh;
normalize.columns[3][0] = -1.f;
normalize.columns[3][1] = 1.f;
normalize.columns[3][3] = 1.0;
normalize.columns[4][4] = 1.0;
mtlc->transform4x4 = simd_mul(normalize, transform);
mtlc->useTransform = JNI_TRUE;
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_SetTransform");
// _traceMatrix(&(transform));
// _traceMatrix(&(normalize));
// _traceMatrix(&(mtlc->transform4x4));
}
/**
* Initializes the OpenGL 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
MTLContext_SetTransform(MTLContext *mtlc,
jdouble m00, jdouble m10,
jdouble m01, jdouble m11,
jdouble m02, jdouble m12)
{
J2dTracePrimitive("MTLContext_SetTransform");
const BMTLSDOps * bmtlsdOps = MTLRenderQueue_GetCurrentDestination();
if (bmtlsdOps == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLContext_SetTransform: current dest is null");
return;
}
_setTransform(mtlc, bmtlsdOps->pTexture, m00, m10, m01, m11, m02, m12);
}
/**
* Creates a 2D texture of the given format and dimensions and returns the
* texture object identifier. This method is typically used to create a
* temporary texture for intermediate work, such as in the
* MTLContext_InitBlitTileTexture() method below.
*/
jint
MTLContext_CreateBlitTexture(jint internalFormat, jint pixelFormat,
jint width, jint height)
{
//TODO
J2dTraceNotImplPrimitive("MTLContext_CreateBlitTexture");
return 0;
}
/**
* Initializes a small texture tile for use with tiled blit operations (see
* MTLBlitLoops.c and MTLMaskBlit.c for usage examples). The texture ID for
* the tile is stored in the given MTLContext. The tile is initially filled
* with garbage values, but the tile is updated as needed (via
* glTexSubImage2D()) with real RGBA values used in tiled blit situations.
* The internal format for the texture is GL_RGBA8, which should be sufficient
* for storing system memory surfaces of any known format (see PixelFormats
* for a list of compatible surface formats).
*/
jboolean
MTLContext_InitBlitTileTexture(MTLContext *mtlc)
{
//TODO
J2dTraceNotImplPrimitive("MTLContext_InitBlitTileTexture");
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_InitBlitTileTexture");
return JNI_TRUE;
}
/**
* Destroys the OpenGL resources associated with the given MTLContext.
* It is required that the native context associated with the MTLContext
* be made current prior to calling this method.
*/
void
MTLContext_DestroyContextResources(MTLContext *mtlc)
{
//TODO
J2dTraceNotImplPrimitive("MTLContext_DestroyContextResources");
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_DestroyContextResources");
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[mtlc->mtlTexturePool release];
[mtlc->mtlDevice release];
mtlc->mtlDevice = nil;
[pool drain];
//if (mtlc->blitTextureID != 0) {
//}
}
/*
* Class: sun_java2d_metal_MTLContext
* Method: getMTLIdString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_sun_java2d_metal_MTLContext_getMTLIdString
(JNIEnv *env, jclass mtlcc)
{
char *vendor, *renderer, *version;
char *pAdapterId;
jobject ret = NULL;
int len;
return NULL;
}
MTLRenderPassDescriptor * _createRenderPassDesc(id<MTLTexture> dest) {
MTLRenderPassDescriptor * result = [MTLRenderPassDescriptor renderPassDescriptor];
if (result == nil)
return nil;
if (dest == nil) {
J2dTraceLn(J2D_TRACE_ERROR, "_createRenderPassDesc: destination texture is null");
return nil;
}
MTLRenderPassColorAttachmentDescriptor * ca = result.colorAttachments[0];
ca.texture = dest;
ca.loadAction = MTLLoadActionLoad;
ca.clearColor = MTLClearColorMake(0.0f, 0.9f, 0.0f, 1.0f);
ca.storeAction = MTLStoreActionStore;
return result;
}
void MTLContext_SetColor(MTLContext *mtlc, int r, int g, int b, int a) {
mtlc->mtlColor = 0;
mtlc->mtlColor |= (r & (0xFF)) << 16;
mtlc->mtlColor |= (g & (0xFF)) << 8;
mtlc->mtlColor |= b & (0xFF);
mtlc->mtlColor |= (a & (0xFF)) << 24;
J2dTraceLn4(J2D_TRACE_INFO, "MTLContext_SetColor (%d, %d, %d) %d", r,g,b,a);
}
void MTLContext_SetColorInt(MTLContext *mtlc, int pixel) {
mtlc->mtlColor = pixel;
J2dTraceLn5(J2D_TRACE_INFO, "MTLContext_SetColorInt: pixel=%08x [r=%d g=%d b=%d a=%d]", pixel, (pixel >> 16) & (0xFF), (pixel >> 8) & 0xFF, (pixel) & 0xFF, (pixel >> 24) & 0xFF);
}
static id<MTLCommandBuffer> _getCommandBuffer(MTLContext *mtlc) {
if (mtlc == NULL)
return nil;
if (mtlc->mtlCommandBuffer == nil) {
// NOTE: Command queues are thread-safe and allow multiple outstanding command buffers to be encoded simultaneously.
mtlc->mtlCommandBuffer = [[mtlc->mtlCommandQueue commandBuffer] retain];// released in [layer blitTexture]
}
return mtlc->mtlCommandBuffer;
}
// NOTE: debug parameners will be removed soon
id<MTLRenderCommandEncoder> _createRenderEncoder(MTLContext *mtlc, id<MTLTexture> dest, int clearRed/*debug param*/) {
id<MTLCommandBuffer> cb = _getCommandBuffer(mtlc);
if (cb == nil)
return nil;
MTLRenderPassDescriptor * rpd = _createRenderPassDesc(dest);
if (rpd == nil)
return nil;
if (clearRed > 0) {//dbg
MTLRenderPassColorAttachmentDescriptor * ca = rpd.colorAttachments[0];
ca.loadAction = MTLLoadActionClear;
ca.clearColor = MTLClearColorMake(clearRed/255.0f, 0.0f, 0.0f, 1.0f);
}
// J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLContext: created render encoder to draw on tex=%p", dest);
id <MTLRenderCommandEncoder> mtlEncoder = [cb renderCommandEncoderWithDescriptor:rpd];
// set viewport and pipeline state
dest = rpd.colorAttachments[0].texture;
MTLViewport vp = {0, 0, dest.width, dest.height, 0, 1};
[mtlEncoder setViewport:vp];
[mtlEncoder setRenderPipelineState:mtlc->mtlPipelineState];
if (mtlc->useClip)
[mtlEncoder setScissorRect:mtlc->mtlClipRect];
// set color from ctx
int r = (mtlc->mtlColor >> 16) & (0xFF);
int g = (mtlc->mtlColor >> 8) & 0xFF;
int b = (mtlc->mtlColor) & 0xFF;
int a = (mtlc->mtlColor >> 24) & 0xFF;
vector_float4 color = {r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f};
struct FrameUniforms uf = {color};
[mtlEncoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
return mtlEncoder;
}
id<MTLRenderCommandEncoder> MTLContext_CreateRenderEncoder(MTLContext *mtlc, id<MTLTexture> dest) {
return _createRenderEncoder(mtlc, dest, -1);
}
// NOTE: debug parameners will be removed soon
id<MTLRenderCommandEncoder> _createSamplingEncoder(MTLContext * mtlc, id<MTLTexture> dest, int clearRed/*debug param*/) {
id<MTLCommandBuffer> cb = _getCommandBuffer(mtlc);
if (cb == nil)
return nil;
MTLRenderPassDescriptor * rpd = _createRenderPassDesc(dest);
if (rpd == nil)
return nil;
if (clearRed > 0) {//dbg
MTLRenderPassColorAttachmentDescriptor *ca = rpd.colorAttachments[0];
ca.loadAction = MTLLoadActionClear;
ca.clearColor = MTLClearColorMake(clearRed/255.f, 0.0f, 0.0f, 1.0f);
}
id <MTLRenderCommandEncoder> mtlEncoder = [cb renderCommandEncoderWithDescriptor:rpd];
//J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLContext: created sampling encoder to draw on tex=%p", dest);
// set viewport and pipeline state
MTLViewport vp = {0, 0, dest.width, dest.height, 0, 1};
[mtlEncoder setViewport:vp];
if (mtlc->alphaCompositeRule == RULE_Src)
[mtlEncoder setRenderPipelineState:mtlc->mtlBlitPipelineState];
else if (mtlc->alphaCompositeRule == RULE_SrcOver)
[mtlEncoder setRenderPipelineState:mtlc->mtlBlitSrcOverPipelineState];
else {
J2dTraceLn2(J2D_TRACE_ERROR, "MTLContext: alpha-composite rule %d isn't implemented (draw on %p)", mtlc->alphaCompositeRule, dest);
[mtlEncoder setRenderPipelineState:mtlc->mtlBlitPipelineState];
}
return mtlEncoder;
}
// NOTE: debug parameners will be removed soon
id<MTLRenderCommandEncoder> _createSamplingTransformEncoder(MTLContext * mtlc, id<MTLTexture> dest, int clearRed/*debug param*/) {
id<MTLCommandBuffer> cb = _getCommandBuffer(mtlc);
if (cb == nil)
return nil;
MTLRenderPassDescriptor * rpd = _createRenderPassDesc(dest);
if (rpd == nil)
return nil;
if (clearRed > 0) {//dbg
MTLRenderPassColorAttachmentDescriptor *ca = rpd.colorAttachments[0];
ca.loadAction = MTLLoadActionClear;
ca.clearColor = MTLClearColorMake(clearRed/255.f, 0.0f, 0.0f, 1.0f);
}
id <MTLRenderCommandEncoder> mtlEncoder = [cb renderCommandEncoderWithDescriptor:rpd];
//J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLContext: created sampling-transform encoder to draw on tex=%p", dest);
// set viewport and pipeline state
MTLViewport vp = {0, 0, dest.width, dest.height, 0, 1};
[mtlEncoder setViewport:vp];
if (mtlc->alphaCompositeRule == RULE_Src)
[mtlEncoder setRenderPipelineState:mtlc->mtlBlitMatrixPipelineState];
else if (mtlc->alphaCompositeRule == RULE_SrcOver)
[mtlEncoder setRenderPipelineState:mtlc->mtlBlitMatrixSrcOverPipelineState];
else {
J2dTraceLn2(J2D_TRACE_ERROR, "MTLContext: alpha-composite rule %d isn't implemented (draw on %p with transform)", mtlc->alphaCompositeRule, dest);
[mtlEncoder setRenderPipelineState:mtlc->mtlBlitMatrixPipelineState];
}
[mtlEncoder setVertexBytes:&(mtlc->transform4x4) length:sizeof(mtlc->transform4x4) atIndex:FrameUniformBuffer];
return mtlEncoder;
}
id<MTLRenderCommandEncoder> MTLContext_CreateSamplingEncoder(MTLContext * mtlc, id<MTLTexture> dest) {
if (mtlc->useTransform)
return _createSamplingTransformEncoder(mtlc, dest, -1);
return _createSamplingEncoder(mtlc, dest, -1);
}
id<MTLBlitCommandEncoder> MTLContext_CreateBlitEncoder(MTLContext *mtlc) {
id<MTLCommandBuffer> cb = _getCommandBuffer(mtlc);
return cb == nil ? nil : [cb blitCommandEncoder];
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLFuncs_h_Included
#define MTLFuncs_h_Included
#ifdef MACOSX
#include <dlfcn.h>
#endif
#include "jni.h"
#include "Trace.h"
jboolean MTLFuncs_OpenLibrary();
void MTLFuncs_CloseLibrary();
jboolean MTLFuncs_InitPlatformFuncs();
jboolean MTLFuncs_InitBaseFuncs();
jboolean MTLFuncs_InitExtFuncs();
#endif /* MTLFuncs_h_Included */

View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include "MTLFuncs.h"
jboolean
MTLFuncs_OpenLibrary()
{
//TODO
J2dTraceNotImplPrimitive("MTLFuncs_OpenLibrary");
J2dRlsTraceLn(J2D_TRACE_INFO, "MTLFuncs_OpenLibrary");
return JNI_TRUE;
}
void
MTLFuncs_CloseLibrary()
{
//TODO
J2dTraceNotImplPrimitive("MTLFuncs_CloseLibrary");
J2dRlsTraceLn(J2D_TRACE_INFO, "MTLFuncs_CloseLibrary");
}
jboolean
MTLFuncs_InitPlatformFuncs()
{
//TODO
J2dTraceNotImplPrimitive("MTLFuncs_InitPlatformFuncs");
J2dRlsTraceLn(J2D_TRACE_INFO, "MTLFuncs_InitPlatformFuncs");
return JNI_TRUE;
}
jboolean
MTLFuncs_InitBaseFuncs()
{
//TODO
J2dTraceNotImplPrimitive("MTLFuncs_InitBaseFuncs");
J2dRlsTraceLn(J2D_TRACE_INFO, "MTLFuncs_InitBaseFuncs");
return JNI_TRUE;
}
jboolean
MTLFuncs_InitExtFuncs()
{
//TODO
J2dTraceNotImplPrimitive("MTLFuncs_InitExtFuncs");
J2dRlsTraceLn(J2D_TRACE_INFO, "MTLFuncs_InitExtFuncs");
return JNI_TRUE;
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLGraphicsConfig_h_Included
#define MTLGraphicsConfig_h_Included
#import "jni.h"
#import "MTLSurfaceDataBase.h"
#import "MTLContext.h"
#import <Cocoa/Cocoa.h>
#import <Metal/Metal.h>
#import <MetalKit/MetalKit.h>
@interface MTLGraphicsConfigUtil : NSObject {}
+ (void) _getMTLConfigInfo: (NSMutableArray *)argValue;
@end
// REMIND: Using an NSOpenGLPixelBuffer as the scratch surface has been
// problematic thus far (seeing garbage and flickering when switching
// between an NSView and the scratch surface), so the following enables
// an alternate codepath that uses a hidden NSWindow/NSView as the scratch
// surface, for the purposes of making a context current in certain
// situations. It appears that calling [NSOpenGLContext setView] too
// frequently contributes to the bad behavior, so we should try to avoid
// switching to the scratch surface whenever possible.
/* Do we need this if we are using all off-screen drawing ? */
#define USE_NSVIEW_FOR_SCRATCH 1
/* Uncomment to have an additional CAOGLLayer instance tied to
* each instance, which can be used to test remoting the layer
* to an out of process window. The additional layer is needed
* because a layer can only be attached to one context (view/window).
* This is only for testing purposes and can be removed if/when no
* longer needed.
*/
/**
* The MTLGraphicsConfigInfo structure contains information specific to a
* given CGLGraphicsConfig (pixel format).
*
* jint screen;
* The screen and PixelFormat for the associated CGLGraphicsConfig.
*
* NSOpenGLPixelFormat *pixfmt;
* The pixel format of the native NSOpenGL context.
*
* MTLContext *context;
* The context associated with this CGLGraphicsConfig.
*/
typedef struct _MTLGraphicsConfigInfo {
jint screen;
NSOpenGLPixelFormat *pixfmt;
MTLContext *context;
} MTLGraphicsConfigInfo;
#endif /* MTLGraphicsConfig_h_Included */

View File

@@ -0,0 +1,327 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#import "sun_java2d_metal_MTLGraphicsConfig.h"
#import "MTLGraphicsConfig.h"
#import "MTLSurfaceData.h"
#import "ThreadUtilities.h"
#import <stdlib.h>
#import <string.h>
#import <ApplicationServices/ApplicationServices.h>
#import <JavaNativeFoundation/JavaNativeFoundation.h>
#pragma mark -
#pragma mark "--- Mac OS X specific methods for GL pipeline ---"
/**
* Disposes all memory and resources associated with the given
* CGLGraphicsConfigInfo (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) {
MTLContext_DestroyContextResources(mtlc);
mtlinfo->context = NULL;
}
free(mtlinfo);
}
#pragma mark -
#pragma mark "--- MTLGraphicsConfig methods ---"
/**
* Attempts to initialize CGL and the core OpenGL library.
*/
JNIEXPORT jboolean JNICALL
Java_sun_java2d_metal_MTLGraphicsConfig_initMTL
(JNIEnv *env, jclass cglgc)
{
J2dRlsTraceLn(J2D_TRACE_INFO, "MTLGraphicsConfig_initMTL");
if (!MTLFuncs_OpenLibrary()) {
return JNI_FALSE;
}
if (!MTLFuncs_InitPlatformFuncs() ||
!MTLFuncs_InitBaseFuncs() ||
!MTLFuncs_InitExtFuncs())
{
MTLFuncs_CloseLibrary();
return JNI_FALSE;
}
return JNI_TRUE;
}
/**
* Determines whether the CGL pipeline can be used for a given GraphicsConfig
* provided its screen number and visual ID. If the minimum requirements are
* met, the native CGLGraphicsConfigInfo 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 CGL
* 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 cglgc, jint displayID, jstring mtlShadersLib)
{
jlong ret = 0L;
JNF_COCOA_ENTER(env);
NSMutableArray * retArray = [NSMutableArray arrayWithCapacity:3];
[retArray addObject: [NSNumber numberWithInt: (int)displayID]];
[retArray addObject: [NSString stringWithUTF8String: JNU_GetStringPlatformChars(env, mtlShadersLib, 0)]];
if ([NSThread isMainThread]) {
[MTLGraphicsConfigUtil _getMTLConfigInfo: retArray];
} else {
[MTLGraphicsConfigUtil performSelectorOnMainThread: @selector(_getMTLConfigInfo:) withObject: retArray waitUntilDone: YES];
}
NSNumber * num = (NSNumber *)[retArray objectAtIndex: 0];
ret = (jlong)[num longValue];
JNF_COCOA_EXIT(env);
return ret;
}
static struct TxtVertex verts[PGRAM_VERTEX_COUNT] = {
{{-1.0, 1.0, 0.0}, {0.0, 0.0}},
{{1.0, 1.0, 0.0}, {1.0, 0.0}},
{{1.0, -1.0, 0.0}, {1.0, 1.0}},
{{1.0, -1.0, 0.0}, {1.0, 1.0}},
{{-1.0, -1.0, 0.0}, {0.0, 1.0}},
{{-1.0, 1.0, 0.0}, {0.0, 0.0}}
};
@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;
}
NSView *scratchSurface =
[[NSView alloc]
initWithFrame: contentRect];
if (scratchSurface == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLGraphicsConfig_getMTLConfigInfo: NSView is NULL");
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
[window setContentView: scratchSurface];
MTLContext *mtlc = (MTLContext *)malloc(sizeof(MTLContext));
if (mtlc == 0L) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLGC_InitMTLContext: could not allocate memory for mtlc");
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
memset(mtlc, 0, sizeof(MTLContext));
mtlc->mtlDevice = [CGDirectDisplayCopyCurrentMetalDevice(displayID) retain];
mtlc->mtlTexturePool = [[[MTLTexturePool alloc] initWithDevice:mtlc->mtlDevice] retain];
mtlc->mtlVertexBuffer = [[mtlc->mtlDevice newBufferWithBytes:verts
length:sizeof(verts)
options:MTLResourceCPUCacheModeDefaultCache] retain];
NSError *error = nil;
mtlc->mtlLibrary = [mtlc->mtlDevice newLibraryWithFile: mtlShadersLib error:&error];
if (!mtlc->mtlLibrary) {
NSLog(@"Failed to load library. error %@", error);
exit(0);
}
id <MTLFunction> vertColFunc = [mtlc->mtlLibrary newFunctionWithName:@"vert_col"];
id <MTLFunction> vertTxtFunc = [mtlc->mtlLibrary newFunctionWithName:@"vert_txt"];
id <MTLFunction> vertTxtMatrixFunc = [mtlc->mtlLibrary newFunctionWithName:@"vert_txt_matrix"];
id <MTLFunction> fragColFunc = [mtlc->mtlLibrary newFunctionWithName:@"frag_col"];
id <MTLFunction> fragTxtFunc = [mtlc->mtlLibrary newFunctionWithName:@"frag_txt"];
// Create depth state.
MTLDepthStencilDescriptor *depthDesc = [MTLDepthStencilDescriptor new];
depthDesc.depthCompareFunction = MTLCompareFunctionLess;
depthDesc.depthWriteEnabled = YES;
MTLVertexDescriptor *vertDesc = [MTLVertexDescriptor new];
vertDesc.attributes[VertexAttributePosition].format = MTLVertexFormatFloat3;
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;
// Create pipeline state.
MTLRenderPipelineDescriptor *pipelineDesc = [MTLRenderPipelineDescriptor new];
pipelineDesc.sampleCount = 1;
pipelineDesc.vertexFunction = vertColFunc;
pipelineDesc.fragmentFunction = fragColFunc;
pipelineDesc.vertexDescriptor = vertDesc;
pipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
mtlc->mtlPipelineState = [mtlc->mtlDevice newRenderPipelineStateWithDescriptor:pipelineDesc error:&error];
if (!mtlc->mtlPipelineState) {
NSLog(@"Failed to create pipeline state, error %@", error);
exit(0);
}
vertDesc = [MTLVertexDescriptor new];
vertDesc.attributes[VertexAttributePosition].format = MTLVertexFormatFloat3;
vertDesc.attributes[VertexAttributePosition].offset = 0;
vertDesc.attributes[VertexAttributePosition].bufferIndex = MeshVertexBuffer;
vertDesc.attributes[VertexAttributeTexPos].format = MTLVertexFormatFloat2;
vertDesc.attributes[VertexAttributeTexPos].offset = 3*sizeof(float);
vertDesc.attributes[VertexAttributeTexPos].bufferIndex = MeshVertexBuffer;
vertDesc.layouts[MeshVertexBuffer].stride = sizeof(struct TxtVertex);
vertDesc.layouts[MeshVertexBuffer].stepRate = 1;
vertDesc.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;
// Create pipeline state.
pipelineDesc = [MTLRenderPipelineDescriptor new];
pipelineDesc.sampleCount = 1;
pipelineDesc.vertexFunction = vertTxtFunc;
pipelineDesc.fragmentFunction = fragTxtFunc;
pipelineDesc.vertexDescriptor = vertDesc;
pipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
mtlc->mtlBlitPipelineState = [mtlc->mtlDevice newRenderPipelineStateWithDescriptor:pipelineDesc error:&error];
if (!mtlc->mtlBlitPipelineState) {
NSLog(@"Failed to create blit pipeline state, error %@", error);
exit(0);
}
pipelineDesc = [MTLRenderPipelineDescriptor new];
pipelineDesc.sampleCount = 1;
pipelineDesc.vertexFunction = vertTxtMatrixFunc;
pipelineDesc.fragmentFunction = fragTxtFunc;
pipelineDesc.vertexDescriptor = vertDesc;
pipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
mtlc->mtlBlitMatrixPipelineState = [mtlc->mtlDevice newRenderPipelineStateWithDescriptor:pipelineDesc error:&error];
if (!mtlc->mtlBlitMatrixPipelineState) {
NSLog(@"Failed to create blit with matrix pipeline state, error %@", error);
exit(0);
}
pipelineDesc.vertexFunction = vertTxtFunc;
pipelineDesc.colorAttachments[0].blendingEnabled = YES;
//RGB = Source.rgb * SBF + Dest.rgb * DBF
//A = Source.a * SBF + Dest.a * DBF
//
//default SRC:
//DBF=0
//SBF=1
//
// SRC_OVER (Porter-Duff Source Over Destination rule):
// Ar = As + Ad*(1-As)
// Cr = Cs + Cd*(1-As)
pipelineDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
pipelineDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
const bool isSourcePremultilied = NO; // <==> Cs = Cs_raw * As_raw
if (!isSourcePremultilied)
pipelineDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
mtlc->mtlBlitSrcOverPipelineState = [mtlc->mtlDevice newRenderPipelineStateWithDescriptor:pipelineDesc error:&error];
if (!mtlc->mtlBlitSrcOverPipelineState) {
NSLog(@"Failed to create blit pipeline state (SrcOver), error %@", error);
exit(0);
}
pipelineDesc.vertexFunction = vertTxtMatrixFunc;
mtlc->mtlBlitMatrixSrcOverPipelineState = [mtlc->mtlDevice newRenderPipelineStateWithDescriptor:pipelineDesc error:&error];
if (!mtlc->mtlBlitMatrixSrcOverPipelineState) {
NSLog(@"Failed to create blit with matrix pipeline state (SrcOver), error %@", error);
exit(0);
}
mtlc->mtlCommandBuffer = nil;
// Create command queue
mtlc->mtlCommandQueue = [mtlc->mtlDevice newCommandQueue];
// 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->screen = displayID;
mtlinfo->context = mtlc;
[argValue addObject: [NSNumber numberWithLong:ptr_to_jlong(mtlinfo)]];
[pool drain];
}
@end //GraphicsConfigUtil
JNIEXPORT jint JNICALL
Java_sun_java2d_metal_MTLGraphicsConfig_nativeGetMaxTextureSize
(JNIEnv *env, jclass mtlgc)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLGraphicsConfig_nativeGetMaxTextureSize");
__block int max = 0;
// [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
// }];
return (jint)max;
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLLayer_h_Included
#define MTLLayer_h_Included
#import <Metal/Metal.h>
#import <MetalKit/MetalKit.h>
#import "common.h"
#import <JavaNativeFoundation/JavaNativeFoundation.h>
@interface MTLLayer : CAMetalLayer
{
@private
JNFWeakJObjectWrapper *javaLayer;
// intermediate buffer, used the RQ lock to synchronize
MTLContext* ctx;
float bufferWidth;
float bufferHeight;
id<MTLTexture> buffer;
}
@property (nonatomic, retain) JNFWeakJObjectWrapper *javaLayer;
@property (readwrite, assign) MTLContext* ctx;
@property (readwrite, assign) float bufferWidth;
@property (readwrite, assign) float bufferHeight;
@property (readwrite, assign) id<MTLTexture> buffer;
- (id) initWithJavaLayer:(JNFWeakJObjectWrapper *)layer;
- (void) blitTexture:(id<MTLCommandBuffer>)commandBuf;
- (void) fillParallelogramCtxX:(jfloat)x
Y:(jfloat)y
DX1:(jfloat)dx1
DY1:(jfloat)dy1
DX2:(jfloat)dx2
DY2:(jfloat)dy2;
@end
#endif /* CGLLayer_h_Included */

View File

@@ -0,0 +1,180 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#import "MTLGraphicsConfig.h"
#import "MTLLayer.h"
#import "ThreadUtilities.h"
#import "LWCToolkit.h"
#import "MTLSurfaceData.h"
#import "MTLBlitLoops.h"
@implementation MTLLayer
@synthesize javaLayer;
@synthesize ctx;
@synthesize bufferWidth;
@synthesize bufferHeight;
@synthesize buffer;
- (id) initWithJavaLayer:(JNFWeakJObjectWrapper *)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];
return self;
}
- (void) blitTexture:(id<MTLCommandBuffer>)commandBuf {
if (self.ctx == NULL || self.javaLayer == NULL || self.buffer == nil || ctx->mtlDevice == nil) {
J2dTraceLn4(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: uninitialized (mtlc=%p, javaLayer=%p, buffer=%p, devide=%p)", self.ctx, self.javaLayer, self.buffer, ctx->mtlDevice);
return;
}
if (commandBuf == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: nothing to do (commandBuf is null)");
return;
}
@autoreleasepool {
self.device = ctx->mtlDevice;
self.pixelFormat = MTLPixelFormatBGRA8Unorm;
self.framebufferOnly = NO;
self.drawableSize =
CGSizeMake(self.buffer.width,
self.buffer.height);
id<CAMetalDrawable> mtlDrawable = [self nextDrawable];
if (mtlDrawable == nil) {
return;
}
J2dTraceLn6(J2D_TRACE_INFO, "MTLLayer.blitTexture: src tex=%p (w=%d, h=%d), dst tex=%p (w=%d, h=%d)", self.buffer, self.buffer.width, self.buffer.height, mtlDrawable.texture, mtlDrawable.texture.width, mtlDrawable.texture.height);
id <MTLBlitCommandEncoder> blitEncoder = [commandBuf blitCommandEncoder];
[blitEncoder
copyFromTexture:self.buffer sourceSlice:0 sourceLevel:0 sourceOrigin:MTLOriginMake(0, 0, 0) sourceSize:MTLSizeMake(self.buffer.width, self.buffer.height, 1)
toTexture:mtlDrawable.texture destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(0, 0, 0)];
[blitEncoder endEncoding];
[commandBuf presentDrawable:mtlDrawable];
[commandBuf addCompletedHandler:^(id <MTLCommandBuffer> cmdBuff) {
[cmdBuff release];
[ctx->mtlTexturePool markAllTexturesFree];
}];
[commandBuf commit];
}
}
- (void) dealloc {
self.javaLayer = nil;
[super dealloc];
}
@end
/*
* Class: sun_java2d_metal_CGLLayer
* Method: nativeCreateLayer
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_sun_java2d_metal_MTLLayer_nativeCreateLayer
(JNIEnv *env, jobject obj)
{
__block MTLLayer *layer = nil;
JNF_COCOA_ENTER(env);
JNFWeakJObjectWrapper *javaLayer = [JNFWeakJObjectWrapper wrapperWithJObject:obj withEnv:env];
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
AWT_ASSERT_APPKIT_THREAD;
layer = [[MTLLayer alloc] initWithJavaLayer: javaLayer];
}];
JNF_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->mtlDevice;
} else {
layer.ctx = NULL;
}
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLLayer_nativeSetScale
(JNIEnv *env, jclass cls, jlong layerPtr, jdouble scale)
{
JNF_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;
}];
JNF_COCOA_EXIT(env);
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLMaskBlit_h_Included
#define MTLMaskBlit_h_Included
#include "MTLContext.h"
void MTLMaskBlit_MaskBlit(JNIEnv *env, MTLContext *mtlc,
jint dstx, jint dsty,
jint width, jint height,
void *pPixels);
#endif /* MTLMaskBlit_h_Included */

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <stdlib.h>
#include <jlong.h>
#include "MTLMaskBlit.h"
#include "MTLRenderQueue.h"
#include "MTLSurfaceDataBase.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,
jint dstx, jint dsty,
jint width, jint height,
void *pPixels)
{
//TODO
J2dTraceNotImplPrimitive("MTLMaskBlit_MaskBlit");
J2dTraceLn(J2D_TRACE_INFO, "MTLMaskBlit_MaskBlit");
if (width <= 0 || height <= 0) {
J2dTraceLn(J2D_TRACE_WARNING,
"MTLMaskBlit_MaskBlit: invalid dimensions");
return;
}
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLMaskFill_h_Included
#define MTLMaskFill_h_Included
#include "MTLContext.h"
void MTLMaskFill_MaskFill(MTLContext *mtlc,
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,111 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include "sun_java2d_metal_MTLMaskFill.h"
#include "MTLMaskFill.h"
#include "MTLRenderQueue.h"
#include "MTLVertexCache.h"
/**
* This implementation first copies the alpha tile into a texture and then
* maps that texture to the destination surface. This approach appears to
* offer the best performance despite being a two-step process.
*
* When the source paint is a Color, we can simply use the GL_MODULATE
* function to multiply the current color (already premultiplied with the
* extra alpha value from the AlphaComposite) with the alpha value from
* the mask texture tile. In picture form, this process looks like:
*
* A R G B
* primary color Pa Pr Pg Pb (modulated with...)
* texture unit 0 Ca Ca Ca Ca
* ---------------------------------------
* resulting color Ra Rr Rg Rb
*
* where:
* Px = current color (already premultiplied by extra alpha)
* Cx = coverage value from mask tile
* Rx = resulting color/alpha component
*
* When the source paint is not a Color, it means that we are rendering with
* a complex paint (e.g. GradientPaint, TexturePaint). In this case, we
* rely on the GL_ARB_multitexture extension to effectively multiply the
* paint fragments (autogenerated on texture unit 1, see the
* MTLPaints_Set{Gradient,Texture,etc}Paint() methods for more details)
* with the coverage values from the mask texture tile (provided on texture
* unit 0), all of which is multiplied with the current color value (which
* contains the extra alpha value). In picture form:
*
* A R G B
* primary color Ea Ea Ea Ea (modulated with...)
* texture unit 0 Ca Ca Ca Ca (modulated with...)
* texture unit 1 Pa Pr Pg Pb
* ---------------------------------------
* resulting color Ra Rr Rg Rb
*
* where:
* Ea = extra alpha
* Cx = coverage value from mask tile
* Px = gradient/texture paint color (generated for each fragment)
* Rx = resulting color/alpha component
*
* Here are some 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,
jint x, jint y, jint w, jint h,
jint maskoff, jint maskscan, jint masklen,
unsigned char *pMask)
{
//TODO
J2dTraceNotImplPrimitive("MTLMaskFill_MaskFill");
J2dTraceLn(J2D_TRACE_INFO, "MTLMaskFill_MaskFill");
}
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();
unsigned char *mask;
//TODO
J2dTraceNotImplPrimitive("MTLMaskFill_maskFill");
J2dTraceLn(J2D_TRACE_ERROR, "MTLMaskFill_maskFill");
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*/
#ifndef MTLPaints_h_Included
#define MTLPaints_h_Included
#include "MTLContext.h"
void MTLPaints_ResetPaint(MTLContext *mtlc);
void MTLPaints_SetColor(MTLContext *mtlc, jint pixel);
void MTLPaints_SetGradientPaint(MTLContext *mtlc,
jboolean useMask, jboolean cyclic,
jdouble p0, jdouble p1, jdouble p3,
jint pixel1, jint pixel2);
void MTLPaints_SetLinearGradientPaint(MTLContext *mtlc, BMTLSDOps *dstOps,
jboolean useMask, jboolean linear,
jint cycleMethod, jint numStops,
jfloat p0, jfloat p1, jfloat p3,
void *fractions, void *pixels);
void MTLPaints_SetRadialGradientPaint(MTLContext *mtlc, BMTLSDOps *dstOps,
jboolean useMask, jboolean linear,
jint cycleMethod, jint numStops,
jfloat m00, jfloat m01, jfloat m02,
jfloat m10, jfloat m11, jfloat m12,
jfloat focusX,
void *fractions, void *pixels);
void MTLPaints_SetTexturePaint(MTLContext *mtlc,
jboolean useMask,
jlong pSrcOps, jboolean filter,
jdouble xp0, jdouble xp1, jdouble xp3,
jdouble yp0, jdouble yp1, jdouble yp3);
#endif /* MTLPaints_h_Included */

View File

@@ -0,0 +1,471 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <jlong.h>
#include <string.h>
#include "sun_java2d_SunGraphics2D.h"
#include "sun_java2d_pipe_BufferedPaints.h"
#include "MTLPaints.h"
#include "MTLContext.h"
#include "MTLRenderQueue.h"
#include "MTLSurfaceData.h"
void
MTLPaints_ResetPaint(MTLContext *mtlc)
{
//TODO
J2dTraceNotImplPrimitive("MTLPaints_ResetPaint");
J2dTraceLn(J2D_TRACE_INFO, "MTLPaints_ResetPaint");
}
void
MTLPaints_SetColor(MTLContext *mtlc, jint pixel)
{
//TODO
J2dTraceNotImplPrimitive("MTLPaints_SetColor");
MTLContext_SetColorInt(mtlc, pixel);
}
/************************* GradientPaint support ****************************/
static GLuint gradientTexID = 0;
static void
MTLPaints_InitGradientTexture()
{
//TODO
J2dTraceNotImplPrimitive("MTLPaints_InitGradientTexture");
J2dTraceLn(J2D_TRACE_INFO, "MTLPaints_InitGradientTexture");
}
void
MTLPaints_SetGradientPaint(MTLContext *mtlc,
jboolean useMask, jboolean cyclic,
jdouble p0, jdouble p1, jdouble p3,
jint pixel1, jint pixel2)
{
//TODO
J2dTraceNotImplPrimitive("MTLPaints_SetGradientPaint");
J2dTraceLn(J2D_TRACE_INFO, "MTLPaints_SetGradientPaint");
}
/************************** TexturePaint support ****************************/
void
MTLPaints_SetTexturePaint(MTLContext *mtlc,
jboolean useMask,
jlong pSrcOps, jboolean filter,
jdouble xp0, jdouble xp1, jdouble xp3,
jdouble yp0, jdouble yp1, jdouble yp3)
{
//TODO
J2dTraceNotImplPrimitive("MTLPaints_SetTexturePaint");
}
/****************** Shared MultipleGradientPaint support ********************/
/**
* These constants are identical to those defined in the
* MultipleGradientPaint.CycleMethod enum; they are copied here for
* convenience (ideally we would pull them directly from the Java level,
* but that entails more hassle than it is worth).
*/
#define CYCLE_NONE 0
#define CYCLE_REFLECT 1
#define CYCLE_REPEAT 2
/**
* The following constants are flags that can be bitwise-or'ed together
* to control how the MultipleGradientPaint shader source code is generated:
*
* MULTI_CYCLE_METHOD
* Placeholder for the CycleMethod enum constant.
*
* MULTI_LARGE
* If set, use the (slower) shader that supports a larger number of
* gradient colors; otherwise, use the optimized codepath. See
* the MAX_FRACTIONS_SMALL/LARGE constants below for more details.
*
* MULTI_USE_MASK
* If set, apply the alpha mask value from texture unit 0 to the
* final color result (only used in the MaskFill case).
*
* MULTI_LINEAR_RGB
* If set, convert the linear RGB result back into the sRGB color space.
*/
#define MULTI_CYCLE_METHOD (3 << 0)
#define MULTI_LARGE (1 << 2)
#define MULTI_USE_MASK (1 << 3)
#define MULTI_LINEAR_RGB (1 << 4)
/**
* This value determines the size of the array of programs for each
* MultipleGradientPaint type. This value reflects the maximum value that
* can be represented by performing a bitwise-or of all the MULTI_*
* constants defined above.
*/
#define MAX_PROGRAMS 32
/** Evaluates to true if the given bit is set on the local flags variable. */
#define IS_SET(flagbit) \
(((flags) & (flagbit)) != 0)
/** Composes the given parameters as flags into the given flags variable.*/
#define COMPOSE_FLAGS(flags, cycleMethod, large, useMask, linear) \
do { \
flags |= ((cycleMethod) & MULTI_CYCLE_METHOD); \
if (large) flags |= MULTI_LARGE; \
if (useMask) flags |= MULTI_USE_MASK; \
if (linear) flags |= MULTI_LINEAR_RGB; \
} while (0)
/** Extracts the CycleMethod enum value from the given flags variable. */
#define EXTRACT_CYCLE_METHOD(flags) \
((flags) & MULTI_CYCLE_METHOD)
/**
* The maximum number of gradient "stops" supported by the fragment shader
* and related code. When the MULTI_LARGE flag is set, we will use
* MAX_FRACTIONS_LARGE; otherwise, we use MAX_FRACTIONS_SMALL. By having
* two separate values, we can have one highly optimized shader (SMALL) that
* supports only a few fractions/colors, and then another, less optimal
* shader that supports more stops.
*/
#define MAX_FRACTIONS sun_java2d_pipe_BufferedPaints_MULTI_MAX_FRACTIONS
#define MAX_FRACTIONS_LARGE MAX_FRACTIONS
#define MAX_FRACTIONS_SMALL 4
/**
* The maximum number of gradient colors supported by all of the gradient
* fragment shaders. Note that this value must be a power of two, as it
* determines the size of the 1D texture created below. It also must be
* greater than or equal to MAX_FRACTIONS (there is no strict requirement
* that the two values be equal).
*/
#define MAX_COLORS 16
/**
* The handle to the gradient color table texture object used by the shaders.
*/
static jint multiGradientTexID = 0;
/**
* This is essentially a template of the shader source code that can be used
* for either LinearGradientPaint or RadialGradientPaint. It includes the
* structure and some variables that are common to each; the remaining
* code snippets (for CycleMethod, ColorSpaceType, and mask modulation)
* are filled in prior to compiling the shader at runtime depending on the
* paint parameters. See MTLPaints_CreateMultiGradProgram() for more details.
*/
static const char *multiGradientShaderSource =
// gradient texture size (in texels)
"const int TEXTURE_SIZE = %d;"
// maximum number of fractions/colors supported by this shader
"const int MAX_FRACTIONS = %d;"
// size of a single texel
"const float FULL_TEXEL = (1.0 / float(TEXTURE_SIZE));"
// size of half of a single texel
"const float HALF_TEXEL = (FULL_TEXEL / 2.0);"
// texture containing the gradient colors
"uniform sampler1D colors;"
// array of gradient stops/fractions
"uniform float fractions[MAX_FRACTIONS];"
// array of scale factors (one for each interval)
"uniform float scaleFactors[MAX_FRACTIONS-1];"
// (placeholder for mask variable)
"%s"
// (placeholder for Linear/RadialGP-specific variables)
"%s"
""
"void main(void)"
"{"
" float dist;"
// (placeholder for Linear/RadialGradientPaint-specific code)
" %s"
""
" float tc;"
// (placeholder for CycleMethod-specific code)
" %s"
""
// calculate interpolated color
" vec4 result = texture1D(colors, tc);"
""
// (placeholder for ColorSpace conversion code)
" %s"
""
// (placeholder for mask modulation code)
" %s"
""
// modulate with gl_Color in order to apply extra alpha
" gl_FragColor = result * gl_Color;"
"}";
/**
* This code takes a "dist" value as input (as calculated earlier by the
* LGP/RGP-specific code) in the range [0,1] and produces a texture
* coordinate value "tc" that represents the position of the chosen color
* in the one-dimensional gradient texture (also in the range [0,1]).
*
* One naive way to implement this would be to iterate through the fractions
* to figure out in which interval "dist" falls, and then compute the
* relative distance between the two nearest stops. This approach would
* require an "if" check on every iteration, and it is best to avoid
* conditionals in fragment shaders for performance reasons. Also, one might
* be tempted to use a break statement to jump out of the loop once the
* interval was found, but break statements (and non-constant loop bounds)
* are not natively available on most graphics hardware today, so that is
* a non-starter.
*
* The more optimal approach used here avoids these issues entirely by using
* an accumulation function that is equivalent to the process described above.
* The scaleFactors array is pre-initialized at enable time as follows:
* scaleFactors[i] = 1.0 / (fractions[i+1] - fractions[i]);
*
* For each iteration, we subtract fractions[i] from dist and then multiply
* that value by scaleFactors[i]. If we are within the target interval,
* this value will be a fraction in the range [0,1] indicating the relative
* distance between fraction[i] and fraction[i+1]. If we are below the
* target interval, this value will be negative, so we clamp it to zero
* to avoid accumulating any value. If we are above the target interval,
* the value will be greater than one, so we clamp it to one. Upon exiting
* the loop, we will have accumulated zero or more 1.0's and a single
* fractional value. This accumulated value tells us the position of the
* fragment color in the one-dimensional gradient texture, i.e., the
* texcoord called "tc".
*/
static const char *texCoordCalcCode =
"int i;"
"float relFraction = 0.0;"
"for (i = 0; i < MAX_FRACTIONS-1; i++) {"
" relFraction +="
" clamp((dist - fractions[i]) * scaleFactors[i], 0.0, 1.0);"
"}"
// we offset by half a texel so that we find the linearly interpolated
// color between the two texel centers of interest
"tc = HALF_TEXEL + (FULL_TEXEL * relFraction);";
/** Code for NO_CYCLE that gets plugged into the CycleMethod placeholder. */
static const char *noCycleCode =
"if (dist <= 0.0) {"
" tc = 0.0;"
"} else if (dist >= 1.0) {"
" tc = 1.0;"
"} else {"
// (placeholder for texcoord calculation)
" %s"
"}";
/** Code for REFLECT that gets plugged into the CycleMethod placeholder. */
static const char *reflectCode =
"dist = 1.0 - (abs(fract(dist * 0.5) - 0.5) * 2.0);"
// (placeholder for texcoord calculation)
"%s";
/** Code for REPEAT that gets plugged into the CycleMethod placeholder. */
static const char *repeatCode =
"dist = fract(dist);"
// (placeholder for texcoord calculation)
"%s";
static void
MTLPaints_InitMultiGradientTexture()
{
//TODO
J2dTraceNotImplPrimitive("MTLPaints_InitMultiGradientTexture");
}
/**
* Compiles and links the MultipleGradientPaint shader program. If
* successful, this function returns a handle to the newly created
* shader program; otherwise returns 0.
*/
static GLhandleARB
MTLPaints_CreateMultiGradProgram(jint flags,
char *paintVars, char *distCode)
{
//TODO
J2dTraceNotImplPrimitive("MTLPaints_CreateMultiGradProgram");
return NULL;
}
/**
* Called from the MTLPaints_SetLinear/RadialGradientPaint() methods
* in order to setup the fraction/color values that are common to both.
*/
static void
MTLPaints_SetMultiGradientPaint(GLhandleARB multiGradProgram,
jint numStops,
void *pFractions, void *pPixels)
{
//TODO
J2dTraceNotImplPrimitive("MTLPaints_SetMultiGradientPaint");
}
/********************** LinearGradientPaint support *************************/
/**
* The handles to the LinearGradientPaint fragment program objects. The
* index to the array should be a bitwise-or'ing of the MULTI_* flags defined
* above. Note that most applications will likely need to initialize one
* or two of these elements, so the array is usually sparsely populated.
*/
static GLhandleARB linearGradPrograms[MAX_PROGRAMS];
/**
* Compiles and links the LinearGradientPaint shader program. If successful,
* this function returns a handle to the newly created shader program;
* otherwise returns 0.
*/
static GLhandleARB
MTLPaints_CreateLinearGradProgram(jint flags)
{
char *paintVars;
char *distCode;
J2dTraceLn1(J2D_TRACE_INFO,
"MTLPaints_CreateLinearGradProgram",
flags);
/*
* To simplify the code and to make it easier to upload a number of
* uniform values at once, we pack a bunch of scalar (float) values
* into vec3 values below. Here's how the values are related:
*
* params.x = p0
* params.y = p1
* params.z = p3
*
* yoff = dstOps->yOffset + dstOps->height
*/
paintVars =
"uniform vec3 params;"
"uniform float yoff;";
distCode =
// note that gl_FragCoord is in window space relative to the
// lower-left corner, so we have to flip the y-coordinate here
"vec3 fragCoord = vec3(gl_FragCoord.x, yoff-gl_FragCoord.y, 1.0);"
"dist = dot(params, fragCoord);";
return MTLPaints_CreateMultiGradProgram(flags, paintVars, distCode);
}
void
MTLPaints_SetLinearGradientPaint(MTLContext *mtlc, BMTLSDOps *dstOps,
jboolean useMask, jboolean linear,
jint cycleMethod, jint numStops,
jfloat p0, jfloat p1, jfloat p3,
void *fractions, void *pixels)
{
//TODO
J2dTraceNotImplPrimitive("MTLPaints_SetMultiGradientPaint");
}
/********************** RadialGradientPaint support *************************/
/**
* The handles to the RadialGradientPaint fragment program objects. The
* index to the array should be a bitwise-or'ing of the MULTI_* flags defined
* above. Note that most applications will likely need to initialize one
* or two of these elements, so the array is usually sparsely populated.
*/
static GLhandleARB radialGradPrograms[MAX_PROGRAMS];
/**
* Compiles and links the RadialGradientPaint shader program. If successful,
* this function returns a handle to the newly created shader program;
* otherwise returns 0.
*/
static GLhandleARB
MTLPaints_CreateRadialGradProgram(jint flags)
{
char *paintVars;
char *distCode;
J2dTraceLn1(J2D_TRACE_INFO,
"MTLPaints_CreateRadialGradProgram",
flags);
/*
* To simplify the code and to make it easier to upload a number of
* uniform values at once, we pack a bunch of scalar (float) values
* into vec3 and vec4 values below. Here's how the values are related:
*
* m0.x = m00
* m0.y = m01
* m0.z = m02
*
* m1.x = m10
* m1.y = m11
* m1.z = m12
*
* precalc.x = focusX
* precalc.y = yoff = dstOps->yOffset + dstOps->height
* precalc.z = 1.0 - (focusX * focusX)
* precalc.w = 1.0 / precalc.z
*/
paintVars =
"uniform vec3 m0;"
"uniform vec3 m1;"
"uniform vec4 precalc;";
/*
* The following code is derived from Daniel Rice's whitepaper on
* radial gradient performance (attached to the bug report for 6521533).
* Refer to that document as well as the setup code in the Java-level
* BufferedPaints.setRadialGradientPaint() method for more details.
*/
distCode =
// note that gl_FragCoord is in window space relative to the
// lower-left corner, so we have to flip the y-coordinate here
"vec3 fragCoord ="
" vec3(gl_FragCoord.x, precalc.y - gl_FragCoord.y, 1.0);"
"float x = dot(fragCoord, m0);"
"float y = dot(fragCoord, m1);"
"float xfx = x - precalc.x;"
"dist = (precalc.x*xfx + sqrt(xfx*xfx + y*y*precalc.z))*precalc.w;";
return MTLPaints_CreateMultiGradProgram(flags, paintVars, distCode);
}
void
MTLPaints_SetRadialGradientPaint(MTLContext *mtlc, BMTLSDOps *dstOps,
jboolean useMask, jboolean linear,
jint cycleMethod, jint numStops,
jfloat m00, jfloat m01, jfloat m02,
jfloat m10, jfloat m11, jfloat m12,
jfloat focusX,
void *fractions, void *pixels)
{
//TODO
J2dTraceNotImplPrimitive("MTLPaints_SetRadialGradientPaint");
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,134 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLRenderQueue_h_Included
#define MTLRenderQueue_h_Included
#include "MTLContext.h"
#include "MTLSurfaceData.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)
/*
* 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)
/*
* Parameter used by the RESET_PREVIOUS_OP() convenience macro, which
* indicates that any "open" state (such as an unmatched glBegin() or
* glEnable(GL_TEXTURE_2D)) should be completed before the following operation
* is performed. SET_SURFACES is an example of an operation that needs to
* call RESET_PREVIOUS_OP() before completing the surface change operation.
*/
#define MTL_STATE_RESET -1
/*
* Parameter passed to the CHECK_PREVIOUS_OP() macro to indicate that the
* following operation represents a "simple" state change. A simple state
* change is one that is allowed to occur within a series of texturing
* operations; in other words, this type of state change can occur without
* first calling glDisable(GL_TEXTURE_2D). An example of such an operation
* is SET_RECT_CLIP.
*/
#define MTL_STATE_CHANGE -2
/*
* Parameter passed to the CHECK_PREVIOUS_OP() macro to indicate that the
* following operation represents an operation that uses an alpha mask,
* such as MTLMaskFill and MTLTR_DrawGrayscaleGlyphNoCache().
*/
#define MTL_STATE_MASK_OP -3
/*
* Parameter passed to the CHECK_PREVIOUS_OP() macro to indicate that the
* following operation represents an operation that uses the glyph cache,
* such as MTLTR_DrawGrayscaleGlyphViaCache().
*/
#define MTL_STATE_GLYPH_OP -4
/*
* Parameter passed to the CHECK_PREVIOUS_OP() macro to indicate that the
* following operation represents an operation that renders a
* parallelogram via a fragment program (see MTLRenderer).
*/
#define MTL_STATE_PGRAM_OP -5
/*
* Initializes the "previous operation" state to its default value.
*/
#define INIT_PREVIOUS_OP() previousOp = MTL_STATE_RESET
/*
* These macros now simply delegate to the CheckPreviousOp() method.
*/
#define CHECK_PREVIOUS_OP(op) MTLRenderQueue_CheckPreviousOp(op)
#define RESET_PREVIOUS_OP() CHECK_PREVIOUS_OP(MTL_STATE_RESET)
/*
* 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)
/*
* Exports.
*/
extern jint previousOp;
MTLContext *MTLRenderQueue_GetCurrentContext();
BMTLSDOps *MTLRenderQueue_GetCurrentDestination();
void MTLRenderQueue_CheckPreviousOp(jint op);
void MTLTR_DisableGlyphModeState();
#endif /* MTLRenderQueue_h_Included */

View File

@@ -0,0 +1,844 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#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"
/**
* Used to track whether we are in a series of a simple primitive operations
* or texturing operations. This variable should be controlled only via
* the INIT/CHECK/RESET_PREVIOUS_OP() macros. See the
* MTLRenderQueue_CheckPreviousOp() method below for more information.
*/
jint previousOp;
/**
* References to the "current" context and destination surface.
*/
static MTLContext *mtlc = NULL;
static BMTLSDOps *dstOps = NULL;
/**
* The following methods are implemented in the windowing system (i.e. GLX
* and WGL) source files.
*/
extern void MTLGC_DestroyMTLGraphicsConfig(jlong pConfigInfo);
extern void MTLSD_SwapBuffers(JNIEnv *env, jlong window);
/**
* Helper methods to manage modified layers
*/
static MTLLayer ** g_modifiedLayers = NULL;
static int g_modifiedLayersCount = 0;
static int g_modifiedLayersAllocatedCount = 0;
static void _markLayerModified(MTLLayer * modifiedLayer) {
if (modifiedLayer == NULL)
return;
if (g_modifiedLayers == NULL) {
g_modifiedLayersAllocatedCount = 3;
g_modifiedLayers = malloc(g_modifiedLayersAllocatedCount * sizeof(MTLLayer *));
}
for (int c = 0; c < g_modifiedLayersCount; ++c) {
if (g_modifiedLayers[c] == modifiedLayer)
return;
}
++g_modifiedLayersCount;
if (g_modifiedLayersCount > g_modifiedLayersAllocatedCount) {
g_modifiedLayersAllocatedCount = g_modifiedLayersCount;
g_modifiedLayers = realloc(g_modifiedLayers, g_modifiedLayersAllocatedCount * sizeof(MTLLayer *));
}
g_modifiedLayers[g_modifiedLayersCount - 1] = modifiedLayer;
}
static void _scheduleBlitAllModifiedLayers() {
for (int c = 0; c < g_modifiedLayersCount; ++c) {
MTLLayer * layer = g_modifiedLayers[c];
MTLContext * ctx = layer.ctx;
if (layer == NULL || ctx == NULL)
continue;
id<MTLCommandBuffer> bufferToCommit = ctx->mtlCommandBuffer;
[JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
[layer blitTexture:bufferToCommit];
}];
ctx->mtlCommandBuffer = nil;
}
g_modifiedLayersCount = 0;
}
static void _onSurfaceModified(BMTLSDOps *bmtldst) {
if (bmtldst != NULL && bmtldst->privOps != NULL && ((MTLSDOps *)bmtldst->privOps)->layer != NULL)
_markLayerModified(((MTLSDOps *)bmtldst->privOps)->layer);
}
static const jint g_drawOpcodes[] = {
sun_java2d_pipe_BufferedOpCodes_DRAW_LINE,
sun_java2d_pipe_BufferedOpCodes_DRAW_RECT,
sun_java2d_pipe_BufferedOpCodes_DRAW_POLY,
sun_java2d_pipe_BufferedOpCodes_DRAW_PIXEL,
sun_java2d_pipe_BufferedOpCodes_DRAW_SCANLINES,
sun_java2d_pipe_BufferedOpCodes_DRAW_PARALLELOGRAM,
sun_java2d_pipe_BufferedOpCodes_DRAW_AAPARALLELOGRAM,
sun_java2d_pipe_BufferedOpCodes_DRAW_GLYPH_LIST,
sun_java2d_pipe_BufferedOpCodes_FILL_RECT,
sun_java2d_pipe_BufferedOpCodes_FILL_SPANS,
sun_java2d_pipe_BufferedOpCodes_FILL_PARALLELOGRAM,
sun_java2d_pipe_BufferedOpCodes_FILL_AAPARALLELOGRAM,
sun_java2d_pipe_BufferedOpCodes_COPY_AREA,
sun_java2d_pipe_BufferedOpCodes_MASK_FILL,
sun_java2d_pipe_BufferedOpCodes_MASK_BLIT,
sun_java2d_pipe_BufferedOpCodes_SET_SHAPE_CLIP_SPANS
};
static jboolean _isDrawOpcode(jint opcode) {
for (int c = 0; c < sizeof(g_drawOpcodes)/sizeof(g_drawOpcodes[0]); ++c) {
if (opcode == g_drawOpcodes[c])
return JNI_TRUE;
}
return JNI_FALSE;
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLRenderQueue_flushBuffer
(JNIEnv *env, jobject mtlrq,
jlong buf, jint limit)
{
jboolean sync = JNI_FALSE;
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;
}
INIT_PREVIOUS_OP();
end = b + limit;
while (b < end) {
jint opcode = NEXT_INT(b);
J2dTraceLn2(J2D_TRACE_VERBOSE,
"MTLRenderQueue_flushBuffer: opcode=%d, rem=%d",
opcode, (end-b));
if (opcode != sun_java2d_pipe_BufferedOpCodes_DRAW_GLYPH_LIST &&
opcode != sun_java2d_pipe_BufferedOpCodes_NOOP)
{
//MTLTR_DisableGlyphModeState();
}
switch (opcode) {
// draw ops
case sun_java2d_pipe_BufferedOpCodes_DRAW_LINE:
{
J2dTraceLn(J2D_TRACE_VERBOSE, "sun_java2d_pipe_BufferedOpCodes_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, x1, y1, x2, y2);
}
break;
case sun_java2d_pipe_BufferedOpCodes_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, x, y, w, h);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DRAW_POLY:
{
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;
MTLRenderer_DrawPoly(mtlc, nPoints, isClosed, transX, transY, xPoints, yPoints);
SKIP_BYTES(b, nPoints * BYTES_PER_POLY_POINT);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DRAW_PIXEL:
{
jint x = NEXT_INT(b);
jint y = NEXT_INT(b);
CONTINUE_IF_NULL(mtlc);
//TODO
J2dTraceNotImplPrimitive("MTLRenderQueue_DRAW_PIXEL");
}
break;
case sun_java2d_pipe_BufferedOpCodes_DRAW_SCANLINES:
{
jint count = NEXT_INT(b);
MTLRenderer_DrawScanlines(mtlc, count, (jint *)b);
SKIP_BYTES(b, count * BYTES_PER_SCANLINE);
}
break;
case sun_java2d_pipe_BufferedOpCodes_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,
x11, y11,
dx21, dy21,
dx12, dy12,
lwr21, lwr12);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DRAW_AAPARALLELOGRAM:
{
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:
{
jint x = NEXT_INT(b);
jint y = NEXT_INT(b);
jint w = NEXT_INT(b);
jint h = NEXT_INT(b);
MTLRenderer_FillRect(mtlc, x, y, w, h);
}
break;
case sun_java2d_pipe_BufferedOpCodes_FILL_SPANS:
{
jint count = NEXT_INT(b);
MTLRenderer_FillSpans(mtlc, count, (jint *)b);
SKIP_BYTES(b, count * BYTES_PER_SPAN);
}
break;
case sun_java2d_pipe_BufferedOpCodes_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,
x11, y11,
dx21, dy21,
dx12, dy12);
}
break;
case sun_java2d_pipe_BufferedOpCodes_FILL_AAPARALLELOGRAM:
{
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:
{
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:
{
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:
{
J2dTracePrimitive("MTLRenderQueue_BLIT");
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 rtt = EXTRACT_BOOLEAN(packedParams,
OFFSET_RTT);
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, rtt,
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);
}
_onSurfaceModified(jlong_to_ptr(pDst));
}
break;
case sun_java2d_pipe_BufferedOpCodes_SURFACE_TO_SW_BLIT:
{
J2dTracePrimitive("MTLRenderQueue_SURFACE_TO_SW_BLIT");
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:
{
J2dTracePrimitive("MTLRenderQueue_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;
MTLMaskFill_MaskFill(mtlc, x, y, w, h,
maskoff, maskscan, masklen, pMask);
SKIP_BYTES(b, masklen);
}
break;
case sun_java2d_pipe_BufferedOpCodes_MASK_BLIT:
{
J2dTracePrimitive("MTLRenderQueue_MASK_BLIT");
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,
dstx, dsty, width, height, b);
SKIP_BYTES(b, masklen);
}
break;
// state-related ops
case sun_java2d_pipe_BufferedOpCodes_SET_RECT_CLIP:
{
jint x1 = NEXT_INT(b);
jint y1 = NEXT_INT(b);
jint x2 = NEXT_INT(b);
jint y2 = NEXT_INT(b);
MTLContext_SetRectClip(mtlc, dstOps, x1, y1, x2, y2);
}
break;
case sun_java2d_pipe_BufferedOpCodes_BEGIN_SHAPE_CLIP:
{
MTLContext_BeginShapeClip(mtlc);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_SHAPE_CLIP_SPANS:
{
jint count = NEXT_INT(b);
MTLRenderer_FillSpans(mtlc, count, (jint *)b);
SKIP_BYTES(b, count * BYTES_PER_SPAN);
}
break;
case sun_java2d_pipe_BufferedOpCodes_END_SHAPE_CLIP:
{
MTLContext_EndShapeClip(mtlc, dstOps);
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESET_CLIP:
{
MTLContext_ResetClip(mtlc);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_ALPHA_COMPOSITE:
{
jint rule = NEXT_INT(b);
jfloat extraAlpha = NEXT_FLOAT(b);
jint flags = NEXT_INT(b);
MTLContext_SetAlphaComposite(mtlc, rule, extraAlpha, flags);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_XOR_COMPOSITE:
{
jint xorPixel = NEXT_INT(b);
MTLContext_SetXorComposite(mtlc, xorPixel);
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESET_COMPOSITE:
{
MTLContext_ResetComposite(mtlc);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_TRANSFORM:
{
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);
MTLContext_SetTransform(mtlc, m00, m10, m01, m11, m02, m12);
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESET_TRANSFORM:
{
MTLContext_ResetTransform(mtlc);
}
break;
// context-related ops
case sun_java2d_pipe_BufferedOpCodes_SET_SURFACES:
{
J2dTracePrimitive("MTLRenderQueue_SET_SURFACES");
jlong pSrc = NEXT_LONG(b);
jlong pDst = NEXT_LONG(b);
if (mtlc != NULL) {
RESET_PREVIOUS_OP();
}
dstOps = (BMTLSDOps *)jlong_to_ptr(pDst);
MTLContext_SetSurfaces(env, pSrc, pDst);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_SCRATCH_SURFACE:
{
J2dTracePrimitive("MTLRenderQueue_SET_SCRATCH_SURFACE");
jlong pConfigInfo = NEXT_LONG(b);
MTLGraphicsConfigInfo *mtlInfo =
(MTLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo);
if (mtlInfo == NULL) {
J2dTraceImplPrimitive(
"MTLRenderQueue_SET_SCRATCH_SURFACE",
"ERROR: mtl config info is null");
} else {
MTLContext *newMtlc = mtlInfo->context;
if (newMtlc == NULL) {
J2dTraceImplPrimitive(
"MTLRenderQueue_SET_SCRATCH_SURFACE",
"ERROR: mtl context is null");
} else {
mtlc = newMtlc;
dstOps = NULL;
}
}
}
break;
case sun_java2d_pipe_BufferedOpCodes_FLUSH_SURFACE:
{
J2dTracePrimitive("MTLRenderQueue_FLUSH_SURFACE");
jlong pData = NEXT_LONG(b);
BMTLSDOps *mtlsdo = (BMTLSDOps *)jlong_to_ptr(pData);
if (mtlsdo != NULL) {
CONTINUE_IF_NULL(mtlc);
RESET_PREVIOUS_OP();
MTLSD_Delete(env, mtlsdo);
}
}
break;
case sun_java2d_pipe_BufferedOpCodes_DISPOSE_SURFACE:
{
J2dTracePrimitive("MTLRenderQueue_DISPOSE_SURFACE");
jlong pData = NEXT_LONG(b);
BMTLSDOps *mtlsdo = (BMTLSDOps *)jlong_to_ptr(pData);
if (mtlsdo != NULL) {
CONTINUE_IF_NULL(mtlc);
RESET_PREVIOUS_OP();
MTLSD_Delete(env, mtlsdo);
if (mtlsdo->privOps != NULL) {
free(mtlsdo->privOps);
}
}
}
break;
case sun_java2d_pipe_BufferedOpCodes_DISPOSE_CONFIG:
{
J2dTracePrimitive("MTLRenderQueue_DISPOSE_CONFIG");
jlong pConfigInfo = NEXT_LONG(b);
CONTINUE_IF_NULL(mtlc);
RESET_PREVIOUS_OP();
[JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
MTLGC_DestroyMTLGraphicsConfig(pConfigInfo);
}];
// the previous method will call glX/wglMakeCurrent(None),
// so we should nullify the current mtlc and dstOps to avoid
// calling glFlush() (or similar) while no context is current
mtlc = NULL;
// dstOps = NULL;
}
break;
case sun_java2d_pipe_BufferedOpCodes_INVALIDATE_CONTEXT:
{
//TODO
J2dTraceNotImplPrimitive("MTLRenderQueue_INVALIDATE_CONTEXT");
// flush just in case there are any pending operations in
// the hardware pipe
if (mtlc != NULL) {
RESET_PREVIOUS_OP();
}
// invalidate the references to the current context and
// destination surface that are maintained at the native level
mtlc = NULL;
// dstOps = NULL;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SAVE_STATE:
{
//TODO
J2dTraceNotImplPrimitive("MTLRenderQueue_INVALIDATE_CONTEXT");
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESTORE_STATE:
{
//TODO
J2dTraceNotImplPrimitive("MTLRenderQueue_RESTORE_STATE");
}
break;
case sun_java2d_pipe_BufferedOpCodes_SYNC:
{
sync = JNI_TRUE;
}
break;
// multibuffering ops
case sun_java2d_pipe_BufferedOpCodes_SWAP_BUFFERS:
{
jlong window = NEXT_LONG(b);
if (mtlc != NULL) {
RESET_PREVIOUS_OP();
}
MTLSD_SwapBuffers(env, window);
}
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:
{
MTLPaints_ResetPaint(mtlc);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_COLOR:
{
jint pixel = NEXT_INT(b);
if (dstOps != NULL) {
MTLSDOps *dstCGLOps = (MTLSDOps *)dstOps->privOps;
dstCGLOps->configInfo->context->mtlColor = pixel;
}
MTLPaints_SetColor(mtlc, pixel);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_GRADIENT_PAINT:
{
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);
MTLPaints_SetGradientPaint(mtlc, useMask, cyclic,
p0, p1, p3,
pixel1, pixel2);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_LINEAR_GRADIENT_PAINT:
{
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));
MTLPaints_SetLinearGradientPaint(mtlc, dstOps,
useMask, linear,
cycleMethod, numStops,
p0, p1, p3,
fractions, pixels);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_RADIAL_GRADIENT_PAINT:
{
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));
MTLPaints_SetRadialGradientPaint(mtlc, dstOps,
useMask, linear,
cycleMethod, numStops,
m00, m01, m02,
m10, m11, m12,
focusX,
fractions, pixels);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_TEXTURE_PAINT:
{
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);
MTLPaints_SetTexturePaint(mtlc, useMask, pSrc, filter,
xp0, xp1, xp3,
yp0, yp1, yp3);
}
break;
// BufferedImageOp-related ops
case sun_java2d_pipe_BufferedOpCodes_ENABLE_CONVOLVE_OP:
{
jlong pSrc = NEXT_LONG(b);
jboolean edgeZero = NEXT_BOOLEAN(b);
jint kernelWidth = NEXT_INT(b);
jint kernelHeight = NEXT_INT(b);
MTLBufImgOps_EnableConvolveOp(mtlc, pSrc, edgeZero,
kernelWidth, kernelHeight, b);
SKIP_BYTES(b, kernelWidth * kernelHeight * sizeof(jfloat));
}
break;
case sun_java2d_pipe_BufferedOpCodes_DISABLE_CONVOLVE_OP:
{
MTLBufImgOps_DisableConvolveOp(mtlc);
}
break;
case sun_java2d_pipe_BufferedOpCodes_ENABLE_RESCALE_OP:
{
jlong pSrc = NEXT_LONG(b);
jboolean nonPremult = NEXT_BOOLEAN(b);
jint numFactors = 4;
unsigned char *scaleFactors = b;
unsigned char *offsets = (b + numFactors * sizeof(jfloat));
MTLBufImgOps_EnableRescaleOp(mtlc, pSrc, nonPremult,
scaleFactors, offsets);
SKIP_BYTES(b, numFactors * sizeof(jfloat) * 2);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DISABLE_RESCALE_OP:
{
MTLBufImgOps_DisableRescaleOp(mtlc);
}
break;
case sun_java2d_pipe_BufferedOpCodes_ENABLE_LOOKUP_OP:
{
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;
MTLBufImgOps_EnableLookupOp(mtlc, pSrc, nonPremult, shortData,
numBands, bandLength, offset,
tableValues);
SKIP_BYTES(b, numBands * bandLength * bytesPerElem);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DISABLE_LOOKUP_OP:
{
MTLBufImgOps_DisableLookupOp(mtlc);
}
break;
default:
J2dRlsTraceLn1(J2D_TRACE_ERROR,
"MTLRenderQueue_flushBuffer: invalid opcode=%d", opcode);
if (mtlc != NULL) {
RESET_PREVIOUS_OP();
}
return;
}
if (_isDrawOpcode(opcode)) // performed rendering operation on dstOps
_onSurfaceModified(dstOps);
}
MTLTR_DisableGlyphModeState();
_scheduleBlitAllModifiedLayers();
}
/**
* 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;
}
/**
* Used to track whether we are within a series of simple primitive operations
* or texturing operations. The op parameter determines the nature of the
* operation that is to follow. Valid values for this op parameter are:
*/
void
MTLRenderQueue_CheckPreviousOp(jint op)
{
//TODO
J2dTraceNotImplPrimitive("MTLRenderQueue_CheckPreviousOp");
previousOp = op;
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#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,
jint x1, jint y1, jint x2, jint y2);
void MTLRenderer_DrawRect(MTLContext *mtlc,
jint x, jint y, jint w, jint h);
void MTLRenderer_DrawPoly(MTLContext *mtlc,
jint nPoints, jint isClosed,
jint transX, jint transY,
jint *xPoints, jint *yPoints);
void MTLRenderer_DrawScanlines(MTLContext *mtlc,
jint count, jint *scanlines);
void MTLRenderer_DrawParallelogram(MTLContext *mtlc,
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,
jint x, jint y, jint w, jint h);
void MTLRenderer_FillSpans(MTLContext *mtlc,
jint count, jint *spans);
void MTLRenderer_FillParallelogram(MTLContext *mtlc,
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);
void MTLRenderer_EnableAAParallelogramProgram();
void MTLRenderer_DisableAAParallelogramProgram();
void MTLRenderer_BeginFrame(MTLContext* ctx, MTLLayer* layer);
#endif /* MTLRenderer_h_Included */

View File

@@ -0,0 +1,670 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <jlong.h>
#include <jni_util.h>
#include <math.h>
//#define DEBUG 1
#include "sun_java2d_metal_MTLRenderer.h"
#include "MTLRenderer.h"
#include "MTLRenderQueue.h"
#include "MTLSurfaceData.h"
#include "MTLUtils.h"
#import "MTLLayer.h"
void MTLRenderer_FillParallelogramMetal(
MTLContext* mtlc, id<MTLTexture> dest, jfloat x, jfloat y, jfloat dx1, jfloat dy1, jfloat dx2, jfloat dy2)
{
if (mtlc == NULL || dest == nil)
return;
J2dTraceLn7(J2D_TRACE_INFO,
"MTLRenderer_FillParallelogramMetal "
"(x=%6.2f y=%6.2f "
"dx1=%6.2f dy1=%6.2f "
"dx2=%6.2f dy2=%6.2f dst tex=%p)",
x, y,
dx1, dy1,
dx2, dy2, dest);
struct Vertex verts[PGRAM_VERTEX_COUNT] = {
{ {(2.0*x/dest.width) - 1.0,
2.0*(1.0 - y/dest.height) - 1.0, 0.0}},
{ {2.0*(x+dx1)/dest.width - 1.0,
2.0*(1.0 - (y+dy1)/dest.height) - 1.0, 0.0}},
{ {2.0*(x+dx2)/dest.width - 1.0,
2.0*(1.0 - (y+dy2)/dest.height) - 1.0, 0.0}},
{ {2.0*(x+dx1)/dest.width - 1.0,
2.0*(1.0 - (y+dy1)/dest.height) - 1.0, 0.0}},
{ {2.0*(x + dx1 + dx2)/dest.width - 1.0,
2.0*(1.0 - (y+ dy1 + dy2)/dest.height) - 1.0, 0.0}},
{ {2.0*(x+dx2)/dest.width - 1.0,
2.0*(1.0 - (y+dy2)/dest.height) - 1.0, 0.0},
}};
// Encode render command.
id<MTLRenderCommandEncoder> mtlEncoder = MTLContext_CreateRenderEncoder(mtlc, dest);
if (mtlEncoder == nil)
return;
[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount: PGRAM_VERTEX_COUNT];
[mtlEncoder endEncoding];
}
/**
* Note: Some of the methods in this file apply a "magic number"
* translation to line segments. The OpenGL specification lays out the
* "diamond exit rule" for line rasterization, but it is loose enough to
* allow for a wide range of line rendering hardware. (It appears that
* some hardware, such as the Nvidia GeForce2 series, does not even meet
* the spec in all cases.) As such it is difficult to find a mapping
* between the Java2D and OpenGL line specs that works consistently across
* all hardware combinations.
*
* Therefore 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_DrawLineMetal(MTLContext *mtlc, id<MTLTexture> dest, jfloat x1, jfloat y1, jfloat x2, jfloat y2) {
if (mtlc == NULL || dest == nil)
return;
J2dTraceLn5(J2D_TRACE_INFO, "MTLRenderer_DrawLineMetal (x1=%1.2f y1=%1.2f x2=%1.2f y2=%1.2f), dst tex=%p", x1, y1, x2, y2, dest);
id<MTLRenderCommandEncoder> mtlEncoder = MTLContext_CreateRenderEncoder(mtlc, dest);
if (mtlEncoder == nil)
return;
struct Vertex verts[2] = {
{{MTLUtils_normalizeX(dest, x1), MTLUtils_normalizeY(dest, y1), 0.0}},
{{MTLUtils_normalizeX(dest, x2), MTLUtils_normalizeY(dest, y2), 0.0}}
};
[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:2];
[mtlEncoder endEncoding];
}
void MTLRenderer_DrawLine(MTLContext *mtlc, jint x1, jint y1, jint x2, jint y2) {
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
if (dstOps == NULL || dstOps->privOps == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawLine: dest is null");
return;
}
MTLSDOps *dstCGLOps = (MTLSDOps *)dstOps->privOps;
MTLRenderer_DrawLineMetal(dstCGLOps->configInfo->context, dstOps->pTexture, x1, y1, x2, y2);
}
void MTLRenderer_DrawRectMetal(MTLContext *mtlc, id<MTLTexture> dest, jint x, jint y, jint w, jint h) {
if (mtlc == NULL || dest == nil)
return;
J2dTraceLn5(J2D_TRACE_INFO, "MTLRenderer_DrawRectMetal (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 = MTLContext_CreateRenderEncoder(mtlc, dest);
if (mtlEncoder == nil)
return;
const int verticesCount = 5;
struct Vertex vertices[verticesCount] = {
{{MTLUtils_normalizeX(dest, x), MTLUtils_normalizeY(dest, y), 0.0}},
{{MTLUtils_normalizeX(dest, x + w), MTLUtils_normalizeY(dest, y), 0.0}},
{{MTLUtils_normalizeX(dest, x + w), MTLUtils_normalizeY(dest, y + h), 0.0}},
{{MTLUtils_normalizeX(dest, x), MTLUtils_normalizeY(dest, y + h), 0.0}},
{{MTLUtils_normalizeX(dest, x), MTLUtils_normalizeY(dest, y), 0.0}},
};
[mtlEncoder setVertexBytes:vertices length:sizeof(vertices) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLineStrip vertexStart:0 vertexCount:verticesCount];
[mtlEncoder endEncoding];
}
void MTLRenderer_DrawRect(MTLContext *mtlc, jint x, jint y, jint w, jint h) {
BMTLSDOps *bmtldst = MTLRenderQueue_GetCurrentDestination();
if (bmtldst == NULL || bmtldst->privOps == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawRect: dest is null");
return;
}
MTLSDOps *dstCGLOps = (MTLSDOps *)bmtldst->privOps;
MTLRenderer_DrawRectMetal(dstCGLOps->configInfo->context, bmtldst->pTexture, x, y, w, h);
}
void _tracePoints(jint nPoints, jint *xPoints, jint *yPoints) {
for (int i = 0; i < nPoints; i++)
J2dTraceLn2(J2D_TRACE_INFO, "\t(%d, %d)", *(xPoints++), *(yPoints++));
}
const int POLYLINE_BUF_SIZE = 64;
void _fillVertex(struct Vertex * vertex, int x, int y, int destW, int destH) {
vertex->position[0] = 2.0*x/destW - 1.0;
vertex->position[1] = 2.0*(1.0 - y/destH) - 1.0;
vertex->position[2] = 0;
}
void MTLRenderer_DrawPoly(MTLContext *mtlc,
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;
}
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
if (dstOps == NULL || dstOps->privOps == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawPoly: current dest is null");
return;
}
MTLSDOps *dstCGLOps = (MTLSDOps *) dstOps->privOps;
MTLContext* ctx = dstCGLOps->configInfo->context;
if (ctx == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawPoly: current mtlContext os 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;
jint prevX = *(xPoints++);
jint prevY = *(yPoints++);
--nPoints;
const jint firstX = prevX;
const jint firstY = prevY;
while (nPoints > 0) {
_fillVertex(pointsChunk.verts, prevX + transX, prevY + transY, dstOps->width, dstOps->height);
const bool isLastChunk = nPoints + 1 <= POLYLINE_BUF_SIZE;
__block int chunkSize = isLastChunk ? nPoints : POLYLINE_BUF_SIZE - 1;
for (int i = 1; i < chunkSize; i++) {
prevX = *(xPoints++);
prevY = *(yPoints++);
_fillVertex(pointsChunk.verts + i, prevX + transX, prevY + transY, dstOps->width, dstOps->height);
}
bool drawCloseSegment = false;
if (isClosed && isLastChunk) {
if (chunkSize + 2 <= POLYLINE_BUF_SIZE) {
_fillVertex(pointsChunk.verts + chunkSize, firstX + transX, firstY + transY, dstOps->width, dstOps->height);
++chunkSize;
} else
drawCloseSegment = true;
}
nPoints -= chunkSize;
id<MTLRenderCommandEncoder> mtlEncoder = MTLContext_CreateRenderEncoder(ctx, dstOps->pTexture);
if (mtlEncoder == nil)
return;
[mtlEncoder setVertexBytes:pointsChunk.verts length:sizeof(pointsChunk.verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLineStrip vertexStart:0 vertexCount:chunkSize + 1];
if (drawCloseSegment) {
struct Vertex vertices[2] = {
{{MTLUtils_normalizeX(dstOps->pTexture, prevX + transX), MTLUtils_normalizeY(dstOps->pTexture, prevY + transY), 0.0}},
{{MTLUtils_normalizeX(dstOps->pTexture, firstX + transX), MTLUtils_normalizeY(dstOps->pTexture, firstY + transY), 0.0}},
};
[mtlEncoder setVertexBytes:vertices length:sizeof(vertices) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:2];
}
[mtlEncoder endEncoding];
}
}
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;
//TODO
J2dTraceNotImplPrimitive("MTLRenderer_drawPoly");
J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_drawPoly");
}
void
MTLRenderer_DrawScanlines(MTLContext *mtlc,
jint scanlineCount, jint *scanlines)
{
//TODO
J2dTraceNotImplPrimitive("MTLRenderer_DrawScanlines");
J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_DrawScanlines");
}
void
MTLRenderer_FillRect(MTLContext *mtlc, jint x, jint y, jint w, jint h)
{
//TODO
J2dTraceNotImplPrimitive("MTLRenderer_FillRect");
J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_FillRect");
}
const int SPAN_BUF_SIZE=64;
void
MTLRenderer_FillSpans(MTLContext *mtlc, jint spanCount, jint *spans)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_FillSpans");
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
if (dstOps == NULL || dstOps->privOps == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillSpans: current dest is null");
return;
}
MTLSDOps *dstCGLOps = (MTLSDOps *) dstOps->privOps;
MTLContext* ctx = dstCGLOps->configInfo->context;
if (ctx == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillSpans: current mtlContext os null");
return;
}
while (spanCount > 0) {
__block struct {
jfloat spns[SPAN_BUF_SIZE*4];
} spanStruct;
__block jfloat sc = spanCount > SPAN_BUF_SIZE ? SPAN_BUF_SIZE : spanCount;
for (int i = 0; i < sc; i++) {
spanStruct.spns[i * 4] = *(spans++);
spanStruct.spns[i * 4 + 1] = *(spans++);
spanStruct.spns[i * 4 + 2] = *(spans++);
spanStruct.spns[i * 4 + 3] = *(spans++);
}
spanCount -= sc;
id<MTLTexture> dest = dstOps->pTexture;
id<MTLRenderCommandEncoder> mtlEncoder = MTLContext_CreateRenderEncoder(ctx, dest);
if (mtlEncoder == nil)
return;
for (int i = 0; i < sc; i++) {
jfloat x1 = spanStruct.spns[i * 4];
jfloat y1 = spanStruct.spns[i * 4 + 1];
jfloat x2 = spanStruct.spns[i * 4 + 2];
jfloat y2 = spanStruct.spns[i * 4 + 3];
struct Vertex verts[PGRAM_VERTEX_COUNT] = {
{{(2.0 * x1 / dest.width) - 1.0,
2.0 * (1.0 - y1 / dest.height) - 1.0, 0.0}},
{{2.0 * (x2) / dest.width - 1.0,
2.0 * (1.0 - y1 / dest.height) - 1.0, 0.0}},
{{2.0 * x1 / dest.width - 1.0,
2.0 * (1.0 - y2 / dest.height) - 1.0, 0.0}},
{{2.0 * x2 / dest.width - 1.0,
2.0 * (1.0 - y1 / dest.height) - 1.0, 0.0}},
{{2.0 * (x2) / dest.width - 1.0,
2.0 * (1.0 - y2 / dest.height) - 1.0, 0.0}},
{{2.0 * (x1) / dest.width - 1.0,
2.0 * (1.0 - y2 / dest.height) - 1.0, 0.0},
}};
[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:PGRAM_VERTEX_COUNT];
}
[mtlEncoder endEncoding];
}
}
void
MTLRenderer_FillParallelogram(MTLContext *mtlc,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12)
{
J2dTracePrimitive("MTLRenderer_FillParallelogram");
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
if (dstOps == NULL || dstOps->privOps == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillParallelogram: current dest is null");
return;
}
MTLSDOps *dstCGLOps = (MTLSDOps *) dstOps->privOps;
MTLContext* ctx = dstCGLOps->configInfo->context;
if (ctx == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillParallelogram: current mtlContext os null");
return;
}
MTLRenderer_FillParallelogramMetal(
dstCGLOps->configInfo->context, dstOps->pTexture,
fx11, fy11, dx21, dy21, dx12, dy12);
}
void
MTLRenderer_DrawParallelogram(MTLContext *mtlc,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12,
jfloat lwr21, jfloat lwr12)
{
//TODO
J2dTraceNotImplPrimitive("MTLRenderer_DrawParallelogram");
// 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);
}
static GLhandleARB aaPgramProgram = 0;
/*
* This shader fills the space between an outer and inner parallelogram.
* It can be used to draw an outline by specifying both inner and outer
* values. It fills pixels by estimating what portion falls inside the
* outer shape, and subtracting an estimate of what portion falls inside
* the inner shape. Specifying both inner and outer values produces a
* standard "wide outline". Specifying an inner shape that falls far
* outside the outer shape allows the same shader to fill the outer
* shape entirely since pixels that fall within the outer shape are never
* inside the inner shape and so they are filled based solely on their
* coverage of the outer shape.
*
* The setup code renders this shader over the bounds of the outer
* shape (or the only shape in the case of a fill operation) and
* sets the texture 0 coordinates so that 0,0=>0,1=>1,1=>1,0 in those
* texture coordinates map to the four corners of the parallelogram.
* Similarly the texture 1 coordinates map the inner shape to the
* unit square as well, but in a different coordinate system.
*
* When viewed in the texture coordinate systems the parallelograms
* we are filling are unit squares, but the pixels have then become
* tiny parallelograms themselves. Both of the texture coordinate
* systems are affine transforms so the rate of change in X and Y
* of the texture coordinates are essentially constants and happen
* to correspond to the size and direction of the slanted sides of
* the distorted pixels relative to the "square mapped" boundary
* of the parallelograms.
*
* The shader uses the dFdx() and dFdy() functions to measure the "rate
* of change" of these texture coordinates and thus gets an accurate
* measure of the size and shape of a pixel relative to the two
* parallelograms. It then uses the bounds of the size and shape
* of a pixel to intersect with the unit square to estimate the
* coverage of the pixel. Unfortunately, without a lot more work
* to calculate the exact area of intersection between a unit
* square (the original parallelogram) and a parallelogram (the
* distorted pixel), this shader only approximates the pixel
* coverage, but emperically the estimate is very useful and
* produces visually pleasing results, if not theoretically accurate.
*/
static const char *aaPgramShaderSource =
"void main() {"
// Calculate the vectors for the "legs" of the pixel parallelogram
// for the outer parallelogram.
" vec2 oleg1 = dFdx(gl_TexCoord[0].st);"
" vec2 oleg2 = dFdy(gl_TexCoord[0].st);"
// Calculate the bounds of the distorted pixel parallelogram.
" vec2 corner = gl_TexCoord[0].st - (oleg1+oleg2)/2.0;"
" vec2 omin = min(corner, corner+oleg1);"
" omin = min(omin, corner+oleg2);"
" omin = min(omin, corner+oleg1+oleg2);"
" vec2 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.
" vec2 ileg1 = dFdx(gl_TexCoord[1].st);"
" vec2 ileg2 = dFdy(gl_TexCoord[1].st);"
// Calculate the bounds of the distorted pixel parallelogram.
" corner = gl_TexCoord[1].st - (ileg1+ileg2)/2.0;"
" vec2 imin = min(corner, corner+ileg1);"
" imin = min(imin, corner+ileg2);"
" imin = min(imin, corner+ileg1+ileg2);"
" vec2 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.
" vec2 o1 = clamp(omin, 0.0, 1.0);"
" vec2 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);"
" vec2 i1 = clamp(imin, 0.0, 1.0);"
" vec2 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;"
" gl_FragColor = gl_Color * coverage;"
"}";
#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)
{
//TODO
J2dTraceNotImplPrimitive("MTLRenderer_FillAAParallelogram");
DECLARE_MATRIX(om);
// parameters for parallelogram bounding box
jfloat bx11, by11, bx22, by22;
// parameters for uv texture coordinates of parallelogram corners
jfloat u11, v11, u12, v12, u21, v21, u22, v22;
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);
}
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)
{
//TODO
J2dTraceNotImplPrimitive("MTLRenderer_FillAAParallelogramInnerOuter");
}
void
MTLRenderer_DrawAAParallelogram(MTLContext *mtlc, BMTLSDOps *dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12,
jfloat lwr21, jfloat lwr12)
{
//TODO
J2dTraceNotImplPrimitive("MTLRenderer_DrawAAParallelogram");
// 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);
}
void
MTLRenderer_EnableAAParallelogramProgram()
{
//TODO
J2dTraceNotImplPrimitive("MTLRenderer_EnableAAParallelogramProgram");
J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_EnableAAParallelogramProgram");
}
void
MTLRenderer_DisableAAParallelogramProgram()
{
//TODO
J2dTraceNotImplPrimitive("MTLRenderer_DisableAAParallelogramProgram");
J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_DisableAAParallelogramProgram");
}
void debugDraw(MTLContext *ctx, id<MTLTexture> dst, int red/*othrewise blue*/, int freq) {
J2dTraceLn1(J2D_TRACE_VERBOSE, "draw debug onto dst tex=%p", dst);
MTLContext_SetColor(ctx, red > 0 ? red : 0, 0, red <= 0 ? 255 : 0, 255);
id <MTLRenderCommandEncoder> encoder = MTLContext_CreateRenderEncoder(ctx, dst);
const int w = dst.width;
const int h = dst.height;
int x = 2;
int y = 2;
do {
struct Vertex vvv[2] = {
{{MTLUtils_normalizeX(dst,x), MTLUtils_normalizeY(dst, y), 0.0}},
{{MTLUtils_normalizeX(dst, dst.width - x), MTLUtils_normalizeY(dst, dst.height - y), 0.0}},
};
[encoder setVertexBytes:vvv length:sizeof(vvv) atIndex:MeshVertexBuffer];
[encoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:2];
if (freq == 0)
break;
if (freq > 0)
x += (w - 3)/freq;
else
y += (h - 3)/freq;
} while (x < w && y < h);
[encoder endEncoding];
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLSurfaceData_h_Included
#define MTLSurfaceData_h_Included
#import "MTLSurfaceDataBase.h"
#import "MTLGraphicsConfig.h"
#import "AWTWindow.h"
#import "MTLLayer.h"
/**
* The CGLSDOps structure contains the CGL-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;
#endif /* MTLSurfaceData_h_Included */

View File

@@ -0,0 +1,458 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#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 *mtlsdo);
extern jboolean MTLSD_InitMTLWindow(JNIEnv *env, MTLSDOps *mtlsdo);
extern void MTLSD_DestroyMTLSurface(JNIEnv *env, MTLSDOps *mtlsdo);
void MTLSD_SetNativeDimensions(JNIEnv *env, BMTLSDOps *mtlsdo, jint w, jint h);
/**
* This table contains the "pixel formats" for all system memory surfaces
* that OpenGL is capable of handling, indexed by the "PF_" constants defined
* in MTLSurfaceData.java. These pixel formats contain information that is
* passed to OpenGL when copying from a system memory ("Sw") surface to
* an OpenGL "Surface" (via glDrawPixels()) or "Texture" (via glTexImage2D()).
*/
MTLPixelFormat MTPixelFormats[] = {};
/**
* Given a starting value and a maximum limit, returns the first power-of-two
* greater than the starting value. If the resulting value is greater than
* the maximum limit, zero is returned.
*/
jint
MTLSD_NextPowerOfTwo(jint val, jint max)
{
jint i;
if (val > max) {
return 0;
}
for (i = 1; i < val; i *= 2);
return i;
}
/**
* Returns true if both given dimensions are a power of two.
*/
static jboolean
MTLSD_IsPowerOfTwo(jint width, jint height)
{
return (((width & (width-1)) | (height & (height-1))) == 0);
}
/**
* 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,
jboolean texNonPow2, jboolean texRect,
jint width, jint height)
{
BMTLSDOps *bmtlsdo = (BMTLSDOps *)jlong_to_ptr(pData);
J2dTraceLn3(J2D_TRACE_INFO, "MTLSurfaceData_initTexture: w=%d h=%d p=%p", width, height, bmtlsdo);
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;
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: MTLPixelFormatBGRA8Unorm width: width height: height mipmapped: NO];
bmtlsdo->pTexture = [[ctx->mtlDevice newTextureWithDescriptor: textureDescriptor] retain];
bmtlsdo->isOpaque = isOpaque;
bmtlsdo->xOffset = 0;
bmtlsdo->yOffset = 0;
bmtlsdo->width = width;
bmtlsdo->height = height;
bmtlsdo->textureWidth = width;
bmtlsdo->textureHeight = height;
bmtlsdo->textureTarget = -1;
bmtlsdo->drawableType = MTLSD_TEXTURE;
MTLSD_SetNativeDimensions(env, bmtlsdo, width, width);
J2dTraceLn4(J2D_TRACE_VERBOSE, "\tcreated MTLTexture [texture]: w=%d h=%d bp=%p [tex=%p]", width, height, bmtlsdo, bmtlsdo->pTexture);
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,
jboolean texNonPow2, jboolean texRect,
jint width, jint height)
{
BMTLSDOps *bmtlsdo = (BMTLSDOps *)jlong_to_ptr(pData);
if (bmtlsdo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLSurfaceData_initRTexture: BMTLSDOps are null");
return JNI_FALSE;
}
MTLSDOps *mtlsdo = (MTLSDOps *)bmtlsdo->privOps;
if (mtlsdo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLSurfaceData_initRTexture: MTLSDOps are null");
return JNI_FALSE;
}
if (mtlsdo->configInfo == NULL || mtlsdo->configInfo->context == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLSurfaceData_initRTexture: MTLSDOps wasn't initialized (context is null)");
return JNI_FALSE;
}
const MTLContext* ctx = mtlsdo->configInfo->context;
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: MTLPixelFormatBGRA8Unorm width: width height: height mipmapped: NO];
bmtlsdo->pTexture = [[ctx->mtlDevice newTextureWithDescriptor: textureDescriptor] retain];;
bmtlsdo->isOpaque = isOpaque;
bmtlsdo->xOffset = 0;
bmtlsdo->yOffset = 0;
bmtlsdo->width = width;
bmtlsdo->height = height;
bmtlsdo->textureWidth = width;
bmtlsdo->textureHeight = height;
bmtlsdo->textureTarget = -1;
bmtlsdo->drawableType = MTLSD_RT_TEXTURE;
MTLSD_SetNativeDimensions(env, bmtlsdo, width, width);
J2dTraceLn4(J2D_TRACE_VERBOSE, "\tcreated MTLTexture [FBObject]: w=%d h=%d bp=%p [tex=%p]", width, height, bmtlsdo, bmtlsdo->pTexture);
return JNI_TRUE;
}
/**
* Initializes a surface in the backbuffer of a given double-buffered
* onscreen window for use in a BufferStrategy.Flip situation. The bounds of
* the backbuffer surface should always be kept in sync with the bounds of
* the underlying native window.
*/
JNIEXPORT jboolean JNICALL
Java_sun_java2d_metal_MTLSurfaceData_initFlipBackbuffer
(JNIEnv *env, jobject mtlsd,
jlong pData)
{
//TODO
J2dTraceNotImplPrimitive("MTLSurfaceData_initFlipBackbuffer");
MTLSDOps *mtlsdo = (MTLSDOps *)jlong_to_ptr(pData);
J2dTraceLn(J2D_TRACE_INFO, "MTLSurfaceData_initFlipBackbuffer");
return JNI_TRUE;
}
JNIEXPORT jint JNICALL
Java_sun_java2d_metal_MTLSurfaceData_getTextureTarget
(JNIEnv *env, jobject mtlsd,
jlong pData)
{
//TODO
J2dTraceNotImplPrimitive("MTLSurfaceData_getTextureTarget");
MTLSDOps *mtlsdo = (MTLSDOps *)jlong_to_ptr(pData);
J2dTraceLn(J2D_TRACE_INFO, "MTLSurfaceData_getTextureTarget");
return 0;
}
JNIEXPORT jint JNICALL
Java_sun_java2d_metal_MTLSurfaceData_getTextureID
(JNIEnv *env, jobject mtlsd,
jlong pData)
{
//TODO
J2dTraceNotImplPrimitive("MTLSurfaceData_getTextureID");
return 0;
}
/**
* 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 OpenGL resources associated with this surface.
*/
void
MTLSD_Delete(JNIEnv *env, BMTLSDOps *mtlsdo)
{
//TODO
J2dTraceNotImplPrimitive("MTLSD_Delete");
J2dTraceLn1(J2D_TRACE_INFO, "MTLSD_Delete: type=%d",
mtlsdo->drawableType);
}
/**
* This is the implementation of the general DisposeFunc defined in
* SurfaceData.h and used by the Disposer mechanism. It first flushes all
* native OpenGL resources and then frees any memory allocated within the
* native MTLSDOps structure.
*/
void
MTLSD_Dispose(JNIEnv *env, SurfaceDataOps *ops)
{
MTLSDOps *mtlsdo = (MTLSDOps *)ops;
jlong pConfigInfo = MTLSD_GetNativeConfigInfo(mtlsdo);
JNU_CallStaticMethodByName(env, NULL, "sun/java2d/metal/MTLSurfaceData",
"dispose", "(JJ)V",
ptr_to_jlong(ops), pConfigInfo);
}
/**
* 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, MTLSDOps *mtlsdo)
{
J2dTraceLn(J2D_TRACE_INFO, "OGLSD_DestroyOGLSurface");
}
/**
* Returns a pointer (as a jlong) to the native CGLGraphicsConfigInfo
* associated with the given OGLSDOps. This method can be called from
* shared code to retrieve the native GraphicsConfig data in a platform-
* independent manner.
*/
jlong
MTLSD_GetNativeConfigInfo(BMTLSDOps *mtlsdo)
{
J2dTraceLn(J2D_TRACE_INFO, "OGLSD_GetNativeConfigInfo");
return 0;
}
/**
* This function initializes a native window surface and caches the window
* bounds in the given OGLSDOps. Returns JNI_TRUE if the operation was
* successful; JNI_FALSE otherwise.
*/
jboolean
MTLSD_InitMTLWindow(JNIEnv *env, MTLSDOps *oglsdo)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLSD_InitMTLWindow");
return JNI_TRUE;
}
void
MTLSD_SwapBuffers(JNIEnv *env, jlong pPeerData)
{
J2dTraceLn(J2D_TRACE_INFO, "OGLSD_SwapBuffers");
}
#pragma mark -
#pragma mark "--- CGLSurfaceData 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 cglsd,
jlong pConfigInfo, jlong pPeerData, jlong layerPtr,
jint xoff, jint yoff, jboolean isOpaque)
{
BMTLSDOps *bmtlsdo = (BMTLSDOps *)SurfaceData_InitOps(env, cglsd, 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);
if (mtlsdo == NULL) {
JNU_ThrowOutOfMemoryError(env, "creating native cgl ops");
return;
}
bmtlsdo->privOps = mtlsdo;
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->needsInit = JNI_TRUE;
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 cglsd)
{
J2dTraceLn(J2D_TRACE_INFO, "CGLSurfaceData_clearWindow");
BMTLSDOps *mtlsdo = (MTLSDOps*) SurfaceData_GetOps(env, cglsd);
MTLSDOps *cglsdo = (MTLSDOps*) mtlsdo->privOps;
cglsdo->peerData = NULL;
cglsdo->layer = NULL;
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLSurfaceData_validate
(JNIEnv *env, jobject jsurfacedata,
jint xoff, jint yoff, jint width, jint height, jboolean isOpaque)
{
J2dTraceLn2(J2D_TRACE_INFO, "MTLLSurfaceData_validate: w=%d h=%d", width, height);
BMTLSDOps *mtlsdo = (BMTLSDOps*)SurfaceData_GetOps(env, jsurfacedata);
mtlsdo->needsInit = JNI_TRUE;
mtlsdo->xOffset = xoff;
mtlsdo->yOffset = yoff;
mtlsdo->width = width;
mtlsdo->height = height;
mtlsdo->isOpaque = isOpaque;
if (mtlsdo->drawableType == MTLSD_WINDOW) {
// J2dTraceLn4(J2D_TRACE_INFO, "MTLContext_SetSurfaces: w=%d h=%d src=%p dst=%p", width, height, mtlsdo, mtlsdo);
MTLContext_SetSurfaces(env, ptr_to_jlong(mtlsdo), ptr_to_jlong(mtlsdo));
// we have to explicitly tell the NSOpenGLContext that its target
// drawable has changed size
MTLSDOps *cglsdo = (MTLSDOps *)mtlsdo->privOps;
MTLContext *mtlc = cglsdo->configInfo->context;
}
}

View File

@@ -0,0 +1,212 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#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"
#include "MTLFuncs.h"
/**
* The MTLPixelFormat structure contains all the information OpenGL needs to
* know when copying from or into a particular system memory image buffer (via
* glDrawPixels(), glReadPixels, glTexSubImage2D(), etc).
*
* GLenum format;
* The pixel format parameter used in glDrawPixels() and other similar calls.
* Indicates the component ordering for each pixel (e.g. GL_BGRA).
*
* GLenum type;
* The pixel data type parameter used in glDrawPixels() and other similar
* calls. Indicates the data type for an entire pixel or for each component
* in a pixel (e.g. GL_UNSIGNED_BYTE with GL_BGR means a pixel consists of
* 3 unsigned byte components, blue first, then green, then red;
* GL_UNSIGNED_INT_8_8_8_8_REV with GL_BGRA means a pixel consists of 1
* unsigned integer comprised of four byte components, alpha first, then red,
* then green, then blue).
*
* jint alignment;
* The byte alignment parameter used in glPixelStorei(GL_UNPACK_ALIGNMENT). A
* value of 4 indicates that each pixel starts on a 4-byte aligned region in
* memory, and so on. This alignment parameter helps OpenGL speed up pixel
* transfer operations by transferring memory in aligned blocks.
*
* jboolean hasAlpha;
* If true, indicates that this pixel format contains an alpha component.
*
* jboolean isPremult;
* If true, indicates that this pixel format contains color components that
* have been pre-multiplied by their corresponding alpha component.
*/
typedef struct {
//GLenum format;
//GLenum type;
jint format;
jint type;
jint alignment;
jboolean hasAlpha;
jboolean isPremult;
} MTPixelFormat;
/**
* The MTLSDOps structure describes a native OpenGL surface and contains all
* information pertaining to the native surface. Some information about
* the more important/different fields:
*
* void *privOps;
* Pointer to native-specific (GLX, WGL, etc.) 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).
*
* GLenum activeBuffer;
* Can be either GL_FRONT if this is the front buffer surface of an onscreen
* window or a pbuffer surface, or GL_BACK if this is the backbuffer surface
* of an onscreen window.
*
* 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.
*
* jboolean needsInit;
* If true, the surface requires some one-time initialization, which should
* be performed after a context has been made current to the surface for
* the first time.
*
* jint x/yOffset
* The offset in pixels of the OpenGL 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 OpenGL 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 OpenGL 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.
*
* GLuint textureID;
* The texture object handle, as generated by glGenTextures(). If this value
* is zero, the texture has not yet been initialized.
*
* jint textureWidth/Height;
* The actual bounds of the texture object for this surface. If the
* GL_ARB_texture_non_power_of_two extension is not present, the dimensions
* of an OpenGL texture object must be a power-of-two (e.g. 64x32 or 128x512).
* The texture image that we care about has dimensions specified by the width
* and height fields in this MTLSDOps structure. For example, if the image
* to be stored in the texture has dimensions 115x47, the actual OpenGL
* texture we allocate will have dimensions 128x64 to meet the pow2
* restriction. The image bounds within the texture can be accessed using
* floating point texture coordinates in the range [0.0,1.0].
*
* GLenum textureTarget;
* The texture target of the texture object for this surface. If this
* surface is not backed by a texture, this value is set to zero. Otherwise,
* this value is GL_TEXTURE_RECTANGLE_ARB when the GL_ARB_texture_rectangle
* extension is in use; if not, it is set to GL_TEXTURE_2D.
*
* GLint textureFilter;
* The current filter state for this texture object (can be either GL_NEAREST
* or GL_LINEAR). We cache this value here and check it before updating
* the filter state to avoid redundant calls to glTexParameteri() when the
* filter state remains constant (see the MTLSD_UPDATE_TEXTURE_FILTER()
* macro below).
*
* GLuint fbobjectID, depthID;
* The object handles for the framebuffer object and depth renderbuffer
* associated with this surface. These fields are only used when
* drawableType is MTLSD_FBOBJECT, otherwise they are zero.
*/
typedef struct {
SurfaceDataOps sdOps;
void *privOps;
jint drawableType;
jint activeBuffer;
jboolean isOpaque;
jboolean needsInit;
jint xOffset;
jint yOffset;
jint width;
jint height;
void* pTexture;
jint textureWidth;
jint textureHeight;
/* GLenum */ jint textureTarget;
/* GLint */ jint textureFilter;
/* GLuint */ jint fbobjectID;
/* GLuint */ jint depthID;
} 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
/**
* 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,57 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#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);
void MTLTR_DisableGlyphVertexCache(MTLContext *mtlc);
void MTLTR_DrawGlyphList(JNIEnv *env, MTLContext *mtlc, MTLSDOps *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,382 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#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 "AccelGlyphCache.h"
/**
* The following constants define the inner and outer bounds of the
* accelerated glyph cache.
*/
#define MTLTR_CACHE_WIDTH 1024
#define MTLTR_CACHE_HEIGHT 1024
#define MTLTR_CACHE_CELL_WIDTH 64
#define MTLTR_CACHE_CELL_HEIGHT 64
/**
* 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) OpenGL drivers should be smart enough to manage that
* texture across all screens.
*/
static GlyphCacheInfo *glyphCacheLCD = NULL;
static GlyphCacheInfo *glyphCacheAA = NULL;
/**
* The handle to the LCD text fragment program object.
*/
static GLhandleARB lcdTextProgram = 0;
/**
* This value tracks the previous LCD contrast setting, so if the contrast
* value hasn't changed since the last time the gamma uniforms were
* updated (not very common), then we can skip updating the unforms.
*/
static jint lastLCDContrast = -1;
/**
* 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 64
/**
* These constants define the size of the "cached destination" texture.
* This texture is only used when rendering LCD-optimized text, as that
* codepath needs direct access to the destination. There is no way to
* access the framebuffer directly from an OpenGL shader, so we need to first
* copy the destination region corresponding to a particular glyph into
* this cached texture, and then that texture will be accessed inside the
* shader. Copying the destination into this cached texture can be a very
* expensive operation (accounting for about half the rendering time for
* LCD text), so to mitigate this cost we try to bulk read a horizontal
* region of the destination at a time. (These values are empirically
* derived for the common case where text runs horizontally.)
*
* Note: It is assumed in various calculations below that:
* (MTLTR_CACHED_DEST_WIDTH >= MTLTR_CACHE_CELL_WIDTH) &&
* (MTLTR_CACHED_DEST_WIDTH >= MTLTR_NOCACHE_TILE_SIZE) &&
* (MTLTR_CACHED_DEST_HEIGHT >= MTLTR_CACHE_CELL_HEIGHT) &&
* (MTLTR_CACHED_DEST_HEIGHT >= MTLTR_NOCACHE_TILE_SIZE)
*/
#define MTLTR_CACHED_DEST_WIDTH 1024
#define MTLTR_CACHED_DEST_HEIGHT (MTLTR_CACHE_CELL_HEIGHT * 2)
/**
* The handle to the "cached destination" texture object.
*/
static GLuint cachedDestTextureID = 0;
/**
* The current bounds of the "cached destination" texture, in destination
* coordinate space. The width/height of these bounds will not exceed the
* MTLTR_CACHED_DEST_WIDTH/HEIGHT values defined above. These bounds are
* only considered valid when the isCachedDestValid flag is JNI_TRUE.
*/
static SurfaceDataBounds cachedDestBounds;
/**
* This flag indicates whether the "cached destination" texture contains
* valid data. This flag is reset to JNI_FALSE at the beginning of every
* call to MTLTR_DrawGlyphList(). Once we copy valid destination data
* into the cached texture, this flag is set to JNI_TRUE. This way, we can
* limit the number of times we need to copy destination data, which is a
* very costly operation.
*/
static jboolean isCachedDestValid = JNI_FALSE;
/**
* The bounds of the previously rendered LCD glyph, in destination
* coordinate space. We use these bounds to determine whether the glyph
* currently being rendered overlaps the previously rendered glyph (i.e.
* its bounding box intersects that of the previously rendered glyph). If
* so, we need to re-read the destination area associated with that previous
* glyph so that we can correctly blend with the actual destination data.
*/
static SurfaceDataBounds previousGlyphBounds;
/**
* 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 (which work well with the GL_MODULATE function).
*/
static jboolean
MTLTR_InitGlyphCache(jboolean lcdCache)
{
//TODO
J2dTraceNotImplPrimitive("MTLTR_InitGlyphCache");
return JNI_TRUE;
}
/**
* Adds the given glyph to the glyph cache (texture and data structure)
* associated with the given MTLContext.
*/
static void
MTLTR_AddTmTLyphCache(GlyphInfo *glyph, GLenum pixelFormat)
{
//TODO
J2dTraceNotImplPrimitive("MTLTR_AddTmTLyphCache");
CacheCellInfo *ccinfo;
GlyphCacheInfo *gcinfo;
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_AddTmTLyphCache");
J2dTracePrimitive("MTLTR_InitGlyphCache");
}
/**
* (Re)Initializes the gamma related uniforms.
*
* The given contrast value is an int in the range [100, 250] which we will
* then scale to fit in the range [1.0, 2.5].
*/
static jboolean
MTLTR_UpdateLCDTextContrast(jint contrast)
{
//TODO
J2dTraceNotImplPrimitive("MTLTR_UpdateLCDTextContrast");
return JNI_TRUE;
}
/**
* Updates the current gamma-adjusted source color ("src_adj") of the LCD
* text shader program. Note that we could calculate this value in the
* shader (e.g. just as we do for "dst_adj"), but would be unnecessary work
* (and a measurable performance hit, maybe around 5%) since this value is
* constant over the entire glyph list. So instead we just calculate the
* gamma-adjusted value once and update the uniform parameter of the LCD
* shader as needed.
*/
static jboolean
MTLTR_UpdateLCDTextColor(jint contrast)
{
//TODO
J2dTraceNotImplPrimitive("MTLTR_UpdateLCDTextColor");
return JNI_TRUE;
}
/**
* Enables the LCD text shader and updates any related state, such as the
* gamma lookup table textures.
*/
static jboolean
MTLTR_EnableLCDGlyphModeState(GLuint glyphTextureID,
GLuint dstTextureID,
jint contrast)
{
//TODO
J2dTraceNotImplPrimitive("MTLTR_EnableLCDGlyphModeState");
return JNI_TRUE;
}
void
MTLTR_EnableGlyphVertexCache(MTLContext *mtlc)
{
//TODO
J2dTraceNotImplPrimitive("MTLTR_EnableGlyphVertexCache");
}
void
MTLTR_DisableGlyphVertexCache(MTLContext *mtlc)
{
//TODO
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DisableGlyphVertexCache");
J2dTraceNotImplPrimitive("MTLTR_DisableGlyphVertexCache");
}
/**
* Disables any pending state associated with the current "glyph mode".
*/
void
MTLTR_DisableGlyphModeState()
{
//TODO
J2dTraceNotImplPrimitive("MTLTR_DisableGlyphModeState");
J2dTraceLn1(J2D_TRACE_VERBOSE,
"MTLTR_DisableGlyphModeState: mode=%d", glyphMode);
}
static jboolean
MTLTR_DrawGrayscaleGlyphViaCache(MTLContext *mtlc,
GlyphInfo *ginfo, jint x, jint y)
{
//TODO
J2dTraceNotImplPrimitive("MTLTR_DisableGlyphVertexCache");
return JNI_TRUE;
}
/**
* Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 is
* inside outerBounds.
*/
#define INSIDE(gx1, gy1, gx2, gy2, outerBounds) \
(((gx1) >= outerBounds.x1) && ((gy1) >= outerBounds.y1) && \
((gx2) <= outerBounds.x2) && ((gy2) <= outerBounds.y2))
/**
* Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 intersects
* the rectangle defined by bounds.
*/
#define INTERSECTS(gx1, gy1, gx2, gy2, bounds) \
((bounds.x2 > (gx1)) && (bounds.y2 > (gy1)) && \
(bounds.x1 < (gx2)) && (bounds.y1 < (gy2)))
/**
* This method checks to see if the given LCD glyph bounds fall within the
* cached destination texture bounds. If so, this method can return
* immediately. If not, this method will copy a chunk of framebuffer data
* into the cached destination texture and then update the current cached
* destination bounds before returning.
*/
static void
MTLTR_UpdateCachedDestination(MTLSDOps *dstOps, GlyphInfo *ginfo,
jint gx1, jint gy1, jint gx2, jint gy2,
jint glyphIndex, jint totalGlyphs)
{
//TODO
J2dTraceNotImplPrimitive("MTLTR_UpdateCachedDestination");
}
static jboolean
MTLTR_DrawLCDGlyphViaCache(MTLContext *mtlc, MTLSDOps *dstOps,
GlyphInfo *ginfo, jint x, jint y,
jint glyphIndex, jint totalGlyphs,
jboolean rgbOrder, jint contrast,
jint dstTextureID, jboolean * opened)
{
//TODO
J2dTraceNotImplPrimitive("MTLTR_DrawLCDGlyphViaCache");
return JNI_TRUE;
}
static jboolean
MTLTR_DrawGrayscaleGlyphNoCache(MTLContext *mtlc,
GlyphInfo *ginfo, jint x, jint y)
{
//TODO
J2dTraceNotImplPrimitive("MTLTR_DrawGrayscaleGlyphNoCache");
return JNI_TRUE;
}
static jboolean
MTLTR_DrawLCDGlyphNoCache(MTLContext *mtlc, MTLSDOps *dstOps,
GlyphInfo *ginfo, jint x, jint y,
jint rowBytesOffset,
jboolean rgbOrder, jint contrast,
jint dstTextureID)
{
//TODO
J2dTraceNotImplPrimitive("MTLTR_DrawLCDGlyphNoCache");
return JNI_TRUE;
}
static jboolean
MTLTR_DrawColorGlyphNoCache(MTLContext *mtlc, GlyphInfo *ginfo, jint x, jint y)
{
//TODO
J2dTraceNotImplPrimitive("MTLTR_DrawColorGlyphNoCache");
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, MTLSDOps *dstOps,
jint totalGlyphs, jboolean usePositions,
jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
jfloat glyphListOrigX, jfloat glyphListOrigY,
unsigned char *images, unsigned char *positions)
{
//TODO
J2dTraceNotImplPrimitive("MTLTR_DrawGlyphList");
}
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)
{
//TODO
J2dTraceNotImplPrimitive("MTLTextRenderer_drawGlyphList");
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,37 @@
#ifndef MTLTexturePool_h_Included
#define MTLTexturePool_h_Included
#import <Metal/Metal.h>
@interface MTLTexturePoolItem : NSObject
{
@private
id<MTLTexture> texture;
bool isBusy;
}
@property (readwrite, retain) id<MTLTexture> texture;
@property (readwrite, assign) bool isBusy;
- (id) initWithTexture:(id<MTLTexture>)tex;
@end
// NOTE: owns all MTLTexture objects
@interface MTLTexturePool : NSObject
{
@private
id<MTLDevice> device;
NSMutableArray<MTLTexturePoolItem*> * pool;
}
@property (readwrite, assign) id<MTLDevice> device;
@property (readwrite, retain) NSMutableArray<MTLTexturePoolItem*> * pool;
- (id) initWithDevice:(id<MTLDevice>)device;
- (id<MTLTexture>) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format;
- (void) markTextureFree:(id<MTLTexture>)texture;
- (void) markAllTexturesFree;
@end
#endif /* MTLTexturePool_h_Included */

View File

@@ -0,0 +1,92 @@
#import "MTLTexturePool.h"
#import "Trace.h"
@implementation MTLTexturePoolItem
@synthesize texture;
@synthesize isBusy;
- (id) initWithTexture:(id<MTLTexture>)tex {
self = [super init];
if (self == nil) return self;
self.texture = tex;
isBusy = NO;
return self;
}
@end
@implementation MTLTexturePool
@synthesize device;
@synthesize pool;
- (id) initWithDevice:(id<MTLDevice>)dev {
self = [super init];
if (self == nil) return self;
self.device = dev;
self.pool = [NSMutableArray arrayWithCapacity:10];
return self;
}
// NOTE: called from RQ-thread (on blit operations)
- (id<MTLTexture>) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format {
@synchronized (self) {
// 1. find free item
// TODO: optimize search, use Map<(w,h,pf), TexPoolItem>
const int count = [self.pool count];
for (int c = 0; c < count; ++c) {
MTLTexturePoolItem *tpi = [self.pool objectAtIndex:c];
if (tpi == nil)
continue;
// TODO: use checks tpi.texture.width <= width && tpi.texture.height <= height
if (tpi.texture.width == width && tpi.texture.height == height && tpi.texture.pixelFormat == format &&
!tpi.isBusy) {
tpi.isBusy = YES;
return tpi.texture;
}
}
// 2. create
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format width:width height:height mipmapped:NO];
id <MTLTexture> tex = [self.device newTextureWithDescriptor:textureDescriptor];
MTLTexturePoolItem *tpi = [[MTLTexturePoolItem alloc] initWithTexture:tex];
[self.pool addObject:tpi];
J2dTraceLn4(J2D_TRACE_VERBOSE, "MTLTexturePool: created pool item: tex=%p, w=%d h=%d, pf=%d", tex, width, height, format);
return tpi.texture;
}
};
// NOTE: called from completion-handler (pooled thread)
- (void) markTextureFree:(id<MTLTexture>)texture {
// TODO: optimize search, use Map<(w,h,pf), TexPoolItem>
@synchronized (self) {
const int count = [self.pool count];
for (int c = 0; c < count; ++c) {
MTLTexturePoolItem * tpi = [self.pool objectAtIndex:c];
if (tpi == nil)
continue;
if (tpi.texture == texture) {
tpi.isBusy = NO;
return;
}
}
J2dTraceLn1(J2D_TRACE_ERROR, "MTLTexturePool: can't find item with texture %p", texture);
}
}
// NOTE: called from completion-handler (pooled thread)
- (void) markAllTexturesFree {
@synchronized (self) {
const int count = [self.pool count];
for (int c = 0; c < count; ++c) {
MTLTexturePoolItem *tpi = [self.pool objectAtIndex:c];
if (tpi == nil)
continue;
tpi.isBusy = NO;
}
}
}
@end

View File

@@ -0,0 +1,9 @@
#ifndef MTLUtils_h_Included
#define MTLUtils_h_Included
#import <Metal/Metal.h>
float MTLUtils_normalizeX(id<MTLTexture> dest, float x);
float MTLUtils_normalizeY(id<MTLTexture> dest, float y);
#endif /* MTLUtils_h_Included */

View File

@@ -0,0 +1,26 @@
#include "MTLUtils.h"
float MTLUtils_normalizeX(id<MTLTexture> dest, float x) { return (2.0*x/dest.width) - 1.0; }
float MTLUtils_normalizeY(id<MTLTexture> dest, float y) { return 2.0*(1.0 - y/dest.height) - 1.0; }
#include <jni.h>
#include <simd/simd.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(struct TxtVertex txpt, simd_float4x4 transform4x4) {
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE, "check transform: ");
simd_float4 fpt = simd_make_float4(txpt.position[0], txpt.position[1], txpt.position[2], 1.f);
simd_float4 fpt_trans = simd_mul(transform4x4, fpt);
J2dTraceTraceVector(fpt);
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE, " ===>>> ");
J2dTraceTraceVector(fpt_trans);
J2dTraceLn(J2D_TRACE_VERBOSE, " ");
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLVertexCache_h_Included
#define MTLVertexCache_h_Included
#include "j2d_md.h"
#include "MTLContext.h"
/**
* Constants that control the size of the vertex cache.
*/
#define MTLVC_MAX_INDEX 1024
/**
* 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(MTLContext *mtlc);
void MTLVertexCache_FlushVertexCache();
void MTLVertexCache_RestoreColorState(MTLContext *mtlc);
void MTLVertexCache_EnableMaskCache(MTLContext *mtlc);
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);
void MTLVertexCache_AddGlyphQuad(MTLContext *mtlc,
jfloat tx1, jfloat ty1,
jfloat tx2, jfloat ty2,
jfloat dx1, jfloat dy1,
jfloat dx2, jfloat dy2);
#endif /* MTLVertexCache_h_Included */

View File

@@ -0,0 +1,175 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <stdlib.h>
#include <string.h>
#include "sun_java2d_SunGraphics2D.h"
#include "MTLPaints.h"
#include "MTLVertexCache.h"
typedef struct _J2DVertex {
jfloat tx, ty;
jubyte r, g, b, a;
jfloat dx, dy;
} J2DVertex;
static J2DVertex *vertexCache = NULL;
static jint vertexCacheIndex = 0;
static jint maskCacheTexID = 0;
static jint maskCacheIndex = 0;
#define MTLVC_ADD_VERTEX(TX, TY, R, G, B, A, DX, DY) \
do { \
J2DVertex *v = &vertexCache[vertexCacheIndex++]; \
v->tx = TX; \
v->ty = TY; \
v->r = R; \
v->g = G; \
v->b = B; \
v->a = A; \
v->dx = DX; \
v->dy = DY; \
} while (0)
#define MTLVC_ADD_QUAD(TX1, TY1, TX2, TY2, DX1, DY1, DX2, DY2, R, G, B, A) \
do { \
MTLVC_ADD_VERTEX(TX1, TY1, R, G, B, A, DX1, DY1); \
MTLVC_ADD_VERTEX(TX2, TY1, R, G, B, A, DX2, DY1); \
MTLVC_ADD_VERTEX(TX2, TY2, R, G, B, A, DX2, DY2); \
MTLVC_ADD_VERTEX(TX1, TY2, R, G, B, A, DX1, DY2); \
} while (0)
jboolean
MTLVertexCache_InitVertexCache(MTLContext *mtlc)
{
//TODO
J2dTraceNotImplPrimitive("MTLVertexCache_InitVertexCache");
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_InitVertexCache");
return JNI_TRUE;
}
void
MTLVertexCache_FlushVertexCache()
{
// TODO
J2dTraceNotImplPrimitive("MTLVertexCache_FlushVertexCache");
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_FlushVertexCache");
vertexCacheIndex = 0;
}
/**
* This method is somewhat hacky, but necessary for the foreseeable future.
* The problem is the way OpenGL handles color values in vertex arrays. When
* a vertex in a vertex array contains a color, and then the vertex array
* is rendered via glDrawArrays(), the global OpenGL color state is actually
* modified each time a vertex is rendered. This means that after all
* vertices have been flushed, the global OpenGL color state will be set to
* the color of the most recently rendered element in the vertex array.
*
* The reason this is a problem for us is that we do not want to flush the
* vertex array (in the case of mask/glyph operations) or issue a glEnd()
* (in the case of non-antialiased primitives) everytime the current color
* changes, which would defeat any benefit from batching in the first place.
* We handle this in practice by not calling CHECK/RESET_PREVIOUS_OP() when
* the simple color state is changing in MTLPaints_SetColor(). This is
* problematic for vertex caching because we may end up with the following
* situation, for example:
* SET_COLOR (orange)
* MASK_FILL
* MASK_FILL
* SET_COLOR (blue; remember, this won't cause a flush)
* FILL_RECT (this will cause the vertex array to be flushed)
*
* In this case, we would actually end up rendering an orange FILL_RECT,
* not a blue one as intended, because flushing the vertex cache flush would
* override the color state from the most recent SET_COLOR call.
*
* Long story short, the easiest way to resolve this problem is to call
* this method just after disabling the mask/glyph cache, which will ensure
* that the appropriate color state is restored.
*/
void
MTLVertexCache_RestoreColorState(MTLContext *mtlc)
{
// TODO
J2dTraceNotImplPrimitive("MTLVertexCache_RestoreColorState");
if (mtlc->paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) {
MTLPaints_SetColor(mtlc, mtlc->pixel);
}
}
static jboolean
MTLVertexCache_InitMaskCache()
{
// TODO
J2dTraceNotImplPrimitive("MTLVertexCache_InitMaskCache");
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_InitMaskCache");
return JNI_TRUE;
}
void
MTLVertexCache_EnableMaskCache(MTLContext *mtlc)
{
// TODO
J2dTraceNotImplPrimitive("MTLVertexCache_EnableMaskCache");
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_EnableMaskCache");
}
void
MTLVertexCache_DisableMaskCache(MTLContext *mtlc)
{
// TODO
J2dTraceNotImplPrimitive("MTLVertexCache_DisableMaskCache");
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_DisableMaskCache");
maskCacheIndex = 0;
}
void
MTLVertexCache_AddMaskQuad(MTLContext *mtlc,
jint srcx, jint srcy,
jint dstx, jint dsty,
jint width, jint height,
jint maskscan, void *mask)
{
// TODO
J2dTraceNotImplPrimitive("MTLVertexCache_AddMaskQuad");
}
void
MTLVertexCache_AddGlyphQuad(MTLContext *mtlc,
jfloat tx1, jfloat ty1, jfloat tx2, jfloat ty2,
jfloat dx1, jfloat dy1, jfloat dx2, jfloat dy2)
{
// TODO
J2dTraceNotImplPrimitive("MTLVertexCache_AddGlyphQuad");
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_AddGlyphQuad");
}
#endif /* !HEADLESS */

View File

@@ -324,9 +324,13 @@ public class Blit extends GraphicsPrimitive
int srcx, int srcy, int dstx, int dsty,
int width, int height)
{
tracePrimitive(target);
if ((traceflags & TRACEPTIME) == 0) {
tracePrimitive(target);
}
long time = System.nanoTime();
target.Blit(src, dst, comp, clip,
srcx, srcy, dstx, dsty, width, height);
tracePrimitiveTime(target, System.nanoTime() - time);
}
}
}

View File

@@ -209,9 +209,13 @@ public class BlitBg extends GraphicsPrimitive
int srcx, int srcy, int dstx, int dsty,
int width, int height)
{
tracePrimitive(target);
if ((traceflags & TRACEPTIME) == 0) {
tracePrimitive(target);
}
long time = System.nanoTime();
target.BlitBg(src, dst, comp, clip, bgColor,
srcx, srcy, dstx, dsty, width, height);
tracePrimitiveTime(target, System.nanoTime() - time);
}
}
}

View File

@@ -152,8 +152,12 @@ public class DrawGlyphListAA extends GraphicsPrimitive {
public void DrawGlyphListAA(SunGraphics2D sg2d, SurfaceData dest,
GlyphList glyphs)
{
tracePrimitive(target);
if ((traceflags & TRACEPTIME) == 0) {
tracePrimitive(target);
}
long time = System.nanoTime();
target.DrawGlyphListAA(sg2d, dest, glyphs);
tracePrimitiveTime(target, System.nanoTime() - time);
}
}
}

View File

@@ -109,8 +109,12 @@ public class DrawGlyphListLCD extends GraphicsPrimitive {
public void DrawGlyphListLCD(SunGraphics2D sg2d, SurfaceData dest,
GlyphList glyphs)
{
tracePrimitive(target);
if ((traceflags & TRACEPTIME) == 0) {
tracePrimitive(target);
}
long time = System.nanoTime();
target.DrawGlyphListLCD(sg2d, dest, glyphs);
tracePrimitiveTime(target, System.nanoTime() - time);
}
}
}

View File

@@ -104,8 +104,12 @@ public class DrawLine extends GraphicsPrimitive
public void DrawLine(SunGraphics2D sg2d, SurfaceData dest,
int x1, int y1, int x2, int y2)
{
tracePrimitive(target);
if ((traceflags & TRACEPTIME) == 0) {
tracePrimitive(target);
}
long time = System.nanoTime();
target.DrawLine(sg2d, dest, x1, y1, x2, y2);
tracePrimitiveTime(target, System.nanoTime() - time);
}
}
}

View File

@@ -111,8 +111,12 @@ public class FillParallelogram extends GraphicsPrimitive
double dx1, double dy1,
double dx2, double dy2)
{
tracePrimitive(target);
if ((traceflags & TRACEPTIME) == 0) {
tracePrimitive(target);
}
long time = System.nanoTime();
target.FillParallelogram(sg2d, dest, x0, y0, dx1, dy1, dx2, dy2);
tracePrimitiveTime(target, System.nanoTime() - time);
}
}
}

View File

@@ -125,8 +125,12 @@ public class FillRect extends GraphicsPrimitive
public void FillRect(SunGraphics2D sg2d, SurfaceData dest,
int x, int y, int w, int h)
{
tracePrimitive(target);
if ((traceflags & TRACEPTIME) == 0) {
tracePrimitive(target);
}
long time = System.nanoTime();
target.FillRect(sg2d, dest, x, y, w, h);
tracePrimitiveTime(target, System.nanoTime() - time);
}
}
}

View File

@@ -112,8 +112,12 @@ public class FillSpans extends GraphicsPrimitive
public void FillSpans(SunGraphics2D sg2d, SurfaceData dest,
SpanIterator si)
{
tracePrimitive(target);
if ((traceflags & TRACEPTIME) == 0) {
tracePrimitive(target);
}
long time = System.nanoTime();
target.FillSpans(sg2d, dest, si);
tracePrimitiveTime(target, System.nanoTime() - time);
}
}
}

View File

@@ -40,6 +40,7 @@ import java.lang.reflect.Field;
import java.util.StringTokenizer;
import java.util.Iterator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.io.PrintStream;
import java.io.OutputStream;
@@ -322,17 +323,28 @@ public abstract class GraphicsPrimitive {
public static int traceflags;
public static String tracefile;
public static String pname;
public static PrintStream traceout;
public static long treshold = 0;
public static boolean verbose = false;
public static final int TRACELOG = 1;
public static final int TRACETIMESTAMP = 2;
public static final int TRACECOUNTS = 4;
public static final int TRACEPTIME = 8;
public static final int TRACEPNAME = 16;
public static final int TRACEPIMPL = 32;
static void showTraceUsage() {
System.err.println("usage: -Dsun.java2d.trace="+
"[log[,timestamp]],[count],[ptime],[pimpl],[name:<substr pattern>],"+
"[out:<filename>],[td=<treshold>],[help],[verbose]");
}
static {
GetPropertyAction gpa = new GetPropertyAction("sun.java2d.trace");
String trace = AccessController.doPrivileged(gpa);
if (trace != null) {
boolean verbose = false;
int traceflags = 0;
StringTokenizer st = new StringTokenizer(trace, ",");
while (st.hasMoreTokens()) {
@@ -343,19 +355,33 @@ public abstract class GraphicsPrimitive {
traceflags |= GraphicsPrimitive.TRACELOG;
} else if (tok.equalsIgnoreCase("timestamp")) {
traceflags |= GraphicsPrimitive.TRACETIMESTAMP;
} else if (tok.equalsIgnoreCase("ptime")) {
traceflags |=GraphicsPrimitive.TRACEPTIME;
} else if (tok.equalsIgnoreCase("pimpl")) {
traceflags |=GraphicsPrimitive.TRACEPIMPL;
} else if (tok.regionMatches(true, 0, "name:", 0, 5)) {
traceflags |=GraphicsPrimitive.TRACEPNAME;
pname = tok.substring(6);
} else if (tok.equalsIgnoreCase("verbose")) {
verbose = true;
} else if (tok.regionMatches(true, 0, "out:", 0, 4)) {
tracefile = tok.substring(4);
} else if (tok.regionMatches(true, 0, "td=", 0, 3)) {
try {
treshold = Long.parseLong(tok.substring(3));
} catch (NumberFormatException e) {
showTraceUsage();
}
} else {
if (!tok.equalsIgnoreCase("help")) {
System.err.println("unrecognized token: "+tok);
}
System.err.println("usage: -Dsun.java2d.trace="+
"[log[,timestamp]],[count],"+
"[out:<filename>],[help],[verbose]");
showTraceUsage();
}
}
GraphicsPrimitiveMgr.setTraceFlags(traceflags);
if (verbose) {
System.err.print("GraphicsPrimitive logging ");
if ((traceflags & GraphicsPrimitive.TRACELOG) != 0) {
@@ -416,7 +442,12 @@ public abstract class GraphicsPrimitive {
}
public static class TraceReporter implements Runnable {
public static void setShutdownHook() {
private static boolean hookEnabled = false;
public static synchronized void setShutdownHook() {
if (hookEnabled) return;
hookEnabled = true;
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
TraceReporter t = new TraceReporter();
Thread thread = new Thread(
@@ -430,33 +461,39 @@ public abstract class GraphicsPrimitive {
public void run() {
PrintStream ps = getTraceOutputFile();
Iterator<Map.Entry<Object, int[]>> iterator =
traceMap.entrySet().iterator();
long total = 0;
int numprims = 0;
while (iterator.hasNext()) {
Map.Entry<Object, int[]> me = iterator.next();
Object prim = me.getKey();
int[] count = me.getValue();
if (count[0] == 1) {
ps.print("1 call to ");
} else {
ps.print(count[0]+" calls to ");
if (traceMap != null) {
Iterator<Map.Entry<Object, int[]>> iterator =
traceMap.entrySet().iterator();
long total = 0;
int numprims = 0;
while (iterator.hasNext()) {
Map.Entry<Object, int[]> me = iterator.next();
Object prim = me.getKey();
int[] count = me.getValue();
if (count[0] == 1) {
ps.print("1 call to ");
} else {
ps.print(count[0] + " calls to ");
}
ps.println(prim);
numprims++;
total += count[0];
}
if (numprims == 0) {
ps.println("No graphics primitives executed");
} else if (numprims > 1) {
ps.println(total + " total calls to " +
numprims + " different primitives");
}
ps.println(prim);
numprims++;
total += count[0];
}
if (numprims == 0) {
ps.println("No graphics primitives executed");
} else if (numprims > 1) {
ps.println(total+" total calls to "+
numprims+" different primitives");
}
}
}
public static synchronized void tracePrimitive(Object prim) {
public synchronized static void tracePrimitive(Object prim) {
if ((traceflags & TRACEPNAME) != 0) {
if (!prim.toString().contains(pname)) return;
}
if ((traceflags & TRACECOUNTS) != 0) {
if (traceMap == null) {
traceMap = new HashMap<>();
@@ -478,6 +515,40 @@ public abstract class GraphicsPrimitive {
}
}
public synchronized static void traceImplPrimitive(Object prim, Object msg) {
if ((traceflags & TRACEPNAME) != 0) {
if (!prim.toString().contains(pname)) return;
}
if ((traceflags & TRACEPIMPL) != 0) {
PrintStream ps = getTraceOutputFile();
if ((traceflags & TRACETIMESTAMP) != 0) {
ps.print(System.currentTimeMillis());
}
ps.println(prim + " : " + msg);
}
}
public synchronized static void tracePrimitiveTime(Object prim, long time) {
if ((traceflags & TRACEPNAME) != 0) {
if (!prim.toString().contains(pname)) return;
}
if (time > treshold && (traceflags & TRACEPTIME) != 0 && (traceflags & TRACELOG) != 0) {
PrintStream ps = getTraceOutputFile();
ps.println(prim + " time: " + time);
if (verbose) {
final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
if (stackTrace.length > 3) {
for (int i = 3; i < stackTrace.length; i++) {
ps.println(" " + stackTrace[i].toString());
}
}
ps.println();
}
}
}
protected void setupGeneralBinaryOp(GeneralBinaryOp gbo) {
int primID = gbo.getPrimTypeID();
String methodSignature = gbo.getSignature();

View File

@@ -51,6 +51,7 @@ public final class GraphicsPrimitiveMgr {
Class<?> Path2D, Class<?> Path2DFloat,
Class<?> SHints);
private static native void registerNativeLoops();
static native void setTraceFlags(int traceflags);
static {
initIDs(GraphicsPrimitive.class,

View File

@@ -255,10 +255,14 @@ public class MaskBlit extends GraphicsPrimitive
int width, int height,
byte[] mask, int maskoff, int maskscan)
{
tracePrimitive(target);
if ((traceflags & TRACEPTIME) == 0) {
tracePrimitive(target);
}
long time = System.nanoTime();
target.MaskBlit(src, dst, comp, clip,
srcx, srcy, dstx, dsty, width, height,
mask, maskoff, maskscan);
tracePrimitiveTime(target, System.nanoTime() - time);
}
}
}

View File

@@ -237,9 +237,13 @@ public class MaskFill extends GraphicsPrimitive
int x, int y, int w, int h,
byte[] mask, int maskoff, int maskscan)
{
tracePrimitive(target);
if ((traceflags & TRACEPTIME) == 0) {
tracePrimitive(target);
}
long time = System.nanoTime();
target.MaskFill(sg2d, sData, comp, x, y, w, h,
mask, maskoff, maskscan);
tracePrimitiveTime(target, System.nanoTime() - time);
}
public void FillAAPgram(SunGraphics2D sg2d, SurfaceData sData,
@@ -248,9 +252,13 @@ public class MaskFill extends GraphicsPrimitive
double dx1, double dy1,
double dx2, double dy2)
{
tracePrimitive(fillPgramTarget);
if ((traceflags & TRACEPTIME) == 0) {
tracePrimitive(fillPgramTarget);
}
long time = System.nanoTime();
target.FillAAPgram(sg2d, sData, comp,
x, y, dx1, dy1, dx2, dy2);
tracePrimitiveTime(fillPgramTarget, System.nanoTime() - time);
}
public void DrawAAPgram(SunGraphics2D sg2d, SurfaceData sData,
@@ -260,9 +268,13 @@ public class MaskFill extends GraphicsPrimitive
double dx2, double dy2,
double lw1, double lw2)
{
tracePrimitive(drawPgramTarget);
if ((traceflags & TRACEPTIME) == 0) {
tracePrimitive(drawPgramTarget);
}
long time = System.nanoTime();
target.DrawAAPgram(sg2d, sData, comp,
x, y, dx1, dy1, dx2, dy2, lw1, lw2);
tracePrimitiveTime(drawPgramTarget, System.nanoTime() - time);
}
public boolean canDoParallelograms() {

View File

@@ -146,10 +146,14 @@ public class ScaledBlit extends GraphicsPrimitive
double dx1, double dy1,
double dx2, double dy2)
{
tracePrimitive(target);
if ((traceflags & TRACEPTIME) == 0) {
tracePrimitive(target);
}
long time = System.nanoTime();
target.Scale(src, dst, comp, clip,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2);
tracePrimitiveTime(target, System.nanoTime() - time);
}
}
}

View File

@@ -129,11 +129,15 @@ public class TransformHelper extends GraphicsPrimitive
int dx1, int dy1, int dx2, int dy2,
int[] edges, int dxoff, int dyoff)
{
tracePrimitive(target);
if ((traceflags & TRACEPTIME) == 0) {
tracePrimitive(target);
}
long time = System.nanoTime();
target.Transform(output, src, dst, comp, clip, itx, txtype,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2,
edges, dxoff, dyoff);
tracePrimitiveTime(target, System.nanoTime() - time);
}
}
}

View File

@@ -42,7 +42,7 @@ public class OGLContext extends BufferedContext {
private final OGLGraphicsConfig config;
OGLContext(RenderQueue rq, OGLGraphicsConfig config) {
public OGLContext(RenderQueue rq, OGLGraphicsConfig config) {
super(rq);
this.config = config;
}
@@ -71,7 +71,7 @@ public class OGLContext extends BufferedContext {
* situations where we may not otherwise have a current context (e.g.
* when disposing a texture-based surface).
*/
static void setScratchSurface(long pConfigInfo) {
public static void setScratchSurface(long pConfigInfo) {
// assert OGLRenderQueue.getInstance().lock.isHeldByCurrentThread();
// invalidate the current context
@@ -92,7 +92,7 @@ public class OGLContext extends BufferedContext {
* that affect the current context state (e.g. disposing a context or
* surface).
*/
static void invalidateCurrentContext() {
public static void invalidateCurrentContext() {
// assert OGLRenderQueue.getInstance().lock.isHeldByCurrentThread();
// invalidate the current Java-level context so that we
@@ -121,7 +121,7 @@ public class OGLContext extends BufferedContext {
*
* @return an id string for the adapter
*/
static final native String getOGLIdString();
public static final native String getOGLIdString();
@Override
public void saveState() {
@@ -155,18 +155,18 @@ public class OGLContext extends BufferedContext {
rq.flushNow();
}
static class OGLContextCaps extends ContextCapabilities {
public static class OGLContextCaps extends ContextCapabilities {
/**
* Indicates the presence of the GL_EXT_framebuffer_object extension.
* This cap will only be set if the fbobject system property has been
* enabled and we are able to create an FBO with depth buffer.
*/
@Native
static final int CAPS_EXT_FBOBJECT =
public static final int CAPS_EXT_FBOBJECT =
(CAPS_RT_TEXTURE_ALPHA | CAPS_RT_TEXTURE_OPAQUE);
/** Indicates that the context is doublebuffered. */
@Native
static final int CAPS_DOUBLEBUFFERED = (FIRST_PRIVATE_CAP << 0);
public static final int CAPS_DOUBLEBUFFERED = (FIRST_PRIVATE_CAP << 0);
/**
* Indicates the presence of the GL_ARB_fragment_shader extension.
* This cap will only be set if the lcdshader system property has been
@@ -196,7 +196,7 @@ public class OGLContext extends BufferedContext {
static final int CAPS_EXT_TEXBARRIER = (FIRST_PRIVATE_CAP << 5);
OGLContextCaps(int caps, String adapterId) {
public OGLContextCaps(int caps, String adapterId) {
super(caps, adapterId);
}

View File

@@ -34,7 +34,7 @@ import sun.java2d.pipe.hw.AccelGraphicsConfig;
* GLXGraphicsConfig and WGLGraphicsConfig, making it easier to invoke these
* methods directly from OGLSurfaceData.
*/
interface OGLGraphicsConfig extends
public interface OGLGraphicsConfig extends
AccelGraphicsConfig, SurfaceManager.ProxiedGraphicsConfig
{
OGLContext getContext();

View File

@@ -64,8 +64,13 @@ class OGLTextRenderer extends BufferedTextPipe {
super(ogltr.rq);
}
protected void drawGlyphList(SunGraphics2D sg2d, GlyphList gl) {
GraphicsPrimitive.tracePrimitive("OGLDrawGlyphs");
final String prim = "OGLDrawGlyphs" + (gl.isRGBOrder() ? "LCD" : "Gray");
if ((GraphicsPrimitive.traceflags & GraphicsPrimitive.TRACEPTIME) == 0) {
GraphicsPrimitive.tracePrimitive(prim);
}
long time = System.nanoTime();
super.drawGlyphList(sg2d, gl);
GraphicsPrimitive.tracePrimitiveTime(prim, System.nanoTime() - time);
}
}
}

View File

@@ -140,4 +140,8 @@ public abstract class GlyphListPipe implements TextPipe {
int aaHint) {
drawGlyphList(sg2d, gl);
}
public String toString() {
return getClass().getSimpleName();
}
}

View File

@@ -27,11 +27,14 @@
#define _Included_Trace
#include <jni.h>
#include "jni_util.h"
#include "debug_trace.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
extern JavaVM *jvm;
extern jint graphicsPrimitive_traceflags;
/**
* J2dTrace
@@ -175,6 +178,59 @@ J2dTraceInit();
J2dTraceImpl(level, JNI_TRUE, string, arg1, arg2, arg3, arg4, arg5); \
}
#define J2dTracePrimitive(string) { \
if (graphicsPrimitive_traceflags && jvm) { \
JNIEnv *env; \
jstring jstr; \
(*jvm)->AttachCurrentThreadAsDaemon(jvm, &env, NULL); \
jstr = (*env)->NewStringUTF(env, string); \
JNU_CallStaticMethodByName(env, NULL, "sun/java2d/loops/GraphicsPrimitive", \
"tracePrimitive", "(Ljava/lang/Object;)V", jstr); \
(*env)->DeleteLocalRef(env, jstr); \
} \
}
#define J2dTraceNotImplPrimitive(prim) { \
if (graphicsPrimitive_traceflags && jvm) { \
char cbuf[255]; \
JNIEnv *env; \
jstring jprim; \
jstring jmsg; \
snprintf(cbuf, 255, "[NOT IMPL] at %s:%d", __FILE__, __LINE__); \
(*jvm)->AttachCurrentThreadAsDaemon(jvm, &env, NULL); \
jprim = (*env)->NewStringUTF(env, prim); \
jmsg = (*env)->NewStringUTF(env, cbuf); \
JNU_CallStaticMethodByName(env, NULL, \
"sun/java2d/loops/GraphicsPrimitive", \
"traceImplPrimitive", \
"(Ljava/lang/Object;Ljava/lang/Object;)V", \
jprim, jmsg); \
(*env)->DeleteLocalRef(env, jprim); \
(*env)->DeleteLocalRef(env, jmsg); \
} \
}
#define J2dTraceImplPrimitive(prim, msg) { \
if (graphicsPrimitive_traceflags && jvm) { \
char cbuf[255]; \
JNIEnv *env; \
jstring jprim; \
jstring jmsg; \
snprintf(cbuf, 255, "%s at %s:%d", msg, __FILE__, __LINE__); \
(*jvm)->AttachCurrentThreadAsDaemon(jvm, &env, NULL); \
jprim = (*env)->NewStringUTF(env, prim); \
jmsg = (*env)->NewStringUTF(env, cbuf); \
JNU_CallStaticMethodByName(env, NULL, \
"sun/java2d/loops/GraphicsPrimitive", \
"traceImplPrimitive", \
"(Ljava/lang/Object;Ljava/lang/Object;)V", \
jprim, jmsg); \
(*env)->DeleteLocalRef(env, jprim); \
(*env)->DeleteLocalRef(env, jmsg); \
} \
}
#ifdef __cplusplus
};
#endif /* __cplusplus */

View File

@@ -48,7 +48,6 @@ Java_sun_java2d_loops_FillParallelogram_FillParallelogram
CompositeInfo compInfo;
jint pixel;
jint ix1, iy1, ix2, iy2;
if ((dy1 == 0 && dx1 == 0) || (dy2 == 0 && dx2 == 0)) {
return;
}

View File

@@ -75,6 +75,7 @@ JNIEXPORT jfieldID path2DWindingRuleID;
JNIEXPORT jfieldID path2DFloatCoordsID;
JNIEXPORT jfieldID sg2dStrokeHintID;
JNIEXPORT jint sunHints_INTVAL_STROKE_PURE;
JNIEXPORT jint graphicsPrimitive_traceflags = 0;
/*
* Class: sun_java2d_loops_GraphicsPrimitiveMgr
@@ -148,6 +149,13 @@ Java_sun_java2d_loops_GraphicsPrimitiveMgr_initIDs
sunHints_INTVAL_STROKE_PURE = (*env)->GetStaticIntField(env, SHints, fid);
}
JNIEXPORT void JNICALL
Java_sun_java2d_loops_GraphicsPrimitiveMgr_setTraceFlags
(JNIEnv *env, jclass GPMgr, jint traceflags)
{
graphicsPrimitive_traceflags = traceflags;
}
void GrPrim_RefineBounds(SurfaceDataBounds *bounds, jint transX, jint transY,
jfloat *coords, jint maxCoords)
{

View File

@@ -558,6 +558,7 @@ JNIEXPORT extern jfieldID path2DWindingRuleID;
JNIEXPORT extern jfieldID path2DFloatCoordsID;
JNIEXPORT extern jfieldID sg2dStrokeHintID;
JNIEXPORT extern jint sunHints_INTVAL_STROKE_PURE;
JNIEXPORT extern jint graphicsPrimitive_traceflags;
/*
* Macros for using jlong variables as 32bits.32bits fractional values

View File

@@ -191,7 +191,7 @@ typedef AwtObject* PDATA;
#define LO_INT(l) ((int)(short)(l))
#define HI_INT(l) ((int)(short)(((DWORD)(l) >> 16) & 0xFFFF))
extern JavaVM *jvm;
extern "C" JavaVM *jvm;
// Platform encoding is Unicode (UTF-16), re-define JNU_ functions
// to proper JNI functions.

View File

@@ -0,0 +1,599 @@
/*
* Copyright 2019 JetBrains s.r.o.
* 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 perf.metal;
import org.junit.Test;
import javax.swing.*;
import java.awt.*;
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.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
public class MetalPerfTest {
private final static int N = 500;
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;
interface Renderable {
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];
}
}
}
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 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], Color.WHITE};
LinearGradientPaint p =
new LinearGradientPaint(start, end, 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 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]));
}
}
}
class PerfMeter {
private int frame = 0;
private JPanel panel;
private long time;
private double execTime = 0;
private Color expColor = Color.RED;
AtomicBoolean waiting = new AtomicBoolean(false);
double exec(final Renderable renderable) throws Exception{
final CountDownLatch latch = new CountDownLatch(COUNT);
final CountDownLatch latchFrame = new CountDownLatch(1);
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;
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() + 25,
panel.getTopLevelAncestor().getY() + 25);
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() + BW / 2,
panel.getTopLevelAncestor().getY() + BH / 2),
expColor))
{
try {
Thread.sleep(RESOLUTION);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
time = System.nanoTime() - time;
execTime += time;
frame++;
waiting.set(false);
}
latch.countDown();
});
timer.start();
latch.await();
SwingUtilities.invokeAndWait(() -> {
timer.stop();
f.setVisible(false);
f.dispose();
});
latchFrame.await();
return 1e9/(execTime / frame);
}
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 flatOvalRotRenderer = new FlatOvalRotParticleRenderer(N, R);
private static final ParticleRenderer flatBoxRenderer = new FlatBoxParticleRenderer(N, R);
private static final ParticleRenderer flatBoxRotRenderer = new FlatBoxRotParticleRenderer(N, R);
private static final ParticleRenderer linGradOvalRotRenderer = new LinGradOvalRotParticleRenderer(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);
@Test
public void testFlatBubbles() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, flatRenderer);
}
@Override
public void update() {
balls.update();
}
});
System.out.println(fps);
}
@Test
public void testFlatBoxBubbles() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, flatBoxRenderer);
}
@Override
public void update() {
balls.update();
}
});
System.out.println(fps);
}
@Test
public void testFlatBoxRotBubbles() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, flatBoxRotRenderer);
}
@Override
public void update() {
balls.update();
}
});
System.out.println(fps);
}
@Test
public void testFlatOvalRotBubbles() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, flatOvalRotRenderer);
}
@Override
public void update() {
balls.update();
}
});
System.out.println(fps);
}
@Test
public void testLinGradOvalRotBubbles() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, linGradOvalRotRenderer);
}
@Override
public void update() {
balls.update();
}
});
System.out.println(fps);
}
@Test
public void testWiredBubbles() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, wiredRenderer);
}
@Override
public void update() {
balls.update();
}
});
System.out.println(fps);
}
@Test
public void testWiredBoxBubbles() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, wiredBoxRenderer);
}
@Override
public void update() {
balls.update();
}
});
System.out.println(fps);
}
@Test
public void testLines() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, segRenderer);
}
@Override
public void update() {
balls.update();
}
});
System.out.println(fps);
}
@Test
public void testFlatQuad() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, flatQuadRenderer);
}
@Override
public void update() {
balls.update();
}
});
System.out.println(fps);
}
@Test
public void testWiredQuad() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, wiredQuadRenderer);
}
@Override
public void update() {
balls.update();
}
});
System.out.println(fps);
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package quality.metal;
import org.junit.Test;
import quality.util.RenderUtil;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
public class MetalRenderTest {
@Test
public void testMetal() throws Exception {
BufferedImage bi = RenderUtil.capture(120, 120,
graphics2D -> {
graphics2D.setColor(Color.GREEN);
graphics2D.fillRect(10, 10, 50, 50);
graphics2D.setColor(Color.RED);
graphics2D.fillOval(30, 30, 150, 150);
});
RenderUtil.checkImage(bi, "metal", "geom.png");
}
@Test
public void testMetal1() throws Exception {
JFrame[] f = new JFrame[1];
SwingUtilities.invokeAndWait(() -> {
f[0] = new JFrame();
f[0].setSize(300, 300);
// for frame border effects,
// e.g. rounded frame
f[0].setVisible(true);
});
Thread.sleep(4000);
}
@Test
public void testMetal2() throws Exception {
Frame[] f = new Frame[1];
SwingUtilities.invokeAndWait(() -> {
f[0] = new Frame();
f[0].setSize(300, 300);
// for frame border effects,
// e.g. rounded frame
f[0].setVisible(true);
});
Thread.sleep(4000);
}
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package quality.util;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.File;
import java.util.function.Consumer;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class RenderUtil {
final static int TOLERANCE = 1;
public static BufferedImage capture(int width, int height, Consumer<Graphics2D> painter)
throws Exception
{
JFrame[] f = new JFrame[1];
Point[] p = new Point[1];
double[] scale = new double[2];
SwingUtilities.invokeAndWait(() -> {
f[0] = new JFrame();
JComponent c = new MyComponent(painter);
f[0].add(c);
c.setSize(width + 10, height + 10);
f[0].setSize(width + 100, height + 100); // giving some space
// for frame border effects,
// e.g. rounded frame
c.setLocation(50, 50);
f[0].setVisible(true);
p[0]= c.getLocationOnScreen();
scale[0] = f[0].getGraphicsConfiguration().getDefaultTransform().getScaleX();
scale[1] = f[0].getGraphicsConfiguration().getDefaultTransform().getScaleY();
});
Rectangle screenRect;
Robot r = new Robot();
while (!Color.black.equals(r.getPixelColor(p[0].x+1, p[0].y))) {
Thread.sleep(100);
}
screenRect = new Rectangle(
p[0].x + 5,
p[0].y + 5,
(int)((width - 20) * scale[0]), (int)((height - 30) * scale[1]));
BufferedImage result = r.createScreenCapture(screenRect);
SwingUtilities.invokeAndWait(f[0]::dispose);
return result;
}
private static class MyComponent extends JComponent {
private final Consumer<Graphics2D> painter;
private MyComponent(Consumer<Graphics2D> painter) {
this.painter = painter;
}
@Override
protected void paintComponent(Graphics g) {
Shape savedClip = g.getClip();
g.translate(5, 5);
painter.accept((Graphics2D)g);
g.translate(-5, -5);
g.setClip(savedClip);
g.setColor(Color.black);
g.fillRect(0, 0, getWidth() + 10, 5);
g.fillRect(0, getHeight()-5, getWidth() + 10, 5);
g.fillRect(getWidth() - 10, -10, getWidth() + 5, getHeight() + 5);
g.fillRect(-5, -10, 10, getHeight() + 5);
}
}
@SuppressWarnings("SameParameterValue")
public static void checkImage(BufferedImage image, String path, String gfName) throws Exception {
String[] testDataVariant = {
"osx_hardware_rendering", "osx_software_rendering",
"osx_sierra_rendering", "osx_lowres_rendering",
"linux_rendering", "windows_rendering"};
String testDataStr = System.getProperty("testdata");
assertNotNull("testdata property is not set", testDataStr);
File testData = new File(testDataStr, "quality" + File.separator + path);
assertTrue("Test data dir does not exist", testData.exists());
if (System.getProperty("gentestdata") == null) {
boolean failed = true;
StringBuilder failureReason = new StringBuilder();
for (String variant : testDataVariant) {
File goldenFile = new File(testData, variant + File.separator +
gfName);
if (!goldenFile.exists()) continue;
BufferedImage goldenImage = ImageIO.read(goldenFile);
failed = true;
if (image.getWidth() != goldenImage.getWidth() ||
image.getHeight() != image.getHeight())
{
failureReason.append(variant).append(" : Golden image and result have different sizes\n");
continue;
}
Raster gRaster = goldenImage.getData();
Raster rRaster = image.getData();
int[] gArr = new int[3];
int[] rArr = new int[3];
failed = false;
scan:
for (int i = 0; i < gRaster.getWidth(); i++) {
for (int j = 0; j < gRaster.getHeight(); j++) {
gRaster.getPixel(i, j, gArr);
rRaster.getPixel(i, j, rArr);
assertTrue(gArr.length == rArr.length);
for (int k = 0; k < gArr.length; k++) {
int diff = Math.abs(gArr[k] - rArr[k]);
if (diff > TOLERANCE) {
failureReason.append(variant).append(" : Different pixels found (").
append("c[").append(k).append("]=").append(diff).
append(") at (").append(i).append(",").append(j).append(")");
failed = true;
break scan;
}
}
}
}
if (!failed) break;
}
if (failed) throw new RuntimeException(failureReason.toString());
}
else {
ImageIO.write(image, "png", new File(testData, gfName));
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB