8273972: Multi-core choke point in CMM engine (LCMSTransform.doTransform)
Reviewed-by: prr
This commit is contained in:
parent
2072bc77b4
commit
e49e5b5a7e
src/java.desktop/share
test/jdk/sun/java2d/cmm/ColorConvertOp
@ -148,7 +148,7 @@ final class LCMS implements PCMM {
|
||||
}
|
||||
|
||||
/* methods invoked from LCMSTransform */
|
||||
public static native void colorConvert(LCMSTransform trans,
|
||||
public static native void colorConvert(long trans,
|
||||
LCMSImageLayout src,
|
||||
LCMSImageLayout dest);
|
||||
|
||||
|
@ -44,18 +44,30 @@ import java.awt.image.DataBuffer;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.SampleModel;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.lang.ref.Reference;
|
||||
|
||||
import sun.java2d.cmm.ColorTransform;
|
||||
|
||||
import static sun.java2d.cmm.lcms.LCMSImageLayout.ImageLayoutException;
|
||||
|
||||
final class LCMSTransform implements ColorTransform {
|
||||
long ID;
|
||||
private int inFormatter = 0;
|
||||
private boolean isInIntPacked = false;
|
||||
private int outFormatter = 0;
|
||||
private boolean isOutIntPacked = false;
|
||||
|
||||
private final static class NativeTransform {
|
||||
private long ID;
|
||||
private int inFormatter;
|
||||
private boolean isInIntPacked;
|
||||
private int outFormatter;
|
||||
private boolean isOutIntPacked;
|
||||
|
||||
private boolean match(LCMSImageLayout in, LCMSImageLayout out) {
|
||||
return inFormatter == in.pixelType
|
||||
&& isInIntPacked == in.isIntPacked
|
||||
&& outFormatter == out.pixelType
|
||||
&& isOutIntPacked == out.isIntPacked;
|
||||
}
|
||||
}
|
||||
|
||||
private volatile NativeTransform transform;
|
||||
ICC_Profile[] profiles;
|
||||
LCMSProfile[] lcmsProfiles;
|
||||
int renderType;
|
||||
@ -64,8 +76,6 @@ final class LCMSTransform implements ColorTransform {
|
||||
private int numInComponents = -1;
|
||||
private int numOutComponents = -1;
|
||||
|
||||
private Object disposerReferent = new Object();
|
||||
|
||||
public LCMSTransform(ICC_Profile profile, int renderType,
|
||||
int transformType)
|
||||
{
|
||||
@ -122,31 +132,32 @@ final class LCMSTransform implements ColorTransform {
|
||||
return numOutComponents;
|
||||
}
|
||||
|
||||
private synchronized void doTransform(LCMSImageLayout in,
|
||||
LCMSImageLayout out) {
|
||||
// update native transfrom if needed
|
||||
if (ID == 0L ||
|
||||
inFormatter != in.pixelType || isInIntPacked != in.isIntPacked ||
|
||||
outFormatter != out.pixelType || isOutIntPacked != out.isIntPacked)
|
||||
{
|
||||
private void doTransform(LCMSImageLayout in, LCMSImageLayout out) {
|
||||
NativeTransform tfm = transform;
|
||||
// update native transform if needed
|
||||
if (tfm == null || !tfm.match(in, out)) {
|
||||
synchronized (this) {
|
||||
tfm = transform;
|
||||
if (tfm == null || !tfm.match(in, out)) {
|
||||
tfm = new NativeTransform();
|
||||
tfm.inFormatter = in.pixelType;
|
||||
tfm.isInIntPacked = in.isIntPacked;
|
||||
|
||||
if (ID != 0L) {
|
||||
// Disposer will destroy forgotten transform
|
||||
disposerReferent = new Object();
|
||||
tfm.outFormatter = out.pixelType;
|
||||
tfm.isOutIntPacked = out.isIntPacked;
|
||||
|
||||
tfm.ID = LCMS.createTransform(lcmsProfiles, renderType,
|
||||
tfm.inFormatter,
|
||||
tfm.isInIntPacked,
|
||||
tfm.outFormatter,
|
||||
tfm.isOutIntPacked, tfm);
|
||||
// Disposer will destroy forgotten transform
|
||||
transform = tfm;
|
||||
}
|
||||
}
|
||||
inFormatter = in.pixelType;
|
||||
isInIntPacked = in.isIntPacked;
|
||||
|
||||
outFormatter = out.pixelType;
|
||||
isOutIntPacked = out.isIntPacked;
|
||||
|
||||
ID = LCMS.createTransform(lcmsProfiles, renderType,
|
||||
inFormatter, isInIntPacked,
|
||||
outFormatter, isOutIntPacked,
|
||||
disposerReferent);
|
||||
}
|
||||
|
||||
LCMS.colorConvert(this, in, out);
|
||||
LCMS.colorConvert(tfm.ID, in, out);
|
||||
Reference.reachabilityFence(tfm); // prevent deallocation of "tfm.ID"
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,7 +71,6 @@ typedef union {
|
||||
} TagSignature_t, *TagSignature_p;
|
||||
|
||||
static jfieldID Trans_renderType_fID;
|
||||
static jfieldID Trans_ID_fID;
|
||||
static jfieldID IL_isIntPacked_fID;
|
||||
static jfieldID IL_dataType_fID;
|
||||
static jfieldID IL_pixelType_fID;
|
||||
@ -510,9 +509,9 @@ void releaseILData (JNIEnv *env, void* pData, jint dataType,
|
||||
* Signature: (Lsun/java2d/cmm/lcms/LCMSTransform;Lsun/java2d/cmm/lcms/LCMSImageLayout;Lsun/java2d/cmm/lcms/LCMSImageLayout;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert
|
||||
(JNIEnv *env, jclass cls, jobject trans, jobject src, jobject dst)
|
||||
(JNIEnv *env, jclass cls, jlong ID, jobject src, jobject dst)
|
||||
{
|
||||
cmsHTRANSFORM sTrans = NULL;
|
||||
cmsHTRANSFORM sTrans = jlong_to_ptr(ID);
|
||||
int srcDType, dstDType;
|
||||
int srcOffset, srcNextRowOffset, dstOffset, dstNextRowOffset;
|
||||
int width, height, i;
|
||||
@ -533,8 +532,6 @@ JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert
|
||||
srcAtOnce = (*env)->GetBooleanField(env, src, IL_imageAtOnce_fID);
|
||||
dstAtOnce = (*env)->GetBooleanField(env, dst, IL_imageAtOnce_fID);
|
||||
|
||||
sTrans = jlong_to_ptr((*env)->GetLongField (env, trans, Trans_ID_fID));
|
||||
|
||||
if (sTrans == NULL) {
|
||||
J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_colorConvert: transform == NULL");
|
||||
JNU_ThrowByName(env, "java/awt/color/CMMException",
|
||||
@ -626,11 +623,6 @@ JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_initLCMS
|
||||
if (Trans_renderType_fID == NULL) {
|
||||
return;
|
||||
}
|
||||
Trans_ID_fID = (*env)->GetFieldID (env, Trans, "ID", "J");
|
||||
if (Trans_ID_fID == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
IL_isIntPacked_fID = (*env)->GetFieldID (env, IL, "isIntPacked", "Z");
|
||||
if (IL_isIntPacked_fID == NULL) {
|
||||
return;
|
||||
|
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorConvertOp;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8273972
|
||||
* @summary Verifies that ColorConvertOp works fine if shared between threads
|
||||
* @run main/othervm/timeout=600 MTTransformValidation
|
||||
*/
|
||||
public final class MTPerLineTransformValidation {
|
||||
|
||||
private volatile static BufferedImage[] lines;
|
||||
|
||||
public static final int SIZE = 255;
|
||||
private static volatile boolean failed = false;
|
||||
|
||||
private static final int[] spaces = {
|
||||
ColorSpace.CS_CIEXYZ, ColorSpace.CS_GRAY, ColorSpace.CS_LINEAR_RGB,
|
||||
ColorSpace.CS_PYCC, ColorSpace.CS_sRGB
|
||||
};
|
||||
|
||||
private static final int[] types = new int[]{
|
||||
BufferedImage.TYPE_INT_RGB, BufferedImage.TYPE_INT_ARGB,
|
||||
BufferedImage.TYPE_INT_ARGB_PRE, BufferedImage.TYPE_INT_BGR,
|
||||
BufferedImage.TYPE_3BYTE_BGR, BufferedImage.TYPE_4BYTE_ABGR,
|
||||
BufferedImage.TYPE_4BYTE_ABGR_PRE,
|
||||
BufferedImage.TYPE_USHORT_565_RGB,
|
||||
BufferedImage.TYPE_USHORT_555_RGB, BufferedImage.TYPE_BYTE_GRAY,
|
||||
BufferedImage.TYPE_USHORT_GRAY, BufferedImage.TYPE_BYTE_BINARY,
|
||||
BufferedImage.TYPE_BYTE_INDEXED
|
||||
};
|
||||
|
||||
/**
|
||||
* For all possible combinations of color spaces and image types, convert
|
||||
* the source image using one shared ColorConvertOp per line on the
|
||||
* different threads. The result is validated against images converted on
|
||||
* one thread only.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
for (int srcCS : spaces) {
|
||||
for (int dstCS : spaces) {
|
||||
if(srcCS != dstCS) {
|
||||
for (int type : types) {
|
||||
checkTypes(ColorSpace.getInstance(srcCS),
|
||||
ColorSpace.getInstance(dstCS), type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkTypes(ColorSpace srcCS, ColorSpace dstCS, int type)
|
||||
throws Exception {
|
||||
lines = new BufferedImage[SIZE];
|
||||
ColorConvertOp goldOp = new ColorConvertOp(srcCS, dstCS, null);
|
||||
BufferedImage src = createSrc(type);
|
||||
BufferedImage gold = goldOp.filter(src, null);
|
||||
|
||||
// we do not share the goldOp since it is already initialized and used
|
||||
// for the whole image, instead we will create a separate sharedOp and
|
||||
// use it for each line of a different threads
|
||||
ColorConvertOp sharedOp = new ColorConvertOp(srcCS, dstCS, null);
|
||||
Thread[] threads = new Thread[SIZE];
|
||||
for (int y = 0; y < SIZE; ++y) {
|
||||
BufferedImage line = src.getSubimage(0, y, SIZE, 1);
|
||||
threads[y] = test(sharedOp, line, y);
|
||||
}
|
||||
|
||||
for (Thread t: threads) {
|
||||
t.start();
|
||||
}
|
||||
for (Thread t: threads) {
|
||||
t.join();
|
||||
}
|
||||
for (int y = 0; y < SIZE; ++y) {
|
||||
validate(gold, lines[y], y);
|
||||
}
|
||||
if (failed) {
|
||||
throw new RuntimeException("Unexpected exception");
|
||||
}
|
||||
}
|
||||
|
||||
private static Thread test(ColorConvertOp sharedOp,
|
||||
BufferedImage line, int y){
|
||||
return new Thread(() -> {
|
||||
try {
|
||||
BufferedImage image = sharedOp.filter(line, null);
|
||||
lines[y] = image;
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
failed = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static BufferedImage createSrc(int type) {
|
||||
BufferedImage img = new BufferedImage(SIZE, SIZE, type);
|
||||
fill(img);
|
||||
return img;
|
||||
}
|
||||
|
||||
private static void fill(BufferedImage image) {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
for (int j = 0; j < SIZE; j++) {
|
||||
image.setRGB(i, j,
|
||||
(i << 24) | (i << 16) | (j << 8) | ((i + j) >> 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void validate(BufferedImage full, BufferedImage line, int y) {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
int rgb1 = full.getRGB(i, y);
|
||||
int rgb2 = line.getRGB(i, 0);
|
||||
if (rgb1 != rgb2) {
|
||||
System.err.println("rgb1 = " + Integer.toHexString(rgb1));
|
||||
System.err.println("rgb2 = " + Integer.toHexString(rgb2));
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorConvertOp;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8273972
|
||||
* @summary Verifies that ColorConvertOp works fine if shared between threads
|
||||
* @run main/othervm/timeout=600 MTTransformValidation
|
||||
*/
|
||||
public final class MTTransformValidation {
|
||||
|
||||
public static final int SIZE = 255;
|
||||
private static volatile boolean failed = false;
|
||||
|
||||
private static final int[] spaces = {
|
||||
ColorSpace.CS_CIEXYZ, ColorSpace.CS_GRAY, ColorSpace.CS_LINEAR_RGB,
|
||||
ColorSpace.CS_PYCC, ColorSpace.CS_sRGB
|
||||
};
|
||||
|
||||
private static final int[] types = new int[]{
|
||||
BufferedImage.TYPE_INT_RGB, BufferedImage.TYPE_INT_ARGB,
|
||||
BufferedImage.TYPE_INT_ARGB_PRE, BufferedImage.TYPE_INT_BGR,
|
||||
BufferedImage.TYPE_3BYTE_BGR, BufferedImage.TYPE_4BYTE_ABGR,
|
||||
BufferedImage.TYPE_4BYTE_ABGR_PRE,
|
||||
BufferedImage.TYPE_USHORT_565_RGB,
|
||||
BufferedImage.TYPE_USHORT_555_RGB, BufferedImage.TYPE_BYTE_GRAY,
|
||||
BufferedImage.TYPE_USHORT_GRAY, BufferedImage.TYPE_BYTE_BINARY,
|
||||
BufferedImage.TYPE_BYTE_INDEXED
|
||||
};
|
||||
|
||||
/**
|
||||
* For all possible combinations of color spaces and image types, convert
|
||||
* the source image using one shared ColorConvertOp. The result is validated
|
||||
* against images converted on one thread only.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
for (int srcCS : spaces) {
|
||||
for (int dstCS : spaces) {
|
||||
if(srcCS != dstCS) {
|
||||
for (int type : types) {
|
||||
checkTypes(ColorSpace.getInstance(srcCS),
|
||||
ColorSpace.getInstance(dstCS), type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkTypes(ColorSpace srcCS, ColorSpace dstCS, int type)
|
||||
throws Exception {
|
||||
ColorConvertOp goldOp = new ColorConvertOp(srcCS, dstCS, null);
|
||||
BufferedImage gold = goldOp.filter(createSrc(type), null);
|
||||
// we do not share the goldOp since it is already initialized, but
|
||||
// instead we will trigger initialization/usage of the new sharedOp on
|
||||
// different threads at once
|
||||
ColorConvertOp sharedOp = new ColorConvertOp(srcCS, dstCS, null);
|
||||
test(gold, sharedOp, type);
|
||||
|
||||
if (failed) {
|
||||
throw new RuntimeException("Unexpected exception");
|
||||
}
|
||||
}
|
||||
|
||||
private static void test(BufferedImage gold, ColorConvertOp sharedOp,
|
||||
int type) throws Exception {
|
||||
Thread[] ts = new Thread[7];
|
||||
CountDownLatch latch = new CountDownLatch(ts.length);
|
||||
for (int i = 0; i < ts.length; i++) {
|
||||
ts[i] = new Thread(() -> {
|
||||
BufferedImage local = createSrc(type);
|
||||
latch.countDown();
|
||||
try {
|
||||
latch.await();
|
||||
BufferedImage image = sharedOp.filter(local, null);
|
||||
validate(image, gold);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
failed = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
for (Thread t : ts) {
|
||||
t.start();
|
||||
}
|
||||
for (Thread t : ts) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
|
||||
private static BufferedImage createSrc(int type) {
|
||||
BufferedImage img = new BufferedImage(SIZE, SIZE, type);
|
||||
fill(img);
|
||||
return img;
|
||||
}
|
||||
|
||||
private static void fill(BufferedImage image) {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
for (int j = 0; j < SIZE; j++) {
|
||||
image.setRGB(i, j,
|
||||
(i << 24) | (i << 16) | (j << 8) | ((i + j) >> 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void validate(BufferedImage img1, BufferedImage img2) {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
for (int j = 0; j < SIZE; j++) {
|
||||
int rgb1 = img1.getRGB(i, j);
|
||||
int rgb2 = img2.getRGB(i, j);
|
||||
if (rgb1 != rgb2) {
|
||||
System.err.println("rgb1 = " + Integer.toHexString(rgb1));
|
||||
System.err.println("rgb2 = " + Integer.toHexString(rgb2));
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user