Compare commits

..

13 Commits

Author SHA1 Message Date
Nikita Tsarev
7c84fbe810 JBR-8353: Fix wrong order of java/wayland object destruction in DataOffer/DataSource [WLToolkit] 2025-10-10 14:29:00 +02:00
Vitaly Provodin
6c47534225 update exclude list on results of 25.152.37 test runs 2025-10-10 03:17:36 +04:00
Maxim Kartashev
ffde0964ef JBR-9451 Wayland: Calling other JNI functions in the scope of Get/ReleasePrimitiveArrayCritical or Get/ReleaseStringCritical 2025-10-09 16:38:25 +04:00
Alexey Ushakov
d0299a4c6a JBR-9292 Vulkan: RenderPerfTest missing frames 2025-10-08 22:51:35 +02:00
Nikita Gubarkov
1186447190 JBR-9457 Vulkan: Enable accelerated surfaces by default 2025-10-08 22:10:35 +02:00
Nikita Gubarkov
4eab05ccd9 JBR-7646 Vulkan: Implement painting modes 2025-10-08 22:10:35 +02:00
Nikita Gubarkov
74b4df4496 JBR-9450 Vulkan: Unify pipelines 2025-10-08 22:09:48 +02:00
Nikita Gubarkov
8f660e630c JBR-9439 Vulkan: Fix blit composites 2025-10-08 22:09:40 +02:00
Nikita Gubarkov
4bd1551dc4 JBR-8344 Vulkan: Fix color XOR 2025-10-08 16:46:02 +02:00
Nikita Tsarev
4879508145 JBR-9449: Use wl_proxy_create_wrapper when creating data source objects for thread-safety [WLToolkit] 2025-10-07 10:25:16 +02:00
Maxim Kartashev
84473294fb JBR-9364 Wayland: Popups are shifted with multiple monitor setup after monitor reconnected (Ubuntu) 2025-10-07 10:17:17 +04:00
bourgesl
57e694c1ae JBR-9408 Fix Marlin renderer statistics
Revert JBR-9283 changes to StatLong (completely) to avoid future conflicts

(cherry picked from commit bc60599b45bddcb2d251035f945b5616e43554d2)
(cherry picked from commit 5be4830ecf30e2c74d1e828e3482edca03d166c6)
2025-10-05 23:17:06 +02:00
bourgesl
12e0466566 JDK-8341381 Random lines appear in graphic causing by the fix of JDK-8297230
- Fix cubic offsetting artefacts (sort cubic roots + fixed numerical accuracy problem in ROC^2-w^2 = 0 solver + fixed EliminateInf)
- Restored lower precision using ulp(float) in point, line or flat bezier curve checks

(cherry picked from commit e72b87e6538dda97e6f0f2840040c6864b3f146e)
2025-10-05 23:04:09 +02:00
47 changed files with 1615 additions and 589 deletions

View File

@@ -89,7 +89,7 @@ AC_DEFUN_ONCE([LIB_SETUP_VULKAN],
if (test "x${with_vulkan_shader_compiler}" = x || test "x${with_vulkan_shader_compiler}" = xglslc); then
UTIL_LOOKUP_PROGS(GLSLC, glslc)
SHADER_COMPILER="$GLSLC"
VULKAN_SHADER_COMPILER="glslc --target-env=vulkan1.2 -mfmt=num -o"
VULKAN_SHADER_COMPILER="glslc --target-env=vulkan1.2 -mfmt=num"
fi
# Check glslangValidator
@@ -97,7 +97,8 @@ AC_DEFUN_ONCE([LIB_SETUP_VULKAN],
test "x$SHADER_COMPILER" = x; then
UTIL_LOOKUP_PROGS(GLSLANG, glslangValidator)
SHADER_COMPILER="$GLSLANG"
VULKAN_SHADER_COMPILER="glslangValidator --target-env vulkan1.2 -x -o"
# Newer glslangValidator could use -P\"\#extension GL_GOOGLE_include_directive: require\"
VULKAN_SHADER_COMPILER="glslangValidator --target-env vulkan1.2 -x"
fi
if test "x$SHADER_COMPILER" = x; then

View File

@@ -220,7 +220,7 @@ endif
# Compile Vulkan shaders
define compile-spirv
$(call MakeTargetDir)
$(VULKAN_SHADER_COMPILER) '$(call DecodeSpace, $@)' '$(call DecodeSpace, $<)'
$(VULKAN_SHADER_COMPILER) -D$(call uppercase,$(patsubst .%,STAGE_%,$(suffix $<))) -o '$(call DecodeSpace, $@)' '$(call DecodeSpace, $<)'
endef
spirv-name = $(strip $1).h

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2025, 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
@@ -144,7 +144,9 @@ final class Curve {
// finds points where the first and second derivative are
// perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where
// * is a dot product). Unfortunately, we have to solve a cubic.
private int perpendiculardfddf(final double[] pts, final int off) {
private int perpendiculardfddf(final double[] pts, final int off,
final double A, final double B)
{
assert pts.length >= off + 4;
// these are the coefficients of some multiple of g(t) (not g(t),
@@ -155,7 +157,7 @@ final class Curve {
final double c = 2.0d * (dax * cx + day * cy) + dbx * dbx + dby * dby;
final double d = dbx * cx + dby * cy;
return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0.0d, 1.0d);
return Helpers.cubicRootsInAB(a, b, c, d, pts, off, A, B);
}
// Tries to find the roots of the function ROC(t)-w in [0, 1). It uses
@@ -171,35 +173,43 @@ final class Curve {
// at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection
// points, so roc-w can have at least 6 roots. This shouldn't be a
// problem for what we're trying to do (draw a nice looking curve).
int rootsOfROCMinusW(final double[] roots, final int off, final double w2, final double err) {
int rootsOfROCMinusW(final double[] roots, final int off, final double w2,
final double A, final double B)
{
// no OOB exception, because by now off<=6, and roots.length >= 10
assert off <= 6 && roots.length >= 10;
int ret = off;
final int end = off + perpendiculardfddf(roots, off);
final int end = off + perpendiculardfddf(roots, off, A, B);
Helpers.isort(roots, off, end);
roots[end] = 1.0d; // always check interval end points
double t0 = 0.0d, ft0 = ROCsq(t0) - w2;
double t0 = 0.0d;
double ft0 = eliminateInf(ROCsq(t0) - w2);
double t1, ft1;
for (int i = off; i <= end; i++) {
double t1 = roots[i], ft1 = ROCsq(t1) - w2;
t1 = roots[i];
ft1 = eliminateInf(ROCsq(t1) - w2);
if (ft0 == 0.0d) {
roots[ret++] = t0;
} else if (ft1 * ft0 < 0.0d) { // have opposite signs
// (ROC(t)^2 == w^2) == (ROC(t) == w) is true because
// ROC(t) >= 0 for all t.
roots[ret++] = falsePositionROCsqMinusX(t0, t1, w2, err);
roots[ret++] = falsePositionROCsqMinusX(t0, t1, ft0, ft1, w2, A); // A = err
}
t0 = t1;
ft0 = ft1;
}
return ret - off;
}
private static double eliminateInf(final double x) {
return (x == Double.POSITIVE_INFINITY ? Double.MAX_VALUE :
(x == Double.NEGATIVE_INFINITY ? Double.MIN_VALUE : x));
private final static double MAX_ROC_SQ = 1e20;
private static double eliminateInf(final double x2) {
// limit the value of x to avoid numerical problems (smaller step):
// must handle NaN and +Infinity:
return (x2 <= MAX_ROC_SQ) ? x2 : MAX_ROC_SQ;
}
// A slight modification of the false position algorithm on wikipedia.
@@ -210,17 +220,18 @@ final class Curve {
// and turn out. Same goes for the newton's method
// algorithm in Helpers.java
private double falsePositionROCsqMinusX(final double t0, final double t1,
final double ft0, final double ft1,
final double w2, final double err)
{
final int iterLimit = 100;
int side = 0;
double t = t1, ft = eliminateInf(ROCsq(t) - w2);
double s = t0, fs = eliminateInf(ROCsq(s) - w2);
double s = t0, fs = eliminateInf(ft0);
double t = t1, ft = eliminateInf(ft1);
double r = s, fr;
for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {
for (int i = 0; i < iterLimit && Math.abs(t - s) > err; i++) {
r = (fs * t - ft * s) / (fs - ft);
fr = ROCsq(r) - w2;
fr = eliminateInf(ROCsq(r) - w2);
if (sameSign(fr, ft)) {
ft = fr; t = r;
if (side < 0) {
@@ -241,7 +252,7 @@ final class Curve {
break;
}
}
return r;
return (Math.abs(ft) <= Math.abs(fs)) ? t : s;
}
private static boolean sameSign(final double x, final double y) {
@@ -256,9 +267,9 @@ final class Curve {
final double dy = t * (t * day + dby) + cy;
final double ddx = 2.0d * dax * t + dbx;
final double ddy = 2.0d * day * t + dby;
final double dx2dy2 = dx * dx + dy * dy;
final double ddx2ddy2 = ddx * ddx + ddy * ddy;
final double ddxdxddydy = ddx * dx + ddy * dy;
return dx2dy2 * ((dx2dy2 * dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy * ddxdxddydy));
final double dx2dy2 = dx * dx + dy * dy; // positive
final double dxddyddxdy = dx * ddy - dy * ddx;
// may return +Infinity if dxddyddxdy = 0 or NaN if 0/0:
return (dx2dy2 * dx2dy2 * dx2dy2) / (dxddyddxdy * dxddyddxdy); // both positive
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2025, 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
@@ -564,7 +564,7 @@ public final class DMarlinRenderingEngine extends RenderingEngine
}
private static boolean nearZero(final double num) {
return Math.abs(num) < 2.0d * Math.ulp(num);
return Math.abs(num) < 2.0d * Helpers.ulp(num);
}
abstract static class NormalizingPathIterator implements PathIterator {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2025, 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
@@ -31,12 +31,19 @@ import sun.java2d.marlin.stats.StatLong;
final class Helpers implements MarlinConst {
private final static double T_ERR = 1e-4;
private final static double T_A = T_ERR;
private final static double T_B = 1.0 - T_ERR;
private static final double EPS = 1e-9d;
private Helpers() {
throw new Error("This is a non instantiable class");
}
/** use lower precision like former Pisces and Marlin (float-precision) */
static double ulp(final double value) { return Math.ulp((float)value); }
static boolean within(final double x, final double y) {
return within(x, y, EPS);
}
@@ -322,10 +329,10 @@ final class Helpers implements MarlinConst {
// now we must subdivide at points where one of the offset curves will have
// a cusp. This happens at ts where the radius of curvature is equal to w.
ret += c.rootsOfROCMinusW(ts, ret, w2, 0.0001d);
ret += c.rootsOfROCMinusW(ts, ret, w2, T_A, T_B);
ret = filterOutNotInAB(ts, 0, ret, 0.0001d, 0.9999d);
isort(ts, ret);
ret = filterOutNotInAB(ts, 0, ret, T_A, T_B);
isort(ts, 0, ret);
return ret;
}
@@ -354,7 +361,7 @@ final class Helpers implements MarlinConst {
if ((outCodeOR & OUTCODE_BOTTOM) != 0) {
ret += curve.yPoints(ts, ret, clipRect[1]);
}
isort(ts, ret);
isort(ts, 0, ret);
return ret;
}
@@ -374,11 +381,11 @@ final class Helpers implements MarlinConst {
}
}
static void isort(final double[] a, final int len) {
for (int i = 1, j; i < len; i++) {
static void isort(final double[] a, final int off, final int len) {
for (int i = off + 1, j; i < len; i++) {
final double ai = a[i];
j = i - 1;
for (; j >= 0 && a[j] > ai; j--) {
for (; j >= off && a[j] > ai; j--) {
a[j + 1] = a[j];
}
a[j + 1] = ai;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2025, 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
@@ -886,8 +886,8 @@ final class Stroker implements StartFlagPathConsumer2D, MarlinConst {
// if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
// in which case ignore if p1 == p2
final boolean p1eqp2 = Helpers.withinD(dx1, dy1, 6.0d * Math.ulp(y2));
final boolean p3eqp4 = Helpers.withinD(dx4, dy4, 6.0d * Math.ulp(y4));
final boolean p1eqp2 = Helpers.withinD(dx1, dy1, 6.0d * Helpers.ulp(y2));
final boolean p3eqp4 = Helpers.withinD(dx4, dy4, 6.0d * Helpers.ulp(y4));
if (p1eqp2 && p3eqp4) {
return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
@@ -905,7 +905,7 @@ final class Stroker implements StartFlagPathConsumer2D, MarlinConst {
final double l1sq = dx1 * dx1 + dy1 * dy1;
final double l4sq = dx4 * dx4 + dy4 * dy4;
if (Helpers.within(dotsq, l1sq * l4sq, 4.0d * Math.ulp(dotsq))) {
if (Helpers.within(dotsq, l1sq * l4sq, 4.0d * Helpers.ulp(dotsq))) {
return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
}
@@ -1078,8 +1078,8 @@ final class Stroker implements StartFlagPathConsumer2D, MarlinConst {
// equal if they're very close to each other.
// if p1 == p2 or p2 == p3: draw line from p1->p3
final boolean p1eqp2 = Helpers.withinD(dx12, dy12, 6.0d * Math.ulp(y2));
final boolean p2eqp3 = Helpers.withinD(dx23, dy23, 6.0d * Math.ulp(y3));
final boolean p1eqp2 = Helpers.withinD(dx12, dy12, 6.0d * Helpers.ulp(y2));
final boolean p2eqp3 = Helpers.withinD(dx23, dy23, 6.0d * Helpers.ulp(y3));
if (p1eqp2 || p2eqp3) {
return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
@@ -1091,7 +1091,7 @@ final class Stroker implements StartFlagPathConsumer2D, MarlinConst {
final double l1sq = dx12 * dx12 + dy12 * dy12;
final double l3sq = dx23 * dx23 + dy23 * dy23;
if (Helpers.within(dotsq, l1sq * l3sq, 4.0d * Math.ulp(dotsq))) {
if (Helpers.within(dotsq, l1sq * l3sq, 4.0d * Helpers.ulp(dotsq))) {
return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,7 +27,7 @@ package sun.java2d.marlin;
public final class Version {
private static final String VERSION = "marlin-0.9.4.7-Unsafe-OpenJDK";
private static final String VERSION = "marlin-0.9.4.9-Unsafe-OpenJDK";
public static String getVersion() {
return VERSION;

View File

@@ -31,18 +31,20 @@ package sun.java2d.marlin.stats;
public class StatLong {
public final String name;
public long count, sum, min, max;
public long count = 0L;
public long sum = 0L;
public long min = Integer.MAX_VALUE;
public long max = Integer.MIN_VALUE;
public StatLong(final String name) {
this.name = name;
reset();
}
public void reset() {
count = 0L;
sum = 0L;
min = Long.MAX_VALUE;
max = Long.MIN_VALUE;
min = Integer.MAX_VALUE;
max = Integer.MIN_VALUE;
}
public void add(final int val) {
@@ -76,7 +78,7 @@ public class StatLong {
sb.append(name).append('[').append(count);
sb.append("] sum: ").append(sum).append(" avg: ");
sb.append(trimTo3Digits(((double) sum) / count));
sb.append(" [").append(min).append(" - ").append(max).append("]");
sb.append(" [").append(min).append(" | ").append(max).append("]");
return sb;
}
@@ -87,7 +89,7 @@ public class StatLong {
* @return double value with only 3 decimal digits
*/
public static double trimTo3Digits(final double value) {
return Double.isFinite(value) ? ((long) (1e3d * value)) / 1e3d : Double.NaN;
return ((long) (1e3d * value)) / 1e3d;
}
}

View File

@@ -48,7 +48,7 @@ public final class VKEnv {
@SuppressWarnings("removal")
private static final boolean accelsd = vulkan && "true".equalsIgnoreCase(AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty("sun.java2d.vulkan.accelsd", "")));
(PrivilegedAction<String>) () -> System.getProperty("sun.java2d.vulkan.accelsd", "true")));
@SuppressWarnings("removal")
private static final int deviceNumber = !vulkan ? 0 : AccessController.doPrivileged(

View File

@@ -1,20 +0,0 @@
#define ALPHA_TYPE_PRE_MULTIPLIED 0U
#define ALPHA_TYPE_STRAIGHT 1U
vec4 convertAlpha(vec4 color, uint inType, uint outType) {
if (inType == ALPHA_TYPE_STRAIGHT && outType == ALPHA_TYPE_PRE_MULTIPLIED) {
return vec4(color.rgb * color.a, color.a);
} else if (inType == ALPHA_TYPE_PRE_MULTIPLIED && outType == ALPHA_TYPE_STRAIGHT && color.a > 0.0) {
return vec4(color.rgb / color.a, color.a);
} else return color;
}
#ifdef ALPHA_TYPE_SPEC_INDEX
layout (constant_id = ALPHA_TYPE_SPEC_INDEX ) const uint const_InAlphaType = ALPHA_TYPE_PRE_MULTIPLIED;
layout (constant_id = ALPHA_TYPE_SPEC_INDEX+1) const uint const_OutAlphaType = ALPHA_TYPE_PRE_MULTIPLIED;
vec4 convertAlpha(vec4 color) {
return convertAlpha(color, const_InAlphaType, const_OutAlphaType);
}
#endif

View File

@@ -1,7 +1,7 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#define ALPHA_TYPE_SPEC_INDEX 0
#include "alpha_type.glsl"
#include "common.glsl"
DEFAULT_PUSH_CONSTANTS();
layout(set = 0, binding = 0) uniform texture2D u_Texture;
layout(set = 1, binding = 0) uniform sampler u_Sampler;
@@ -9,5 +9,5 @@ layout(location = 0) in vec2 in_TexCoord;
layout(location = 0) out vec4 out_Color;
void main() {
out_Color = convertAlpha(texture(sampler2D(u_Texture, u_Sampler), in_TexCoord));
out_Color = APPLY_COMPOSITE(convertAlpha(texture(sampler2D(u_Texture, u_Sampler), in_TexCoord)));
}

View File

@@ -1,14 +1,12 @@
#version 450
layout(push_constant) uniform PushConstants {
mat2x3 transform;
} push;
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(location = 0) in vec2 in_Position;
layout(location = 1) in vec2 in_TexCoord;
layout(location = 0) out vec2 out_TexCoord;
void main() {
gl_Position = vec4(vec3(in_Position, 1.0)*push.transform, 0.0, 1.0);
gl_Position = transformToDeviceSpace(in_Position);
out_TexCoord = in_TexCoord;
}

View File

@@ -1,11 +1,9 @@
#version 450
layout(push_constant) uniform PushConstants {
mat2x3 transform;
} push;
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(location = 0) in ivec2 in_Position;
void main() {
gl_Position = vec4(vec3(in_Position, 1)*push.transform, 0.0, 1.0);
gl_Position = transformToDeviceSpace(in_Position);
}

View File

@@ -1,14 +1,12 @@
#version 450
layout(push_constant) uniform PushConstants {
mat2x3 transform;
} push;
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(location = 0) in vec2 in_Position;
layout(location = 1) in vec4 in_Color;
layout(location = 1) in uint in_Color;
layout(location = 0) out flat vec4 out_Color;
void main() {
gl_Position = vec4(vec3(in_Position, 1)*push.transform, 0.0, 1.0);
out_Color = in_Color;
gl_Position = transformToDeviceSpace(in_Position);
out_Color = convertAlpha(decodeColor(in_Color)); // No need to APPLY_COMPOSITE - it was already done on the host.
}

View File

@@ -0,0 +1,103 @@
// Shader specialization.
#define SHADER_MOD_XOR 1U // Xor composite mode
#define SHADER_MOD_MASK 2U // MASK_FILL / MASK_BLIT
layout (constant_id = 0) const uint const_InAlphaType = 0;
layout (constant_id = 1) const uint const_OutAlphaType = 0;
layout (constant_id = 2) const uint const_ShaderVariant = 0;
layout (constant_id = 3) const uint const_ShaderModifier = 0;
// Host structs.
struct VKTransform {
float m00, m01, m02;
float m10, m11, m12;
};
struct VKCompositeConstants {
uint xorColor;
float extraAlpha;
};
// Vertex shader transformation support.
#ifdef STAGE_VERT
layout(push_constant) uniform PushConstants { VKTransform transform; } push;
vec4 transformToDeviceSpace(vec2 v) {
return vec4(vec3(v, 1.0) * mat2x3(push.transform.m00, push.transform.m01, push.transform.m02, push.transform.m10, push.transform.m11, push.transform.m12), 0.0, 1.0);
}
#endif
// Fragment shader push constant support.
#ifdef STAGE_FRAG
#define PUSH_CONSTANTS_IMPL(STATEMENT) \
layout(push_constant) uniform PushConstants { VKTransform _; VKCompositeConstants push_composite; STATEMENT }
#define DEFAULT_PUSH_CONSTANTS() PUSH_CONSTANTS_IMPL(STAGE_FRAG)
#define PUSH_CONSTANTS(TYPE) PUSH_CONSTANTS_IMPL(TYPE push;)
#endif
// Color conversion support.
#define ALPHA_TYPE_PRE_MULTIPLIED 0U
#define ALPHA_TYPE_STRAIGHT 1U
vec4 convertAlpha(vec4 color, uint inType, uint outType) {
if (inType == ALPHA_TYPE_STRAIGHT && outType == ALPHA_TYPE_PRE_MULTIPLIED) {
return vec4(color.rgb * color.a, color.a);
} else if (inType == ALPHA_TYPE_PRE_MULTIPLIED && outType == ALPHA_TYPE_STRAIGHT && color.a > 0.0) {
return vec4(color.rgb / color.a, color.a);
} else return color;
}
vec4 convertAlpha(vec4 color) {
return convertAlpha(color, const_InAlphaType, const_OutAlphaType);
}
// When applying alpha to a color, straight alpha only multiplies alpha,
// and pre-multiplied multiplies the whole color. Use this for convenience.
vec4 alphaMask(float alpha, uint alphaType) {
return alphaType == ALPHA_TYPE_PRE_MULTIPLIED ? vec4(alpha) : vec4(1.0, 1.0, 1.0, alpha);
}
// Decode color from uint-packed ARGB components.
vec4 decodeColor(uint srgb) {
return vec4((uvec4(srgb) >> uvec4(16, 8, 0, 24)) & 0xFFU) / 255.0;
}
#ifdef STAGE_FRAG
// Before outputting the color, some post-processing is needed:
// - For alpha composite, apply extra alpha.
// - For XOR composite, apply xor.
vec4 applyComposite(vec4 color, VKCompositeConstants composite) {
if ((const_ShaderModifier & SHADER_MOD_XOR) != 0) {
uvec4 xor = uvec4(composite.xorColor) >> uvec4(16, 8, 0, 24);
xor = (uvec4(color * 255.0) ^ xor) & 0xFFU;
return vec4(xor) / 255.0;
} else return color * alphaMask(composite.extraAlpha, const_OutAlphaType);
}
#define APPLY_COMPOSITE(COLOR) applyComposite(COLOR, push_composite)
// MASK_FILL / MASK_BLIT support.
int calculateMaskIndex(vec2 localCoord, ivec4 originOffsetAndScanline) {
ivec2 maskPos = ivec2(localCoord - vec2(originOffsetAndScanline.xy));
int offset = originOffsetAndScanline.z;
int scanline = originOffsetAndScanline.w;
return offset + scanline * maskPos.y + min(scanline, maskPos.x);
}
vec4 applyMaskOp(vec4 color, float mask) {
if ((const_ShaderModifier & SHADER_MOD_XOR) != 0) return color * float(mask > 0.0);
else return color * alphaMask(mask, const_OutAlphaType);
}
#define APPLY_MASK(COLOR) applyMaskOp(COLOR, imageLoad(u_Mask, calculateMaskIndex(gl_FragCoord.xy, in_OriginOffsetAndScanline)).r)
// Generic shader support.
#define GENERIC_INOUT() \
layout(location = 0) out vec4 out_Color; \
layout(location = 0) in vec2 in_Position; \
layout(location = 1) in flat uint in_Data; \
layout(location = 2) in flat ivec4 in_OriginOffsetAndScanline; \
layout(origin_upper_left) in vec4 gl_FragCoord; \
layout(set = 0, binding = 0, r8) uniform readonly restrict imageBuffer u_Mask
// Generic color output - handles composite and mask automatically.
#define OUTPUT(COLOR) out_Color = COLOR; out_Color = APPLY_COMPOSITE(out_Color); \
if ((const_ShaderModifier & SHADER_MOD_MASK) != 0) out_Color = APPLY_MASK(out_Color)
#endif

View File

@@ -0,0 +1,21 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
#define SHADER_VARIANT_GRADIENT_CLAMP 0
#define SHADER_VARIANT_GRADIENT_CYCLE 1
struct VKGradientPaintConstants {
vec4 c0, c1;
vec3 p;
};
PUSH_CONSTANTS(VKGradientPaintConstants);
GENERIC_INOUT();
void main() {
float t = dot(vec3(in_Position, 1.0), push.p);
t = const_ShaderVariant == SHADER_VARIANT_GRADIENT_CYCLE ?
abs(mod(t + 1.0, 2.0) - 1.0) : // Cycle
clamp(t, 0.0, 1.0); // Clamp
OUTPUT(convertAlpha(mix(push.c0, push.c1, t)));
}

View File

@@ -0,0 +1,21 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(location = 0) in ivec4 in_PositionOffsetAndScanline;
layout(location = 1) in uint in_Data;
layout(location = 0) out vec2 out_Position;
layout(location = 1) out uint out_Data;
// This starts with "Origin" and not "Position" intentionally.
// When drawing, vertices are ordered in a such way, that provoking vertex is always the top-left one.
// This gives us an easy way to calculate offset within the rectangle without additional inputs.
layout(location = 2) out flat ivec4 out_OriginOffsetAndScanline;
void main() {
out_Position = in_PositionOffsetAndScanline.xy;
out_Data = in_Data;
out_OriginOffsetAndScanline = in_PositionOffsetAndScanline;
gl_Position = transformToDeviceSpace(in_PositionOffsetAndScanline.xy);
}

View File

@@ -1,4 +1,6 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(set = 0, binding = 0, r8) uniform readonly restrict imageBuffer u_Mask;
@@ -10,9 +12,5 @@ layout(location = 1) in flat vec4 in_Color;
layout(location = 0) out vec4 out_Color;
void main() {
ivec2 maskPos = ivec2(gl_FragCoord.xy - vec2(in_OriginOffsetAndScanline.xy));
int offset = in_OriginOffsetAndScanline.z;
int scanline = in_OriginOffsetAndScanline.w;
int maskIndex = offset + scanline * maskPos.y + min(scanline, maskPos.x);
out_Color = in_Color * imageLoad(u_Mask, maskIndex).r;
out_Color = APPLY_MASK(in_Color);
}

View File

@@ -1,11 +1,9 @@
#version 450
layout(push_constant) uniform PushConstants {
mat2x3 transform;
} push;
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(location = 0) in ivec4 in_PositionOffsetAndScanline;
layout(location = 1) in vec4 in_Color;
layout(location = 1) in uint in_Color;
// This starts with "Origin" and not "Position" intentionally.
// When drawing, vertices are ordered in a such way, that provoking vertex is always the top-left one.
@@ -14,7 +12,7 @@ layout(location = 0) out flat ivec4 out_OriginOffsetAndScanline;
layout(location = 1) out flat vec4 out_Color;
void main() {
gl_Position = vec4(vec3(in_PositionOffsetAndScanline.xy, 1)*push.transform, 0.0, 1.0);
gl_Position = transformToDeviceSpace(in_PositionOffsetAndScanline.xy);
out_OriginOffsetAndScanline = in_PositionOffsetAndScanline;
out_Color = in_Color;
out_Color = convertAlpha(decodeColor(in_Color));
}

View File

@@ -0,0 +1,15 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(location = 0) in vec2 in_Position;
layout(location = 1) in uint in_Data;
layout(location = 0) out vec2 out_Position;
layout(location = 1) out uint out_Data;
layout(location = 2) out flat ivec4 _; // Unused output
void main() {
out_Position = in_Position;
out_Data = in_Data;
gl_Position = transformToDeviceSpace(in_Position);
}

View File

@@ -113,7 +113,7 @@ void VKBlitLoops_IsoBlit(VKSDOps* srcOps, jint filter,
VK_COMPONENT_SWIZZLE_ONE);
VKPackedSwizzle swizzle = srcOpaque ? OPAQUE_SWIZZLE : 0;
if (!VKRenderer_Validate(SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, alphaType)) return;
if (!VKRenderer_Validate(SHADER_BLIT, NO_SHADER_VARIANT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, alphaType)) return;
VKRenderer_DrawImage(srcOps->image, srcOps->image->format, swizzle, filter, SAMPLER_WRAP_BORDER,
(float)sx1, (float)sy1, (float)sx2, (float)sy2, (float)dx1, (float)dy1, (float)dx2, (float)dy2);
VKRenderer_AddSurfaceDependency(srcOps, context->surface);
@@ -165,7 +165,7 @@ void VKBlitLoops_Blit(JNIEnv *env, SurfaceDataOps* src, jshort srctype, jint fil
// Need to validate render pass early, as image may not yet be configured.
AlphaType alphaType = getSrcAlphaType(srctype);
if (!VKRenderer_Validate(SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, alphaType)) return;
if (!VKRenderer_Validate(SHADER_BLIT, NO_SHADER_VARIANT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, alphaType)) return;
VKDevice* device = context->surface->device;
BlitSrcType type = decodeSrcType(device, srctype);

View File

@@ -56,7 +56,7 @@ VKComposites VKComposites_Create() {
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT },
{ .logicOpEnable = VK_TRUE,
.logicOp = VK_LOGIC_OP_XOR }, ALPHA_TYPE_PRE_MULTIPLIED });
.logicOp = VK_LOGIC_OP_XOR }, ALPHA_TYPE_STRAIGHT });
// NAME | SRC_COLOR | DST_COLOR | SRC_ALPHA | DST_ALPHA ||
ALPHA_BLEND( CLEAR , ZERO , ZERO , ZERO , ZERO );
@@ -138,7 +138,6 @@ void VKComposites_AddState(VKComposites* composites, VKCompositeMode mode, VKCom
state.blendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
state.blendState.pNext = NULL;
state.blendState.attachmentCount = 1;
state.outAlphaType = ALPHA_TYPE_PRE_MULTIPLIED;
MAP_AT(composites->map, (VKCompositeDescriptor) { mode, VK_FALSE }) = state;
// Using pre-multiplied alpha is necessary for correct blending,

View File

@@ -49,6 +49,7 @@ static size_t pipelineDescriptorHash(const void* ptr) {
hash(&h, d->inAlphaType);
hash(&h, d->composite);
hash(&h, d->shader);
hash(&h, d->shaderVariant);
hash(&h, d->topology);
return (size_t) h;
}
@@ -59,6 +60,7 @@ static bool pipelineDescriptorEquals(const void* ap, const void* bp) {
a->inAlphaType == b->inAlphaType &&
a->composite == b->composite &&
a->shader == b->shader &&
a->shaderVariant == b->shaderVariant &&
a->topology == b->topology;
}
@@ -144,12 +146,20 @@ static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPas
VkPipelineShaderStageCreateInfo createInfos[2]; // vert + frag
} ShaderStages;
ShaderStages stages[count];
typedef struct {
uint32_t inAlphaType, outAlphaType, shaderVariant, shaderModifier;
} SpecializationData;
const VkSpecializationMapEntry SPECIALIZATION_ENTRIES[] = {
{ 0, 0, 4 },
{ 1, 4, 4 },
{ 2, 8, 4 },
{ 3, 12, 4 }
};
typedef struct {
VkSpecializationInfo info;
VkSpecializationMapEntry entries[2];
uint64_t data[1];
SpecializationData data;
} Specialization;
Specialization specializations[count][2];
Specialization specializations[count];
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStates[count];
VkPipelineDepthStencilStateCreateInfo depthStencilStates[count];
VkPipelineDynamicStateCreateInfo dynamicStates[count];
@@ -163,14 +173,19 @@ static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPas
// - pStages (but stageCount is set to 2)
// - pVertexInputState
// - createInfo.layout
for (uint32_t j = 0; j < SARRAY_COUNT_OF(specializations[i]); j++) {
specializations[i][j].info = (VkSpecializationInfo) {
.mapEntryCount = 0,
.pMapEntries = specializations[i][j].entries,
.dataSize = 0,
.pData = specializations[i][j].data
};
}
specializations[i] = (Specialization) {
.info = {
.mapEntryCount = SARRAY_COUNT_OF(SPECIALIZATION_ENTRIES),
.pMapEntries = SPECIALIZATION_ENTRIES,
.dataSize = sizeof(SpecializationData),
.pData = &specializations[i].data
},
.data = {
descriptors[i].inAlphaType, pipelineInfos[i].outAlphaType, (uint32_t) descriptors[i].shaderVariant,
(descriptors[i].composite == LOGIC_COMPOSITE_XOR ? 1 : 0) |
(descriptors[i].shader & SHADER_MASK ? 2 : 0)
}
};
inputAssemblyStates[i] = (VkPipelineInputAssemblyStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = descriptors[i].topology
@@ -234,37 +249,38 @@ static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPas
}
// Setup input states.
MAKE_INPUT_STATE(COLOR, VKColorVertex, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT);
MAKE_INPUT_STATE(MASK_FILL_COLOR, VKMaskFillColorVertex, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R32G32B32A32_SFLOAT);
MAKE_INPUT_STATE(PRIMITIVE, VKVertex, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32_UINT);
MAKE_INPUT_STATE(MASK_FILL, VKMaskFillVertex, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R32_UINT);
MAKE_INPUT_STATE(BLIT, VKTxVertex, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32_SFLOAT);
MAKE_INPUT_STATE(CLIP, VKIntVertex, VK_FORMAT_R32G32_SINT);
for (uint32_t i = 0; i < count; i++) {
// Setup shader-specific pipeline parameters.
switch (descriptors[i].shader) {
switch ((int) descriptors[i].shader) {
case SHADER_COLOR:
createInfos[i].pVertexInputState = &INPUT_STATE_COLOR;
createInfos[i].layout = pipelineContext->colorPipelineLayout;
createInfos[i].pVertexInputState = &INPUT_STATE_PRIMITIVE;
createInfos[i].layout = pipelineContext->commonPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->color_vert, shaders->color_frag }};
break;
case SHADER_MASK_FILL_COLOR:
createInfos[i].pVertexInputState = &INPUT_STATE_MASK_FILL_COLOR;
case SHADER_COLOR | SHADER_MASK:
createInfos[i].pVertexInputState = &INPUT_STATE_MASK_FILL;
createInfos[i].layout = pipelineContext->maskFillPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->mask_fill_color_vert, shaders->mask_fill_color_frag }};
break;
case SHADER_GRADIENT:
createInfos[i].pVertexInputState = &INPUT_STATE_PRIMITIVE;
createInfos[i].layout = pipelineContext->maskFillPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->primitive_vert, shaders->gradient_frag }};
break;
case SHADER_GRADIENT | SHADER_MASK:
createInfos[i].pVertexInputState = &INPUT_STATE_MASK_FILL;
createInfos[i].layout = pipelineContext->maskFillPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->mask_fill_vert, shaders->gradient_frag }};
break;
case SHADER_BLIT:
createInfos[i].pVertexInputState = &INPUT_STATE_BLIT;
createInfos[i].layout = pipelineContext->texturePipelineLayout;
stages[i] = (ShaderStages) {{ shaders->blit_vert, shaders->blit_frag }};
// Alpha conversion specialization.
uint32_t* spec = (uint32_t*) specializations[i][1].data;
spec[0] = descriptors[i].inAlphaType;
spec[1] = pipelineInfos[i].outAlphaType;
specializations[i][1].info.dataSize = 8;
specializations[i][1].entries[0] = (VkSpecializationMapEntry) { 0, 0, 4 };
specializations[i][1].entries[1] = (VkSpecializationMapEntry) { 1, 4, 4 };
specializations[i][1].info.mapEntryCount = 2;
stages[i].createInfos[1].pSpecializationInfo = &specializations[i][1].info;
break;
case SHADER_CLIP:
createInfos[i].pVertexInputState = &INPUT_STATE_CLIP;
@@ -290,6 +306,9 @@ static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPas
default:
VK_FATAL_ERROR("Cannot create pipeline, unknown shader requested!");
}
for (uint32_t j = 0; j < createInfos[i].stageCount; j++) {
stages[i].createInfos[j].pSpecializationInfo = &specializations[i].info;
}
assert(createInfos[i].pDynamicState->dynamicStateCount <= MAX_DYNAMIC_STATES);
J2dRlsTraceLn(J2D_TRACE_INFO, "VKPipelines_CreatePipelines: stencilMode=%d, dstOpaque=%d, composite=%d, shader=%d, topology=%d",
descriptors[i].stencilMode, descriptors[i].dstOpaque, descriptors[i].composite, descriptors[i].shader, descriptors[i].topology);
@@ -303,6 +322,7 @@ static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPas
J2dRlsTraceLn(J2D_TRACE_INFO, "VKPipelines_CreatePipelines: created %d pipelines", count);
for (uint32_t i = 0; i < count; i++) {
pipelineInfos[i].pipeline = pipelines[i];
pipelineInfos[i].layout = createInfos[i].layout;
MAP_AT(renderPassContext->pipelines, descriptors[i]) = pipelineInfos[i];
}
return pipelineInfos[0];
@@ -403,21 +423,49 @@ static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineCont
assert(device != NULL && pipelines != NULL);
VkResult result;
// We want all our pipelines to have same push constant range to ensure common state is compatible between pipelines.
VkPushConstantRange pushConstantRange = {
// We want all our pipelines to have the same push constant ranges to ensure a common state is compatible between pipelines.
VkPushConstantRange pushConstantRanges[] = {{
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.offset = 0,
.size = sizeof(VKTransform)
};
}, {
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.offset = PUSH_CONSTANTS_OFFSET,
.size = PUSH_CONSTANTS_SIZE
}};
// Common pipeline.
VkPipelineLayoutCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 0,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pushConstantRange
.pushConstantRangeCount = SARRAY_COUNT_OF(pushConstantRanges),
.pPushConstantRanges = pushConstantRanges
};
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->colorPipelineLayout);
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->commonPipelineLayout);
VK_IF_ERROR(result) return result;
// Mask fill pipeline.
VkDescriptorSetLayoutBinding maskBufferLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = NULL
};
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1,
.pBindings = &maskBufferLayoutBinding
};
result = device->vkCreateDescriptorSetLayout(device->handle, &descriptorSetLayoutCreateInfo, NULL, &pipelines->maskFillDescriptorSetLayout);
VK_IF_ERROR(result) return result;
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &pipelines->maskFillDescriptorSetLayout;
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->maskFillPipelineLayout);
VK_IF_ERROR(result) return result;
// Texture pipeline.
VkDescriptorSetLayoutBinding textureLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
@@ -437,31 +485,11 @@ static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineCont
pipelines->textureDescriptorSetLayout,
pipelines->samplers.descriptorSetLayout
};
createInfo.setLayoutCount = 2;
createInfo.setLayoutCount = SARRAY_COUNT_OF(textureDescriptorSetLayouts);
createInfo.pSetLayouts = textureDescriptorSetLayouts;
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->texturePipelineLayout);
VK_IF_ERROR(result) return result;
VkDescriptorSetLayoutBinding maskBufferLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = NULL
};
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1,
.pBindings = &maskBufferLayoutBinding
};
result = device->vkCreateDescriptorSetLayout(device->handle, &descriptorSetLayoutCreateInfo, NULL, &pipelines->maskFillDescriptorSetLayout);
VK_IF_ERROR(result) return result;
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &pipelines->maskFillDescriptorSetLayout;
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->maskFillPipelineLayout);
VK_IF_ERROR(result) return result;
return VK_SUCCESS;
}
@@ -504,7 +532,7 @@ void VKPipelines_DestroyContext(VKPipelineContext* pipelineContext) {
VKPipelines_DestroyShaders(device, pipelineContext->shaders);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->colorPipelineLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->commonPipelineLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->texturePipelineLayout, NULL);
device->vkDestroyDescriptorSetLayout(device->handle, pipelineContext->textureDescriptorSetLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->maskFillPipelineLayout, NULL);

View File

@@ -36,13 +36,26 @@
* Shader programs.
*/
typedef enum {
// Base shaders.
SHADER_COLOR,
SHADER_MASK_FILL_COLOR,
SHADER_GRADIENT,
SHADER_BLIT,
SHADER_CLIP,
NO_SHADER = 0x7FFFFFFF
NO_SHADER = 0x7FFFFFFF,
// Mask modifier bit (MASK_FILL & MASK_BLIT).
SHADER_MASK = ~NO_SHADER
} VKShader;
/**
* Shader variant.
* It is used to specialize shader behavior, and its meaning varies with each particular shader.
*/
typedef enum {
SHADER_VARIANT_GRADIENT_CLAMP = 0,
SHADER_VARIANT_GRADIENT_CYCLE = 1,
NO_SHADER_VARIANT = 0x7FFFFFFF
} VKShaderVariant;
typedef enum {
STENCIL_MODE_NONE = 0, // No stencil attachment.
STENCIL_MODE_OFF = 1, // Has stencil attachment, stencil test disabled.
@@ -59,12 +72,14 @@ typedef struct {
AlphaType inAlphaType : 1;
VKCompositeMode composite;
VKShader shader;
VKShaderVariant shaderVariant;
VkPrimitiveTopology topology;
} VKPipelineDescriptor;
typedef struct {
VkPipeline pipeline;
AlphaType outAlphaType;
VkPipeline pipeline;
VkPipelineLayout layout;
AlphaType outAlphaType;
} VKPipelineInfo;
/**
@@ -72,7 +87,7 @@ typedef struct {
*/
struct VKPipelineContext {
VKDevice* device;
VkPipelineLayout colorPipelineLayout;
VkPipelineLayout commonPipelineLayout;
VkDescriptorSetLayout textureDescriptorSetLayout;
VkPipelineLayout texturePipelineLayout;
VkDescriptorSetLayout maskFillDescriptorSetLayout;
@@ -93,14 +108,42 @@ struct VKRenderPassContext {
MAP(VKPipelineDescriptor, VKPipelineInfo) pipelines;
};
typedef struct {
unsigned int xorColor;
float extraAlpha;
} VKCompositeConstants;
typedef struct {
RGBA c0, c1;
float p0, p1, p3;
} VKGradientPaintConstants;
typedef union {
// The minimum guaranteed size of push constants is 128 bytes.
alignas(32) // The maximum alignment for built-in glsl types is 32 bytes (dvec4).
char data[(128 - sizeof(VKTransform) - sizeof(VKCompositeConstants)) / 32 * 32];
VKGradientPaintConstants gradientPaint;
} VKShaderConstants;
#define MAX_SHADER_CONSTANTS_SIZE 96 // We expect 96 bytes
typedef char VKShaderConstantsCheckOffset[sizeof(VKShaderConstants) == MAX_SHADER_CONSTANTS_SIZE ? 1 : -1]; // Verify.
typedef struct {
VKTransform transform;
VKCompositeConstants composite;
VKShaderConstants shader;
} VKPushConstants;
typedef char VKPushConstantsCheckSize[sizeof(VKPushConstants) <= 128 ? 1 : -1]; // We should not exceed 128 bytes.
static const uint32_t PUSH_CONSTANTS_OFFSET = (uintptr_t) &((VKPushConstants*) NULL)->composite;
static const uint32_t PUSH_CONSTANTS_SIZE = sizeof(VKPushConstants) - PUSH_CONSTANTS_OFFSET;
typedef struct {
int x, y;
} VKIntVertex;
typedef struct {
float x, y;
RGBA color;
} VKColorVertex;
unsigned int data;
} VKVertex;
typedef struct {
float px, py;
@@ -109,8 +152,8 @@ typedef struct {
typedef struct {
int x, y, maskOffset, maskScanline;
RGBA color;
} VKMaskFillColorVertex;
unsigned int data;
} VKMaskFillVertex;
VKPipelineContext* VKPipelines_CreateContext(VKDevice* device);
void VKPipelines_DestroyContext(VKPipelineContext* pipelines);

View File

@@ -93,6 +93,21 @@
#define OFFSET_XFORM sun_java2d_vulkan_VKBlitLoops_OFFSET_XFORM
#define OFFSET_ISOBLIT sun_java2d_vulkan_VKBlitLoops_OFFSET_ISOBLIT
static void applyXor() {
if (VKRenderer_GetContext()->shader == SHADER_COLOR) {
VKRenderer_GetContext()->vertexData ^= VKRenderer_GetContext()->constants.composite.xorColor;
}
}
static void setComposite(VKCompositeMode comp, unsigned int xorColor, float extraAlpha) {
VKRenderer_GetContext()->composite = comp;
if (VKRenderer_GetContext()->constants.composite.xorColor != xorColor ||
VKRenderer_GetContext()->constants.composite.extraAlpha != extraAlpha) {
VKRenderer_GetContext()->constants.composite = (VKCompositeConstants) { xorColor, extraAlpha };
VKRenderer_GetContext()->constantsModCount++;
}
}
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
(JNIEnv *env, jobject oglrq, jlong buf, jint limit)
{
@@ -476,30 +491,26 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint flags = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_ALPHA_COMPOSITE(%d, %f, %d)", rule, extraAlpha, flags);
VKRenderer_GetContext()->renderColor = VKRenderer_GetContext()->color;
VKRenderer_GetContext()->composite = (VKCompositeMode) rule;
VKRenderer_GetContext()->extraAlpha = extraAlpha;
applyXor();
setComposite((VKCompositeMode) rule, 0, extraAlpha);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_XOR_COMPOSITE:
{
jint xorPixel = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_XOR_COMPOSITE");
VKRenderer_GetContext()->renderColor = VKUtil_DecodeJavaColor(xorPixel, ALPHA_TYPE_STRAIGHT);
// TODO Fix XOR mode!
// VKRenderer_GetContext()->renderColor.a = 0.0f; // Alpha is left unchanged in XOR mode.
VKRenderer_GetContext()->composite = LOGIC_COMPOSITE_XOR;
VKRenderer_GetContext()->extraAlpha = 1.0f;
"VKRenderQueue_flushBuffer: SET_XOR_COMPOSITE(0x%08x)", xorPixel);
applyXor();
setComposite(LOGIC_COMPOSITE_XOR, xorPixel, -1.0f);
applyXor();
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESET_COMPOSITE:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: RESET_COMPOSITE");
VKRenderer_GetContext()->renderColor = VKRenderer_GetContext()->color;
VKRenderer_GetContext()->composite = ALPHA_COMPOSITE_SRC;
VKRenderer_GetContext()->extraAlpha = 1.0f;
applyXor();
setComposite(ALPHA_COMPOSITE_SRC, 0, 1.0f);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_TRANSFORM:
@@ -522,8 +533,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
};
VKRenderingContext* context = VKRenderer_GetContext();
if (VK_IS_NEQ_TRANSFORM(&context->transform, &transform)) {
context->transform = transform;
if (VK_IS_NEQ_TRANSFORM(&context->constants.transform, &transform)) {
context->constants.transform = transform;
context->transformModCount++;
}
}
@@ -533,8 +544,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: RESET_TRANSFORM");
VKRenderingContext* context = VKRenderer_GetContext();
if (VK_IS_NEQ_TRANSFORM(&context->transform, &VK_ID_TRANSFORM)) {
context->transform = VK_ID_TRANSFORM;
if (VK_IS_NEQ_TRANSFORM(&context->constants.transform, &VK_ID_TRANSFORM)) {
context->constants.transform = VK_ID_TRANSFORM;
context->transformModCount++;
}
}
@@ -644,31 +655,34 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
break;
case sun_java2d_pipe_BufferedOpCodes_SET_COLOR:
{
jint javaColor = NEXT_INT(b);
VKRenderer_GetContext()->color = VKUtil_DecodeJavaColor(javaColor, ALPHA_TYPE_STRAIGHT);
if (COMPOSITE_GROUP(VKRenderer_GetContext()->composite) == ALPHA_COMPOSITE_GROUP) {
VKRenderer_GetContext()->renderColor = VKRenderer_GetContext()->color;
}
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: SET_COLOR(0x%08x)", javaColor);
J2dTraceLn(J2D_TRACE_VERBOSE, // Print color values with straight alpha for convenience.
" srgb={%.3f, %.3f, %.3f, %.3f}",
VKUtil_GetRGBA(VKRenderer_GetContext()->color, ALPHA_TYPE_STRAIGHT).r,
VKUtil_GetRGBA(VKRenderer_GetContext()->color, ALPHA_TYPE_STRAIGHT).g,
VKUtil_GetRGBA(VKRenderer_GetContext()->color, ALPHA_TYPE_STRAIGHT).b,
VKUtil_GetRGBA(VKRenderer_GetContext()->color, ALPHA_TYPE_STRAIGHT).a);
VKRenderer_GetContext()->inAlphaType = ALPHA_TYPE_STRAIGHT;
VKRenderer_GetContext()->shader = SHADER_COLOR;
VKRenderer_GetContext()->shaderVariant = NO_SHADER_VARIANT;
VKRenderer_GetContext()->vertexData = NEXT_INT(b);
applyXor();
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: SET_COLOR(0x%08x)", VKRenderer_GetContext()->vertexData);
}
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);
jboolean useMask = NEXT_BOOLEAN(b); // Unused.
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);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_GRADIENT_PAINT");
VKRenderer_GetContext()->inAlphaType = ALPHA_TYPE_PRE_MULTIPLIED;
VKRenderer_GetContext()->shader = SHADER_GRADIENT;
VKRenderer_GetContext()->shaderVariant = cyclic ? SHADER_VARIANT_GRADIENT_CYCLE : SHADER_VARIANT_GRADIENT_CLAMP;
VKRenderer_GetContext()->constants.shader.gradientPaint = (VKGradientPaintConstants) {
VKUtil_GetRGBA(VKUtil_DecodeJavaColor(pixel1, ALPHA_TYPE_PRE_MULTIPLIED), ALPHA_TYPE_PRE_MULTIPLIED),
VKUtil_GetRGBA(VKUtil_DecodeJavaColor(pixel2, ALPHA_TYPE_PRE_MULTIPLIED), ALPHA_TYPE_PRE_MULTIPLIED),
p0*2.0, p1*2.0, p3*2.0-0.5
};
VKRenderer_GetContext()->constantsModCount++;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_LINEAR_GRADIENT_PAINT:

View File

@@ -164,6 +164,7 @@ struct VKRenderPass {
BufferWritingState maskFillBufferWriting;
VKPipelineDescriptor state;
uint64_t constantsModCount; // Just a tag to detect when constants were changed.
uint64_t transformModCount; // Just a tag to detect when transform was changed.
uint64_t clipModCount; // Just a tag to detect when clip was changed.
VkBool32 pendingFlush : 1;
@@ -176,12 +177,17 @@ struct VKRenderPass {
// which is only called from queue flusher thread, no need for synchronization.
static VKRenderingContext context = {
.surface = NULL,
.transform = VK_ID_TRANSFORM,
.transformModCount = 1,
.color = {},
.renderColor = {},
.composite = ALPHA_COMPOSITE_SRC_OVER,
.extraAlpha = 1.0f,
.inAlphaType = ALPHA_TYPE_UNKNOWN,
.shader = NO_SHADER,
.shaderVariant = NO_SHADER_VARIANT,
.vertexData = 0,
.constantsModCount = 1,
.transformModCount = 1,
.constants = {
.transform = VK_ID_TRANSFORM,
.composite = { 0, 1.0f }
},
.clipModCount = 1,
.clipRect = NO_CLIP,
.clipSpanVertices = NULL
@@ -221,7 +227,7 @@ static VkBool32 VKRenderer_CheckPoolDrain(void* pool, void* entry) {
return VK_FALSE;
}
#define VERTEX_BUFFER_SIZE (128 * 1024) // 128KiB - enough to draw 910 quads (6 verts) with VKColorVertex.
#define VERTEX_BUFFER_SIZE (128 * 1024) // 128KiB - enough to draw 1820 quads (6 verts) with VKVertex.
#define VERTEX_BUFFER_PAGE_SIZE (1 * 1024 * 1024) // 1MiB - fits 8 buffers.
static void VKRenderer_FindVertexBufferMemoryType(VKMemoryRequirements* requirements) {
VKAllocator_FindMemoryType(requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
@@ -600,6 +606,7 @@ static void VKRenderer_ResetDrawing(VKSDOps* surface) {
VKRenderPass* renderPass = surface->renderPass;
renderPass->state.composite = NO_COMPOSITE;
renderPass->state.shader = NO_SHADER;
renderPass->constantsModCount = 0;
renderPass->transformModCount = 0;
renderPass->firstVertex = 0;
renderPass->vertexCount = 0;
@@ -724,6 +731,7 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
.composite = NO_COMPOSITE,
.shader = NO_SHADER
},
.constantsModCount = 0,
.transformModCount = 0,
.clipModCount = 0,
.pendingFlush = VK_FALSE,
@@ -1127,7 +1135,7 @@ static uint32_t VKRenderer_AllocateVertices(uint32_t primitives, uint32_t vertic
* This function can invalidate drawing state, always call it before VK_DRAW.
*/
static BufferWritingState VKRenderer_AllocateMaskFillBytes(uint32_t size) {
assert(size > 0);
// assert(size > 0); // size can be 0 when binding the buffer without allocating any data.
assert(size <= MASK_FILL_BUFFER_SIZE);
VKSDOps* surface = VKRenderer_GetContext()->surface;
BufferWritingState state = VKRenderer_AllocateBufferData(
@@ -1163,16 +1171,35 @@ static void VKRenderer_ValidateTransform() {
0.0f, 2.0f / (float) surface->image->extent.height, -1.0f
};
// Combine it with user transform.
VKUtil_ConcatenateTransform(&transform, &context->transform);
VKUtil_ConcatenateTransform(&transform, &context->constants.transform);
// Push the transform into shader.
surface->device->vkCmdPushConstants(
renderPass->commandBuffer,
surface->device->renderer->pipelineContext->colorPipelineLayout, // TODO what if our pipeline layout differs?
surface->device->renderer->pipelineContext->commonPipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(VKTransform), &transform
);
}
}
static void VKRenderer_ValidateConstants() {
VKRenderingContext* context = VKRenderer_GetContext();
assert(context->surface != NULL);
VKSDOps* surface = context->surface;
VKRenderPass* renderPass = surface->renderPass;
// Update constants, ignoring clip and color shaders.
if (renderPass->constantsModCount != context->constantsModCount &&
renderPass->state.shader != SHADER_CLIP && renderPass->state.shader != SHADER_COLOR) {
J2dTraceLn(J2D_TRACE_VERBOSE, "VKRenderer_ValidateConstants: updating constants");
VKRenderer_FlushDraw(surface);
renderPass->constantsModCount = context->constantsModCount;
surface->device->vkCmdPushConstants(
renderPass->commandBuffer,
surface->device->renderer->pipelineContext->commonPipelineLayout,
VK_SHADER_STAGE_FRAGMENT_BIT, PUSH_CONSTANTS_OFFSET, PUSH_CONSTANTS_SIZE, &context->constants.composite
);
}
}
/**
* Setup stencil attachment according to the context clip state.
* If there is a clip shape, attachment is cleared with "fail" value and then
@@ -1209,6 +1236,7 @@ static void VKRenderer_SetupStencil() {
.inAlphaType = ALPHA_TYPE_UNKNOWN,
.composite = NO_COMPOSITE,
.shader = SHADER_CLIP,
.shaderVariant = NO_SHADER_VARIANT,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
}).pipeline);
// Reset vertex buffer binding.
@@ -1253,7 +1281,7 @@ void VKRenderer_RecordBarriers(VKRenderer* renderer,
/**
* Setup pipeline for drawing. Returns FALSE if surface is not yet ready for drawing.
*/
VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, AlphaType inAlphaType) {
VkBool32 VKRenderer_Validate(VKShader shader, VKShaderVariant shaderVariant, VkPrimitiveTopology topology, AlphaType inAlphaType) {
assert(context.surface != NULL);
VKSDOps* surface = context.surface;
@@ -1295,10 +1323,18 @@ VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, Alph
J2dTraceLn(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating clip");
surface->device->vkCmdSetScissor(renderPass->commandBuffer, 0, 1, &context.clipRect);
if (clipChanged) {
VKStencilMode stencilMode = STENCIL_MODE_NONE;
if (ARRAY_SIZE(context.clipSpanVertices) > 0) {
VKRenderer_SetupStencil();
renderPass->state.stencilMode = STENCIL_MODE_ON;
} else renderPass->state.stencilMode = surface->stencil != NULL ? STENCIL_MODE_OFF : STENCIL_MODE_NONE;
stencilMode = STENCIL_MODE_ON;
} else if (surface->stencil != NULL) {
stencilMode = STENCIL_MODE_OFF;
}
// Reset the pipeline when changing stencil mode.
if (renderPass->state.stencilMode != stencilMode) {
renderPass->state.shader = NO_SHADER;
}
renderPass->state.stencilMode = stencilMode;
}
}
// Validate current composite.
@@ -1314,6 +1350,7 @@ VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, Alph
// Validate current pipeline.
if (renderPass->state.shader != shader ||
renderPass->state.shaderVariant != shaderVariant ||
renderPass->state.topology != topology ||
renderPass->state.inAlphaType != inAlphaType) {
J2dTraceLn(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating pipeline, old=%d, new=%d",
@@ -1321,6 +1358,7 @@ VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, Alph
VKRenderer_FlushDraw(surface);
VkCommandBuffer cb = renderPass->commandBuffer;
renderPass->state.shader = shader;
renderPass->state.shaderVariant = shaderVariant;
renderPass->state.topology = topology;
renderPass->state.inAlphaType = inAlphaType;
VKPipelineInfo pipelineInfo = VKPipelines_GetPipelineInfo(renderPass->context, renderPass->state);
@@ -1328,10 +1366,26 @@ VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, Alph
surface->device->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineInfo.pipeline);
renderPass->vertexBufferWriting.bound = VK_FALSE;
renderPass->maskFillBufferWriting.bound = VK_FALSE;
// If pipeline uses mask fill layout, but the shader is not actually a MASK one, that must be a generic-layout pipeline.
// In that case, we need to bind a mask buffer, even if we won't ever use it.
// We could use VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT or nullDescriptor, but those require
// optional features or extensions, so don't bother for now...
// TODO this is ugly, do something with it.
if (pipelineInfo.layout == surface->device->renderer->pipelineContext->maskFillPipelineLayout && !(shader & SHADER_MASK)) {
VKRenderer_AllocateMaskFillBytes(0);
}
}
VKRenderer_ValidateConstants();
return VK_TRUE;
}
static VkBool32 VKRenderer_ValidatePaint(VKShader shaderModifier, VkBool32 fill) {
return VKRenderer_Validate(context.shader | shaderModifier, context.shaderVariant,
fill ? VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST : VK_PRIMITIVE_TOPOLOGY_LINE_LIST, context.inAlphaType);
}
// Drawing operations.
void VKRenderer_RenderRect(VkBool32 fill, jint x, jint y, jint w, jint h) {
@@ -1342,12 +1396,9 @@ void VKRenderer_RenderRect(VkBool32 fill, jint x, jint y, jint w, jint h) {
void VKRenderer_RenderParallelogram(VkBool32 fill,
jfloat x11, jfloat y11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12)
{
if (!VKRenderer_Validate(SHADER_COLOR,
fill ? VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
: VK_PRIMITIVE_TOPOLOGY_LINE_LIST, ALPHA_TYPE_UNKNOWN)) return; // Not ready.
RGBA c = VKRenderer_GetRGBA(context.surface, context.renderColor);
jfloat dx12, jfloat dy12) {
if (!VKRenderer_ValidatePaint(0, fill)) return; // Not ready.
/* dx21
* (p1)---------(p2) | (p1)------
* |\ \ | | \ dy21
@@ -1358,12 +1409,12 @@ void VKRenderer_RenderParallelogram(VkBool32 fill,
* dy21 \ |
* -----(p3)
*/
VKColorVertex p1 = {x11, y11, c};
VKColorVertex p2 = {x11 + dx21, y11 + dy21, c};
VKColorVertex p3 = {x11 + dx21 + dx12, y11 + dy21 + dy12, c};
VKColorVertex p4 = {x11 + dx12, y11 + dy12, c};
VKVertex p1 = {x11, y11, context.vertexData};
VKVertex p2 = {x11 + dx21, y11 + dy21, context.vertexData};
VKVertex p3 = {x11 + dx21 + dx12, y11 + dy21 + dy12, context.vertexData};
VKVertex p4 = {x11 + dx12, y11 + dy12, context.vertexData};
VKColorVertex* vs;
VKVertex* vs;
VK_DRAW(vs, 1, fill ? 6 : 8);
uint32_t i = 0;
vs[i++] = p1;
@@ -1380,19 +1431,18 @@ void VKRenderer_RenderParallelogram(VkBool32 fill,
void VKRenderer_FillSpans(jint spanCount, jint *spans) {
if (spanCount == 0) return;
if (!VKRenderer_Validate(SHADER_COLOR, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, ALPHA_TYPE_UNKNOWN)) return; // Not ready.
RGBA c = VKRenderer_GetRGBA(context.surface, context.renderColor);
if (!VKRenderer_ValidatePaint(0, VK_TRUE)) return; // Not ready.
jfloat x1 = (float)*(spans++);
jfloat y1 = (float)*(spans++);
jfloat x2 = (float)*(spans++);
jfloat y2 = (float)*(spans++);
VKColorVertex p1 = {x1, y1, c};
VKColorVertex p2 = {x2, y1, c};
VKColorVertex p3 = {x2, y2, c};
VKColorVertex p4 = {x1, y2, c};
VKVertex p1 = {x1, y1, context.vertexData};
VKVertex p2 = {x2, y1, context.vertexData};
VKVertex p3 = {x2, y2, context.vertexData};
VKVertex p4 = {x1, y2, context.vertexData};
VKColorVertex* vs;
VKVertex* vs;
VK_DRAW(vs, 1, 6);
vs[0] = p1; vs[1] = p2; vs[2] = p3; vs[3] = p3; vs[4] = p4; vs[5] = p1;
@@ -1409,8 +1459,8 @@ void VKRenderer_FillSpans(jint spanCount, jint *spans) {
void VKRenderer_MaskFill(jint x, jint y, jint w, jint h,
jint maskoff, jint maskscan, jint masklen, uint8_t *mask) {
if (!VKRenderer_Validate(SHADER_MASK_FILL_COLOR,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, ALPHA_TYPE_UNKNOWN)) return; // Not ready.
if (!VKRenderer_ValidatePaint(SHADER_MASK, VK_TRUE)) return; // Not ready.
// maskoff is the offset from the beginning of mask,
// it's the same as x and y offset within a tile (maskoff % maskscan, maskoff / maskscan).
// maskscan is the number of bytes in a row/
@@ -1428,14 +1478,13 @@ void VKRenderer_MaskFill(jint x, jint y, jint w, jint h,
*((char *)maskState.data) = (char)0xFF;
}
VKMaskFillColorVertex* vs;
VKMaskFillVertex* vs;
VK_DRAW(vs, 1, 6);
RGBA c = VKRenderer_GetRGBA(context.surface, context.renderColor);
int offset = (int) maskState.offset;
VKMaskFillColorVertex p1 = {x, y, offset, maskscan, c};
VKMaskFillColorVertex p2 = {x + w, y, offset, maskscan, c};
VKMaskFillColorVertex p3 = {x + w, y + h, offset, maskscan, c};
VKMaskFillColorVertex p4 = {x, y + h, offset, maskscan, c};
VKMaskFillVertex p1 = {x, y, offset, maskscan, context.vertexData};
VKMaskFillVertex p2 = {x + w, y, offset, maskscan, context.vertexData};
VKMaskFillVertex p3 = {x + w, y + h, offset, maskscan, context.vertexData};
VKMaskFillVertex p4 = {x, y + h, offset, maskscan, context.vertexData};
// Always keep p1 as provoking vertex for correct origin calculation in vertex shader.
vs[0] = p1; vs[1] = p3; vs[2] = p2;
vs[3] = p1; vs[4] = p3; vs[5] = p4;

View File

@@ -36,21 +36,15 @@
struct VKRenderingContext {
VKSDOps* surface;
VKTransform transform;
VKCompositeMode composite;
AlphaType inAlphaType;
VKShader shader;
VKShaderVariant shaderVariant;
unsigned int vertexData;
VKPushConstants constants;
uint64_t constantsModCount;
uint64_t transformModCount;
// We keep this color separately from renderColor,
// because we need consistent state when switching between XOR and alpha
// composite modes. This variable holds last value set by SET_COLOR, while
// renderColor holds color, currently used for drawing, which may have
// also been provided by SET_XOR_COMPOSITE.
Color color;
Color renderColor;
VKCompositeMode composite;
// Extra alpha is not used when painting with plain color,
// in this case color.a already includes it.
float extraAlpha;
uint64_t clipModCount; // Used to track changes to the clip.
VkRect2D clipRect;
ARRAY(VKIntVertex) clipSpanVertices;
@@ -63,7 +57,7 @@ VKRenderer* VKRenderer_Create(VKDevice* device);
/**
* Setup pipeline for drawing. Returns FALSE if the surface is not yet ready for drawing.
*/
VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, AlphaType inAlphaType);
VkBool32 VKRenderer_Validate(VKShader shader, VKShaderVariant shaderVariant, VkPrimitiveTopology topology, AlphaType inAlphaType);
/**
* Record commands into the primary command buffer (outside of a render pass).

View File

@@ -62,7 +62,7 @@ typedef uint16_t VKPackedSwizzle;
*/
typedef struct {
float m00, m01, m02;
float m10 __attribute__((aligned(16))), m11, m12;
float m10, m11, m12;
} VKTransform;
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VKMemory);

View File

@@ -38,35 +38,9 @@ import sun.java2d.wl.WLSurfaceSizeListener;
public abstract class WLGraphicsConfig extends GraphicsConfiguration {
private final WLGraphicsDevice device;
private final int x;
private final int y;
private final int xLogical; // logical (scaled) horizontal location; optional, could be zero
private final int yLogical; // logical (scaled) vertical location; optional, could be zero
private final int width;
private final int height;
private final int widthLogical; // logical (scaled) width; optional, could be zero
private final int heightLogical;// logical (scaled) height; optional, could be zero
private final int displayScale; // as reported by Wayland
private final double effectiveScale; // as enforced by Java
protected WLGraphicsConfig(WLGraphicsDevice device, int x, int y, int xLogical, int yLogical,
int width, int height, int widthLogical, int heightLogical,
int displayScale) {
protected WLGraphicsConfig(WLGraphicsDevice device) {
this.device = device;
this.x = x;
this.y = y;
this.xLogical = xLogical;
this.yLogical = yLogical;
this.width = width;
this.height = height;
this.widthLogical = widthLogical;
this.heightLogical = heightLogical;
this.displayScale = displayScale;
this.effectiveScale = WLGraphicsEnvironment.effectiveScaleFrom(displayScale);
}
boolean differsFrom(int width, int height, int scale) {
return width != this.width || height != this.height || scale != this.displayScale;
}
@Override
@@ -84,7 +58,7 @@ public abstract class WLGraphicsConfig extends GraphicsConfiguration {
@Override
public AffineTransform getDefaultTransform() {
double scale = effectiveScale;
double scale = device.getEffectiveScale();
return AffineTransform.getScaleInstance(scale, scale);
}
@@ -97,20 +71,18 @@ public abstract class WLGraphicsConfig extends GraphicsConfiguration {
@Override
public Rectangle getBounds() {
return (widthLogical > 0 && heightLogical > 0)
? new Rectangle(xLogical, yLogical, widthLogical, heightLogical)
: new Rectangle(x, y, width, height);
return device.getBounds();
}
public Rectangle getRealBounds() {
return new Rectangle(x, y, width, height);
return device.getRealBounds();
}
/**
* Returns the preferred Wayland buffer scale for this display configuration.
*/
public int getDisplayScale() {
return displayScale;
return device.getDisplayScale();
}
/**
@@ -118,7 +90,7 @@ public abstract class WLGraphicsConfig extends GraphicsConfiguration {
* if overridden with the sun.java2d.uiScale system property.
*/
public double getEffectiveScale() {
return effectiveScale;
return device.getEffectiveScale();
}
public abstract SurfaceType getSurfaceType();
@@ -127,6 +99,7 @@ public abstract class WLGraphicsConfig extends GraphicsConfiguration {
@Override
public String toString() {
return String.format("%dx%d@(%d, %d) %dx scale", width, height, x, y, displayScale);
Rectangle bounds = getBounds();
return String.format("%dx%d@(%d, %d) %dx scale", bounds.width, bounds.height, bounds.x, bounds.y, getDisplayScale());
}
}

View File

@@ -39,15 +39,21 @@ import java.util.HashSet;
import java.util.Set;
/**
* Corresponds to Wayland's output and is identified by its wlID and x, y coordinates
* in the multi-monitor setup. Whenever those change, this device is re-created.
* Corresponds to a Wayland output and is identified by its wlID.
* Owns all graphics configurations associated with this device.
* Encapsulates all the other properties of the output, such as its size
* and location in a multi-monitor configuration.
* Whenever any of these properties change, they are updated and
* the GraphicsConfiguration objects get re-created to let referents know of the change.
*/
public class WLGraphicsDevice extends GraphicsDevice {
private static final double MM_IN_INCH = 25.4;
/**
* ID of the corresponding wl_output object received from Wayland.
* Only changes when the device gets invalidated.
*/
private volatile int wlID; // only changes when the device gets invalidated
private volatile int wlID;
/**
* Some human-readable name of the device that came from Wayland.
@@ -58,75 +64,142 @@ public class WLGraphicsDevice extends GraphicsDevice {
/**
* The horizontal location of this device in the multi-monitor configuration.
*/
private volatile int x; // only changes when the device gets invalidated
private volatile int x;
/**
* The vertical location of this device in the multi-monitor configuration.
*/
private volatile int y; // only changes when the device gets invalidated
private volatile int y;
private volatile int xLogical; // logical (scaled) horizontal location; optional, could be zero
private volatile int yLogical; // logical (scaled) vertical location; optional, could be zero
/**
* Pixel width, mostly for accounting and reporting.
*/
private volatile int width;
private final int widthMm;
private final int heightMm;
/**
* Pixel height, mostly for accounting and reporting.
*/
private volatile int height;
// Configs are always the same in size and scale
private volatile GraphicsConfiguration[] configs = null;
/**
* Logical (scaled) width in Java units.
*/
private volatile int widthLogical;
// The default config is an object from the configs array
private volatile WLGraphicsConfig defaultConfig = null;
/**
* Logical (scaled) height in Java units.
*/
private volatile int heightLogical;
// Top-level window peers that consider this device as their primary one
// and get their graphics configuration from it
/**
* Width in millimeters.
*/
private volatile int widthMm;
/**
* Height in millimeters.
*/
private volatile int heightMm;
/**
* The device's scale factor as reported by Wayland.
* Since it is an integer, it's usually higher than the real fraction scale.
*/
private volatile int displayScale;
/**
* The effective scale factor as determined by Java.
*/
private volatile double effectiveScale;
private volatile GraphicsConfiguration[] configs;
private volatile WLGraphicsConfig defaultConfig; // A reference to the configs array
/**
* Top-level window peers that consider this device as their primary one
* and get their graphics configuration from it
*/
private final Set<WLComponentPeer> toplevels = new HashSet<>(); // guarded by 'this'
private WLGraphicsDevice(int id, int x, int y, int xLogical, int yLogical, int widthMm, int heightMm) {
private WLGraphicsDevice(int id,
String name,
int x, int y,
int width, int height,
int widthLogical, int heightLogical,
int widthMm, int heightMm,
int displayScale) {
assert width > 0 && height > 0;
assert widthLogical > 0 && heightLogical > 0;
assert widthMm > 0 && heightMm > 0;
assert displayScale > 0;
this.wlID = id;
this.name = name;
this.x = x;
this.y = y;
this.xLogical = xLogical;
this.yLogical = yLogical;
this.width = width;
this.height = height;
this.widthLogical = widthLogical;
this.heightLogical = heightLogical;
this.widthMm = widthMm;
this.heightMm = heightMm;
this.displayScale = displayScale;
this.effectiveScale = WLGraphicsEnvironment.effectiveScaleFrom(displayScale);
makeGC();
}
int getID() {
return wlID;
}
void updateConfiguration(String name, int width, int height, int widthLogical, int heightLogical, int scale) {
this.name = name;
WLGraphicsConfig config = defaultConfig;
// Note that all configs are of equal size and scale
if (config == null || config.differsFrom(width, height, scale)) {
GraphicsConfiguration[] newConfigs;
WLGraphicsConfig newDefaultConfig;
// It is necessary to create a new object whenever config changes as its
// identity is used to detect changes in scale, among other things.
if (VKEnv.isPresentationEnabled()) {
newConfigs = VKEnv.getDevices().flatMap(d -> d.getPresentableGraphicsConfigs().map(
gc -> WLVKGraphicsConfig.getConfig(
gc, this, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale)))
.toArray(WLGraphicsConfig[]::new);
newDefaultConfig = (WLGraphicsConfig) newConfigs[0];
} else {
// TODO: Actually, Wayland may support a lot more shared memory buffer configurations, need to
// subscribe to the wl_shm:format event and get the list from there.
newDefaultConfig = WLSMGraphicsConfig.getConfig(this, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale, true);
newConfigs = new GraphicsConfiguration[2];
newConfigs[0] = newDefaultConfig;
newConfigs[1] = WLSMGraphicsConfig.getConfig(this, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale, false);
}
configs = newConfigs;
defaultConfig = newDefaultConfig;
private void makeGC() {
GraphicsConfiguration[] newConfigs;
WLGraphicsConfig newDefaultConfig;
if (VKEnv.isPresentationEnabled()) {
newConfigs = VKEnv.getDevices().flatMap(d -> d.getPresentableGraphicsConfigs().map(
gc -> WLVKGraphicsConfig.getConfig(gc, this)))
.toArray(WLGraphicsConfig[]::new);
newDefaultConfig = (WLGraphicsConfig) newConfigs[0];
} else {
// TODO: Actually, Wayland may support a lot more shared memory buffer configurations, need to
// subscribe to the wl_shm:format event and get the list from there.
newDefaultConfig = WLSMGraphicsConfig.getConfig(this, true);
newConfigs = new GraphicsConfiguration[2];
newConfigs[0] = newDefaultConfig;
newConfigs[1] = WLSMGraphicsConfig.getConfig(this, false);
}
configs = newConfigs;
defaultConfig = newDefaultConfig;
}
void updateConfiguration(String name,
int x, int y,
int width, int height,
int widthLogical, int heightLogical,
int widthMm, int heightMm,
int scale) {
assert width > 0 && height > 0;
assert widthLogical > 0 && heightLogical > 0;
assert widthMm > 0 && heightMm > 0;
assert scale > 0;
this.name = name;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.widthLogical = widthLogical;
this.heightLogical = heightLogical;
this.widthMm = widthMm;
this.heightMm = heightMm;
this.displayScale = scale;
this.effectiveScale = WLGraphicsEnvironment.effectiveScaleFrom(scale);
// It is necessary to create new config objects whenever this device changes
// as GraphicsConfiguration identity is used to detect changes in scale, among other things.
makeGC();
// It is important that by the time displayChanged() events are delivered,
// all the peers on this device had their graphics configuration updated
// to refer to the new ones with, perhaps, different scale or resolution.
// to refer to the new ones with, perhaps, a different scale or resolution.
// This affects various BufferStrategy that use volatile images as their buffers.
notifyToplevels();
}
@@ -136,47 +209,36 @@ public class WLGraphicsDevice extends GraphicsDevice {
synchronized (this) {
toplevelsCopy.addAll(toplevels);
}
int wlOutputID = this.wlID;
// NB: each of those peers will likely receive another such notification
// from Wayland when it gets the wl_surface::enter event, but the second one
// will effectively be a no-op.
toplevelsCopy.forEach((peer) -> peer.notifyEnteredOutput(wlOutputID));
toplevelsCopy.forEach(WLComponentPeer::checkIfOnNewScreen);
}
/**
* Changes all aspects of this device including its identity to be that of the given
* Changes all aspects of this device, including its identity to be that of the given
* device. Only used for devices that are no longer physically present, but references
* to which may still exist in the program.
*/
void invalidate(WLGraphicsDevice similarDevice) {
// Note: It is expected that all the surface this device used to host have already received
// the 'leave' event and updated their device/graphics configurations accordingly.
this.wlID = similarDevice.wlID;
this.x = similarDevice.x;
this.y = similarDevice.y;
this.xLogical = similarDevice.xLogical;
this.yLogical = similarDevice.yLogical;
int newScale = similarDevice.getDisplayScale();
Rectangle newBounds = similarDevice.defaultConfig.getBounds();
Rectangle newRealBounds = similarDevice.defaultConfig.getRealBounds();
updateConfiguration(similarDevice.name, newRealBounds.width, newRealBounds.height, newBounds.width, newBounds.height, newScale);
updateConfiguration(similarDevice.name,
similarDevice.x,
similarDevice.y,
similarDevice.width,
similarDevice.height,
similarDevice.widthLogical,
similarDevice.heightLogical,
similarDevice.widthMm,
similarDevice.heightMm,
similarDevice.displayScale);
}
public static WLGraphicsDevice createWithConfiguration(int id, String name,
int x, int y, int xLogical, int yLogical,
int x, int y,
int width, int height, int widthLogical, int heightLogical,
int widthMm, int heightMm,
int scale) {
WLGraphicsDevice device = new WLGraphicsDevice(id, x, y, xLogical, yLogical, widthMm, heightMm);
device.updateConfiguration(name, width, height, widthLogical, heightLogical, scale);
return device;
}
/**
* Compares the identity of this device with the given attributes
* and returns true iff the attributes identify the same device.
*/
boolean isSameDeviceAs(int wlID, int x, int y, int xLogical, int yLogical) {
return this.wlID == wlID && this.x == x && this.y == y && this.xLogical == xLogical && this.yLogical == yLogical;
return new WLGraphicsDevice(id, name, x, y, width, height, widthLogical, heightLogical, widthMm, heightMm, scale);
}
boolean hasSameNameAs(WLGraphicsDevice otherDevice) {
@@ -207,8 +269,8 @@ public class WLGraphicsDevice extends GraphicsDevice {
public GraphicsConfiguration[] getConfigurations() {
// From wayland.xml, wl_output.mode event:
// "Non-current modes are deprecated. A compositor can decide to only
// advertise the current mode and never send other modes. Clients
// should not rely on non-current modes."
// advertise the current mode and never send other modes. Clients
// should not rely on non-current modes."
// So there's always the same set of configs.
return configs.clone();
}
@@ -218,8 +280,24 @@ public class WLGraphicsDevice extends GraphicsDevice {
return defaultConfig;
}
int getID() {
return wlID;
}
Rectangle getBounds() {
return new Rectangle(x, y, widthLogical, heightLogical);
}
Rectangle getRealBounds() {
return new Rectangle(x, y, width, height);
}
int getDisplayScale() {
return defaultConfig.getDisplayScale();
return displayScale;
}
double getEffectiveScale() {
return effectiveScale;
}
int getResolution() {
@@ -227,13 +305,6 @@ public class WLGraphicsDevice extends GraphicsDevice {
return getResolutionX(defaultConfig);
}
double getPhysicalResolution() {
Rectangle bounds = defaultConfig.getRealBounds();
double daigInPixels = Math.sqrt(bounds.width * bounds.width + bounds.height * bounds.height);
double diagInMm = Math.sqrt(widthMm * widthMm + heightMm * heightMm);
return daigInPixels * MM_IN_INCH / diagInMm;
}
double getPhysicalScale() {
Rectangle bounds = defaultConfig.getRealBounds();
double daigInPixels = Math.sqrt(bounds.width * bounds.width + bounds.height * bounds.height);
@@ -267,7 +338,7 @@ public class WLGraphicsDevice extends GraphicsDevice {
boolean fsSupported = isFullScreenSupported();
if (fsSupported && old != null) {
// enter windowed mode and restore original display mode
// enter windowed mode and restore the original display mode
exitFullScreenExclusive(old);
}
@@ -311,9 +382,8 @@ public class WLGraphicsDevice extends GraphicsDevice {
@Override
public String toString() {
var config = defaultConfig;
return String.format("WLGraphicsDevice: '%s' id=%d at (%d, %d) ((%d, %d) logical) with %s",
name, wlID, x, y, xLogical, yLogical,
config != null ? config : "<no configs>");
return String.format("WLGraphicsDevice: '%s' id=%d at (%d, %d) with %s",
name, wlID, x, y, config != null ? config : "<no configs>");
}
public Insets getInsets() {

View File

@@ -26,6 +26,7 @@
package sun.awt.wl;
import java.awt.AWTError;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
@@ -54,6 +55,8 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment implements HiD
private static final boolean debugScaleEnabled;
private final Dimension totalDisplayBounds = new Dimension();
private final List<WLGraphicsDevice> devices = new ArrayList<>(5);
@SuppressWarnings("restricted")
private static void loadAwt() {
System.loadLibrary("awt");
@@ -94,15 +97,30 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment implements HiD
}
}
@Override
public GraphicsDevice getDefaultScreenDevice() {
synchronized (devices) {
if (devices.isEmpty()) {
throw new AWTError("no screen devices");
}
return devices.getFirst();
}
}
@Override
public synchronized GraphicsDevice[] getScreenDevices() {
synchronized (devices) {
return devices.toArray(new GraphicsDevice[0]);
}
}
@Override
public boolean isDisplayLocal() {
return true;
}
private final List<WLGraphicsDevice> devices = new ArrayList<>(5);
private void notifyOutputConfigured(String name, String make, String model, int wlID,
int x, int y, int xLogical, int yLogical,
int x, int y,
int width, int height,
int widthLogical, int heightLogical,
int widthMm, int heightMm,
@@ -110,46 +128,35 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment implements HiD
// Called from native code whenever a new output appears or an existing one changes its properties
// NB: initially called during WLToolkit.initIDs() on the main thread; later on EDT.
if (log.isLoggable(Level.FINE)) {
log.fine(String.format("Output configured id=%d at (%d, %d) (%d, %d logical) %dx%d (%dx%d logical) %dx scale",
wlID, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale));
log.fine(String.format("Output configured id=%d at (%d, %d) %dx%d (%dx%d logical) %dx scale",
wlID, x, y, width, height, widthLogical, heightLogical, scale));
}
String humanID =
(name != null ? name + " " : "")
+ (make != null ? make + " " : "")
+ (model != null ? model : "");
synchronized (devices) {
boolean newOutput = true;
for (int i = 0; i < devices.size(); i++) {
final WLGraphicsDevice gd = devices.get(i);
if (gd.getID() == wlID) {
newOutput = false;
if (gd.isSameDeviceAs(wlID, x, y, xLogical, yLogical)) {
// These coordinates and the size are not scaled.
gd.updateConfiguration(humanID, width, height, widthLogical, heightLogical, scale);
} else {
final WLGraphicsDevice updatedDevice = WLGraphicsDevice.createWithConfiguration(wlID, humanID,
x, y, xLogical, yLogical, width, height, widthLogical, heightLogical,
widthMm, heightMm, scale);
devices.set(i, updatedDevice);
gd.invalidate(updatedDevice);
}
break;
}
}
if (newOutput) {
final WLGraphicsDevice gd = WLGraphicsDevice.createWithConfiguration(wlID, humanID,
x, y, xLogical, yLogical, width, height, widthLogical, heightLogical,
widthMm, heightMm, scale);
devices.add(gd);
}
if (LogDisplay.ENABLED) {
double effectiveScale = effectiveScaleFrom(scale);
LogDisplay log = newOutput ? LogDisplay.ADDED : LogDisplay.CHANGED;
log.log(wlID, (int) (width / effectiveScale) + "x" + (int) (height / effectiveScale), effectiveScale);
// Logical size comes from an optional protocol, so take the data from the main one, if absent
if (widthLogical == 0) widthLogical = width;
if (heightLogical == 0) heightLogical = height;
String humanID = deviceNameFrom(name, make, model);
WLGraphicsDevice gd = deviceWithID(wlID);
if (gd != null) {
// Some properties of an existing device have changed; update the existing device and
// let all the windows it hosts know about the change.
gd.updateConfiguration(humanID, x, y, width, height, widthLogical, heightLogical, widthMm, heightMm, scale);
} else {
WLGraphicsDevice newGD = WLGraphicsDevice.createWithConfiguration(wlID, humanID,
x, y, width, height, widthLogical, heightLogical,
widthMm, heightMm, scale);
synchronized (devices) {
devices.add(newGD);
}
}
if (LogDisplay.ENABLED) {
double effectiveScale = effectiveScaleFrom(scale);
LogDisplay log = (gd == null) ? LogDisplay.ADDED : LogDisplay.CHANGED;
log.log(wlID, (int) (width / effectiveScale) + "x" + (int) (height / effectiveScale), effectiveScale);
}
updateTotalDisplayBounds();
// Skip notification during the initial configuration events
@@ -158,6 +165,12 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment implements HiD
}
}
private static String deviceNameFrom(String name, String make, String model) {
return (name != null ? name + " " : "")
+ (make != null ? make + " " : "")
+ (model != null ? model : "");
}
private WLGraphicsDevice getSimilarDevice(WLGraphicsDevice modelDevice) {
WLGraphicsDevice similarDevice = devices.isEmpty() ? null : devices.getFirst();
for (WLGraphicsDevice device : devices) {
@@ -181,47 +194,33 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment implements HiD
log.fine(String.format("Output destroyed id=%d", wlID));
}
// NB: id may *not* be that of any output; if so, just ignore this event.
synchronized (devices) {
final Optional<WLGraphicsDevice> deviceOptional = devices.stream()
.filter(device -> device.getID() == wlID)
.findFirst();
if (deviceOptional.isPresent()) {
final WLGraphicsDevice destroyedDevice = deviceOptional.get();
if (LogDisplay.ENABLED) {
WLGraphicsConfig config = (WLGraphicsConfig) destroyedDevice.getDefaultConfiguration();
Rectangle bounds = config.getBounds();
LogDisplay.REMOVED.log(wlID, bounds.width + "x" + bounds.height, config.getEffectiveScale());
}
devices.remove(destroyedDevice);
final WLGraphicsDevice similarDevice = getSimilarDevice(destroyedDevice);
if (similarDevice != null) destroyedDevice.invalidate(similarDevice);
WLGraphicsDevice gd = deviceWithID(wlID);
if (gd != null) {
if (LogDisplay.ENABLED) {
WLGraphicsConfig config = (WLGraphicsConfig) gd.getDefaultConfiguration();
Rectangle bounds = config.getBounds();
LogDisplay.REMOVED.log(wlID, bounds.width + "x" + bounds.height, config.getEffectiveScale());
}
synchronized (devices) {
devices.remove(gd);
}
final WLGraphicsDevice similarDevice = getSimilarDevice(gd);
if (similarDevice != null) gd.invalidate(similarDevice);
}
updateTotalDisplayBounds();
displayChanged();
}
WLGraphicsDevice notifySurfaceEnteredOutput(WLComponentPeer wlComponentPeer, int wlOutputID) {
WLGraphicsDevice deviceWithID(int wlOutputID) {
synchronized (devices) {
for (WLGraphicsDevice gd : devices) {
if (gd.getID() == wlOutputID) {
return gd;
}
}
return null;
}
}
WLGraphicsDevice notifySurfaceLeftOutput(WLComponentPeer wlComponentPeer, int wlOutputID) {
synchronized (devices) {
for (WLGraphicsDevice gd : devices) {
if (gd.getID() == wlOutputID) {
return gd;
}
}
return null;
}
return null;
}
public Dimension getTotalDisplayBounds() {
@@ -231,17 +230,15 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment implements HiD
}
private void updateTotalDisplayBounds() {
Rectangle virtualBounds = new Rectangle();
synchronized (devices) {
Rectangle virtualBounds = new Rectangle();
for (GraphicsDevice gd : devices) {
for (GraphicsConfiguration gc : gd.getConfigurations()) {
virtualBounds = virtualBounds.union(gc.getBounds());
}
}
synchronized (totalDisplayBounds) {
totalDisplayBounds.setSize(virtualBounds.getSize());
for (var gd : devices) {
virtualBounds = virtualBounds.union(gd.getBounds());
}
}
synchronized (totalDisplayBounds) {
totalDisplayBounds.setSize(virtualBounds.getSize());
}
}
static double effectiveScaleFrom(int displayScale) {

View File

@@ -67,7 +67,7 @@ public class WLMainSurface extends WLSurface {
// Called from native code whenever the corresponding wl_surface enters an output (monitor)
synchronized (devices) {
final WLGraphicsEnvironment ge = (WLGraphicsEnvironment)WLGraphicsEnvironment.getLocalGraphicsEnvironment();
final WLGraphicsDevice gd = ge.notifySurfaceEnteredOutput(peer, wlOutputID);
final WLGraphicsDevice gd = ge.deviceWithID(wlOutputID);
if (gd != null) {
devices.add(gd);
}
@@ -81,7 +81,7 @@ public class WLMainSurface extends WLSurface {
// Called from native code whenever the corresponding wl_surface leaves an output (monitor)
synchronized (devices) {
final WLGraphicsEnvironment ge = (WLGraphicsEnvironment)WLGraphicsEnvironment.getLocalGraphicsEnvironment();
final WLGraphicsDevice gd = ge.notifySurfaceLeftOutput(peer, wlOutputID);
final WLGraphicsDevice gd = ge.deviceWithID(wlOutputID);
if (gd != null) {
devices.remove(gd);
}

View File

@@ -58,36 +58,16 @@ public class WLSMGraphicsConfig extends WLGraphicsConfig
private final ColorModel colorModel;
private final SurfaceType surfaceType;
private WLSMGraphicsConfig(WLGraphicsDevice device,
int x,
int y,
int xLogical,
int yLogical,
int width,
int height,
int widthLogical,
int heightLogical,
int scale,
boolean translucencyCapable) {
super(device, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale);
private WLSMGraphicsConfig(WLGraphicsDevice device, boolean translucencyCapable) {
super(device);
this.translucencyCapable = translucencyCapable;
this.colorModel = colorModelFor(translucencyCapable ? Transparency.TRANSLUCENT : Transparency.OPAQUE);
// Note: GNOME Shell definitely expects alpha values to be pre-multiplied
this.surfaceType = translucencyCapable ? SurfaceType.IntArgbPre : SurfaceType.IntRgb;
}
public static WLSMGraphicsConfig getConfig(WLGraphicsDevice device,
int x,
int y,
int xLogical,
int yLogical,
int width,
int height,
int widthLogical,
int heightLogical,
int scale,
boolean translucencyCapable) {
var newConfig = new WLSMGraphicsConfig(device, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale, translucencyCapable);
public static WLSMGraphicsConfig getConfig(WLGraphicsDevice device, boolean translucencyCapable) {
var newConfig = new WLSMGraphicsConfig(device, translucencyCapable);
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("New shared memory config " + newConfig);
}

View File

@@ -47,11 +47,8 @@ public final class WLVKGraphicsConfig extends WLGraphicsConfig
private final VKGraphicsConfig offscreenConfig;
public WLVKGraphicsConfig(VKGraphicsConfig offscreenConfig, WLGraphicsDevice device,
int x, int y, int xLogical, int yLogical,
int width, int height, int widthLogical, int heightLogical,
int scale) {
super(device, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale);
public WLVKGraphicsConfig(VKGraphicsConfig offscreenConfig, WLGraphicsDevice device) {
super(device);
this.offscreenConfig = offscreenConfig;
}
@@ -65,11 +62,8 @@ public final class WLVKGraphicsConfig extends WLGraphicsConfig
return getEffectiveScale();
}
public static WLVKGraphicsConfig getConfig(VKGraphicsConfig offscreenConfig, WLGraphicsDevice device,
int x, int y, int xLogical, int yLogical,
int width, int height, int widthLogical, int heightLogical,
int scale) {
return new WLVKGraphicsConfig(offscreenConfig, device, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale);
public static WLVKGraphicsConfig getConfig(VKGraphicsConfig offscreenConfig, WLGraphicsDevice device) {
return new WLVKGraphicsConfig(offscreenConfig, device);
}
@Override

View File

@@ -643,8 +643,19 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_GtkFrameDecoration_nativePaintTitleBar
jint pixel_width = ceil(width * scale);
jint pixel_height = ceil(height * scale);
jboolean is_copy = JNI_FALSE;
const char *title_c_str = "";
if (title) {
title_c_str = JNU_GetStringPlatformChars(env, title, &is_copy);
if (!title_c_str)
return;
}
unsigned char *buffer = (*env)->GetPrimitiveArrayCritical(env, dest, 0);
if (!buffer) {
if (is_copy) {
JNU_ReleaseStringPlatformChars(env, title, title_c_str);
}
JNU_ThrowOutOfMemoryError(env, "Could not get image buffer");
return;
}
@@ -660,25 +671,19 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_GtkFrameDecoration_nativePaintTitleBar
cairo_t *cr = p_cairo_create(surface);
jboolean is_copy = JNI_FALSE;
const char *title_c_str = "";
if (title) {
title_c_str = JNU_GetStringPlatformChars(env, title, &is_copy);
if (!title_c_str)
return;
}
draw_title_bar(decor, surface, cr, width, height, scale, title_c_str, buttonsState);
if (is_copy) {
JNU_ReleaseStringPlatformChars(env, title, title_c_str);
}
// Make sure pixels have been flush into the underlying buffer
p_cairo_surface_flush(surface);
p_gdk_threads_leave();
(*env)->ReleasePrimitiveArrayCritical(env, dest, buffer, 0);
if (is_copy) {
JNU_ReleaseStringPlatformChars(env, title, title_c_str);
}
p_cairo_destroy(cr);
p_cairo_surface_destroy(surface);
}

View File

@@ -34,7 +34,6 @@
#include "sun_awt_wl_WLDataOffer.h"
#include "wayland-client-protocol.h"
// Types
enum DataTransferProtocol
@@ -271,9 +270,6 @@ DataSource_setDnDActions(const struct DataSource *source, uint32_t actions);
static struct DataOffer *
DataOffer_create(struct DataDevice *dataDevice, enum DataTransferProtocol protocol, void *waylandObject);
static void
DataOffer_destroy(struct DataOffer *offer);
static void
DataOffer_receive(struct DataOffer *offer, const char *mime, int fd);
@@ -334,7 +330,7 @@ DataOffer_create(struct DataDevice *dataDevice, enum DataTransferProtocol protoc
return NULL;
}
// Cleared in DataOffer_destroy
// Cleared in DataOffer.destroy()
jobject globalRef = (*env)->NewGlobalRef(env, obj);
EXCEPTION_CLEAR(env);
@@ -345,50 +341,23 @@ DataOffer_create(struct DataDevice *dataDevice, enum DataTransferProtocol protoc
}
offer->javaObject = globalRef;
offer->protocol = protocol;
if (protocol == DATA_TRANSFER_PROTOCOL_WAYLAND) {
struct wl_data_offer *wlDataOffer = waylandObject;
wl_data_offer_add_listener(wlDataOffer, &wlDataOfferListener, offer);
offer->wlDataOffer = wlDataOffer;
offer->protocol = DATA_TRANSFER_PROTOCOL_WAYLAND;
wl_data_offer_add_listener(wlDataOffer, &wlDataOfferListener, offer);
}
if (protocol == DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION) {
struct zwp_primary_selection_offer_v1 *zwpPrimarySelectionOffer = waylandObject;
zwp_primary_selection_offer_v1_add_listener(zwpPrimarySelectionOffer, &zwpPrimarySelectionOfferListener, offer);
offer->zwpPrimarySelectionOffer = zwpPrimarySelectionOffer;
offer->protocol = DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION;
zwp_primary_selection_offer_v1_add_listener(zwpPrimarySelectionOffer, &zwpPrimarySelectionOfferListener, offer);
}
return offer;
}
static void
DataOffer_destroy(struct DataOffer *offer)
{
if (offer == NULL) {
return;
}
if (offer->javaObject != NULL) {
JNIEnv *env = getEnv();
assert(env != NULL);
(*env)->DeleteGlobalRef(env, offer->javaObject);
offer->javaObject = NULL;
}
if (offer->protocol == DATA_TRANSFER_PROTOCOL_WAYLAND) {
wl_data_offer_destroy(offer->wlDataOffer);
} else if (offer->protocol == DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION) {
zwp_primary_selection_offer_v1_destroy(offer->zwpPrimarySelectionOffer);
}
free(offer);
}
static void
DataOffer_receive(struct DataOffer *offer, const char *mime, int fd)
{
@@ -984,36 +953,53 @@ Java_sun_awt_wl_WLDataSource_initNative(JNIEnv *env, jobject javaObject, jlong d
protocol = DATA_TRANSFER_PROTOCOL_WAYLAND;
}
dataSource->protocol = protocol;
if (protocol == DATA_TRANSFER_PROTOCOL_WAYLAND) {
struct wl_data_source *wlDataSource = wl_data_device_manager_create_data_source(wl_ddm);
// To avoid race conditions when setting the dispatch queue,
// it's necessary to do this dance with wl_proxy_create_wrapper.
// See the docs for wl_proxy_create_wrapper for more details
struct wl_data_device_manager* dmWrapper = wl_proxy_create_wrapper(wl_ddm);
struct wl_data_source *wlDataSource = NULL;
if (dmWrapper != NULL) {
wl_proxy_set_queue((struct wl_proxy *) dmWrapper, dataDevice->dataSourceQueue);
wlDataSource = wl_data_device_manager_create_data_source(dmWrapper);
wl_proxy_wrapper_destroy(dmWrapper);
}
if (wlDataSource == NULL) {
free(dataSource);
JNU_ThrowByName(env, "java/awt/AWTError", "Wayland error creating wl_data_source proxy");
return 0;
}
wl_proxy_set_queue((struct wl_proxy *) wlDataSource, dataDevice->dataSourceQueue);
wl_data_source_add_listener(wlDataSource, &wl_data_source_listener, dataSource);
dataSource->protocol = DATA_TRANSFER_PROTOCOL_WAYLAND;
dataSource->wlDataSource = wlDataSource;
wl_data_source_add_listener(wlDataSource, &wl_data_source_listener, dataSource);
}
if (protocol == DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION) {
struct zwp_primary_selection_source_v1 *zwpPrimarySelectionSource =
zwp_primary_selection_device_manager_v1_create_source(zwp_selection_dm);
struct zwp_primary_selection_device_manager_v1 *dmWrapper = wl_proxy_create_wrapper(zwp_selection_dm);
struct zwp_primary_selection_source_v1 *zwpPrimarySelectionSource = NULL;
if (dmWrapper != NULL) {
wl_proxy_set_queue((struct wl_proxy *) dmWrapper, dataDevice->dataSourceQueue);
zwpPrimarySelectionSource = zwp_primary_selection_device_manager_v1_create_source(dmWrapper);
wl_proxy_wrapper_destroy(dmWrapper);
}
if (zwpPrimarySelectionSource == NULL) {
free(dataSource);
JNU_ThrowByName(env, "java/awt/AWTError", "Wayland error creating zwp_primary_selection_source_v1 proxy");
return 0;
}
wl_proxy_set_queue((struct wl_proxy *) zwpPrimarySelectionSource, dataDevice->dataSourceQueue);
dataSource->zwpPrimarySelectionSource = zwpPrimarySelectionSource;
zwp_primary_selection_source_v1_add_listener(zwpPrimarySelectionSource,
&zwp_primary_selection_source_v1_listener, dataSource);
dataSource->protocol = DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION;
dataSource->zwpPrimarySelectionSource = zwpPrimarySelectionSource;
}
return ptr_to_jlong(dataSource);
@@ -1040,11 +1026,6 @@ Java_sun_awt_wl_WLDataSource_destroyImpl(JNIEnv *env, jclass clazz, jlong native
return;
}
if (source->javaObject != NULL) {
(*env)->DeleteGlobalRef(env, source->javaObject);
source->javaObject = NULL;
}
if (source->protocol == DATA_TRANSFER_PROTOCOL_WAYLAND) {
wl_data_source_destroy(source->wlDataSource);
} else if (source->protocol == DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION) {
@@ -1059,6 +1040,11 @@ Java_sun_awt_wl_WLDataSource_destroyImpl(JNIEnv *env, jclass clazz, jlong native
wl_surface_destroy(source->dragIcon);
}
if (source->javaObject != NULL) {
(*env)->DeleteGlobalRef(env, source->javaObject);
source->javaObject = NULL;
}
free(source);
}
@@ -1139,8 +1125,25 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_WLDataSource_setDnDIconImpl
JNIEXPORT void JNICALL
Java_sun_awt_wl_WLDataOffer_destroyImpl(JNIEnv *env, jclass clazz, jlong nativePtr)
{
assert(env != NULL);
struct DataOffer *offer = jlong_to_ptr(nativePtr);
DataOffer_destroy(offer);
if (offer == NULL) {
return;
}
if (offer->protocol == DATA_TRANSFER_PROTOCOL_WAYLAND) {
wl_data_offer_destroy(offer->wlDataOffer);
} else if (offer->protocol == DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION) {
zwp_primary_selection_offer_v1_destroy(offer->zwpPrimarySelectionOffer);
}
if (offer->javaObject != NULL) {
(*env)->DeleteGlobalRef(env, offer->javaObject);
offer->javaObject = NULL;
}
free(offer);
}
JNIEXPORT void JNICALL

View File

@@ -43,8 +43,6 @@ typedef struct WLOutput {
int32_t x;
int32_t y;
int32_t x_logical;
int32_t y_logical;
int32_t width;
int32_t height;
int32_t width_logical;
@@ -59,6 +57,8 @@ typedef struct WLOutput {
char * make;
char * model;
char * name;
bool offset_known; // whether x and y were set by xdg_output
} WLOutput;
static jclass geClass;
@@ -83,10 +83,13 @@ wl_output_geometry(
{
WLOutput *output = data;
// TODO: there's also a recommended, but unstable interface xdg_output;
// we may want to switch to that one day.
output->x = x;
output->y = y;
if (!output->offset_known) {
// Ubuntu 22.04 has a bug that prevent Mutter from sending out updates to (x, y) in this
// geometry event. So we prefer to learn offset from xdg_output that will override
// (x, y) from this event.
output->x = x;
output->y = y;
}
output->subpixel = subpixel;
output->transform = transform;
output->width_mm = physical_width;
@@ -161,8 +164,6 @@ NotifyOutputConfigured(WLOutput* output)
output->id,
output->x,
output->y,
output->x_logical,
output->y_logical,
output->width,
output->height,
output->width_logical,
@@ -173,6 +174,8 @@ NotifyOutputConfigured(WLOutput* output)
(jint)output->transform,
(jint)output->scale);
JNU_CHECK_EXCEPTION(env);
output->offset_known = false;
}
static void
@@ -218,8 +221,12 @@ static void
zxdg_output_logical_position(void *data, struct zxdg_output_v1 *zxdg_output_v1, int32_t x, int32_t y)
{
WLOutput * output = data;
output->x_logical = x;
output->y_logical = y;
output->x = x;
output->y = y;
// Prevent the geometry event from overriding these values with potentially incorrect ones;
// see wl_output_geometry().
output->offset_known = true;
}
static void
@@ -257,7 +264,7 @@ WLGraphicsEnvironment_initIDs
CHECK_NULL_RETURN(
notifyOutputConfiguredMID = (*env)->GetMethodID(env, clazz,
"notifyOutputConfigured",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIIIIIIIIII)V"),
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIIIIIIII)V"),
JNI_FALSE);
CHECK_NULL_RETURN(
notifyOutputDestroyedMID = (*env)->GetMethodID(env, clazz,

View File

@@ -1034,6 +1034,8 @@ jdk_awt_wayland = \
-java/awt/MultipleGradientPaint \
-java/awt/Multiscreen/MouseEventTest/MouseEventTest.java \
-java/awt/Multiscreen/MultiScreenLocationTest/MultiScreenLocationTest.java \
-java/awt/Multiscreen/MultiScreenCheckScreenIDTest.java \
-java/awt/Multiscreen/UpdateGCTest/UpdateGCTest.java \
-java/awt/Paint/bug8024864.java \
-java/awt/Paint/ButtonRepaint.java \
-java/awt/Paint/CheckboxRepaint.java \
@@ -1087,6 +1089,7 @@ jdk_swing_wayland= \
-javax/swing/DefaultButtonModel/DefaultButtonModelCrashTest.java \
-javax/swing/dnd/7171812/bug7171812.java \
-javax/swing/event/FocusEventCauseTest.java \
-javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java \
-javax/swing/JButton/4368790/bug4368790.java \
-javax/swing/JButton/8151303/PressedIconTest.java \
-javax/swing/JButton/bug4234034.java \

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2025 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.
*/
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Ellipse2D;
import java.util.concurrent.CountDownLatch;
/*
* @test
* @requires os.family == "linux"
* @summary Verifies that robot correctly pick color
* @run main/othervm -Dawt.toolkit.name=WLToolkit -Dsun.java2d.vulkan=True -Dsun.java2d.vulkan.accelsd=true ClipRenderTest
* @run main/othervm -Dawt.toolkit.name=WLToolkit -Dsun.java2d.vulkan=True -Dsun.java2d.vulkan.accelsd=false ClipRenderTest
* @run main/othervm -Dawt.toolkit.name=WLToolkit -Dsun.java2d.vulkan=False ClipRenderTest
*/
public class ClipRenderTest {
final static int W = 600;
final static int H = 600;
final static CountDownLatch latchShownFrame = new CountDownLatch(1);
static volatile boolean failed = false;
static boolean compareColors(Color c1, Color c2, double tolerance) {
return Math.abs(c1.getRed() - c2.getRed()) < tolerance &&
Math.abs(c1.getGreen() - c2.getGreen()) < tolerance &&
Math.abs(c1.getBlue() - c2.getBlue()) < tolerance;
}
public static void main(String[] args) throws InterruptedException, AWTException {
if (GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance()) {
System.out.println("No WLToolkit, skipping test");
return;
}
final Robot robot = new Robot();
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Robot Test");
frame.setSize(W, H);
frame.add(new JPanel(){
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setClip(0, 0, W, H);
g2d.setColor(Color.BLUE);
g2d.setClip(new Ellipse2D.Double(W/3, H/3, W/3, H/3));
g2d.fillRect(W/3, H/3, W/3, H/3);
g2d.setClip(0, 0, W/3, H/3);
g2d.setColor(Color.RED);
g2d.fillRect(0, 0, W/3, H/3);
}
});
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowActivated(WindowEvent e) {
var loc = frame.getLocationOnScreen();
int x = loc.x + frame.getInsets().bottom + W/6;
int y = loc.y + frame.getInsets().left + H/6;
Color c = robot.getPixelColor(x, y);
if (!compareColors(c, Color.RED, 10)) {
System.out.println("Unexpected color: " + c + " at (" + x + ", " + y + ")");
failed = true;
}
x += W / 3;
y += H / 3;
c = robot.getPixelColor(x, y);
if (!compareColors(c, Color.BLUE, 10)) {
System.out.println("Unexpected color: " + c + " at (" + x + ", " + y + ")");
failed = true;
}
latchShownFrame.countDown();
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
latchShownFrame.await();
if (failed) throw new RuntimeException("Test failed");
}
}

View File

@@ -13,7 +13,7 @@ javax/swing/JComponent/7154030/bug7154030.java JBR-7713 macosx-aarch64
javax/swing/JDialog/Transparency/TransparencyTest.java JBR-7554 macosx-aarch64
javax/swing/JSplitPane/4820080/JSplitPaneDragColorTest.java JBR-7247 macosx-all
javax/swing/JWindow/ShapedAndTranslucentWindows/PerPixelTranslucentCanvas.java JBR-7404 macosx-all
javax/swing/JWindow/ShapedAndTranslucentWindows/TranslucentPerPixelTranslucentGradient.java JBR-8327 macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7
javax/swing/JWindow/ShapedAndTranslucentWindows/TranslucentPerPixelTranslucentGradient.java JBR-8327 macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7,macosx-15.7.1
javax/swing/plaf/aqua/CustomComboBoxFocusTest.java JBR-5190 macosx-all
javax/swing/SwingGraphics/TranslateTest.java JBR-7510 macosx-aarch64

View File

@@ -118,14 +118,15 @@ java/awt/Graphics2D/TextPerf.java JBR-8541 linux-all,windows-all
java/awt/GridBagLayout/ComponentShortage.java 8355280,JBR-9347 windows-all,linux-all,macosx-x64
java/awt/KeyboardFocusmanager/TypeAhead/EnqueueWithDialogTest/EnqueueWithDialogTest.java JBR-7077 linux-all,windows-all
java/awt/MenuItem/EnableTest.java JBR-8543 windows-all timeout
java/awt/Modal/MultipleDialogs/MixOfModalAndNonModalDialogs.java JBR-9317 linux-6.16.7-100.fc41.x86_64
java/awt/Robot/NonEmptyErrorStream.java 8340330,JBR-5510,JBR-6275,JBR-8299,JBR-9259 macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7,macosx-all,linux-5.18.2-arch1-1,linux-aarch64,linux-6.15.8-100.fc41.x86_64,linux-6.16.7-100.fc41.x86_64
java/awt/Modal/MultipleDialogs/MixOfModalAndNonModalDialogs.java JBR-9317 linux-6.16.7-100.fc41.x86_64,linux-6.16.7-200.fc42.x86_64
java/awt/Robot/NonEmptyErrorStream.java 8340330,JBR-5510,JBR-6275,JBR-8299,JBR-9259 macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7,macosx-15.7.1,macosx-all,linux-5.18.2-arch1-1,linux-aarch64,linux-6.15.8-100.fc41.x86_64,linux-6.16.7-100.fc41.x86_64
javax/swing/border/TestTitledBorderLeak.java JBR-9272 macosx-14.8
javax/swing/JButton/TestMnemonicAction.java JBR-6508 windows-all,linux-all,macosx-all
javax/swing/JComboBox/8072767/bug8072767.java JBR-5540 windows-all,macosx-all
javax/swing/JWindow/ShapedAndTranslucentWindows/PerPixelTranslucentCanvas.java JBR-7404 macosx-all
javax/swing/plaf/synth/7158712/bug7158712.java 8322653,JBR-9061 macosx-all,linux-6.8.0-1033-aws,linux-6.8.0-1036-aws
javax/swing/JWindow/ShapedAndTranslucentWindows/PerPixelTranslucentGradient.java JBR-9446 macosx-all
javax/swing/plaf/synth/7158712/bug7158712.java 8322653,JBR-9061 macosx-all,linux-6.8.0-1033-aws,linux-6.8.0-1036-aws,linux-6.8.0-1039-aws
javax/swing/UI/UnninstallUIMemoryLeaks/UnninstallUIMemoryLeaks.java JBR-5952,8340330,JBR-6274 windows-all,macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-all
jb/build/ResolveSymbolsTest/ResolveSymbolsRealEnv.java JBR-8544 linux-all

View File

@@ -27,7 +27,7 @@ java/awt/Robot/CheckCommonColors/CheckCommonColors.java 8253184,JBR-5510,JBR-544
java/awt/Robot/HiDPIScreenCapture/ScreenCaptureTest.java 8253184,JBR-5442 windows-all,linux-all
java/awt/Robot/InfiniteLoopException.java 8342638,JBR-8830 windows-x64,windows-all,linux-all
java/awt/Robot/MouseLocationOnScreen/MouseLocationOnScreen.java JBR-5390 macosx-all,linux-all
java/awt/Robot/NonEmptyErrorStream.java JBR-5442,8340330,JBR-5510,JBR-8299 linux-all,macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7,linux-5.18.2-arch1-1,linux-aarch64
java/awt/Robot/NonEmptyErrorStream.java JBR-5442,8340330,JBR-5510,JBR-8299 linux-all,macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7,macosx-15.7.1,linux-5.18.2-arch1-1,linux-aarch64
java/awt/Robot/RobotMoveMultiscreen.java JBR-5442 linux-all
java/awt/Toolkit/AWTEventListenerProxyTest/AWTEventListenerProxyTest.java JBR-6065 windows-all
java/awt/Window/SlowMotion/SlowMotion.java JBR-5442 linux-all

View File

@@ -123,6 +123,7 @@ java/awt/event/MouseEvent/MouseClickTest/MouseClickTest.java 8168389 windows-all
java/awt/event/KeyEvent/CorrectTime/CorrectTime.java JBR-6665 linux-5.15.0-1083-aws,linux-5.15.0-1084-aws,windows-all
java/awt/event/KeyEvent/SwallowKeyEvents/SwallowKeyEvents.java 8224055,JBR-5906 macosx-all,linux-all
java/awt/event/MouseEvent/MouseButtonsAndKeyMasksTest/MouseButtonsAndKeyMasksTest.java JBR-6578 windows-all,linux-all
java/awt/event/SequencedEvent/MultipleContextsFunctionalTest.java 8305061 macosx-x64
java/awt/event/StressTest/MouseAndKeyEventStressTest.java JBR-6479 generic-all
java/awt/FileDialog/8003399/bug8003399.java JBR-6930 windows-all
java/awt/FlowLayout/PreferredLayoutSize.java JBR-6349 linux-all
@@ -134,7 +135,7 @@ java/awt/Focus/FocusTraversalPolicy/ButtonGroupLayoutTraversal/ButtonGroupLayout
java/awt/Focus/FrameMinimizeTest/FrameMinimizeTest.java 8016266 linux-all
java/awt/Focus/IconifiedFrameFocusChangeTest/IconifiedFrameFocusChangeTest.java 6849364 generic-all
java/awt/Focus/InactiveFocusRace.java 8023263,JBR-8586 linux-all,windows-x64
java/awt/Focus/InputVerifierTest3/InputVerifierTest3.java JBR-7311 linux-6.8.0-1036-aws
java/awt/Focus/InputVerifierTest3/InputVerifierTest3.java JBR-7311 linux-6.8.0-1036-aws,linux-6.8.0-1039-aws
java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusToFrontTest.java 6848406 generic-all
java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusSetVisibleTest.java 6848407 generic-all
java/awt/Focus/LabelScrollBarFocus.java JBR-8027 linux-all
@@ -443,7 +444,7 @@ java/awt/Frame/MiscUndecorated/FrameCloseTest.java JBR-5210 windows-all
java/awt/Frame/MiscUndecorated/RepaintTest.java 8266244,JBR-5786 macosx-aarch64,generic-all
java/awt/Robot/HiDPIMouseClick/HiDPIRobotMouseClick.java 8253184 windows-all
java/awt/Robot/InfiniteLoopException.java 8342638 windows-all,linux-all
java/awt/Robot/NonEmptyErrorStream.java 8340330,JBR-5510,JBR-8299,JBR-9259 macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7,linux-5.18.2-arch1-1,linux-aarch64,linux-6.15.8-100.fc41.x86_64,linux-6.16.7-100.fc41.x86_64
java/awt/Robot/NonEmptyErrorStream.java 8340330,JBR-5510,JBR-8299,JBR-9259 macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7,macosx-15.7.1,linux-5.18.2-arch1-1,linux-aarch64,linux-6.15.8-100.fc41.x86_64,linux-6.16.7-100.fc41.x86_64
java/awt/Robot/RobotExtraButton/RobotExtraButton.java JBR-6554 linux-all
java/awt/Modal/FileDialog/FileDialogAppModal1Test.java 7186009,8253184 macosx-all,windows-all
java/awt/Modal/FileDialog/FileDialogAppModal2Test.java 7186009,8253184 macosx-all,windows-all
@@ -891,7 +892,6 @@ java/net/MulticastSocket/Test.java 7145658 macosx-a
# jdk_nio
java/nio/channels/AsynchronousSocketChannel/StressLoopback.java JBR-8817 windows-aarch64
java/nio/channels/AsynchronousSocketChannel/StressLoopback.java JBR-8817 windows-aarch64
java/nio/channels/DatagramChannel/AdaptorMulticasting.java 8144003,JBR-8455,JBR-8456,8308807,JBR-9219 macosx-all,aix-ppc64,linux-6.15.8-100.fc41.x86_64,linux-6.16.7-100.fc41.x86_64,linux-6.16.7-200.fc42.x86_64
java/nio/channels/DatagramChannel/BasicMulticastTests.java 8144003 macosx-all
@@ -900,6 +900,7 @@ java/nio/channels/DatagramChannel/ManySourcesAndTargets.java 8264385 macosx-a
java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java 8144003,JBR-9218 macosx-all,linux-6.15.8-100.fc41.x86_64,linux-6.16.7-100.fc41.x86_64,linux-6.16.7-200.fc42.x86_64
java/nio/channels/DatagramChannel/Promiscuous.java 8144003,JBR-9218 macosx-all,linux-6.15.8-100.fc41.x86_64,linux-6.16.7-100.fc41.x86_64,linux-6.16.7-200.fc42.x86_64
java/nio/channels/DatagramChannel/PromiscuousIPv6.java JBR-8828,JBR-9218 macosx-x64,linux-6.15.8-100.fc41.x86_64,linux-6.16.7-100.fc41.x86_64,linux-6.16.7-200.fc42.x86_64
java/nio/channels/DatagramChannel/SendReceiveMaxSize.java JBR-9447 linux-6.14.0-1013-aws
java/nio/channels/DatagramChannel/Unref.java 8233437 generic-all
java/nio/channels/FileChannel/LargeGatheringWrite.java JBR-9316 linux-6.15.8-100.fc41.x86_64,linux-6.16.7-100.fc41.x86_64,linux-6.16.7-200.fc42.x86_64
java/nio/channels/Selector/LotsOfInterrupts.java#virtual JBR-8940 windows-aarch64
@@ -954,7 +955,7 @@ sun/security/smartcardio/TestExclusive.java 8039280 generic-
sun/security/smartcardio/TestMultiplePresent.java 8039280 generic-all
sun/security/smartcardio/TestPresent.java 8039280 generic-all
sun/security/smartcardio/TestTransmit.java 8039280 generic-all
sun/security/ssl/SSLSessionImpl/MultiNSTClient.java JBR-9058 macosx-15.5,macosx-15.6,macosx-15.7
sun/security/ssl/SSLSessionImpl/MultiNSTClient.java JBR-9058 macosx-15.5,macosx-15.6,macosx-15.7,macosx-15.7.1
com/sun/crypto/provider/Cipher/DES/PerformanceTest.java 8039280 generic-all
com/sun/security/auth/callback/TextCallbackHandler/Default.java 8039280 generic-all
com/sun/security/auth/callback/TextCallbackHandler/Password.java 8039280 generic-all
@@ -1063,6 +1064,7 @@ javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java#id1 JBR-7965 windows-al
javax/swing/JButton/TestMnemonicAction.java JBR-6508 windows-all,linux-all
javax/swing/JColorChooser/Test6524757.java JBR-5210 windows-all
javax/swing/JColorChooser/Test6827032.java JBR-5210 windows-all
javax/swing/JComboBox/4231298/JComboBoxPrototypeDisplayValueTest.java JBR-6777 linux-all
javax/swing/JComboBox/4743225/bug4743225.java JBR-5210 windows-all
javax/swing/JComboBox/6236162/bug6236162.java JBR-5210 windows-all
javax/swing/JComboBox/6406264/bug6406264.java JBR-5210,JBR-5510 windows-all,linux-5.18.2-arch1-1
@@ -1156,14 +1158,14 @@ javax/swing/JFormattedTextField/bug4863121.java JBR-7424 windows-all
javax/swing/JFrame/8016356/bug8016356.java JBR-108 windows-all
javax/swing/JFrame/8175301/ScaledFrameBackgroundTest.java 8274106,JBR-5510 macosx-aarch64,linux-5.18.2-arch1-1
javax/swing/JPopupMenu/6800513/bug6800513.java 7184956,JBR-6533 macosx-all,linux-all,windows-all
javax/swing/JSlider/bug4382876.java JBR-8666 linux-6.14.0-1010-aws,linux-6.14.0-1012-aws
javax/swing/JSlider/bug4382876.java JBR-8666 linux-6.14.0-1010-aws,linux-6.14.0-1012-aws,linux-6.16.7-100.fc41.x86_64
javax/swing/JScrollBar/4708809/bug4708809.java 8169957 windows-x64
javax/swing/JScrollPane/HorizontalMouseWheelOnShiftPressed/HorizontalMouseWheelOnShiftPressed.java 8197552 windows-all
javax/swing/JSplitPane/4885629/bug4885629.java JBR-5510 linux-5.18.2-arch1-1
javax/swing/JTabbedPane/4361477/bug4361477.java JBR-5932 linux-all
javax/swing/JTabbedPane/4624207/bug4624207.java 8064922,8197552,JBR-6235 macosx-all,windows-all,linux-all 8064922:macosx-all, 8197552:windows-all
javax/swing/JTabbedPane/TestBackgroundScrollPolicy.java 8253184,JBR-8498,JBR-8718 windows-all,macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7,linux-5.18.2-arch1-1
javax/swing/JTabbedPane/TestJTabbedPaneBackgroundColor.java JBR-8718,JBR-8493 linux-5.18.2-arch1-1,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7
javax/swing/JTabbedPane/TestBackgroundScrollPolicy.java 8253184,JBR-8498,JBR-8718 windows-all,macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7,macosx-15.7.1,linux-5.18.2-arch1-1
javax/swing/JTabbedPane/TestJTabbedPaneBackgroundColor.java JBR-8718,JBR-8493 linux-5.18.2-arch1-1,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7,macosx-15.7.1
javax/swing/JToggleButton/TestSelectedKey.java JBR-5846 windows-all
javax/swing/JToolBar/4529206/bug4529206.java JBR-5387 linux-all
javax/swing/JToolTip/6219960/bug6219960.java 8253184 windows-all
@@ -1218,7 +1220,7 @@ javax/swing/text/JTextComponent/6361367/bug6361367.java JBR-521,JBR-6687 windows
javax/swing/text/View/8014863/bug8014863.java JBR-5541 windows-all,linux-all
javax/swing/text/View/8048110/bug8048110.java JBR-9256,JBR-9207 windows-all,linux-6.16.7-100.fc41.x86_64
javax/swing/text/View/8156217/FPMethodCalledTest.java JBR-5542 linux-all
javax/swing/UI/UnninstallUIMemoryLeaks/UnninstallUIMemoryLeaks.java JBR-5952,8340330 windows-all,macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7
javax/swing/UI/UnninstallUIMemoryLeaks/UnninstallUIMemoryLeaks.java JBR-5952,8340330 windows-all,macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7,macosx-15.7.1
java/awt/Robot/CheckCommonColors/CheckCommonColors.java 8253184,JBR-5510 windows-all,linux-5.18.2-arch1-1
java/awt/Robot/HiDPIScreenCapture/HiDPIRobotScreenCaptureTest.java 8253184,JBR-5510 windows-all,linux-5.18.2-arch1-1
java/awt/Robot/HiDPIScreenCapture/ScreenCaptureTest.java 8253184,JBR-5510 windows-all,linux-5.18.2-arch1-1
@@ -1326,9 +1328,9 @@ jdk/jfr/event/runtime/TestShutdownEvent.java JBR-8781 windows-aarch64
jdk/jfr/jvm/TestDumpOnCrash.java JBR-8780 windows-aarch64
jdk/jfr/jvm/TestWaste.java 8282427 generic-all
jdk/jfr/api/consumer/recordingstream/TestOnEvent.java 8255404 linux-x64
jdk/jfr/api/consumer/streaming/TestOutOfProcessMigration.java JBR-8460 macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7
jdk/jfr/api/consumer/streaming/TestJVMCrash.java JBR-8459 macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7
jdk/jfr/api/consumer/streaming/TestJVMExit.java JBR-8460 macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7
jdk/jfr/api/consumer/streaming/TestOutOfProcessMigration.java JBR-8460 macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7,macosx-15.7.1
jdk/jfr/api/consumer/streaming/TestJVMCrash.java JBR-8459 macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7,macosx-15.7.1
jdk/jfr/api/consumer/streaming/TestJVMExit.java JBR-8460 macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7,macosx-15.7.1
jdk/jfr/tool/TestPrint.java JBR-8704 macosx-all
############################################################################
@@ -1445,7 +1447,7 @@ com/sun/java/swing/plaf/windows/AltFocusIssueTest.java
jb/hotspot/AsyncProfilerRunnerTest.java JBR-7175 macosx-all
jb/java/awt/CustomTitleBar/DialogNativeControlsTest.java JBR-8794 windows-aarch64
jb/java/awt/Graphics2D/TextRender/OGLMetalTextRender.java JBR-4091,JBR-5392 windows-aarch64,macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7
jb/java/awt/Graphics2D/TextRender/OGLMetalTextRender.java JBR-4091,JBR-5392 windows-aarch64,macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-15.7,macosx-15.7.1
jb/java/awt/event/TouchScreenEvent/TouchScreenEventsTestLinux.sh JBR-4078 linux-all
jb/java/awt/Font/Font467.java JBR-3960 generic-all
jb/java/awt/image/BufferedFontRenderingTest.java JBR-6493 generic-all

View File

@@ -70,7 +70,7 @@ javax/swing/JButton/bug4490179.java JBR-8925 windows-all
javax/swing/JFileChooser/JFileChooserSetLocationTest.java JBR-8098 linux-all,windows-all
javax/swing/JInternalFrame/4202966/IntFrameCoord.java JBR-9006 window-all
javax/swing/JMenu/bug4342646.java JBR-8727 linux-all,windows-all
javax/swing/JPopupMenu/6580930/bug6580930.java JBR-5071 linux-6.8.0-1033-aws,linux-6.8.0-1036-aws,linux-6.15.8-100.fc41.x86_64,linux-6.16.7-100.fc41.x86_64
javax/swing/JPopupMenu/6580930/bug6580930.java JBR-5071 linux-6.8.0-1033-aws,linux-6.8.0-1036-aws,linux-6.8.0-1039-aws,linux-6.15.8-100.fc41.x86_64,linux-6.16.7-100.fc41.x86_64
javax/swing/JTable/JTableRightOrientationTest.java JBR-8102 linux-all,windows-all
javax/swing/text/ParagraphView/6364882/bug6364882.java JBR-8747 linux-all
javax/swing/plaf/basic/BasicGraphicsUtils/8132119/bug8132119.java JBR-8357 linux-all
@@ -138,7 +138,7 @@ java/awt/Robot/HiDPIScreenCapture/ScreenCaptureResolutionTest.java nobug generic
java/awt/Robot/HiDPIScreenCapture/ScreenCaptureTest.java nobug generic-all
java/awt/Robot/ScreenCaptureRobotTest.java#id1 nobug generic-all
java/awt/Toolkit/ScreenInsetsDPIVariation/ScreenInsetsDPIVariation.java nobug generic-all
java/awt/TrayIcon/RightClickWhenBalloonDisplayed/RightClickWhenBalloonDisplayed.java 8238720,JBR-6931 windows-all,linux-aarch64,linux-6.16.7-100.fc41.x86_64
java/awt/TrayIcon/RightClickWhenBalloonDisplayed/RightClickWhenBalloonDisplayed.java 8238720,JBR-6931 windows-all,linux-aarch64,linux-6.16.7-100.fc41.x86_64,linux-6.16.7-200.fc42.x86_64
java/awt/Window/8159168/SetShapeTest.java nobug generic-all
java/awt/Window/BackgroundIsNotUpdated/BackgroundIsNotUpdated.java nobug generic-all
java/awt/Window/GetScreenLocation/GetScreenLocationTest.java nobug generic-all

View File

@@ -28,7 +28,8 @@ javax/swing/GraphicsConfigNotifier/StalePreferredSize.java JBR-9031 linux-all
javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java JBR-8266 linux-x64
javax/swing/GroupLayout/8079640/bug8079640.java JBR-9100 linux-all
javax/swing/JButton/JButtonPaintNPE/JButtonPaintNPE.java JBR-9100 linux-all
javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java#id0 JBR-7928,JBR-9341 linux-6.8.0-1036-aws,linux-6.14.9-arch1-1
javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java#id0 JBR-7928,JBR-9341 linux-6.8.0-1036-aws,linux-6.8.0-1039-aws,linux-6.14.9-arch1-1
javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java#id2 JBR-7928 linux-6.8.0-1036-aws,linux-6.8.0-1039-aws
javax/swing/JColorChooser/Test4177735.java JBR-9100 linux-all
javax/swing/JComboBox/8019180/Test8019180.java JBR-9100 linux-all
javax/swing/JComboBox/bug4276920.java JBR-9100 linux-all

View File

@@ -6,8 +6,9 @@ java/awt/Multiscreen/UpdateGCTest/UpdateGCTest.java JBR-8264 linux-x64
javax/swing/JComponent/6989617/bug6989617.java JBR-8796 linux-6.14.0-1010-aws,linux-6.14.0-1012-aws
javax/swing/JDesktopPane/TestDesktopManagerNPE.java JBR-8449 linux-x64
javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java JBR-8266 linux-x64
javax/swing/InputVerifier/VerifyTarget/VerifyTargetTest.java JBR-7520,JBR-9320 linux-6.8.0-1036-aws,linux-6.14.9-arch1-1
javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java#id0 JBR-7928,JBR-9341 linux-6.8.0-1036-aws,linux-6.14.9-arch1-1
javax/swing/InputVerifier/VerifyTarget/VerifyTargetTest.java JBR-7520,JBR-9320 linux-6.8.0-1036-aws,linux-6.8.0-1039-aws,linux-6.14.9-arch1-1
javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java#id0 JBR-7928,JBR-9341 linux-6.8.0-1036-aws,linux-6.8.0-1039-aws,linux-6.14.9-arch1-1
javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java#id2 JBR-7928,JBR-9341 linux-6.8.0-1036-aws,linux-6.8.0-1039-aws,linux-6.14.9-arch1-1
javax/swing/JEditorPane/JEditorPaneFontFallback.java JBR-8305 linux-all
javax/swing/JFormattedTextField/bug4741926.java JBR-7530,JBR-9321 linux-all,linux-6.14.9-arch1-1
javax/swing/JFormattedTextField/TestSelectedTextBackgroundColor.java JBR-8790 linux-all

View File

@@ -0,0 +1,601 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import static java.lang.System.out;
/**
* @test
* @bug 8341381
* @summary fix cubic offsetting issue (numerical accuracy)
* @run main/othervm/timeout=20 Bug8341381
* @modules java.desktop/sun.java2d.marlin
*/
public final class Bug8341381 {
static final boolean SHOW_GUI = false;
static final boolean CHECK_PIXELS = true;
static final boolean TRACE_ALL = false;
static final boolean TRACE_CHECK_PIXELS = false;
static final boolean SAVE_IMAGE = false;
static final boolean INTENSIVE = false;
static final double DPI = 96;
static final float STROKE_WIDTH = 15f;
// delay is 1 frame at 60hz
static final int DELAY = 16;
// off-screen test step (1.0 by default)
static final double STEP = (INTENSIVE) ? 1.0 / 117 : 1.0;
// stats:
static int N_TEST = 0;
static int N_FAIL = 0;
static final AtomicBoolean isMarlin = new AtomicBoolean();
static final CountDownLatch latch = new CountDownLatch(1);
public static void main(final String[] args) {
Locale.setDefault(Locale.US);
// FIRST: Get Marlin runtime state from its log:
// initialize j.u.l Logger:
final Logger log = Logger.getLogger("sun.java2d.marlin");
log.addHandler(new Handler() {
@Override
public void publish(LogRecord record) {
final String msg = record.getMessage();
if (msg != null) {
// last space to avoid matching other settings:
if (msg.startsWith("sun.java2d.renderer ")) {
isMarlin.set(msg.contains("DMarlinRenderingEngine"));
}
}
final Throwable th = record.getThrown();
// detect any Throwable:
if (th != null) {
out.println("Test failed:\n" + record.getMessage());
th.printStackTrace(out);
throw new RuntimeException("Test failed: ", th);
}
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
});
out.println("Bug8341381: start");
final long startTime = System.currentTimeMillis();
// enable Marlin logging & internal checks:
System.setProperty("sun.java2d.renderer.log", "true");
System.setProperty("sun.java2d.renderer.useLogger", "true");
try {
startTest();
out.println("WAITING ...");
latch.await(15, TimeUnit.SECONDS); // 2s typically
if (isMarlin.get()) {
out.println("Marlin renderer used at runtime.");
} else {
throw new RuntimeException("Marlin renderer NOT used at runtime !");
}
// show test report:
out.println("TESTS: " + N_TEST + " FAILS: " + N_FAIL);
if (N_FAIL > 0) {
throw new RuntimeException("Bug8341381: " + N_FAIL + " / " + N_TEST + " test(s) failed !");
}
} catch (InterruptedException ie) {
throw new RuntimeException(ie);
} catch (InvocationTargetException ite) {
throw new RuntimeException(ite);
} finally {
final double elapsed = (System.currentTimeMillis() - startTime) / 1000.0;
out.println("Bug8341381: end (" + elapsed + " s)");
}
}
private static void startTest() throws InterruptedException, InvocationTargetException {
if (SHOW_GUI) {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
final JFrame viewer = new JFrame();
viewer.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
viewer.setContentPane(new CanvasPanel(viewer));
viewer.pack();
viewer.setVisible(true);
}
});
return;
} else {
out.println("STEP: " + STEP);
new Thread(new Runnable() {
@Override
public void run() {
final Context ctx = new Context();
final Dimension initialDim = ctx.bugDisplay.getSize(DPI);
double w = initialDim.width;
double h = initialDim.height;
do {
ctx.shouldScale(w, h);
ctx.paintImage();
// resize component:
w -= STEP;
h -= STEP;
} while (ctx.iterate());
}
}).start();
}
}
static final class Context {
final BugDisplay bugDisplay = new BugDisplay();
double width = 0.0, height = 0.0;
BufferedImage bimg = null;
boolean shouldScale(final double w, final double h) {
if ((w != width) || (h != height) || !bugDisplay.isScaled) {
width = w;
height = h;
bugDisplay.scale(width, height);
N_TEST++;
return true;
}
return false;
}
void paintImage() {
final int w = bugDisplay.canvasWidth;
final int h = bugDisplay.canvasHeight;
if ((bimg == null) || (w > bimg.getWidth()) || (h > bimg.getHeight())) {
bimg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);
}
final Graphics gi = bimg.getGraphics();
try {
bugDisplay.paint(gi);
} finally {
gi.dispose();
}
if (!bugDisplay.checkImage(bimg)) {
N_FAIL++;
}
}
boolean iterate() {
if ((bugDisplay.canvasWidth > 10) || (bugDisplay.canvasHeight > 10)) {
// continue:
return true;
}
out.println("Stop");
latch.countDown();
return false;
}
}
static final class CanvasPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final Context ctx = new Context();
private boolean resized = false;
private Timer timer = null;
public CanvasPanel(final JFrame frame) {
timer = new Timer(DELAY, e -> {
if (resized) {
resized = false;
if (ctx.iterate()) {
// resize component:
setSize((int) Math.round(ctx.width - 1), (int) Math.round(ctx.height - 1));
} else {
timer.stop();
if (frame != null) {
frame.setVisible(false);
}
}
}
});
timer.setCoalesce(true);
timer.setRepeats(true);
timer.start();
}
@Override
public void paint(final Graphics g) {
final Dimension dim = getSize();
if (ctx.shouldScale(dim.width, dim.height)) {
this.resized = true;
}
super.paint(g);
// paint on buffered image:
if (CHECK_PIXELS) {
final int w = ctx.bugDisplay.canvasWidth;
final int h = ctx.bugDisplay.canvasHeight;
if (this.resized) {
ctx.paintImage();
}
g.drawImage(ctx.bimg.getSubimage(0, 0, w, h), 0, 0, null);
} else {
ctx.bugDisplay.paint(g);
}
}
@Override
public Dimension getPreferredSize() {
return ctx.bugDisplay.getSize(DPI);
}
}
static final class BugDisplay {
boolean isScaled = false;
int canvasWidth;
int canvasHeight;
private final static java.util.List<CubicCurve2D> curves1 = Arrays.asList(
new CubicCurve2D.Double(2191.0, 7621.0, 2191.0, 7619.0, 2191.0, 7618.0, 2191.0, 7617.0),
new CubicCurve2D.Double(2191.0, 7617.0, 2191.0, 7617.0, 2191.0, 7616.0, 2191.0, 7615.0),
new CubicCurve2D.Double(2198.0, 7602.0, 2200.0, 7599.0, 2203.0, 7595.0, 2205.0, 7590.0),
new CubicCurve2D.Double(2205.0, 7590.0, 2212.0, 7580.0, 2220.0, 7571.0, 2228.0, 7563.0),
new CubicCurve2D.Double(2228.0, 7563.0, 2233.0, 7557.0, 2239.0, 7551.0, 2245.0, 7546.0),
new CubicCurve2D.Double(2245.0, 7546.0, 2252.0, 7540.0, 2260.0, 7534.0, 2267.0, 7528.0),
new CubicCurve2D.Double(2267.0, 7528.0, 2271.0, 7526.0, 2275.0, 7524.0, 2279.0, 7521.0),
new CubicCurve2D.Double(2279.0, 7521.0, 2279.0, 7520.0, 2280.0, 7520.0, 2281.0, 7519.0)
);
private final static java.util.List<CubicCurve2D> curves2 = Arrays.asList(
new CubicCurve2D.Double(2281.0, 7519.0, 2282.0, 7518.0, 2282.0, 7517.0, 2283.0, 7516.0),
new CubicCurve2D.Double(2283.0, 7516.0, 2284.0, 7515.0, 2284.0, 7515.0, 2285.0, 7514.0),
new CubicCurve2D.Double(2291.0, 7496.0, 2292.0, 7495.0, 2292.0, 7494.0, 2291.0, 7493.0),
new CubicCurve2D.Double(2291.0, 7493.0, 2290.0, 7492.0, 2290.0, 7492.0, 2289.0, 7492.0),
new CubicCurve2D.Double(2289.0, 7492.0, 2288.0, 7491.0, 2286.0, 7492.0, 2285.0, 7492.0),
new CubicCurve2D.Double(2262.0, 7496.0, 2260.0, 7497.0, 2259.0, 7497.0, 2257.0, 7498.0),
new CubicCurve2D.Double(2257.0, 7498.0, 2254.0, 7498.0, 2251.0, 7499.0, 2248.0, 7501.0),
new CubicCurve2D.Double(2248.0, 7501.0, 2247.0, 7501.0, 2245.0, 7502.0, 2244.0, 7503.0),
new CubicCurve2D.Double(2207.0, 7523.0, 2203.0, 7525.0, 2199.0, 7528.0, 2195.0, 7530.0),
new CubicCurve2D.Double(2195.0, 7530.0, 2191.0, 7534.0, 2186.0, 7538.0, 2182.0, 7541.0)
);
private final static java.util.List<CubicCurve2D> curves3 = Arrays.asList(
new CubicCurve2D.Double(2182.0, 7541.0, 2178.0, 7544.0, 2174.0, 7547.0, 2170.0, 7551.0),
new CubicCurve2D.Double(2170.0, 7551.0, 2164.0, 7556.0, 2158.0, 7563.0, 2152.0, 7569.0),
new CubicCurve2D.Double(2152.0, 7569.0, 2148.0, 7573.0, 2145.0, 7577.0, 2141.0, 7582.0),
new CubicCurve2D.Double(2141.0, 7582.0, 2138.0, 7588.0, 2134.0, 7595.0, 2132.0, 7602.0),
new CubicCurve2D.Double(2132.0, 7602.0, 2132.0, 7605.0, 2131.0, 7608.0, 2131.0, 7617.0),
new CubicCurve2D.Double(2131.0, 7617.0, 2131.0, 7620.0, 2131.0, 7622.0, 2131.0, 7624.0),
new CubicCurve2D.Double(2131.0, 7624.0, 2131.0, 7630.0, 2132.0, 7636.0, 2135.0, 7641.0),
new CubicCurve2D.Double(2135.0, 7641.0, 2136.0, 7644.0, 2137.0, 7647.0, 2139.0, 7650.0),
new CubicCurve2D.Double(2139.0, 7650.0, 2143.0, 7658.0, 2149.0, 7664.0, 2155.0, 7670.0),
new CubicCurve2D.Double(2155.0, 7670.0, 2160.0, 7676.0, 2165.0, 7681.0, 2171.0, 7686.0)
);
private final static java.util.List<CubicCurve2D> curves4 = Arrays.asList(
new CubicCurve2D.Double(2171.0, 7686.0, 2174.0, 7689.0, 2177.0, 7692.0, 2180.0, 7694.0),
new CubicCurve2D.Double(2180.0, 7694.0, 2185.0, 7698.0, 2191.0, 7702.0, 2196.0, 7706.0),
new CubicCurve2D.Double(2196.0, 7706.0, 2199.0, 7708.0, 2203.0, 7711.0, 2207.0, 7713.0),
new CubicCurve2D.Double(2244.0, 7734.0, 2245.0, 7734.0, 2247.0, 7735.0, 2248.0, 7736.0),
new CubicCurve2D.Double(2248.0, 7736.0, 2251.0, 7738.0, 2254.0, 7739.0, 2257.0, 7739.0),
new CubicCurve2D.Double(2257.0, 7739.0, 2259.0, 7739.0, 2260.0, 7739.0, 2262.0, 7740.0),
new CubicCurve2D.Double(2285.0, 7745.0, 2286.0, 7745.0, 2288.0, 7745.0, 2289.0, 7745.0),
new CubicCurve2D.Double(2289.0, 7745.0, 2290.0, 7745.0, 2290.0, 7744.0, 2291.0, 7743.0),
new CubicCurve2D.Double(2291.0, 7743.0, 2292.0, 7742.0, 2292.0, 7741.0, 2291.0, 7740.0),
new CubicCurve2D.Double(2285.0, 7722.0, 2284.0, 7721.0, 2284.0, 7721.0, 2283.0, 7720.0),
new CubicCurve2D.Double(2283.0, 7720.0, 2282.0, 7719.0, 2282.0, 7719.0, 2281.0, 7718.0),
new CubicCurve2D.Double(2281.0, 7718.0, 2280.0, 7717.0, 2279.0, 7716.0, 2279.0, 7716.0),
new CubicCurve2D.Double(2279.0, 7716.0, 2275.0, 7712.0, 2271.0, 7710.0, 2267.0, 7708.0),
new CubicCurve2D.Double(2267.0, 7708.0, 2260.0, 7702.0, 2252.0, 7697.0, 2245.0, 7691.0),
new CubicCurve2D.Double(2245.0, 7691.0, 2239.0, 7685.0, 2233.0, 7679.0, 2228.0, 7673.0),
new CubicCurve2D.Double(2228.0, 7673.0, 2220.0, 7665.0, 2212.0, 7656.0, 2205.0, 7646.0),
new CubicCurve2D.Double(2205.0, 7646.0, 2203.0, 7641.0, 2200.0, 7637.0, 2198.0, 7634.0)
);
private final static Point2D.Double[] extent = {new Point2D.Double(0.0, 0.0), new Point2D.Double(7777.0, 10005.0)};
private final static Stroke STROKE = new BasicStroke(STROKE_WIDTH);
private final static Stroke STROKE_DASHED = new BasicStroke(STROKE_WIDTH, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL,
10.0f, new float[] {100f, 0f}, 0.0f);
// members:
private final java.util.List<CubicCurve2D> allCurves = new ArrayList<>();
private final Rectangle2D bboxAllCurves = new Rectangle2D.Double();
BugDisplay() {
allCurves.addAll(curves1);
allCurves.addAll(curves2);
allCurves.addAll(curves3);
allCurves.addAll(curves4);
// initialize bounding box:
double x1 = Double.POSITIVE_INFINITY;
double y1 = Double.POSITIVE_INFINITY;
double x2 = Double.NEGATIVE_INFINITY;
double y2 = Double.NEGATIVE_INFINITY;
for (final CubicCurve2D c : allCurves) {
final Rectangle2D r = c.getBounds2D();
if (r.getMinX() < x1) {
x1 = r.getMinX();
}
if (r.getMinY() < y1) {
y1 = r.getMinY();
}
if (r.getMaxX() > x2) {
x2 = r.getMaxX();
}
if (r.getMaxY() > y2) {
y2 = r.getMaxY();
}
}
// add margin of 10%:
final double m = 1.1 * STROKE_WIDTH;
bboxAllCurves.setFrameFromDiagonal(x1 - m, y1 - m, x2 + m, y2 + m);
}
public void paint(final Graphics g) {
final Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
// ------ scale
final AffineTransform tx_orig = g2d.getTransform();
final AffineTransform tx = getDrawTransform();
g2d.transform(tx);
// draw bbox:
if (!CHECK_PIXELS) {
g2d.setColor(Color.RED);
g2d.setStroke(STROKE);
g2d.draw(bboxAllCurves);
}
// draw curves:
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
g2d.setColor(Color.BLACK);
// dasher + stroker:
g2d.setStroke(STROKE_DASHED);
this.allCurves.forEach(g2d::draw);
// reset
g2d.setTransform(tx_orig);
}
private AffineTransform getDrawTransform() {
// ------ scale
double minX = extent[0].x, maxX = extent[1].x;
double minY = extent[0].y, maxY = extent[1].y;
// we're scaling and respecting the proportions, check which scale to use
double sx = this.canvasWidth / Math.abs(maxX - minX);
double sy = this.canvasHeight / Math.abs(maxY - minY);
double s = Math.min(sx, sy);
double m00, m11, m02, m12;
if (minX < maxX) {
m00 = s;
m02 = -s * minX;
} else {
// inverted X axis
m00 = -s;
m02 = this.canvasWidth + s * maxX;
}
if (minY < maxY) {
m11 = s;
m12 = -s * minY;
} else {
// inverted Y axis
m11 = -s;
m12 = this.canvasHeight + s * maxY;
}
// scale to the available view port
AffineTransform scaleTransform = new AffineTransform(m00, 0, 0, m11, m02, m12);
// invert the Y axis since (0, 0) is at top left for AWT
AffineTransform invertY = new AffineTransform(1, 0, 0, -1, 0, this.canvasHeight);
invertY.concatenate(scaleTransform);
return invertY;
}
public Dimension getSize(double dpi) {
double metricScalingFactor = 0.02539999969303608;
// 1 inch = 25,4 millimeter
final double factor = dpi * metricScalingFactor / 25.4;
int width = (int) Math.ceil(Math.abs(extent[1].x - extent[0].x) * factor);
int height = (int) Math.ceil(Math.abs(extent[1].y - extent[0].y) * factor);
return new Dimension(width, height);
}
public void scale(double w, double h) {
double extentWidth = Math.abs(extent[1].x - extent[0].x);
double extentHeight = Math.abs(extent[1].y - extent[0].y);
double fx = w / extentWidth;
if (fx * extentHeight > h) {
fx = h / extentHeight;
}
this.canvasWidth = (int) Math.round(fx * extentWidth);
this.canvasHeight = (int) Math.round(fx * extentHeight);
// out.println("canvas scaled (" + canvasWidth + " x " + canvasHeight + ")");
this.isScaled = true;
}
protected boolean checkImage(BufferedImage image) {
final AffineTransform tx = getDrawTransform();
final Point2D pMin = new Point2D.Double(bboxAllCurves.getMinX(), bboxAllCurves.getMinY());
final Point2D pMax = new Point2D.Double(bboxAllCurves.getMaxX(), bboxAllCurves.getMaxY());
final Point2D tMin = tx.transform(pMin, null);
final Point2D tMax = tx.transform(pMax, null);
int xMin = (int) tMin.getX();
int xMax = (int) tMax.getX();
if (xMin > xMax) {
int t = xMin;
xMin = xMax;
xMax = t;
}
int yMin = (int) tMin.getY();
int yMax = (int) tMax.getY();
if (yMin > yMax) {
int t = yMin;
yMin = yMax;
yMax = t;
}
// add pixel margin (AA):
xMin -= 3;
xMax += 4;
yMin -= 3;
yMax += 4;
if (xMin < 0 || xMax > image.getWidth()
|| yMin < 0 || yMax > image.getHeight()) {
return true;
}
// out.println("Checking rectangle: " + tMin + " to " + tMax);
// out.println("X min: " + xMin + " - max: " + xMax);
// out.println("Y min: " + yMin + " - max: " + yMax);
final Raster raster = image.getData();
final int expected = Color.WHITE.getRGB();
int nBadPixels = 0;
// horizontal lines:
for (int x = xMin; x <= xMax; x++) {
if (!checkPixel(raster, x, yMin, expected)) {
nBadPixels++;
}
if (!checkPixel(raster, x, yMax, expected)) {
nBadPixels++;
}
}
// vertical lines:
for (int y = yMin; y <= yMax; y++) {
if (!checkPixel(raster, xMin, y, expected)) {
nBadPixels++;
}
if (!checkPixel(raster, xMax, y, expected)) {
nBadPixels++;
}
}
if (nBadPixels != 0) {
out.println("(" + canvasWidth + " x " + canvasHeight + ") BAD pixels = " + nBadPixels);
if (SAVE_IMAGE) {
try {
final File file = new File("Bug8341381-" + canvasWidth + "-" + canvasHeight + ".png");
out.println("Writing file: " + file.getAbsolutePath());
ImageIO.write(image.getSubimage(0, 0, canvasWidth, canvasHeight), "PNG", file);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
return false;
} else if (TRACE_ALL) {
out.println("(" + canvasWidth + " x " + canvasHeight + ") OK");
}
return true;
}
private final static int[] TMP_RGB = new int[1];
private static boolean checkPixel(final Raster raster,
final int x, final int y,
final int expected) {
final int[] rgb = (int[]) raster.getDataElements(x, y, TMP_RGB);
if (rgb[0] != expected) {
if (TRACE_CHECK_PIXELS) {
out.println("bad pixel at (" + x + ", " + y + ") = " + rgb[0]
+ " expected = " + expected);
}
return false;
}
return true;
}
}
}