Skip to content

Commit a37986b

Browse files
prracepull[bot]
authored andcommitted
8337237: Use FFM instead of Unsafe for Java 2D RenderBuffer class
Reviewed-by: jvernee, jdv
1 parent 34f7860 commit a37986b

File tree

1 file changed

+60
-74
lines changed

1 file changed

+60
-74
lines changed

src/java.desktop/share/classes/sun/java2d/pipe/RenderBuffer.java

Lines changed: 60 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,58 +25,54 @@
2525

2626
package sun.java2d.pipe;
2727

28-
import jdk.internal.misc.Unsafe;
28+
import java.lang.foreign.Arena;
29+
import java.lang.foreign.MemorySegment;
30+
import java.lang.foreign.ValueLayout;
31+
import static java.lang.foreign.ValueLayout.*;
2932

3033

3134
/**
32-
* The RenderBuffer class is a simplified, high-performance, Unsafe wrapper
35+
* The RenderBuffer class is a simplified, high-performance class
3336
* used for buffering rendering operations in a single-threaded rendering
34-
* environment. It's functionality is similar to the ByteBuffer and related
37+
* environment. Its functionality is similar to the ByteBuffer and related
3538
* NIO classes. However, the methods in this class perform little to no
3639
* alignment or bounds checks for performance reasons. Therefore, it is
3740
* the caller's responsibility to ensure that all put() calls are properly
3841
* aligned and within bounds:
3942
* - int and float values must be aligned on 4-byte boundaries
4043
* - long and double values must be aligned on 8-byte boundaries
44+
* Failure to do so will result in exceptions from the FFM API, or worse.
4145
*
4246
* This class only includes the bare minimum of methods to support
4347
* single-threaded rendering. For example, there is no put(double[]) method
4448
* because we currently have no need for such a method in the STR classes.
4549
*/
46-
public class RenderBuffer {
50+
public final class RenderBuffer {
4751

4852
/**
4953
* These constants represent the size of various data types (in bytes).
5054
*/
51-
protected static final long SIZEOF_BYTE = 1L;
52-
protected static final long SIZEOF_SHORT = 2L;
53-
protected static final long SIZEOF_INT = 4L;
54-
protected static final long SIZEOF_FLOAT = 4L;
55-
protected static final long SIZEOF_LONG = 8L;
56-
protected static final long SIZEOF_DOUBLE = 8L;
55+
private static final int SIZEOF_BYTE = Byte.BYTES;
56+
private static final int SIZEOF_SHORT = Short.BYTES;
57+
private static final int SIZEOF_INT = Integer.BYTES;
58+
private static final int SIZEOF_FLOAT = Float.BYTES;
59+
private static final int SIZEOF_LONG = Long.BYTES;
60+
private static final int SIZEOF_DOUBLE = Double.BYTES;
5761

5862
/**
59-
* Represents the number of elements at which we have empirically
60-
* determined that the average cost of a JNI call exceeds the expense
61-
* of an element by element copy. In other words, if the number of
62-
* elements in an array to be copied exceeds this value, then we should
63-
* use the copyFromArray() method to complete the bulk put operation.
64-
* (This value can be adjusted if the cost of JNI downcalls is reduced
65-
* in a future release.)
63+
* Measurements show that using the copy API from a segment backed by a heap
64+
* array gets reliably faster than individual puts around a length of 10.
65+
* However the time is miniscule in the context of what it is used for
66+
* and much more than adequate, so no problem expected if this changes over time.
6667
*/
67-
private static final int COPY_FROM_ARRAY_THRESHOLD = 6;
68-
69-
protected final Unsafe unsafe;
70-
protected final long baseAddress;
71-
protected final long endAddress;
72-
protected long curAddress;
73-
protected final int capacity;
74-
75-
protected RenderBuffer(int numBytes) {
76-
unsafe = Unsafe.getUnsafe();
77-
curAddress = baseAddress = unsafe.allocateMemory(numBytes);
78-
endAddress = baseAddress + numBytes;
79-
capacity = numBytes;
68+
private static final int COPY_FROM_ARRAY_THRESHOLD = 10;
69+
70+
private final MemorySegment segment;
71+
private int curOffset;
72+
73+
private RenderBuffer(int numBytes) {
74+
segment = Arena.global().allocate(numBytes, SIZEOF_DOUBLE);
75+
curOffset = 0;
8076
}
8177

8278
/**
@@ -90,7 +86,7 @@ public static RenderBuffer allocate(int numBytes) {
9086
* Returns the base address of the underlying memory buffer.
9187
*/
9288
public final long getAddress() {
93-
return baseAddress;
89+
return segment.address();
9490
}
9591

9692
/**
@@ -99,27 +95,27 @@ public final long getAddress() {
9995
*/
10096

10197
public final int capacity() {
102-
return capacity;
98+
return (int)segment.byteSize();
10399
}
104100

105101
public final int remaining() {
106-
return (int)(endAddress - curAddress);
102+
return (capacity() - curOffset);
107103
}
108104

109105
public final int position() {
110-
return (int)(curAddress - baseAddress);
106+
return curOffset;
111107
}
112108

113-
public final void position(long numBytes) {
114-
curAddress = baseAddress + numBytes;
109+
public final void position(int bytePos) {
110+
curOffset = bytePos;
115111
}
116112

117113
public final void clear() {
118-
curAddress = baseAddress;
114+
curOffset = 0;
119115
}
120116

121-
public final RenderBuffer skip(long numBytes) {
122-
curAddress += numBytes;
117+
public final RenderBuffer skip(int numBytes) {
118+
curOffset += numBytes;
123119
return this;
124120
}
125121

@@ -128,8 +124,8 @@ public final RenderBuffer skip(long numBytes) {
128124
*/
129125

130126
public final RenderBuffer putByte(byte x) {
131-
unsafe.putByte(curAddress, x);
132-
curAddress += SIZEOF_BYTE;
127+
segment.set(JAVA_BYTE, curOffset, x);
128+
curOffset += SIZEOF_BYTE;
133129
return this;
134130
}
135131

@@ -139,10 +135,8 @@ public RenderBuffer put(byte[] x) {
139135

140136
public RenderBuffer put(byte[] x, int offset, int length) {
141137
if (length > COPY_FROM_ARRAY_THRESHOLD) {
142-
long offsetInBytes = offset * SIZEOF_BYTE + Unsafe.ARRAY_BYTE_BASE_OFFSET;
143-
long lengthInBytes = length * SIZEOF_BYTE;
144-
unsafe.copyMemory(x, offsetInBytes, null, curAddress, lengthInBytes);
145-
position(position() + lengthInBytes);
138+
MemorySegment.copy(x, offset, segment, JAVA_BYTE, curOffset, length);
139+
position(position() + length * SIZEOF_BYTE);
146140
} else {
147141
int end = offset + length;
148142
for (int i = offset; i < end; i++) {
@@ -158,8 +152,8 @@ public RenderBuffer put(byte[] x, int offset, int length) {
158152

159153
public final RenderBuffer putShort(short x) {
160154
// assert (position() % SIZEOF_SHORT == 0);
161-
unsafe.putShort(curAddress, x);
162-
curAddress += SIZEOF_SHORT;
155+
segment.set(JAVA_SHORT, curOffset, x);
156+
curOffset += SIZEOF_SHORT;
163157
return this;
164158
}
165159

@@ -170,10 +164,8 @@ public RenderBuffer put(short[] x) {
170164
public RenderBuffer put(short[] x, int offset, int length) {
171165
// assert (position() % SIZEOF_SHORT == 0);
172166
if (length > COPY_FROM_ARRAY_THRESHOLD) {
173-
long offsetInBytes = offset * SIZEOF_SHORT + Unsafe.ARRAY_SHORT_BASE_OFFSET;
174-
long lengthInBytes = length * SIZEOF_SHORT;
175-
unsafe.copyMemory(x, offsetInBytes, null, curAddress, lengthInBytes);
176-
position(position() + lengthInBytes);
167+
MemorySegment.copy(x, offset, segment, JAVA_SHORT, curOffset, length);
168+
position(position() + length * SIZEOF_SHORT);
177169
} else {
178170
int end = offset + length;
179171
for (int i = offset; i < end; i++) {
@@ -188,15 +180,15 @@ public RenderBuffer put(short[] x, int offset, int length) {
188180
*/
189181

190182
public final RenderBuffer putInt(int pos, int x) {
191-
// assert (baseAddress + pos % SIZEOF_INT == 0);
192-
unsafe.putInt(baseAddress + pos, x);
183+
// assert (getAddress() + pos % SIZEOF_INT == 0);
184+
segment.set(JAVA_INT, pos, x);
193185
return this;
194186
}
195187

196188
public final RenderBuffer putInt(int x) {
197189
// assert (position() % SIZEOF_INT == 0);
198-
unsafe.putInt(curAddress, x);
199-
curAddress += SIZEOF_INT;
190+
segment.set(JAVA_INT, curOffset, x);
191+
curOffset += SIZEOF_INT;
200192
return this;
201193
}
202194

@@ -207,10 +199,8 @@ public RenderBuffer put(int[] x) {
207199
public RenderBuffer put(int[] x, int offset, int length) {
208200
// assert (position() % SIZEOF_INT == 0);
209201
if (length > COPY_FROM_ARRAY_THRESHOLD) {
210-
long offsetInBytes = offset * SIZEOF_INT + Unsafe.ARRAY_INT_BASE_OFFSET;
211-
long lengthInBytes = length * SIZEOF_INT;
212-
unsafe.copyMemory(x, offsetInBytes, null, curAddress, lengthInBytes);
213-
position(position() + lengthInBytes);
202+
MemorySegment.copy(x, offset, segment, JAVA_INT, curOffset, length);
203+
position(position() + length * SIZEOF_INT);
214204
} else {
215205
int end = offset + length;
216206
for (int i = offset; i < end; i++) {
@@ -226,8 +216,8 @@ public RenderBuffer put(int[] x, int offset, int length) {
226216

227217
public final RenderBuffer putFloat(float x) {
228218
// assert (position() % SIZEOF_FLOAT == 0);
229-
unsafe.putFloat(curAddress, x);
230-
curAddress += SIZEOF_FLOAT;
219+
segment.set(JAVA_FLOAT, curOffset, x);
220+
curOffset += SIZEOF_FLOAT;
231221
return this;
232222
}
233223

@@ -238,10 +228,8 @@ public RenderBuffer put(float[] x) {
238228
public RenderBuffer put(float[] x, int offset, int length) {
239229
// assert (position() % SIZEOF_FLOAT == 0);
240230
if (length > COPY_FROM_ARRAY_THRESHOLD) {
241-
long offsetInBytes = offset * SIZEOF_FLOAT + Unsafe.ARRAY_FLOAT_BASE_OFFSET;
242-
long lengthInBytes = length * SIZEOF_FLOAT;
243-
unsafe.copyMemory(x, offsetInBytes, null, curAddress, lengthInBytes);
244-
position(position() + lengthInBytes);
231+
MemorySegment.copy(x, offset, segment, JAVA_FLOAT, curOffset, length);
232+
position(position() + length * SIZEOF_FLOAT);
245233
} else {
246234
int end = offset + length;
247235
for (int i = offset; i < end; i++) {
@@ -257,8 +245,8 @@ public RenderBuffer put(float[] x, int offset, int length) {
257245

258246
public final RenderBuffer putLong(long x) {
259247
// assert (position() % SIZEOF_LONG == 0);
260-
unsafe.putLong(curAddress, x);
261-
curAddress += SIZEOF_LONG;
248+
segment.set(JAVA_LONG, curOffset, x);
249+
curOffset += SIZEOF_LONG;
262250
return this;
263251
}
264252

@@ -269,10 +257,8 @@ public RenderBuffer put(long[] x) {
269257
public RenderBuffer put(long[] x, int offset, int length) {
270258
// assert (position() % SIZEOF_LONG == 0);
271259
if (length > COPY_FROM_ARRAY_THRESHOLD) {
272-
long offsetInBytes = offset * SIZEOF_LONG + Unsafe.ARRAY_LONG_BASE_OFFSET;
273-
long lengthInBytes = length * SIZEOF_LONG;
274-
unsafe.copyMemory(x, offsetInBytes, null, curAddress, lengthInBytes);
275-
position(position() + lengthInBytes);
260+
MemorySegment.copy(x, offset, segment, JAVA_LONG, curOffset, length);
261+
position(position() + length * SIZEOF_LONG);
276262
} else {
277263
int end = offset + length;
278264
for (int i = offset; i < end; i++) {
@@ -288,8 +274,8 @@ public RenderBuffer put(long[] x, int offset, int length) {
288274

289275
public final RenderBuffer putDouble(double x) {
290276
// assert (position() % SIZEOF_DOUBLE == 0);
291-
unsafe.putDouble(curAddress, x);
292-
curAddress += SIZEOF_DOUBLE;
277+
segment.set(JAVA_DOUBLE, curOffset, x);
278+
curOffset += SIZEOF_DOUBLE;
293279
return this;
294280
}
295281
}

0 commit comments

Comments
 (0)