Skip to content
Permalink
Browse files
8273972: Multi-core choke point in CMM engine (LCMSTransform.doTransf…
…orm)

Backport-of: e49e5b5a7ed9e493380fb73100057898c707b31b
  • Loading branch information
mrserb committed Jan 21, 2022
1 parent 17cdd90 commit 55fb592e455ab87c38f17c0e746641a978b88cf3
Showing 5 changed files with 333 additions and 41 deletions.
@@ -148,7 +148,7 @@ public synchronized ColorTransform createTransform(
}

/* 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.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 @@ public int getNumOutComponents() {
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)
{

if (ID != 0L) {
// Disposer will destroy forgotten transform
disposerReferent = new Object();
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;

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();
}
}
}
}

1 comment on commit 55fb592

@openjdk-notifier
Copy link

@openjdk-notifier openjdk-notifier bot commented on 55fb592 Jan 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.