From c0384b6f3584501fb3bd93854734eeacf6620a7e Mon Sep 17 00:00:00 2001 From: Phil Race Date: Wed, 14 Aug 2024 17:58:24 +0000 Subject: [PATCH] 8337237: Use FFM instead of Unsafe for Java 2D RenderBuffer class Reviewed-by: jvernee, jdv --- .../classes/sun/java2d/pipe/RenderBuffer.java | 130 ++++++++---------- 1 file changed, 58 insertions(+), 72 deletions(-) diff --git a/src/java.desktop/share/classes/sun/java2d/pipe/RenderBuffer.java b/src/java.desktop/share/classes/sun/java2d/pipe/RenderBuffer.java index 57f24a4eada..2b57d98ef40 100644 --- a/src/java.desktop/share/classes/sun/java2d/pipe/RenderBuffer.java +++ b/src/java.desktop/share/classes/sun/java2d/pipe/RenderBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, 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 @@ -25,58 +25,54 @@ package sun.java2d.pipe; -import jdk.internal.misc.Unsafe; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import static java.lang.foreign.ValueLayout.*; /** - * The RenderBuffer class is a simplified, high-performance, Unsafe wrapper + * The RenderBuffer class is a simplified, high-performance class * used for buffering rendering operations in a single-threaded rendering - * environment. It's functionality is similar to the ByteBuffer and related + * environment. Its functionality is similar to the ByteBuffer and related * NIO classes. However, the methods in this class perform little to no * alignment or bounds checks for performance reasons. Therefore, it is * the caller's responsibility to ensure that all put() calls are properly * aligned and within bounds: * - int and float values must be aligned on 4-byte boundaries * - long and double values must be aligned on 8-byte boundaries + * Failure to do so will result in exceptions from the FFM API, or worse. * * This class only includes the bare minimum of methods to support * single-threaded rendering. For example, there is no put(double[]) method * because we currently have no need for such a method in the STR classes. */ -public class RenderBuffer { +public final class RenderBuffer { /** * These constants represent the size of various data types (in bytes). */ - protected static final long SIZEOF_BYTE = 1L; - protected static final long SIZEOF_SHORT = 2L; - protected static final long SIZEOF_INT = 4L; - protected static final long SIZEOF_FLOAT = 4L; - protected static final long SIZEOF_LONG = 8L; - protected static final long SIZEOF_DOUBLE = 8L; + private static final int SIZEOF_BYTE = Byte.BYTES; + private static final int SIZEOF_SHORT = Short.BYTES; + private static final int SIZEOF_INT = Integer.BYTES; + private static final int SIZEOF_FLOAT = Float.BYTES; + private static final int SIZEOF_LONG = Long.BYTES; + private static final int SIZEOF_DOUBLE = Double.BYTES; /** - * Represents the number of elements at which we have empirically - * determined that the average cost of a JNI call exceeds the expense - * of an element by element copy. In other words, if the number of - * elements in an array to be copied exceeds this value, then we should - * use the copyFromArray() method to complete the bulk put operation. - * (This value can be adjusted if the cost of JNI downcalls is reduced - * in a future release.) + * Measurements show that using the copy API from a segment backed by a heap + * array gets reliably faster than individual puts around a length of 10. + * However the time is miniscule in the context of what it is used for + * and much more than adequate, so no problem expected if this changes over time. */ - private static final int COPY_FROM_ARRAY_THRESHOLD = 6; + private static final int COPY_FROM_ARRAY_THRESHOLD = 10; - protected final Unsafe unsafe; - protected final long baseAddress; - protected final long endAddress; - protected long curAddress; - protected final int capacity; + private final MemorySegment segment; + private int curOffset; - protected RenderBuffer(int numBytes) { - unsafe = Unsafe.getUnsafe(); - curAddress = baseAddress = unsafe.allocateMemory(numBytes); - endAddress = baseAddress + numBytes; - capacity = numBytes; + private RenderBuffer(int numBytes) { + segment = Arena.global().allocate(numBytes, SIZEOF_DOUBLE); + curOffset = 0; } /** @@ -90,7 +86,7 @@ public class RenderBuffer { * Returns the base address of the underlying memory buffer. */ public final long getAddress() { - return baseAddress; + return segment.address(); } /** @@ -99,27 +95,27 @@ public class RenderBuffer { */ public final int capacity() { - return capacity; + return (int)segment.byteSize(); } public final int remaining() { - return (int)(endAddress - curAddress); + return (capacity() - curOffset); } public final int position() { - return (int)(curAddress - baseAddress); + return curOffset; } - public final void position(long numBytes) { - curAddress = baseAddress + numBytes; + public final void position(int bytePos) { + curOffset = bytePos; } public final void clear() { - curAddress = baseAddress; + curOffset = 0; } - public final RenderBuffer skip(long numBytes) { - curAddress += numBytes; + public final RenderBuffer skip(int numBytes) { + curOffset += numBytes; return this; } @@ -128,8 +124,8 @@ public class RenderBuffer { */ public final RenderBuffer putByte(byte x) { - unsafe.putByte(curAddress, x); - curAddress += SIZEOF_BYTE; + segment.set(JAVA_BYTE, curOffset, x); + curOffset += SIZEOF_BYTE; return this; } @@ -139,10 +135,8 @@ public class RenderBuffer { public RenderBuffer put(byte[] x, int offset, int length) { if (length > COPY_FROM_ARRAY_THRESHOLD) { - long offsetInBytes = offset * SIZEOF_BYTE + Unsafe.ARRAY_BYTE_BASE_OFFSET; - long lengthInBytes = length * SIZEOF_BYTE; - unsafe.copyMemory(x, offsetInBytes, null, curAddress, lengthInBytes); - position(position() + lengthInBytes); + MemorySegment.copy(x, offset, segment, JAVA_BYTE, curOffset, length); + position(position() + length * SIZEOF_BYTE); } else { int end = offset + length; for (int i = offset; i < end; i++) { @@ -158,8 +152,8 @@ public class RenderBuffer { public final RenderBuffer putShort(short x) { // assert (position() % SIZEOF_SHORT == 0); - unsafe.putShort(curAddress, x); - curAddress += SIZEOF_SHORT; + segment.set(JAVA_SHORT, curOffset, x); + curOffset += SIZEOF_SHORT; return this; } @@ -170,10 +164,8 @@ public class RenderBuffer { public RenderBuffer put(short[] x, int offset, int length) { // assert (position() % SIZEOF_SHORT == 0); if (length > COPY_FROM_ARRAY_THRESHOLD) { - long offsetInBytes = offset * SIZEOF_SHORT + Unsafe.ARRAY_SHORT_BASE_OFFSET; - long lengthInBytes = length * SIZEOF_SHORT; - unsafe.copyMemory(x, offsetInBytes, null, curAddress, lengthInBytes); - position(position() + lengthInBytes); + MemorySegment.copy(x, offset, segment, JAVA_SHORT, curOffset, length); + position(position() + length * SIZEOF_SHORT); } else { int end = offset + length; for (int i = offset; i < end; i++) { @@ -188,15 +180,15 @@ public class RenderBuffer { */ public final RenderBuffer putInt(int pos, int x) { - // assert (baseAddress + pos % SIZEOF_INT == 0); - unsafe.putInt(baseAddress + pos, x); + // assert (getAddress() + pos % SIZEOF_INT == 0); + segment.set(JAVA_INT, pos, x); return this; } public final RenderBuffer putInt(int x) { // assert (position() % SIZEOF_INT == 0); - unsafe.putInt(curAddress, x); - curAddress += SIZEOF_INT; + segment.set(JAVA_INT, curOffset, x); + curOffset += SIZEOF_INT; return this; } @@ -207,10 +199,8 @@ public class RenderBuffer { public RenderBuffer put(int[] x, int offset, int length) { // assert (position() % SIZEOF_INT == 0); if (length > COPY_FROM_ARRAY_THRESHOLD) { - long offsetInBytes = offset * SIZEOF_INT + Unsafe.ARRAY_INT_BASE_OFFSET; - long lengthInBytes = length * SIZEOF_INT; - unsafe.copyMemory(x, offsetInBytes, null, curAddress, lengthInBytes); - position(position() + lengthInBytes); + MemorySegment.copy(x, offset, segment, JAVA_INT, curOffset, length); + position(position() + length * SIZEOF_INT); } else { int end = offset + length; for (int i = offset; i < end; i++) { @@ -226,8 +216,8 @@ public class RenderBuffer { public final RenderBuffer putFloat(float x) { // assert (position() % SIZEOF_FLOAT == 0); - unsafe.putFloat(curAddress, x); - curAddress += SIZEOF_FLOAT; + segment.set(JAVA_FLOAT, curOffset, x); + curOffset += SIZEOF_FLOAT; return this; } @@ -238,10 +228,8 @@ public class RenderBuffer { public RenderBuffer put(float[] x, int offset, int length) { // assert (position() % SIZEOF_FLOAT == 0); if (length > COPY_FROM_ARRAY_THRESHOLD) { - long offsetInBytes = offset * SIZEOF_FLOAT + Unsafe.ARRAY_FLOAT_BASE_OFFSET; - long lengthInBytes = length * SIZEOF_FLOAT; - unsafe.copyMemory(x, offsetInBytes, null, curAddress, lengthInBytes); - position(position() + lengthInBytes); + MemorySegment.copy(x, offset, segment, JAVA_FLOAT, curOffset, length); + position(position() + length * SIZEOF_FLOAT); } else { int end = offset + length; for (int i = offset; i < end; i++) { @@ -257,8 +245,8 @@ public class RenderBuffer { public final RenderBuffer putLong(long x) { // assert (position() % SIZEOF_LONG == 0); - unsafe.putLong(curAddress, x); - curAddress += SIZEOF_LONG; + segment.set(JAVA_LONG, curOffset, x); + curOffset += SIZEOF_LONG; return this; } @@ -269,10 +257,8 @@ public class RenderBuffer { public RenderBuffer put(long[] x, int offset, int length) { // assert (position() % SIZEOF_LONG == 0); if (length > COPY_FROM_ARRAY_THRESHOLD) { - long offsetInBytes = offset * SIZEOF_LONG + Unsafe.ARRAY_LONG_BASE_OFFSET; - long lengthInBytes = length * SIZEOF_LONG; - unsafe.copyMemory(x, offsetInBytes, null, curAddress, lengthInBytes); - position(position() + lengthInBytes); + MemorySegment.copy(x, offset, segment, JAVA_LONG, curOffset, length); + position(position() + length * SIZEOF_LONG); } else { int end = offset + length; for (int i = offset; i < end; i++) { @@ -288,8 +274,8 @@ public class RenderBuffer { public final RenderBuffer putDouble(double x) { // assert (position() % SIZEOF_DOUBLE == 0); - unsafe.putDouble(curAddress, x); - curAddress += SIZEOF_DOUBLE; + segment.set(JAVA_DOUBLE, curOffset, x); + curOffset += SIZEOF_DOUBLE; return this; } }