Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 60 additions & 74 deletions src/java.desktop/share/classes/sun/java2d/pipe/RenderBuffer.java
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;

protected final Unsafe unsafe;
protected final long baseAddress;
protected final long endAddress;
protected long curAddress;
protected final int capacity;

protected RenderBuffer(int numBytes) {
unsafe = Unsafe.getUnsafe();
curAddress = baseAddress = unsafe.allocateMemory(numBytes);
endAddress = baseAddress + numBytes;
capacity = numBytes;
private static final int COPY_FROM_ARRAY_THRESHOLD = 10;

private final MemorySegment segment;
private int curOffset;

private RenderBuffer(int numBytes) {
segment = Arena.global().allocate(numBytes, SIZEOF_DOUBLE);
curOffset = 0;
}

/**
Expand All @@ -90,7 +86,7 @@ public static RenderBuffer allocate(int numBytes) {
* Returns the base address of the underlying memory buffer.
*/
public final long getAddress() {
return baseAddress;
return segment.address();
}

/**
Expand All @@ -99,27 +95,27 @@ public final long getAddress() {
*/

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

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

public final RenderBuffer putByte(byte x) {
unsafe.putByte(curAddress, x);
curAddress += SIZEOF_BYTE;
segment.set(JAVA_BYTE, curOffset, x);
curOffset += SIZEOF_BYTE;
return this;
}

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

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++) {
Expand All @@ -158,8 +152,8 @@ public RenderBuffer put(byte[] x, int offset, int length) {

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

Expand All @@ -170,10 +164,8 @@ public RenderBuffer put(short[] x) {
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++) {
Expand All @@ -188,15 +180,15 @@ public RenderBuffer put(short[] x, int offset, int length) {
*/

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

Expand All @@ -207,10 +199,8 @@ public RenderBuffer put(int[] x) {
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++) {
Expand All @@ -226,8 +216,8 @@ public RenderBuffer put(int[] x, int offset, int length) {

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

Expand All @@ -238,10 +228,8 @@ public RenderBuffer put(float[] x) {
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++) {
Expand All @@ -257,8 +245,8 @@ public RenderBuffer put(float[] x, int offset, int length) {

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

Expand All @@ -269,10 +257,8 @@ public RenderBuffer put(long[] x) {
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++) {
Expand All @@ -288,8 +274,8 @@ public RenderBuffer put(long[] x, int offset, int length) {

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