Skip to content
This repository has been archived by the owner on Mar 10, 2020. It is now read-only.

Rendering refactor #31

Open
wants to merge 35 commits into
base: surfer-javafx8
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
64705f7
- remove unneeded constructor declaration: the default constructor, i…
seuribe Jun 6, 2018
d4e3860
- Move members to the top of the class
seuribe Jun 6, 2018
d2a4e6b
- create auxiliary methods for the creation of rendering and stopping…
seuribe Jun 6, 2018
8a3903f
- create auxiliary method for checking if the render area is valid
seuribe Jun 6, 2018
41baf0d
- Use auxiliary method to create render tasks
seuribe Jun 6, 2018
1a11fc3
- Move the scheduling and waiting to a separate method
seuribe Jun 6, 2018
1246738
- Move creation of Rendering Task to an auxiliary method
seuribe Jun 6, 2018
5f1c25b
- RenderingTasks are scheduled always and in order from low -> ultra,…
seuribe Jun 6, 2018
6b95c34
- Move separate concerns of repaint triggering into separate methods
seuribe Jun 6, 2018
7d71453
- using the try-with-resources on Scanner, to avoid leaks
seuribe Jun 3, 2018
490cd6e
- Added explicit type to generics
seuribe Jun 3, 2018
ea50cd6
- referenced function statically instead of with an instance
seuribe Jun 3, 2018
6729736
- removed unused imports
seuribe Jun 3, 2018
35f7378
- removed unused variables
seuribe Jun 3, 2018
a03c59d
- access static constant from static context
seuribe Jun 3, 2018
91fe9f3
Remove unneeded empty constructor
seuribe Jun 6, 2018
fc0baba
Move class members to the top of the file
seuribe Jun 6, 2018
998d7fa
Create auxiliary methods rendering task control
seuribe Jun 6, 2018
b90b354
Create auxiliary method for checking render area validity
seuribe Jun 6, 2018
6e13c1f
Create auxiliary method to create render tasks
seuribe Jun 6, 2018
ea8eaa9
Refactor methods to improve readability
seuribe Jun 6, 2018
2e09b3d
Move creation of Rendering Task to an auxiliary method
seuribe Jun 6, 2018
200fe05
Rearrange the scheduling of RenderingTasks
seuribe Jun 6, 2018
5fc6773
Move separate concerns of repaint triggering into separate methods
seuribe Jun 6, 2018
f4a2db1
Use try-with-resources on Scanner, to avoid leaks
seuribe Jun 3, 2018
e534ff5
Add explicit type to generics
seuribe Jun 3, 2018
65332bb
Reference function statically instead of with an instance
seuribe Jun 3, 2018
c4e5ba7
Remove unused imports
seuribe Jun 3, 2018
050e989
Remove unused variables
seuribe Jun 3, 2018
09861e8
Change static constant access
seuribe Jun 3, 2018
9445282
Use try-with-resources for avoiding leaks with the Scanner
seuribe Jun 7, 2018
b390af1
Unify catch clauses when they do the same
seuribe Jun 7, 2018
14fdca4
Move rendering resolution information to its own class
seuribe Jun 7, 2018
29d33ed
Move image creation utility function to its own class
seuribe Jun 7, 2018
941bd9d
Merge branch 'rendering_refactor' of https://github.com/IMAGINARY/SUR…
seuribe Jun 7, 2018
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
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,8 @@ private DrawcallStaticDataExt( DrawcallStaticData dcsd )
public int[] getColorBuffer() { return privateDcsd.colorBuffer; }
}

public CPUAlgebraicSurfaceRendererExt()
{
super();
}
private Integer degree;
private DegreeCalculator degreeCalculator;

public synchronized DrawcallStaticDataExt collectDrawCallStaticDataExt( int[] colorBuffer, int width, int height )
{
Expand All @@ -37,58 +35,61 @@ public synchronized DrawcallStaticDataExt collectDrawCallStaticDataExt( int[] co

public synchronized void draw( DrawcallStaticDataExt dcsd )
{
final int width = dcsd.privateDcsd.width;
final int height = dcsd.privateDcsd.height;

if( width == 0 || height == 0 )
if (!validArea(dcsd))
return;

int xStep = width / Math.min( width, Math.max( 2, Runtime.getRuntime().availableProcessors() ) );
int yStep = height / Math.min( height, 3 );//Math.max( 2, Runtime.getRuntime().availableProcessors() ) );

boolean success = true;

LinkedList< FutureTask< Boolean > > tasks = new LinkedList< FutureTask< Boolean > >();
for( int x = 0; x < width; x += xStep )
for( int y = 0; y < height; y += yStep )
tasks.add( new FutureTask< Boolean >( new RenderingTask( dcsd.privateDcsd, x, y, Math.min( x + xStep, width - 1 ), Math.min( y + yStep, height - 1 ) ) ) );
List<FutureTask<Boolean>> tasks = createRenderTasks(dcsd);

renderingTasks = tasks;
scheduleAndWait(tasks);
}

private void scheduleAndWait(List<FutureTask<Boolean>> tasks) {
boolean success = false;
try {
tasks.forEach( threadPoolExecutor::execute );

try
{
for( FutureTask< Boolean > task : tasks )
threadPoolExecutor.execute( task );
for( FutureTask< Boolean > task : tasks )
success = success && task.get();
}
catch( ExecutionException ie )
{
success = false;
}
catch( InterruptedException ie )
{
super.stopDrawing();
success = false;
}
catch( RejectedExecutionException ree )
{
success = false;
}
catch( CancellationException ree )
{
success = false;
}
finally
{
if (!task.get())
break;

success = true;
} catch( ExecutionException | RejectedExecutionException | CancellationException e ) {
} catch( InterruptedException ie ) {
stopTasks(tasks);
} finally {
if( !success || Thread.interrupted() )
throw new RenderingInterruptedException( "Rendering interrupted" );
}
}

// TODO: the parent method stopDrawing uses implicitly the renderingTasks field, but it's not actually needed
private void stopTasks(List<FutureTask<Boolean>> tasks) {
tasks.forEach( (task) -> task.cancel(true) );
}

private boolean validArea(DrawcallStaticDataExt callData) {
return callData.privateDcsd.width != 0 && callData.privateDcsd.height != 0;
}

private Integer degree;
private DegreeCalculator degreeCalculator;

private List<FutureTask<Boolean>> createRenderTasks(DrawcallStaticDataExt dcsd) {
int processors = Runtime.getRuntime().availableProcessors();
int xStep = dcsd.privateDcsd.width / Math.min( dcsd.privateDcsd.width, Math.max( 2, processors ) );
int yStep = dcsd.privateDcsd.height / Math.min( dcsd.privateDcsd.height, 3 );

List< FutureTask< Boolean > > tasks = new LinkedList< FutureTask< Boolean > >();
for( int x = 0; x < dcsd.privateDcsd.width; x += xStep )
for( int y = 0; y < dcsd.privateDcsd.height; y += yStep )
tasks.add( createRenderTask(dcsd, x, y, xStep, yStep) );

return tasks;
}

FutureTask<Boolean> createRenderTask(DrawcallStaticDataExt dcsd, int x, int y, int xStep, int yStep) {
int xEnd = Math.min( x + xStep, dcsd.privateDcsd.width - 1 );
int yEnd = Math.min( y + yStep, dcsd.privateDcsd.height - 1 );
return new FutureTask< Boolean >(new RenderingTask( dcsd.privateDcsd, x, y, xEnd, yEnd ) );
}

@Override
public void setSurfaceFamily( String expression )
throws Exception
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/de/mfo/surfer/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class Main extends Application
{
try
{
fxmlRoot = ( Group ) new FXMLLoader().load(
fxmlRoot = ( Group ) FXMLLoader.load(
getClass().getResource( "/de/mfo/surfer/fxml/surfer_touchscreen_1920_x_1080.fxml" )
);
}
Expand Down
1 change: 0 additions & 1 deletion src/main/java/de/mfo/surfer/control/ColorPickerPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import javafx.beans.property.ObjectProperty;
import javafx.geometry.Pos;
import javafx.scene.control.ColorPicker;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
Expand Down
1 change: 0 additions & 1 deletion src/main/java/de/mfo/surfer/control/LanguageSelector.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

import javafx.beans.binding.Bindings;
import javafx.collections.ObservableList;
import javafx.geometry.Bounds;
import javafx.scene.control.ChoiceBox;
import javafx.util.StringConverter;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import de.mfo.surfer.control.SceneNodeButton;
import de.mfo.surfer.Main;

import java.awt.*;
import java.io.File;
import java.util.EnumMap;
import java.util.function.Consumer;
Expand All @@ -12,7 +11,6 @@
import javafx.beans.property.BooleanProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.print.PrinterJob;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.layout.Region;
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/de/mfo/surfer/control/PreferenceDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ else if( ObjectProperty.class.isAssignableFrom( p.getClass() ) )
{
Class<?> c = Utils.wrapInRte( () -> (Class<?>) m.getDeclaringClass().getMethod(m.getName().replaceAll("Property$", "Type")).invoke( null));
if( File.class.isAssignableFrom( c ) ) {
StringConverter stringConverter = new StringConverter<File>() {
StringConverter<File> stringConverter = new StringConverter<File>() {
@Override
public String toString(File file) {
return file == null ? "" : file.toString();
Expand Down Expand Up @@ -174,7 +174,7 @@ private Node createDoubleEditor( DoubleProperty dp )
{
final double originalValue = dp.get();

Spinner< Double > spinner = new Spinner( Double.MIN_VALUE, Double.MAX_VALUE, originalValue, 0.1 );
Spinner< Double > spinner = new Spinner< Double >( Double.MIN_VALUE, Double.MAX_VALUE, originalValue, 0.1 );
spinner.setEditable( true );
spinner.getValueFactory().valueProperty().bindBidirectional( dp.asObject() );
resetters.add( () -> dp.set( originalValue ) );
Expand All @@ -186,7 +186,7 @@ private Node createIntegerEditor( IntegerProperty ip )
{
final int originalValue = ip.get();

Spinner< Integer > spinner = new Spinner( Integer.MIN_VALUE, Integer.MAX_VALUE, originalValue, 1 );
Spinner< Integer > spinner = new Spinner< Integer >( Integer.MIN_VALUE, Integer.MAX_VALUE, originalValue, 1 );
spinner.setEditable( true );
spinner.getValueFactory().valueProperty().bindBidirectional( ip.asObject() );
resetters.add( () -> ip.set( originalValue ) );
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/de/mfo/surfer/control/PrintDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,10 @@ public void changed( ObservableValue<? extends Worker.State> observable, Worker.
else
{
// pass HTML content into the webEngine instead of the path to index.html because otherwise it won't resolve file:// URLs
String html = new Scanner(PrintDialog.class.getResourceAsStream( resourceName ), "UTF-8").useDelimiter("\\A").next();
webEngine.loadContent(html);
try ( Scanner scanner = new Scanner(PrintDialog.class.getResourceAsStream( resourceName ), "UTF-8") ) {
String html = scanner.useDelimiter("\\A").next();
webEngine.loadContent(html);
}
}
}

Expand Down
150 changes: 59 additions & 91 deletions src/main/java/de/mfo/surfer/control/RenderArea.java
Original file line number Diff line number Diff line change
Expand Up @@ -238,95 +238,62 @@ public void onChanged( Change<? extends String,? extends Double> change )
ExecutorService executor = Executors.newSingleThreadExecutor( r -> { Thread t = new Thread( r ); t.setDaemon( true ); return t; } );
void triggerRepaint()
{
if( this.getScene() != null && this.getParent() != null && triggerRepaintOnChange.get() )
{
if( taskLowQuality != null && !taskLowQuality.isRunning() )
taskLowQuality.cancel();
if( taskMediumQuality != null )
taskMediumQuality.cancel();
if( taskHighQuality != null )
taskHighQuality.cancel();
if( taskUltraQuality != null )
taskUltraQuality.cancel();

// set up rendering environemnt
passDataToASR();
if( isValid.get() )
{
// calculate upper bound of the resolution
Bounds b = this.localToScene( this.getBoundsInLocal(), true );
int maxSize = (int) Math.round( Math.max( b.getWidth(), b.getHeight() ) );
int lowResSize = ( int ) Math.max( Math.min( maxSize, Math.sqrt( 1.0 / ( targetFps * secondsPerPixel ) ) ), 100 );

taskUltraQuality = new RenderingTask(
asr,
this.imageView,
maxSize,
renderSize,
AntiAliasingMode.SUPERSAMPLING,
AntiAliasingPattern.OG_4x4
);
if( !shouldRepaint() )
return;

taskHighQuality = new RenderingTask(
asr,
this.imageView,
maxSize,
renderSize,
AntiAliasingMode.ADAPTIVE_SUPERSAMPLING,
AntiAliasingPattern.OG_4x4
);
taskHighQuality.setOnSucceeded( e -> executor.submit( taskUltraQuality ) );
cancelTasks();

if( lowResSize < maxSize / 2 )
{
// add rendering step with intermediate resolution
taskMediumQuality = new RenderingTask(
asr,
this.imageView,
( maxSize + lowResSize ) / 2,
renderSize,
AntiAliasingMode.ADAPTIVE_SUPERSAMPLING,
AntiAliasingPattern.QUINCUNX
);
}
else
{
// add dummy rendering task, that does nothing
taskMediumQuality = new RenderingTask(
asr,
this.imageView,
( maxSize + lowResSize ) / 2,
renderSize,
AntiAliasingMode.ADAPTIVE_SUPERSAMPLING,
AntiAliasingPattern.QUINCUNX
)
{
@Override protected void scheduled() {}
@Override public Double call() { return secondsPerPixel; }
@Override protected void succeeded() {}
};
}
taskMediumQuality.setOnSucceeded( e -> executor.submit( taskHighQuality ) );

taskLowQuality = new RenderingTask(
asr,
this.imageView,
lowResSize,
renderSize,
AntiAliasingMode.ADAPTIVE_SUPERSAMPLING,
AntiAliasingPattern.QUINCUNX
);
taskLowQuality.setOnSucceeded( e ->
{
secondsPerPixel = ( double ) e.getSource().getValue();
executor.submit( taskMediumQuality );
}
);

executor.submit( taskLowQuality );
}
}
}
// set up rendering environemnt
passDataToASR();
if( isValid.get() )
scheduleTasks();
}

private boolean shouldRepaint() {
return this.getScene() != null && this.getParent() != null && triggerRepaintOnChange.get();
}

private void scheduleTasks() {
// calculate upper bound of the resolution
Bounds b = this.localToScene( this.getBoundsInLocal(), true );
int maxSize = (int) Math.round( Math.max( b.getWidth(), b.getHeight() ) );
int lowResSize = ( int ) Math.max( Math.min( maxSize, Math.sqrt( 1.0 / ( targetFps * secondsPerPixel ) ) ), 100 );

taskLowQuality = createRenderingTask(lowResSize, AntiAliasingMode.ADAPTIVE_SUPERSAMPLING, AntiAliasingPattern.QUINCUNX);
taskLowQuality.setOnSucceeded( e ->
{
secondsPerPixel = ( double ) e.getSource().getValue();
});
executor.submit( taskLowQuality );

if( lowResSize < maxSize / 2 )
{
taskMediumQuality = createRenderingTask(( maxSize + lowResSize ) / 2,
AntiAliasingMode.ADAPTIVE_SUPERSAMPLING, AntiAliasingPattern.QUINCUNX);
executor.submit( taskMediumQuality );

}
taskHighQuality = createRenderingTask(maxSize, AntiAliasingMode.ADAPTIVE_SUPERSAMPLING, AntiAliasingPattern.OG_4x4);
executor.submit( taskHighQuality );

taskUltraQuality = createRenderingTask(maxSize, AntiAliasingMode.SUPERSAMPLING, AntiAliasingPattern.OG_4x4);
executor.submit( taskUltraQuality );
}

private void cancelTasks() {
if( taskLowQuality != null && !taskLowQuality.isRunning() )
taskLowQuality.cancel();
if( taskMediumQuality != null )
taskMediumQuality.cancel();
if( taskHighQuality != null )
taskHighQuality.cancel();
if( taskUltraQuality != null )
taskUltraQuality.cancel();
}

private RenderingTask createRenderingTask(int size, AntiAliasingMode aam, AntiAliasingPattern aap) {
return new RenderingTask(asr, this.imageView, size, renderSize, aam, aap);
}

public void setPreviewImage( Image previewImage )
{
Expand Down Expand Up @@ -487,16 +454,17 @@ public void load( Properties props )
asr.getFrontMaterial().loadProperties(props, "front_material_", "");
asr.getBackMaterial().loadProperties(props, "back_material_", "");

for( int i = 0; i < asr.MAX_LIGHTS; i++ )
for( int i = 0; i < AlgebraicSurfaceRenderer.MAX_LIGHTS; i++ )
{
asr.getLightSource( i ).setStatus(LightSource.Status.OFF);
asr.getLightSource( i ).loadProperties( props, "light_", "_" + i );
}
Function< String, Color3f > string2color = s ->
{
Scanner sc = new Scanner( s );
sc.useLocale( Locale.US );
return new Color3f( sc.nextFloat(), sc.nextFloat(), sc.nextFloat() );
try (Scanner sc = new Scanner( s ) ) {
sc.useLocale( Locale.US );
return new Color3f( sc.nextFloat(), sc.nextFloat(), sc.nextFloat() );
}
};

asr.setBackgroundColor( string2color.apply( props.getProperty( "background_color" ) ) );
Expand Down
1 change: 0 additions & 1 deletion src/main/java/de/mfo/surfer/control/SceneNodeButton.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import de.mfo.surfer.util.FXUtils;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.effect.Effect;
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/de/mfo/surfer/control/SceneNodeSlider.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import javafx.geometry.Bounds;
import javafx.geometry.Orientation;
import javafx.scene.control.Slider;
import javafx.scene.input.MouseEvent;
import javafx.scene.Node;
import javafx.util.Duration;
import org.slf4j.Logger;
Expand Down Expand Up @@ -49,7 +48,6 @@ public SceneNodeSlider( Node trackNode, Node thumbNode, Node plusNode, Node minu
}

Bounds trackBB = trackNode.getBoundsInParent();
Bounds thumbBB = thumbNode.getBoundsInParent();
BoundingBox sliderBB = new BoundingBox(
trackBB.getMinX(),
trackBB.getMinY() - 14,
Expand Down
Loading