package com.sun.scenario.animation;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import com.sun.javafx.tk.Toolkit;
public class AnimationPulse implements AnimationPulseMBean {
public static AnimationPulse getDefaultBean() {
return AnimationPulseHolder.holder;
}
private static class AnimationPulseHolder {
private static final AnimationPulse holder = new AnimationPulse();
}
private static class PulseData {
private final long startNanos;
private final long scheduledNanos;
private long animationEndNanos = Long.MIN_VALUE;
private long paintingStartNanos = Long.MIN_VALUE;
private long paintingEndNanos = Long.MIN_VALUE;
private long scenePaintingStartNanos = Long.MIN_VALUE;
private long scenePaintingEndNanos = Long.MIN_VALUE;
private long endNanos = Long.MIN_VALUE;
PulseData(long shiftNanos) {
startNanos = Toolkit.getToolkit().getMasterTimer().nanos();
scheduledNanos = startNanos + shiftNanos;
}
long getPulseStart(TimeUnit unit) {
return unit.convert(startNanos - scheduledNanos, TimeUnit.NANOSECONDS);
}
void recordAnimationEnd() {
animationEndNanos = Toolkit.getToolkit().getMasterTimer().nanos();
}
long getAnimationDuration(TimeUnit unit) {
return (animationEndNanos > Long.MIN_VALUE)
? unit.convert(animationEndNanos - startNanos, TimeUnit.NANOSECONDS)
: 0;
}
long getPaintingDuration(TimeUnit unit) {
return (paintingEndNanos > Long.MIN_VALUE && paintingStartNanos > Long.MIN_VALUE)
? unit.convert(paintingEndNanos - paintingStartNanos, TimeUnit.NANOSECONDS)
: 0;
}
long getScenePaintingDuration(TimeUnit unit) {
return (scenePaintingEndNanos > Long.MIN_VALUE && scenePaintingStartNanos > Long.MIN_VALUE)
? unit.convert(scenePaintingEndNanos - scenePaintingStartNanos, TimeUnit.NANOSECONDS)
: 0;
}
long getPaintingFinalizationDuration(TimeUnit unit) {
return (scenePaintingEndNanos > Long.MIN_VALUE && paintingEndNanos > Long.MIN_VALUE)
? unit.convert(paintingEndNanos - scenePaintingEndNanos, TimeUnit.NANOSECONDS)
: 0;
}
void recordEnd() {
endNanos = Toolkit.getToolkit().getMasterTimer().nanos();
}
long getPulseDuration(TimeUnit unit) {
return unit.convert(endNanos - startNanos, TimeUnit.NANOSECONDS);
}
long getPulseEnd(TimeUnit unit) {
return unit
.convert(endNanos - scheduledNanos, TimeUnit.NANOSECONDS);
}
long getPulseStartFromNow(TimeUnit unit) {
return unit.convert(Toolkit.getToolkit().getMasterTimer().nanos() - startNanos,
TimeUnit.NANOSECONDS);
}
long getSkippedPulses() {
return getPulseEnd(TimeUnit.MILLISECONDS)
/ AnimationPulse.getDefaultBean().getPULSE_DURATION();
}
static interface Accessor {
public long get(PulseData pulseData, TimeUnit unit);
}
static final Accessor PulseStartAccessor = (pulseData1, unit) -> pulseData1.getPulseStart(unit);
static final Accessor AnimationDurationAccessor = (pulseData1, unit) -> pulseData1.getAnimationDuration(unit);
static final Accessor PaintingDurationAccessor = (pulseData1, unit) -> pulseData1.getPaintingDuration(unit);
static final Accessor ScenePaintingDurationAccessor = (pulseData1, unit) -> pulseData1.getScenePaintingDuration(unit);
static final Accessor PulseDurationAccessor = (pulseData1, unit) -> pulseData1.getPulseDuration(unit);
static final Accessor PulseEndAccessor = (pulseData1, unit) -> pulseData1.getPulseEnd(unit);
static final Accessor PaintingPreparationDuration = (pulseData1, unit) -> pulseData1.getPaintingDuration(unit);
static final Accessor PaintingFinalizationDuration = (pulseData1, unit) -> pulseData1.getPaintingFinalizationDuration(unit);
}
private final Queue<PulseData> pulseDataQueue = new ConcurrentLinkedQueue<>();
private PulseData pulseData = null;
private volatile boolean isEnabled = false;
@Override
public boolean getEnabled() {
return isEnabled;
}
@Override
public void setEnabled(boolean enabled) {
if (enabled == isEnabled) {
return;
}
isEnabled = enabled;
}
@Override
public long getPULSE_DURATION() {
return Toolkit.getToolkit().getMasterTimer().getPulseDuration(1000);
}
@Override
public long getSkippedPulses() {
return skippedPulses.get();
}
@Override
public long getSkippedPulsesIn1Sec() {
long rv = 0;
for (PulseData pulseData : pulseDataQueue) {
if (pulseData.getPulseStartFromNow(TimeUnit.SECONDS) == 0) {
rv += pulseData.getSkippedPulses();
}
}
return rv;
}
public void recordStart(long shiftMillis) {
if (! getEnabled()) {
return;
}
pulseData = new PulseData(TimeUnit.MILLISECONDS.toNanos(shiftMillis));
}
private void purgeOldPulseData() {
Iterator<PulseData> iterator = pulseDataQueue.iterator();
while (iterator.hasNext()
&& iterator.next().getPulseStartFromNow(TimeUnit.SECONDS) > 1) {
iterator.remove();
}
}
private final AtomicLong pulseCounter = new AtomicLong();
private final AtomicLong startMax = new AtomicLong();
private final AtomicLong startSum = new AtomicLong();
private final AtomicLong startAv = new AtomicLong();
private final AtomicLong endMax = new AtomicLong();
private final AtomicLong endSum = new AtomicLong();
private final AtomicLong endAv = new AtomicLong();
private final AtomicLong animationDurationMax = new AtomicLong();
private final AtomicLong animationDurationSum = new AtomicLong();
private final AtomicLong animationDurationAv = new AtomicLong();
private final AtomicLong paintingDurationMax = new AtomicLong();
private final AtomicLong paintingDurationSum = new AtomicLong();
private final AtomicLong paintingDurationAv = new AtomicLong();
private final AtomicLong pulseDurationMax = new AtomicLong();
private final AtomicLong pulseDurationSum = new AtomicLong();
private final AtomicLong pulseDurationAv = new AtomicLong();
private final AtomicLong[] maxAndAv = new AtomicLong[] {
startMax, startSum, startAv,
endMax, endSum, endAv,
animationDurationMax, animationDurationSum, animationDurationAv,
paintingDurationMax, paintingDurationSum, paintingDurationAv,
pulseDurationMax, pulseDurationSum, pulseDurationAv
};
private final PulseData.Accessor[] maxAndAvAccessors = new PulseData.Accessor[] {
PulseData.PulseStartAccessor,
PulseData.PulseEndAccessor,
PulseData.AnimationDurationAccessor,
PulseData.PaintingDurationAccessor,
PulseData.PulseDurationAccessor
};
private void updateMaxAndAv() {
long pulseCounterLong = pulseCounter.incrementAndGet();
for (int i = 0; i < maxAndAvAccessors.length; i++) {
int j = i * 3;
long tmpLong = maxAndAvAccessors[i].get(pulseData, TimeUnit.MILLISECONDS);
maxAndAv[j].set(Math.max(maxAndAv[j].get(), tmpLong));
maxAndAv[j + 1].addAndGet(tmpLong);
maxAndAv[j + 2].set(maxAndAv[j + 1].get() / pulseCounterLong);
}
}
private final AtomicLong skippedPulses = new AtomicLong();
private int skipPulses = 100;
public void recordEnd() {
if (! getEnabled()) {
return;
}
if (skipPulses > 0) {
skipPulses--;
pulseData = null;
return;
}
pulseData.recordEnd();
purgeOldPulseData();
updateMaxAndAv();
skippedPulses.addAndGet(pulseData.getSkippedPulses());
pulseDataQueue.add(pulseData);
pulseData = null;
}
private long getAv(PulseData.Accessor accessor, long timeOut, TimeUnit unit) {
if (! getEnabled()) {
return 0;
}
long time = 0;
long items = 0;
for (PulseData currentPulseData : pulseDataQueue) {
if (currentPulseData.getPulseStartFromNow(unit) <= timeOut) {
time += accessor.get(currentPulseData, unit);
items++;
}
}
return (items == 0) ? 0 : time / items;
}
private long getMax(PulseData.Accessor accessor, long timeOut, TimeUnit unit) {
if (! getEnabled()) {
return 0;
}
long max = 0;
for (PulseData currentPulseData : pulseDataQueue) {
if (currentPulseData.getPulseStartFromNow(unit) <= timeOut) {
max = Math.max(accessor.get(currentPulseData, unit), max);
}
}
return max;
}
@Override
public long getStartMax() {
return startMax.get();
}
@Override
public long getStartAv() {
return startAv.get();
}
@Override
public long getStartMaxIn1Sec() {
return getMax(PulseData.PulseStartAccessor, 1000, TimeUnit.MILLISECONDS);
}
@Override
public long getStartAvIn100Millis() {
return getAv(PulseData.PulseStartAccessor, 100, TimeUnit.MILLISECONDS);
}
@Override
public long getEndMax() {
return endMax.get();
}
@Override
public long getEndMaxIn1Sec() {
return getMax(PulseData.PulseEndAccessor, 1000, TimeUnit.MILLISECONDS);
}
@Override
public long getEndAv() {
return endAv.get();
}
@Override
public long getEndAvIn100Millis() {
return getAv(PulseData.PulseEndAccessor, 100, TimeUnit.MILLISECONDS);
}
public void recordAnimationEnd() {
if (getEnabled() && pulseData != null) {
pulseData.recordAnimationEnd();
}
}
@Override
public long getAnimationDurationMax() {
return animationDurationMax.get();
}
@Override
public long getAnimationMaxIn1Sec() {
return getMax(PulseData.AnimationDurationAccessor, 1000, TimeUnit.MILLISECONDS);
}
@Override
public long getAnimationDurationAv() {
return animationDurationAv.get();
}
@Override
public long getAnimationDurationAvIn100Millis() {
return getAv(PulseData.AnimationDurationAccessor, 100, TimeUnit.MILLISECONDS);
}
@Override
public long getPaintingDurationMax() {
return paintingDurationMax.get();
}
@Override
public long getPaintingDurationMaxIn1Sec() {
return getMax(PulseData.PaintingDurationAccessor, 1000, TimeUnit.MILLISECONDS);
}
@Override
public long getPaintingDurationAv() {
return paintingDurationAv.get();
}
@Override
public long getPaintingDurationAvIn100Millis() {
return getAv(PulseData.PaintingDurationAccessor, 100, TimeUnit.MILLISECONDS);
}
@Override
public long getScenePaintingDurationMaxIn1Sec() {
return getMax(PulseData.ScenePaintingDurationAccessor, 1000, TimeUnit.MILLISECONDS);
}
@Override
public long getPulseDurationMax() {
return pulseDurationMax.get();
}
@Override
public long getPulseDurationMaxIn1Sec() {
return getMax(PulseData.PulseDurationAccessor, 1000, TimeUnit.MILLISECONDS);
}
@Override
public long getPulseDurationAv() {
return pulseDurationAv.get();
}
@Override
public long getPulseDurationAvIn100Millis() {
return getAv(PulseData.PulseDurationAccessor, 100, TimeUnit.MILLISECONDS);
}
@Override
public long getPaintingPreparationDurationMaxIn1Sec() {
return getMax(PulseData.PaintingPreparationDuration, 1000, TimeUnit.MILLISECONDS);
}
@Override
public long getPaintingFinalizationDurationMaxIn1Sec() {
return getMax(PulseData.PaintingFinalizationDuration, 1000, TimeUnit.MILLISECONDS);
}
}