/*
* Copyright 2016 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.core.debug;
import org.terasology.context.Context;
import org.terasology.entitySystem.entity.EntityRef;
import org.terasology.math.ChunkMath;
import org.terasology.math.Region3i;
import org.terasology.math.geom.Vector3f;
import org.terasology.math.geom.Vector3i;
import org.terasology.registry.In;
import org.terasology.rendering.nui.BaseInteractionScreen;
import org.terasology.rendering.nui.UIWidget;
import org.terasology.rendering.nui.databinding.Binding;
import org.terasology.rendering.nui.widgets.UIButton;
import org.terasology.rendering.nui.widgets.UIDropdownScrollable;
import org.terasology.rendering.nui.widgets.UIText;
import org.terasology.world.chunks.ChunkConstants;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* This screen can be shown with the showScreen command in order to measure the performance of the block placement.
*/
public class BenchmarkScreen extends BaseInteractionScreen {
static final int DEFAULT_ITERATION_COUNT = 200;
private UIText textArea;
private UIButton closeButton;
private UIButton startStopButton;
private UIDropdownScrollable dropdown;
@In
private Context context;
private long iterationsDone;
private double sum;
private double min;
private double max;
private List<Double> sortedDurations = new ArrayList<Double>(DEFAULT_ITERATION_COUNT);
private AbstractBenchmarkInstance runningBenchmark;
private BenchmarkType selectedBenchmarkType = BenchmarkType.WORLD_PROVIDER_SET_BLOCK;
@Override
protected void initializeWithInteractionTarget(EntityRef interactionTarget) {
// nothing to do
}
@Override
public void initialise() {
textArea = find("textArea", UIText.class);
dropdown = find("dropdown", UIDropdownScrollable.class);
if (dropdown != null) {
dropdown.bindSelection(new Binding() {
@Override
public Object get() {
return selectedBenchmarkType;
}
@Override
public void set(Object value) {
selectedBenchmarkType = (BenchmarkType) value;
if (runningBenchmark != null) {
runningBenchmark = null;
}
textArea.setText(selectedBenchmarkType.getDescription());
}
});
dropdown.setOptions(Arrays.asList(BenchmarkType.values()));
}
closeButton = find("closeButton", UIButton.class);
if (closeButton != null) {
closeButton.subscribe(this::onCloseButton);
}
startStopButton = find("startStopButton", UIButton.class);
if (startStopButton != null) {
startStopButton.subscribe(this::onStartStopButton);
}
}
private void onStartStopButton(UIWidget uiWidget) {
if (runningBenchmark == null) {
handleBenchmarkStart();
} else {
handleBenchmarkEnd();
}
}
private void handleBenchmarkStart() {
runningBenchmark = selectedBenchmarkType.createInstance(context);
iterationsDone = 0;
sum = 0;
min = Double.MAX_VALUE;
max = Double.MIN_VALUE;
sortedDurations.clear();
updateStartStopButton();
}
private void handleBenchmarkEnd() {
runningBenchmark = null;
updateStartStopButton();
}
private void updateStartStopButton() {
if (runningBenchmark != null) {
startStopButton.setText("Stop Benchmark");
} else {
startStopButton.setText("Start Benchmark");
}
}
@Override
public void onOpened() {
handleBenchmarkEnd();
if (textArea != null) {
textArea.setText(selectedBenchmarkType.getDescription());
}
}
private void onCloseButton(UIWidget uiWidget) {
getManager().popScreen();
}
static Region3i getChunkRegionAbove(Vector3f location) {
Vector3i charecterPos = new Vector3i(location);
Vector3i chunkAboveCharacter = ChunkMath.calcChunkPos(charecterPos);
chunkAboveCharacter.addY(1);
Vector3i chunkRelativePos = ChunkMath.calcBlockPos(charecterPos);
Vector3i characterChunkOriginPos = new Vector3i(charecterPos);
characterChunkOriginPos.sub(chunkRelativePos);
Vector3i chunkAboveOrigin = new Vector3i(characterChunkOriginPos);
chunkAboveOrigin.addY(ChunkConstants.CHUNK_SIZE.getY());
return ChunkConstants.CHUNK_REGION.move(chunkAboveOrigin);
}
@Override
public void update(float delta) {
if (runningBenchmark == null) {
return;
}
long startNs = System.nanoTime();
runningBenchmark.runStep();
long endNs = System.nanoTime();
long durationInNs = endNs - startNs;
double durationInMs = durationInNs / 1000000.0;
iterationsDone += 1;
sortedDurations.add(durationInMs);
// sort to calculate median:
Collections.sort(sortedDurations);
double median = sortedDurations.get(sortedDurations.size() / 2);
sum += durationInMs;
min = Math.min(min, durationInMs);
max = Math.max(max, durationInMs);
double avgMs = sum / iterationsDone;
StringBuilder sb = new StringBuilder();
sb.append("benchmark: ");
sb.append(selectedBenchmarkType.getTitle());
sb.append("\n");
sb.append("iteration: ");
sb.append(Long.toString(iterationsDone));
sb.append(" / ");
sb.append(selectedBenchmarkType.getMaxIterations());
sb.append("\n");
sb.append("last duration: ");
sb.append(String.format("%.1f", durationInMs));
sb.append(" ms\n");
sb.append("min duration: ");
sb.append(String.format("%.1f", min));
sb.append(" ms\n");
sb.append("median duration: ");
sb.append(String.format("%.1f", median));
sb.append(" ms\n");
sb.append("avg duration: ");
sb.append(String.format("%.1f", avgMs));
sb.append(" ms\n");
sb.append("max duration: ");
sb.append(String.format("%.1f", max));
sb.append(" ms\n");
if (textArea != null) {
textArea.setText(sb.toString());
}
if (iterationsDone >= selectedBenchmarkType.getMaxIterations()) {
handleBenchmarkEnd();
}
}
}