mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-14 13:29:40 +01:00
JBR-9283: bumpCounter() renamed to incrementCounter() + added addStat(window, name, value) used by MTLLayer.m to report blitTexture & nextDrawable timings (ms)
This commit is contained in:
@@ -33,7 +33,6 @@ import sun.lwawt.macosx.CFLayer;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Window;
|
||||
|
||||
@@ -151,11 +150,25 @@ public final class MTLLayer extends CFLayer {
|
||||
}
|
||||
}
|
||||
|
||||
private final static String[] STAT_NAMES = new String[]{
|
||||
"java2d.native.mtlLayer.drawInMTLContext", // type = 0
|
||||
"java2d.native.mtlLayer.nextDrawable" // type = 1
|
||||
};
|
||||
|
||||
private void addStat(int type, double value) {
|
||||
// Called from the native code when this layer has been presented on screen
|
||||
Component target = peer.getTarget();
|
||||
if (target instanceof Window window) {
|
||||
AWTAccessor.getWindowAccessor().addStat(window,
|
||||
((type >= 0) && (type < STAT_NAMES.length)) ? STAT_NAMES[type] : "undefined", value);
|
||||
}
|
||||
}
|
||||
|
||||
private void countNewFrame() {
|
||||
// Called from the native code when this layer has been presented on screen
|
||||
Component target = peer.getTarget();
|
||||
if (target instanceof Window window) {
|
||||
AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.frames");
|
||||
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.frames");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +178,7 @@ public final class MTLLayer extends CFLayer {
|
||||
// when those attempts are too frequent.
|
||||
Component target = peer.getTarget();
|
||||
if (target instanceof Window window) {
|
||||
AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.framesDropped");
|
||||
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.framesDropped");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
- (void) stopRedraw:(MTLContext*)mtlc displayID:(jint)displayID force:(BOOL)force;
|
||||
- (void) flushBuffer;
|
||||
- (void) commitCommandBuffer:(MTLContext*)mtlc wait:(BOOL)waitUntilCompleted display:(BOOL)updateDisplay;
|
||||
|
||||
- (void) addStatCallback:(int)type value:(double)value;
|
||||
- (void) countFramePresentedCallback;
|
||||
- (void) countFrameDroppedCallback;
|
||||
@end
|
||||
|
||||
@@ -255,18 +255,14 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
CFTimeInterval beforeDrawableTime = (TRACE_DISPLAY_INFO) ? CACurrentMediaTime() : 0.0;
|
||||
CFTimeInterval nextDrawableTime = 0.0;
|
||||
const CFTimeInterval beforeDrawableTime = CACurrentMediaTime();
|
||||
|
||||
[_lockDrawable lock];
|
||||
@try {
|
||||
if (self->_nextDrawableRef != nil) {
|
||||
mtlDrawable = self->_nextDrawableRef;
|
||||
self->_nextDrawableRef = nil;
|
||||
if (TRACE_DISPLAY_INFO) {
|
||||
nextDrawableTime = beforeDrawableTime;
|
||||
beforeDrawableTime = self->_drawableTime;
|
||||
}
|
||||
// TODO: use or validate timestamp in self->_drawableTime ?
|
||||
}
|
||||
} @finally {
|
||||
[_lockDrawable unlock];
|
||||
@@ -299,23 +295,22 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
|
||||
|
||||
// free the running flag:
|
||||
self.asyncNextDrawableRunning = false;
|
||||
|
||||
[self release];
|
||||
});
|
||||
|
||||
// TODO: adjust timeout according to the layer's display rate (see displaylink interval) using tick interval estimate
|
||||
const long asyncTimeout = TIMEOUT_MS; // multiply by 1/60 scale depending on monitor freq (displaySync=true) ?
|
||||
|
||||
dispatch_async(concurrentQueue, async_block);
|
||||
|
||||
// returns zero if the dispatch block completed within the specified timeout, or non-zero if the block timed out.
|
||||
// TODO: adjust timeout according to the layer's display rate (see displaylink interval)
|
||||
intptr_t status = dispatch_block_wait(async_block, dispatch_time(DISPATCH_TIME_NOW, TIMEOUT_MS));
|
||||
|
||||
nextDrawableTime = (TRACE_DISPLAY_INFO) ? CACurrentMediaTime() : 0.0;
|
||||
intptr_t status = dispatch_block_wait(async_block, dispatch_time(DISPATCH_TIME_NOW, asyncTimeout));
|
||||
|
||||
Block_release(async_block);
|
||||
|
||||
if (status != 0) {
|
||||
#if TRACE_DISPLAY_ON
|
||||
const CFTimeInterval nextDrawableTimeout = (nextDrawableTime - beforeDrawableTime);
|
||||
const CFTimeInterval nextDrawableTimeout = (CACurrentMediaTime() - beforeDrawableTime);
|
||||
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "[%.6lf] MTLLayer_blitTexture: nextDrawable failed (status = %d)"
|
||||
" - nextDrawableTimeout = %.3lf ms ",
|
||||
CACurrentMediaTime(), status, 1000.0 * nextDrawableTimeout);
|
||||
@@ -336,20 +331,23 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const CFTimeInterval nextDrawableTime = CACurrentMediaTime();
|
||||
const CFTimeInterval nextDrawableLatency = (nextDrawableTime - beforeDrawableTime);
|
||||
|
||||
if (nextDrawableLatency > 0.0) {
|
||||
[self addStatCallback:1 value:1000.0 * nextDrawableLatency]; // See MTLLayer.STAT_NAMES[1]
|
||||
|
||||
#if TRACE_DISPLAY_ON
|
||||
const CFTimeInterval nextDrawableLatency = (nextDrawableTime - beforeDrawableTime);
|
||||
if (nextDrawableLatency > 0.0) {
|
||||
self.avgNextDrawableTime = nextDrawableLatency * a + self.avgNextDrawableTime * (1.0 - a);
|
||||
}
|
||||
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
|
||||
"[%.6lf] MTLLayer_blitTexture: drawable(%d) acquired"
|
||||
" - nextDrawableLatency = %.3lf ms - average = %.3lf ms",
|
||||
CACurrentMediaTime(), mtlDrawable.drawableID,
|
||||
1000.0 * nextDrawableLatency, 1000.0 * self.avgNextDrawableTime
|
||||
);
|
||||
#endif
|
||||
|
||||
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
|
||||
"[%.6lf] MTLLayer_blitTexture: drawable(%d) acquired"
|
||||
" - nextDrawableLatency = %.3lf ms - average = %.3lf ms",
|
||||
CACurrentMediaTime(), mtlDrawable.drawableID,
|
||||
1000.0 * nextDrawableLatency, 1000.0 * self.avgNextDrawableTime
|
||||
);
|
||||
#endif
|
||||
}
|
||||
id<MTLCommandBuffer> commandBuf = [self.ctx createBlitCommandBuffer];
|
||||
if (commandBuf == nil) {
|
||||
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: commandBuf is null");
|
||||
@@ -503,8 +501,15 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
const CFTimeInterval beforeMethod = CACurrentMediaTime();
|
||||
|
||||
(*env)->CallVoidMethod(env, javaLayerLocalRef, jm_drawInMTLContext);
|
||||
CHECK_EXCEPTION();
|
||||
|
||||
const CFTimeInterval drawInMTLContextLatency = (CACurrentMediaTime() - beforeMethod);
|
||||
if (drawInMTLContextLatency > 0.0) {
|
||||
[self addStatCallback:0 value:1000.0 * drawInMTLContextLatency]; // See MTLLayer.STAT_NAMES[0]
|
||||
}
|
||||
(*env)->DeleteLocalRef(env, javaLayerLocalRef);
|
||||
}
|
||||
|
||||
@@ -630,6 +635,20 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
|
||||
}
|
||||
}
|
||||
|
||||
- (void) addStatCallback:(int)type value:(double)value {
|
||||
// attach the current thread to the JVM if necessary, and get an env
|
||||
JNIEnv* env = [ThreadUtilities getJNIEnvUncached];
|
||||
GET_MTL_LAYER_CLASS();
|
||||
DECLARE_METHOD(jm_addStatFrame, jc_JavaLayer, "addStat", "(ID)V");
|
||||
|
||||
jobject javaLayerLocalRef = (*env)->NewLocalRef(env, self.javaLayer);
|
||||
if (javaLayerLocalRef != NULL) {
|
||||
(*env)->CallVoidMethod(env, javaLayerLocalRef, jm_addStatFrame, (jint)type, (jdouble)value);
|
||||
CHECK_EXCEPTION();
|
||||
(*env)->DeleteLocalRef(env, javaLayerLocalRef);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) countFrameDroppedCallback {
|
||||
// attach the current thread to the JVM if necessary, and get an env
|
||||
JNIEnv* env = [ThreadUtilities getJNIEnvUncached];
|
||||
|
||||
@@ -4237,13 +4237,13 @@ public class Window extends Container implements Accessible {
|
||||
|
||||
private final static long NANO_IN_SEC = java.util.concurrent.TimeUnit.SECONDS.toNanos(1);
|
||||
|
||||
public void bumpCounter(final Window w, final String counterName) {
|
||||
public void incrementCounter(final Window w, final String counterName) {
|
||||
if (USE_COUNTERS) {
|
||||
Objects.requireNonNull(w);
|
||||
Objects.requireNonNull(counterName);
|
||||
|
||||
final long curTimeNanos = System.nanoTime();
|
||||
// use try-catch to avoid throwing runtime exception to native JNI callers !
|
||||
// use try-catch to avoid throwing runtime exception to native JNI callers!
|
||||
try {
|
||||
PerfCounter newCounter, prevCounter;
|
||||
synchronized (w.perfCounters) {
|
||||
@@ -4264,20 +4264,33 @@ public class Window extends Container implements Accessible {
|
||||
synchronized (w.perfCountersPrev) {
|
||||
w.perfCountersPrev.put(counterName, newCounter);
|
||||
}
|
||||
synchronized (w.perfStats) {
|
||||
StatDouble stat = w.perfStats.computeIfAbsent(counterName, StatDouble::new);
|
||||
stat.add(valPerSecond);
|
||||
// update global stats (no reset):
|
||||
stat = w.perfStats.computeIfAbsent(counterName + STATS_ALL_SUFFIX, StatDouble::new);
|
||||
stat.add(valPerSecond);
|
||||
}
|
||||
addStat(w, counterName, valPerSecond);
|
||||
if (TRACE_COUNTERS) {
|
||||
dumpCounter(counterName, valPerSecond);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException re) {
|
||||
perfLog.severe("bumpCounter: failed", re);
|
||||
perfLog.severe("incrementCounter: failed", re);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addStat(final Window w, final String statName, final double value) {
|
||||
if (USE_COUNTERS && Double.isFinite(value)) {
|
||||
Objects.requireNonNull(w);
|
||||
Objects.requireNonNull(statName);
|
||||
|
||||
// use try-catch to avoid throwing runtime exception to native JNI callers!
|
||||
try {
|
||||
synchronized (w.perfStats) {
|
||||
StatDouble stat = w.perfStats.computeIfAbsent(statName, StatDouble::new);
|
||||
stat.add(value);
|
||||
stat = w.perfStats.computeIfAbsent(statName + STATS_ALL_SUFFIX, StatDouble::new);
|
||||
stat.add(value);
|
||||
}
|
||||
} catch (RuntimeException re) {
|
||||
perfLog.severe("addStat: failed", re);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,7 @@ package javax.swing;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.VolatileImage;
|
||||
import java.awt.peer.WindowPeer;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@@ -42,14 +39,12 @@ import sun.java2d.SunGraphicsEnvironment;
|
||||
|
||||
import com.sun.java.swing.SwingUtilities3;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import sun.java2d.SunGraphics2D;
|
||||
import sun.java2d.pipe.Region;
|
||||
import sun.swing.SwingAccessor;
|
||||
import sun.swing.SwingUtilities2;
|
||||
import sun.swing.SwingUtilities2.RepaintListener;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* This class manages repaint requests, allowing the number
|
||||
@@ -723,7 +718,7 @@ public class RepaintManager
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.forEach(w -> AWTAccessor.getWindowAccessor()
|
||||
.bumpCounter(w, "swing.RepaintManager.updateWindows"));
|
||||
.incrementCounter(w, "swing.RepaintManager.updateWindows"));
|
||||
|
||||
if (Toolkit.getDefaultToolkit() instanceof SunToolkit sunToolkit &&
|
||||
sunToolkit.needUpdateWindow()) {
|
||||
@@ -742,14 +737,14 @@ public class RepaintManager
|
||||
|
||||
for (Window window : windows) {
|
||||
AWTAccessor.getWindowAccessor().updateWindow(window);
|
||||
AWTAccessor.getWindowAccessor().bumpCounter(window, "swing.RepaintManager.updateWindows");
|
||||
AWTAccessor.getWindowAccessor().incrementCounter(window, "swing.RepaintManager.updateWindows");
|
||||
}
|
||||
} else {
|
||||
dirtyComponents.keySet().stream()
|
||||
.map(c -> c instanceof Window w ? w : SwingUtilities.getWindowAncestor(c))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(w -> AWTAccessor.getWindowAccessor()
|
||||
.bumpCounter(w, "swing.RepaintManager.updateWindows"));
|
||||
.incrementCounter(w, "swing.RepaintManager.updateWindows"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ import java.awt.event.InputEvent;
|
||||
import java.awt.event.InvocationEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.image.BufferStrategy;
|
||||
import java.awt.peer.ComponentPeer;
|
||||
|
||||
@@ -331,7 +330,8 @@ public final class AWTAccessor {
|
||||
|
||||
/* JBR Window counters API */
|
||||
boolean countersEnabled(Window w);
|
||||
void bumpCounter(Window w, String counterName);
|
||||
void incrementCounter(Window w, String counterName);
|
||||
void addStat(Window w, String statName, double value);
|
||||
|
||||
long getCounter(Window w, String counterName);
|
||||
double getCounterPerSecond(Window w, String counterName);
|
||||
|
||||
@@ -36,7 +36,6 @@ import java.util.Objects;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.wl.WLComponentPeer;
|
||||
import sun.awt.wl.WLGraphicsConfig;
|
||||
import sun.awt.wl.WLSMGraphicsConfig;
|
||||
import sun.java2d.SurfaceData;
|
||||
import sun.java2d.loops.SurfaceType;
|
||||
@@ -189,7 +188,7 @@ public class WLSMSurfaceData extends SurfaceData implements WLSurfaceDataExt, WL
|
||||
private void countNewFrame() {
|
||||
// Called from the native code when this surface data has been sent to the Wayland server
|
||||
if (target instanceof Window window) {
|
||||
AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.frames");
|
||||
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.frames");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +197,7 @@ public class WLSMSurfaceData extends SurfaceData implements WLSurfaceDataExt, WL
|
||||
// the Wayland server, but that attempt was not successful. This can happen, for example,
|
||||
// when those attempts are too frequent.
|
||||
if (target instanceof Window window) {
|
||||
AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.framesDropped");
|
||||
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.framesDropped");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,12 +101,17 @@ public final class RenderPerfTest {
|
||||
|
||||
private final static String VERSION = "RenderPerfTest 2024.02";
|
||||
|
||||
private static final HashSet<String> ignoredRegexps = new HashSet<>();
|
||||
|
||||
private static final HashSet<String> ignoredTests = new HashSet<>();
|
||||
|
||||
static {
|
||||
// add ignored tests here
|
||||
// ignoredTests.add("testMyIgnoredTest");
|
||||
ignoredTests.add("testCalibration"); // not from command line
|
||||
|
||||
ignoredRegexps.add("Sw");
|
||||
ignoredRegexps.add("XOR");
|
||||
}
|
||||
|
||||
private final static String EXEC_MODE_ROBOT = "robot";
|
||||
@@ -2083,14 +2088,6 @@ public final class RenderPerfTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (testCases.isEmpty()) {
|
||||
for (Method m : RenderPerfTest.class.getDeclaredMethods()) {
|
||||
if (m.getName().startsWith("test") && !ignoredTests.contains(m.getName())) {
|
||||
testCases.add(m);
|
||||
}
|
||||
}
|
||||
testCases.sort(Comparator.comparing(Method::getName));
|
||||
}
|
||||
|
||||
if (help) {
|
||||
help();
|
||||
@@ -2127,6 +2124,43 @@ public final class RenderPerfTest {
|
||||
System.out.print("##############################################################\n");
|
||||
}
|
||||
|
||||
if (testCases.isEmpty()) {
|
||||
System.out.println("# No tests selected.");
|
||||
|
||||
if (!ignoredRegexps.isEmpty()) {
|
||||
System.out.print("# Ignored expressions: ");
|
||||
System.out.print(ignoredRegexps);
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
System.out.print("# Ignored tests: ");
|
||||
for (Method m : RenderPerfTest.class.getDeclaredMethods()) {
|
||||
if (m.getName().startsWith("test") && !ignoredTests.contains(m.getName())) {
|
||||
boolean add = true;
|
||||
if (!ignoredRegexps.isEmpty()) {
|
||||
for (final String expr : ignoredRegexps) {
|
||||
if (m.getName().contains(expr)) {
|
||||
System.out.printf("%s ", m.getName());
|
||||
add = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (add) {
|
||||
testCases.add(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println();
|
||||
testCases.sort(Comparator.comparing(Method::getName));
|
||||
}
|
||||
System.out.println("# Running tests: ");
|
||||
for (Method m : testCases) {
|
||||
System.out.print(m.getName());
|
||||
System.out.print(" ");
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
// Graphics Configuration handling:
|
||||
final Set<String> fontNames = new LinkedHashSet<>();
|
||||
final Map<String, GraphicsConfiguration> gcByID = new LinkedHashMap<>();
|
||||
@@ -2220,6 +2254,7 @@ public final class RenderPerfTest {
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
// Run tests:
|
||||
final List<RenderPerfTest> instances = new ArrayList<>();
|
||||
int retCode = 0;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user