8337237: Use FFM instead of Unsafe for Java 2D RenderBuffer class
Reviewed-by: jvernee, jdv
This commit is contained in:
parent
6a39014795
commit
c0384b6f35
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,58 +25,54 @@
|
|||||||
|
|
||||||
package sun.java2d.pipe;
|
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
|
* 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
|
* NIO classes. However, the methods in this class perform little to no
|
||||||
* alignment or bounds checks for performance reasons. Therefore, it is
|
* alignment or bounds checks for performance reasons. Therefore, it is
|
||||||
* the caller's responsibility to ensure that all put() calls are properly
|
* the caller's responsibility to ensure that all put() calls are properly
|
||||||
* aligned and within bounds:
|
* aligned and within bounds:
|
||||||
* - int and float values must be aligned on 4-byte boundaries
|
* - int and float values must be aligned on 4-byte boundaries
|
||||||
* - long and double values must be aligned on 8-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
|
* This class only includes the bare minimum of methods to support
|
||||||
* single-threaded rendering. For example, there is no put(double[]) method
|
* 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.
|
* 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).
|
* These constants represent the size of various data types (in bytes).
|
||||||
*/
|
*/
|
||||||
protected static final long SIZEOF_BYTE = 1L;
|
private static final int SIZEOF_BYTE = Byte.BYTES;
|
||||||
protected static final long SIZEOF_SHORT = 2L;
|
private static final int SIZEOF_SHORT = Short.BYTES;
|
||||||
protected static final long SIZEOF_INT = 4L;
|
private static final int SIZEOF_INT = Integer.BYTES;
|
||||||
protected static final long SIZEOF_FLOAT = 4L;
|
private static final int SIZEOF_FLOAT = Float.BYTES;
|
||||||
protected static final long SIZEOF_LONG = 8L;
|
private static final int SIZEOF_LONG = Long.BYTES;
|
||||||
protected static final long SIZEOF_DOUBLE = 8L;
|
private static final int SIZEOF_DOUBLE = Double.BYTES;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the number of elements at which we have empirically
|
* Measurements show that using the copy API from a segment backed by a heap
|
||||||
* determined that the average cost of a JNI call exceeds the expense
|
* array gets reliably faster than individual puts around a length of 10.
|
||||||
* of an element by element copy. In other words, if the number of
|
* However the time is miniscule in the context of what it is used for
|
||||||
* elements in an array to be copied exceeds this value, then we should
|
* and much more than adequate, so no problem expected if this changes over time.
|
||||||
* 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.)
|
|
||||||
*/
|
*/
|
||||||
private static final int COPY_FROM_ARRAY_THRESHOLD = 6;
|
private static final int COPY_FROM_ARRAY_THRESHOLD = 10;
|
||||||
|
|
||||||
protected final Unsafe unsafe;
|
private final MemorySegment segment;
|
||||||
protected final long baseAddress;
|
private int curOffset;
|
||||||
protected final long endAddress;
|
|
||||||
protected long curAddress;
|
|
||||||
protected final int capacity;
|
|
||||||
|
|
||||||
protected RenderBuffer(int numBytes) {
|
private RenderBuffer(int numBytes) {
|
||||||
unsafe = Unsafe.getUnsafe();
|
segment = Arena.global().allocate(numBytes, SIZEOF_DOUBLE);
|
||||||
curAddress = baseAddress = unsafe.allocateMemory(numBytes);
|
curOffset = 0;
|
||||||
endAddress = baseAddress + numBytes;
|
|
||||||
capacity = numBytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,7 +86,7 @@ public class RenderBuffer {
|
|||||||
* Returns the base address of the underlying memory buffer.
|
* Returns the base address of the underlying memory buffer.
|
||||||
*/
|
*/
|
||||||
public final long getAddress() {
|
public final long getAddress() {
|
||||||
return baseAddress;
|
return segment.address();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -99,27 +95,27 @@ public class RenderBuffer {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public final int capacity() {
|
public final int capacity() {
|
||||||
return capacity;
|
return (int)segment.byteSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int remaining() {
|
public final int remaining() {
|
||||||
return (int)(endAddress - curAddress);
|
return (capacity() - curOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int position() {
|
public final int position() {
|
||||||
return (int)(curAddress - baseAddress);
|
return curOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void position(long numBytes) {
|
public final void position(int bytePos) {
|
||||||
curAddress = baseAddress + numBytes;
|
curOffset = bytePos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void clear() {
|
public final void clear() {
|
||||||
curAddress = baseAddress;
|
curOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final RenderBuffer skip(long numBytes) {
|
public final RenderBuffer skip(int numBytes) {
|
||||||
curAddress += numBytes;
|
curOffset += numBytes;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,8 +124,8 @@ public class RenderBuffer {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public final RenderBuffer putByte(byte x) {
|
public final RenderBuffer putByte(byte x) {
|
||||||
unsafe.putByte(curAddress, x);
|
segment.set(JAVA_BYTE, curOffset, x);
|
||||||
curAddress += SIZEOF_BYTE;
|
curOffset += SIZEOF_BYTE;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,10 +135,8 @@ public class RenderBuffer {
|
|||||||
|
|
||||||
public RenderBuffer put(byte[] x, int offset, int length) {
|
public RenderBuffer put(byte[] x, int offset, int length) {
|
||||||
if (length > COPY_FROM_ARRAY_THRESHOLD) {
|
if (length > COPY_FROM_ARRAY_THRESHOLD) {
|
||||||
long offsetInBytes = offset * SIZEOF_BYTE + Unsafe.ARRAY_BYTE_BASE_OFFSET;
|
MemorySegment.copy(x, offset, segment, JAVA_BYTE, curOffset, length);
|
||||||
long lengthInBytes = length * SIZEOF_BYTE;
|
position(position() + length * SIZEOF_BYTE);
|
||||||
unsafe.copyMemory(x, offsetInBytes, null, curAddress, lengthInBytes);
|
|
||||||
position(position() + lengthInBytes);
|
|
||||||
} else {
|
} else {
|
||||||
int end = offset + length;
|
int end = offset + length;
|
||||||
for (int i = offset; i < end; i++) {
|
for (int i = offset; i < end; i++) {
|
||||||
@ -158,8 +152,8 @@ public class RenderBuffer {
|
|||||||
|
|
||||||
public final RenderBuffer putShort(short x) {
|
public final RenderBuffer putShort(short x) {
|
||||||
// assert (position() % SIZEOF_SHORT == 0);
|
// assert (position() % SIZEOF_SHORT == 0);
|
||||||
unsafe.putShort(curAddress, x);
|
segment.set(JAVA_SHORT, curOffset, x);
|
||||||
curAddress += SIZEOF_SHORT;
|
curOffset += SIZEOF_SHORT;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,10 +164,8 @@ public class RenderBuffer {
|
|||||||
public RenderBuffer put(short[] x, int offset, int length) {
|
public RenderBuffer put(short[] x, int offset, int length) {
|
||||||
// assert (position() % SIZEOF_SHORT == 0);
|
// assert (position() % SIZEOF_SHORT == 0);
|
||||||
if (length > COPY_FROM_ARRAY_THRESHOLD) {
|
if (length > COPY_FROM_ARRAY_THRESHOLD) {
|
||||||
long offsetInBytes = offset * SIZEOF_SHORT + Unsafe.ARRAY_SHORT_BASE_OFFSET;
|
MemorySegment.copy(x, offset, segment, JAVA_SHORT, curOffset, length);
|
||||||
long lengthInBytes = length * SIZEOF_SHORT;
|
position(position() + length * SIZEOF_SHORT);
|
||||||
unsafe.copyMemory(x, offsetInBytes, null, curAddress, lengthInBytes);
|
|
||||||
position(position() + lengthInBytes);
|
|
||||||
} else {
|
} else {
|
||||||
int end = offset + length;
|
int end = offset + length;
|
||||||
for (int i = offset; i < end; i++) {
|
for (int i = offset; i < end; i++) {
|
||||||
@ -188,15 +180,15 @@ public class RenderBuffer {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public final RenderBuffer putInt(int pos, int x) {
|
public final RenderBuffer putInt(int pos, int x) {
|
||||||
// assert (baseAddress + pos % SIZEOF_INT == 0);
|
// assert (getAddress() + pos % SIZEOF_INT == 0);
|
||||||
unsafe.putInt(baseAddress + pos, x);
|
segment.set(JAVA_INT, pos, x);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final RenderBuffer putInt(int x) {
|
public final RenderBuffer putInt(int x) {
|
||||||
// assert (position() % SIZEOF_INT == 0);
|
// assert (position() % SIZEOF_INT == 0);
|
||||||
unsafe.putInt(curAddress, x);
|
segment.set(JAVA_INT, curOffset, x);
|
||||||
curAddress += SIZEOF_INT;
|
curOffset += SIZEOF_INT;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,10 +199,8 @@ public class RenderBuffer {
|
|||||||
public RenderBuffer put(int[] x, int offset, int length) {
|
public RenderBuffer put(int[] x, int offset, int length) {
|
||||||
// assert (position() % SIZEOF_INT == 0);
|
// assert (position() % SIZEOF_INT == 0);
|
||||||
if (length > COPY_FROM_ARRAY_THRESHOLD) {
|
if (length > COPY_FROM_ARRAY_THRESHOLD) {
|
||||||
long offsetInBytes = offset * SIZEOF_INT + Unsafe.ARRAY_INT_BASE_OFFSET;
|
MemorySegment.copy(x, offset, segment, JAVA_INT, curOffset, length);
|
||||||
long lengthInBytes = length * SIZEOF_INT;
|
position(position() + length * SIZEOF_INT);
|
||||||
unsafe.copyMemory(x, offsetInBytes, null, curAddress, lengthInBytes);
|
|
||||||
position(position() + lengthInBytes);
|
|
||||||
} else {
|
} else {
|
||||||
int end = offset + length;
|
int end = offset + length;
|
||||||
for (int i = offset; i < end; i++) {
|
for (int i = offset; i < end; i++) {
|
||||||
@ -226,8 +216,8 @@ public class RenderBuffer {
|
|||||||
|
|
||||||
public final RenderBuffer putFloat(float x) {
|
public final RenderBuffer putFloat(float x) {
|
||||||
// assert (position() % SIZEOF_FLOAT == 0);
|
// assert (position() % SIZEOF_FLOAT == 0);
|
||||||
unsafe.putFloat(curAddress, x);
|
segment.set(JAVA_FLOAT, curOffset, x);
|
||||||
curAddress += SIZEOF_FLOAT;
|
curOffset += SIZEOF_FLOAT;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,10 +228,8 @@ public class RenderBuffer {
|
|||||||
public RenderBuffer put(float[] x, int offset, int length) {
|
public RenderBuffer put(float[] x, int offset, int length) {
|
||||||
// assert (position() % SIZEOF_FLOAT == 0);
|
// assert (position() % SIZEOF_FLOAT == 0);
|
||||||
if (length > COPY_FROM_ARRAY_THRESHOLD) {
|
if (length > COPY_FROM_ARRAY_THRESHOLD) {
|
||||||
long offsetInBytes = offset * SIZEOF_FLOAT + Unsafe.ARRAY_FLOAT_BASE_OFFSET;
|
MemorySegment.copy(x, offset, segment, JAVA_FLOAT, curOffset, length);
|
||||||
long lengthInBytes = length * SIZEOF_FLOAT;
|
position(position() + length * SIZEOF_FLOAT);
|
||||||
unsafe.copyMemory(x, offsetInBytes, null, curAddress, lengthInBytes);
|
|
||||||
position(position() + lengthInBytes);
|
|
||||||
} else {
|
} else {
|
||||||
int end = offset + length;
|
int end = offset + length;
|
||||||
for (int i = offset; i < end; i++) {
|
for (int i = offset; i < end; i++) {
|
||||||
@ -257,8 +245,8 @@ public class RenderBuffer {
|
|||||||
|
|
||||||
public final RenderBuffer putLong(long x) {
|
public final RenderBuffer putLong(long x) {
|
||||||
// assert (position() % SIZEOF_LONG == 0);
|
// assert (position() % SIZEOF_LONG == 0);
|
||||||
unsafe.putLong(curAddress, x);
|
segment.set(JAVA_LONG, curOffset, x);
|
||||||
curAddress += SIZEOF_LONG;
|
curOffset += SIZEOF_LONG;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,10 +257,8 @@ public class RenderBuffer {
|
|||||||
public RenderBuffer put(long[] x, int offset, int length) {
|
public RenderBuffer put(long[] x, int offset, int length) {
|
||||||
// assert (position() % SIZEOF_LONG == 0);
|
// assert (position() % SIZEOF_LONG == 0);
|
||||||
if (length > COPY_FROM_ARRAY_THRESHOLD) {
|
if (length > COPY_FROM_ARRAY_THRESHOLD) {
|
||||||
long offsetInBytes = offset * SIZEOF_LONG + Unsafe.ARRAY_LONG_BASE_OFFSET;
|
MemorySegment.copy(x, offset, segment, JAVA_LONG, curOffset, length);
|
||||||
long lengthInBytes = length * SIZEOF_LONG;
|
position(position() + length * SIZEOF_LONG);
|
||||||
unsafe.copyMemory(x, offsetInBytes, null, curAddress, lengthInBytes);
|
|
||||||
position(position() + lengthInBytes);
|
|
||||||
} else {
|
} else {
|
||||||
int end = offset + length;
|
int end = offset + length;
|
||||||
for (int i = offset; i < end; i++) {
|
for (int i = offset; i < end; i++) {
|
||||||
@ -288,8 +274,8 @@ public class RenderBuffer {
|
|||||||
|
|
||||||
public final RenderBuffer putDouble(double x) {
|
public final RenderBuffer putDouble(double x) {
|
||||||
// assert (position() % SIZEOF_DOUBLE == 0);
|
// assert (position() % SIZEOF_DOUBLE == 0);
|
||||||
unsafe.putDouble(curAddress, x);
|
segment.set(JAVA_DOUBLE, curOffset, x);
|
||||||
curAddress += SIZEOF_DOUBLE;
|
curOffset += SIZEOF_DOUBLE;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user