Showing posts with label performance. Show all posts
Showing posts with label performance. Show all posts

Saturday, July 16, 2011

More Concise and More Powerful

Over a year ago I posted about the focus of Sgine to be extremely concise in creating 3d applications. I've always found it annoying that the barrier to entry writing 3d applications of any sort if overly complicated. I started Sgine with the belief that an engine could be created that abstracts without reducing the power of the system. Scala has been instrumental in making this possible.

Now, the point of this post is to announce that I'm finally back to the UI abstraction layer again. After nearly a year of re-architecting and adding new foundational concepts I'm finally back to the high-level abstraction that is the magic of Sgine.

In the post I mentioned above I was showing off just how concisely you can display an image in Sgine:

package org.sgine.ui

import org.sgine.core.Resource

import org.sgine.render.StandardDisplay

object TestImage extends StandardDisplay {
 def setup() = {
  val component = new Image(Resource("puppies.jpg"))
  scene += component
 }
}

My goal in this re-write was to keep at least as concise as 0.1 and hopefully achieve something even better. I do believe I have accomplished something even more concise in the current iteration of development with only a single line of logic to display an image on the screen:

package org.sgine.ui

object ImageTest extends UI {
  this += Image("sgine.png")
}

There may be some slight revisions to this (ex. I'm considering extracting the base container out into a variable name "container" instead of mixing it into UI), but I honestly don't see any way I can make it any more concise than that. :)

For those of you that love pictures, here's the screenshot of what's displayed:



There hasn't been a lot of chatter on here lately, but now that I'm back on the UI abstraction layer there will be a lot more to see, so check back often.

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.
Scala Engine for high-performance interactive applications.