Tuesday, August 31, 2010

The Advantages of Using Properties Instead of Getters/Setters in Java and Scala

There have been a few concepts I've been pushing for most of my programming career, and one of the top items on that list is Properties. This is a topic I've been meaning to delve into for a while, and have previously, but as this is an incredibly important part of the core of Sgine I would like to take some time to state the case for Properties. This post is all about why Properties are useful and ultimately why Sgine uses them. The next post, Properties - Tutorial, will go into great detail about how to use Properties within the context of Sgine. Properties as a term is bandied about in many different forms, so first a definition is in order.

Definition


In the context of this discussion a Property is the concept of a wrapper class around a value as an alternative to the standard private field, public getter/setter. For example in Java:
public class Person {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

person.setName("John Doe");
String name = person.getName();

The property alternative:
public class Person {
    public final name = new Property<String>(null);
}

person.name.set("John Doe");
String name = person.name.get();

A basic Property class in Java might look something like:
public class Property<T> {
    private T value;

    public void set(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }
}

Or in Scala:
class Person {
    private var _name: String = _

    def name = _name

    def name_=(name: String) = _name = name
}

person.name = "John Doe"
val name = person.name

The property alternative:
class Person {
    val name = new Property[String](null)
}

person.name := "John Doe"
val name = person.name()

A basic Property class in Scala might look something like:
class Property[T] {
    private var value: T = _

    def :=(value: T) = this.value = value

    def apply() = value
}

Since this blog is about Sgine, a 3d engine in Scala, I will be showing all further example code only in Scala though much of this still applies directly to Java.

Advantages


This is all well and good, but for all practical appearances it's about the same as classic getter/setter pattern. Why would you want to break convention and use Properties instead?

In the examples above there isn't much practical difference between the two methodologies. Performance-wise they are identical. Properties reduce the amount of code in your class by a pretty large margin if you consider one line:
val name = new Property[String](null)

Instead of three (or seven in the case of Java):
private var _name: String = _
def name = _name
def name_=(name: String) = _name = name

However, the typical response here is that with today's IDEs they will generate the getter/setters for you automagically, so what does that matter? To some degree I agree with this, but it is still clutter added to your classes that make managing your source code that much more complicated down the road. I will agree though that this in and of itself is not a convincing reason to break convention so lets get on to the real reason.

Ultimately it comes down to one major reason to use the Property concept instead of methods, and that is encapsulation. Given the above Person example with a "name", how would you handle a check upon assignment to make sure the name is no more than 255 characters, and if it is, to chop off the extra characters?

Perhaps something like:
class Person {
    private var _name: String = _

    def name = _name

    def name_=(name: String) = {
        val filtered = ...filter name...
        _name = filtered
    }
}

That's simple enough, but what if you want to add an "address" field as well and limit it to 255 characters? Sure, you can simplify by creating a "filter" method you can invoke, but no matter how you swing it, you end up with boiler-plate code to accomplish your task. The reason this happens is because methods don't have the ability to encapsulate and extend functionality the way that an object does. So for the above example I could simply create a FilteredProperty trait that can be mixed in:
trait FilteredProperty extends Property[String] {
    abstract override :=(value: String) = {
        val filtered = ...filter value...
        super.:=(filtered)
    }
}

Now you simply make a very slight change to your person class to get the benefits of filtering:
class Person {
    val name = new Property[String](null) with FilteredProperty
}

Hopefully you are beginning to see the advantages here. In order to add functionality to your simply extend Property functionality to provide what you want and then mix it together in your resulting Property instance. Not only does this remove any need for boilerplate code, but it makes reading the source code for classes extremely easy as a single line of code describes the make-up of that property.

Here are a few examples of things you can do well with Properties:
  • Events: Adding event listeners to a property is one of the most common uses for a Property system as event handling in a standard Bean scenario involves a lot of boiler-plate code. Here's a simple example of usage:
    person.name.listeners += nameChanged _
    
    private def nameChanged(evt: PropertyChangeEvent[String]) = {
        ... property has changed ...
    }

    As is hopefully obvious, any changes to person.name will result in the invocation of the nameChanged method.
  • Binding: Synchronizing two properties together via binding is probably the second most common reasons Properties are used. This is something that takes quite a bit of effort to support, but by leveraging Properties the functionality can be written once and then re-used everywhere desired. Here's a simple example of usage:
    person1.name bind person2.name

    Sure, that's incredibly simple, but now any changes to person2's name will result in person1's name being updated as well.
  • Translation: The ability to translate incoming values is a very broad topic, whether you are converting Strings to Ints, translating to another language upon set, or simply modifying all applied data to follow a specific standard Properties can provide all of the functionality by simple extension and mix-in.
  • Filtering: Applying a filter may represent limiting of a String to 255 characters as discussed above, or it may include throwing an Exception if invalid data is attempted to be passed (ex. Attempting to set null on a Property that should not be null).
  • Transactions: Simple transaction support may consist of an uncommitted value along with commit and rollback methods. This would allow simple management of objects in a user-interface and the ability to know if the object has uncommitted values and what they are so you are only applying changes to the database instead of sweeping updates.
  • Animation: Also called "adjusters", this is the ability to move from one value to another over time. This is most commonly applied to numbers. For example, a AnimatingProperty trait may start at 0.0 but when the value is set to 100.0 instead of applying it directly, it is applied over time and possibly with an easing function.
  • Delegation: This allows the ability delegate getting/setting functionality to another method, function, or value.

There is a great deal more that can be done with Properties, but this is hopefully a nice glimpse to what is possible.

History


As much as I'd love to claim responsibility for the idea of properties, alas, it is not my own and here are a few URLs to other posts about properties (including some older ones from me):

http://joda-beans.sourceforge.net/
http://www.javalobby.org/java/forums/t88090.html?start=50#92123353
http://www.javalobby.org/java/forums/t90756.html
http://www.javalobby.org/java/forums/m92123242.html
http://www.matthicks.com/2009/06/have-beans-been-holding-us-back.html
http://www.matthicks.com/2009/07/death-of-beans.html

Saturday, August 28, 2010

Stressed - Asynchronously

In a previous post I discussed the improvement in performance to be able to display 1600 cubes on the screen at a time at 32fps. It has been quite a while since that post, and until recently I've been busy with infrastructure cleanup and changes and just recently cycled back around to take another look at performance.

One of the main goals of Sgine from the beginning has been to be a multi-threaded 3d engine. However, early on there were some problems with updating content asynchronously that were causing visual issues so I modified all non-renderer updates to occur in the rendering thread (like every other 3d engine out there). After remembering that I had never switched this back over (yeah, just took a single line of code: Updatable.useWorkManager = true), I re-ran the stress test:



Yes, you read that right, I went from 32fps all the way up to 296fps. That's a pretty substantial jump if I do say so myself. There are additional tweaks I can make to make it even faster I think, but I'll save that for another day.

While we're on the topic of multi-threading 3d engines I'd like to share another recent development. As anyone ever developing multi-threaded support in a 3d engine is aware, there are many times you find yourself outside of the rendering thread, but you need to invoke some rendering logic. This was a big problem in jME and Renanse (Joshua Slack) and I came up with the GameTaskQueue that would allow you to inject "work" to be done into a queue to be done later in the rendering thread.

To some degree I make use of the same concept in Sgine, but have created a trait "org.sgine.work.Worker" to represent this functionality. Either a Function0[Unit] or curried function can be passed to invokeLater or invokeAndWait like so:

renderer.invokeLater {
    glPolygonMode(GL_FRONT, GL_POINT)
}

This is on par, but slightly easier to read than passing a Runnable to a queue, but still not too far off. However, I remember being incredibly frustrated in jME having to do this all the time when I needed to inject some logic into the renderer. Now, in Sgine that shouldn't usually be necessary as its purpose is to abstract the rendering layer, but still I wanted to take this a step further (primarily for internal use).

So here's a real-world example of a problem that was created by asynchronous updates. In the Debug trait it adds a keyboard listener to do specific things upon key press. For example, hitting Escape will shutdown the renderer. This shouldn't be a problem as the Renderer.shutdown() method can be invoked asynchronously with any trouble, but unfortunately I have no reference to the current Renderer, so I have to use Renderer() to get the ThreadLocal Renderer. Obviously if keyboard events are being thrown outside of the renderer thread this causes a problem.

So, in an effort to simplify this process, a new feature has been added to EventHandler to allow a "worker" to be specified. If a worker is specified then all calls to invoke the wrapped listener will be "invokeAndWait" through that worker.

In the case of Debug:

Keyboard.listeners += EventHandler(handleKey, ProcessingMode.Blocking)

Now becomes:

Keyboard.listeners += EventHandler(handleKey, ProcessingMode.Blocking, worker = renderer)

From now on I don't have to worry about finding the right renderer and then invokeAndWait within that renderer for anything within the handleKey function, I can rely on the fact that handleKey is always going to be run within the rendering thread.

Saturday, July 31, 2010

Writing Tetris

I'm currently in the process of writing a couple small games in an effort to use Sgine for some real-world game development in order to fill in any wholes and evaluate how easy it is to use in a complete application. The first simple game on my agenda is Tetris. I'd say I'm about half-way finished writing it, and I think it's coming along pretty well:



As you can see I'm using 3d cubes to represent pieces and though the game itself is still played on two dimensions the appearance is in 3d slightly translucent and rotated on the x-axis in order to give it a little more depth. I'll post a link to play the game once it is finished, but I know many people have asked if there are any practical examples of Sgine and I just wanted to send out this note to let everyone know that there will be soon. :)

Also, you can't see it from the screenshot obviously, but I have the original Tetris theme playing in the background on loop. Oh, how it takes me back to playing on the Gameboy as a kid. :)

Sunday, July 25, 2010

Performance Update

I mentioned previously that I was working on performance tweaks to Sgine and I referenced a performance test of 400 cubes rotating and animating back-and-forth on the screen at 38fps.

Since that post I have made some pretty major updates to the backing renderer to increase performance. There's still quite a bit left that can be tweaked, but at this point I'm much more confident in the overall performance of the engine but wanted to outline a few of the changes made to the system and the current results.

First, I was previously using render.RenderImage, that basically just displays a textured quad on the screen with immediate mode rendering. This has been changed to rely on render.shape.Shape class that allows any OpenGL shape to be represented with vertices with triangles, quads, etc. The new Shape class also automatically detects whether VBO is supported and will use that if available over immediate mode. I plan to add additional support for GLSL in the future, but at the moment my focus is the best compatibility and working upwards.

Next, I created a new ui.Box class that represents a modifiable, texturable box on the screen. Previously the cubes being displayed were represented by six distinct RenderImages backed against the same texture.

Finally, I've added the ComponentInstance class I mentioned in my last post to allow instancing rather than creating completely new cubes each time.

The results of these changes were a pretty good bump in performance:



Now, keep in mind that framerates are not linear, so this is a pretty substantial improvement over the 38fps I was previously getting.

To prove this I added a few more boxes to the scene (1600 to be exact) to get my framerate back into the 30s:



The new source code looks a bit different from the previous stress test, but pretty easy to read I think:

package org.sgine.ui

import org.sgine.core.Resource

import org.sgine.easing.Elastic

import org.sgine.effect._

import org.sgine.property.animate.EasingNumericAnimator
import org.sgine.property.animate.LinearNumericAnimator

import org.sgine.render.Debug
import org.sgine.render.RenderSettings
import org.sgine.render.StandardDisplay

import org.sgine.ui.ext.AdvancedComponent

import scala.math._

object TestStressBoxes extends StandardDisplay with Debug {
 override val settings = RenderSettings.High
 
 def setup() = {
  var box: Box = null
  for (row <- 0 until 40) {
   for (column <- 0 until 40) {
    val y = (row * 30.0) - 350.0
    val z = (column * 90.0) - 1800.0
    if ((row == 0) && (column == 0)) {
     box = createBox(y, z, 0.05)
    } else {
     createInstance(box, y, z, 0.05)
    }
   }
  }
 }
 
 def createBox(y: Double, z: Double, scale: Double) = {
  val box = new Box()
  box.scale.set(scale)
  box.location.set(0.0, y, z)
  box.source := Resource("sgine_256.png")
  
  animate(box)
  
  box.update(0.0)
  scene += box
  
  box
 }
 
 def createInstance(component: Component, y: Double, z: Double, scale: Double) = {
  val instance = ComponentInstance(component)
  instance.scale.set(scale)
  instance.location.set(0.0, y, z)
  
  animate(instance)
  
  instance.update(0.0)
  scene += instance
  
  instance
 }
 
 private def animate(component: AdvancedComponent) = {
  component.rotation.x.animator = new LinearNumericAnimator(2.0)
  component.rotation.y.animator = new LinearNumericAnimator(2.0)
  component.rotation.z.animator = new LinearNumericAnimator(2.0)
  component.location.x.animator = new EasingNumericAnimator(Elastic.easeInOut, 3.0)
  
  component.rotation.x := Double.MaxValue
  component.rotation.y := Double.MaxValue
  component.rotation.z := Double.MaxValue

  // Move the cube back and forth perpetually on the x-axis
  val me0 = new PauseEffect(random * 2.0 + 0.5)
  val me1 = new PropertyChangeEffect(component.location.x, -400.0)
  val me2 = new PropertyChangeEffect(component.location.x, 400.0)
  val move = new CompositeEffect(me0, me1, me2)
  move.repeat = -1
  move.start()
 }
}

This could be cleaned up more, but should give you an idea of how simple it is to fill a scene up with boxes. :) I'd like to make one last note on this test that you can't see unless you actually run it. The previous test took around 20 seconds to asynchronously fully load all cubes on the screen. Since I'm using ComponentInstance that time has dropped to about one second to fully load on the screen. This is a drastic improvement in scene load-time. Now it's time to get back to work on functionality.

Saturday, July 24, 2010

Instancing

I've mostly been working on performance and internal tweaks to Sgine lately, but one of the things I've recently added that is both a performance benefit and a really "nice-to-have" for game development particularly is what I call "instancing". In a game you often have many instances of an object on the screen at any given time whether it's a wall, a character, or something else it can be really performance intensive and a waste of memory to have each instance completely represented independently of the others, particularly for complex objects. To help resolve this I've created ComponentInstance to allow creation of a Component and then re-use without all the baggage.

For example, see the following code snippet:

package org.sgine.ui

import org.sgine.core.Color
import org.sgine.core.Resource

import org.sgine.render.Debug
import org.sgine.render.StandardDisplay

object TestInstancing extends StandardDisplay with Debug {
 def setup() = {
  val image = new Image(Resource("puppies.jpg"))
  scene += image
  
  val i1 = ComponentInstance(image)
  i1.location.set(-300.0, 200.0, 5.0)
  i1.scale.set(0.4)
  i1.alpha := 0.5
  i1.color := Color.Red
  scene += i1
  
  val i2 = ComponentInstance(image)
  i2.location.set(300.0, 200.0, 5.0)
  i2.scale.set(0.4)
  i2.alpha := 0.5
  i2.color := Color.Green
  scene += i2
  
  val i3 = ComponentInstance(image)
  i3.location.set(-300.0, -200.0, 5.0)
  i3.scale.set(0.4)
  i3.alpha := 0.5
  i3.color := Color.Blue
  scene += i3
  
  val i4 = ComponentInstance(image)
  i4.location.set(300.0, -200.0, 5.0)
  i4.scale.set(0.4)
  i4.alpha := 0.5
  scene += i4
 }
}

The result of the above code is this:



This also makes it easy to modify one component and reflect it in all instances. There's a lot more functionality in the pipeline that will help increase the performance of Sgine, but unfortunately most of it is internal and not as cool to look at in a blog post. :)

Sunday, July 18, 2010

Performance and Shapes

The functionality is really starting to come together on Sgine, but the performance has been a bit lower than I had hoped for large scenes. For simple games and business applications Sgine is performing quite well, but it's definitely not yet ready to write a first-person shooter on yet.

I've spent a bit of time doing performance tweaking and optimizations with YourKit and the performance is getting better, but still not where I'd like to see it. Right now I've got 400 cubes (which is made up of four independent Images for greater complexity) rotating and animating back and forth across the screen via easing effect at 38fps:



Not terrible, but over the next few weeks I'm hopeful I can get 4,000 cubes rendering at over 60fps.

Another quick note is that I've created a little Shape system in Sgine that will hopefully eventually be the core of all rendering as it auto-detects and uses additional capabilities of the video card for better performance. Additionally, I've added support for java.awt.Shape to ShapeData to draw nifty things like vector-text directly from a java.awt.Font to the screen. This is not the Shape-to-Texture cheating that most of the other engines do, this is direct to screen via tesselation. I've turned off any anti-aliasing so you can see it for yourself:



I've still got more work before I'm finished with the new Shape system, but once it is done I think this should help me get to the framerates I'm looking for.

Friday, July 16, 2010

Major Changes

I've been pretty quiet lately, but it's because I've been working with Aleksey Nikiforov (lex) on migrating Sgine to using Simplex3d instead of our built-in math library. This has been a pretty long endeavor and lots of work from both Lex and myself have gone into the migration, but I believe the effort has paid off. This morning I merged the simplex3d branch back into 'default' and we've now got better performance along with a much more powerful math library to back against.

There have been a lot of other changes that have been introduced while this migration was happening, but I'll save that for another post.

For now, if you're interested in seeing the merge changes from 'simplex3d' to 'default' you can see it here: http://build.sgine.org/job/sgine/74/changes.
Scala Engine for high-performance interactive applications.