Welcome, Guest. Please login or register. Did you miss your activation email?

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - badlogic

Pages: [1]
1
General discussions / SFML 2.0
« on: January 14, 2011, 05:40:42 pm »
Cool. Don't get bitten by the dirty Java :)

2
General discussions / SFML 2.0
« on: January 14, 2011, 05:46:42 am »
Hi there,

i just stumbled over sfml by accident and was intrigued by it's well thought out design and ease of use. I'm usually a Java guy (ya, start running and screaming :)) but i like getting back to C++ every now and then. SFML is a more than welcome alternative to the mess that is SDL. Great job so far.

I'm actually maintaining a very similar library for Java and was wondering how you pulled of sprite/glyph rendering in SFML. I first had a look at Drawable, Sprite and Renderer and was surprised to find that you actually use immediate mode rendering and only remember the last bound texture to reduce binds.

Now, i figured you have a reason for this and searched the forums. I found this thread, you seem to have tried batching already but reverted to immediate mode rendering. Shortly afterwards i found this thread which gives a little bit more details on why you ditched batching all together.

With our Java based project we have created something very similar to SFML (Win/Lin/Mac/Android). Our Sprite classes look nearly identical from an API point of view, the internals are of course a little different. The interesting part is this: we use batching heavily as it is mandatory on mobile devices like Android. And what's good for a mobile GPU is good for a desktop GPU as well. We support OpenGL ES 1.x as well as 2.0, which is more or less the same as desktop GL minus display lists and immediate mode. So we are forced to go the batching route.

I wanted to compare the performance of both SFML and our lib and create a little microbenchmark. Here's the SFML version:

Code: [Select]

#include <SFML/Audio.hpp>
#include <SFML/Graphics.hpp>

#define SPRITES 50000
#define randf() ((float)rand() / RAND_MAX)

int main()
{
// Create the main window
sf::RenderWindow window(sf::VideoMode(800, 600), "bench");

// Load a sprite to display
sf::Image image;
if (!image.LoadFromFile("badlogicsmall.jpg"))
return EXIT_FAILURE;
sf::Image image2;
if (!image2.LoadFromFile("bob.png"))
return EXIT_FAILURE;

sf::Sprite* sprites[SPRITES];

for(int i = 0; i < SPRITES; i++) {
sf::Sprite* sprite;
if(i < SPRITES / 2) {
sprite = new sf::Sprite(image);
sprite->SetBlendMode(sf::Blend::None);
sprite->SetPosition(randf() * 800, randf() * 600);
sprite->SetOrigin(16, 16);
} else {
sprite = new sf::Sprite(image2);
sprite->SetBlendMode(sf::Blend::Alpha);
sprite->SetPosition(randf() * 800, randf() * 600);
sprite->SetOrigin(16, 16);
}
sprites[i] = sprite;
}

sf::Clock clock;
float timePassed = 0;
unsigned frames = 0;
while (window.IsOpened())
{
sf::Event event;
while (window.GetEvent(event)) {
if (event.Type == sf::Event::Closed)
window.Close();
}

float elapsed = clock.GetElapsedTime();
float scale = timePassed>0.5?1-timePassed / 2:0.5f + timePassed / 2;
clock.Reset();

window.Clear();
for(int i = 0; i < SPRITES; i++) {
sprites[i]->SetRotation(sprites[i]->GetRotation() + elapsed * 45);
sprites[i]->SetScale(scale, scale);
window.Draw(*sprites[i]);
}
window.Display();

timePassed += elapsed;
frames++;
if(timePassed > 1.0f) {
printf("fps: %d\n", frames);
timePassed = 0;
frames = 0;
}
}

return EXIT_SUCCESS;
}


Please forgive my poor C++, it's been years since i actively worked with it.

Our APIs are so similar that the Java version based on our lib looks pretty much the same (we don't have a classic main loop due to Android being a little bitch).

Code: [Select]

package com.badlogic.gdx.tests;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.Texture.TextureWrap;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.tests.utils.GdxTest;

public class SpritePerformanteTest2 extends GdxTest {
static final int SPRITES = 50000;
Texture image;
Texture image2;
Sprite[] sprites;
SpriteBatch batch;
float timePassed = 0;
int frames = 0;

@Override public void create() {
image = Gdx.graphics.newTexture(Gdx.files.internal("data/badlogicsmall.jpg"),
 TextureFilter.Linear, TextureFilter.Linear,
 TextureWrap.ClampToEdge, TextureWrap.ClampToEdge);
image2 = Gdx.graphics.newTexture(Gdx.files.internal("data/bobargb8888-32x32.png"),
TextureFilter.Linear, TextureFilter.Linear,
    TextureWrap.ClampToEdge, TextureWrap.ClampToEdge);

sprites = new Sprite[SPRITES];
for(int i = 0; i < SPRITES; i++) {
Sprite sprite = new Sprite(i < SPRITES / 2?image:image2);
sprite.setPosition((float)Math.random() * 800, (float)Math.random() * 600);
sprites[i] = sprite;
}

batch = new SpriteBatch();
}

@Override public void render() {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

float elapsed = Gdx.graphics.getDeltaTime();
float scale = timePassed>0.5?1-timePassed / 2:0.5f + timePassed / 2;

batch.begin();
batch.disableBlending();
for(int i = 0; i < SPRITES / 2; i++) {
sprites[i].setRotation(sprites[i].getRotation() + elapsed * 45);
sprites[i].setScale(scale, scale);
sprites[i].draw(batch);
}
batch.end();

batch.begin();
batch.enableBlending();
for(int i = SPRITES / 2; i < SPRITES; i++) {
sprites[i].setRotation(sprites[i].getRotation() + elapsed * 45);
sprites[i].setScale(scale, scale);
sprites[i].draw(batch);
}
batch.end();

timePassed += elapsed;
frames++;
if(timePassed > 1.0f) {
Gdx.app.log("SpritePerformanceTest2", "fps: " + frames);
timePassed = 0;
frames = 0;
}
}

@Override public boolean needsGL20 () {
return false;
}
}


As you can see both tests load two imgages (32x32 RGB and RGBA images to be exact), create 25k sprites using the first image, 25k sprites using the second image and draw them brute force. To spice things up a little i also scale and rotate each sprite in each iteration. Half of the sprites are blended, the other half isn't.

Now, as i understand the latest SFML 2 code which i used for this your stuff is all shader based. Ours can work with shaders as well, and also interoperate with normal GL operations much like SFML. For the test above i used the GLES 1.x path, meaning no shaders are involved but only fixed function stuff. Both tests output the number of frames per second, each second.

Here are the results on an Asus EEE 1215n, with an Nvidia ION2 card, the latest drivers and vsynch forced off. SFML was compiled in release mode, as was the test project itself of course, with /O2 in Visual Studio 2008 Express. On the Java side i use the latest client JVM from Oracle.

Code: [Select]

SFML:
fps: 7
fps: 6
fps: 6
fps: 6
fps: 6
fps: 6

Libgdx:
SpritePerformanceTest2: fps: 24
SpritePerformanceTest2: fps: 24
SpritePerformanceTest2: fps: 24
SpritePerformanceTest2: fps: 24
SpritePerformanceTest2: fps: 24
SpritePerformanceTest2: fps: 24
SpritePerformanceTest2: fps: 24
SpritePerformanceTest2: fps: 24


I think this illustrates the case for batching. If you look at the java code you'll see that the difference is the SpriteBatch.begin() and SpriteBatch.end() calls. With the current SFML API there's no need for such explicit batching. However, we decided against the SFML model for two reasons: you are likely to not mix 2D and 3D rendering like draw sprite, draw 3D model, draw sprite and so on. It's also a lot better in terms of state handling since you are forced to cleanly separate your 2D and 3D rendering. I can see though that the SFML model has its merits, especially for newcomers as it eases the code a little bit.

I zipped up the Visual Studio 2008 project which is self contained. You can download it from here. The source code for the Java test can be found at http://code.google.com/p/libgdx/. A runnable cross-platform jar (Windows/Linux/Mac, 32 and 64-bit) can be downloaded from here. To see the fps output you have to start the jar from the command line like this

Code: [Select]

java -jar bench.jar


You can also double click the jar, at least on windows and the thing will start automatically. Without a console though, but you should see the visual difference in performance.

In any case, i think batching should be reconsidered for SFML 2. Laurent, you could check out our SpriteBatch, Sprite and SpriteCache classes if you are interested in our approach. Given that our crap is Java based it should actually perform even better in C++. Because we all know C++ is 10 times faster than Java, right? :)

Goody, sorry for the wall of text. Keep up your extremely good work. I'm looking forward to the full 2.0 release. As it stands it's already mighty awesome, i'll give it a try in one of my next projects maybe. I really want to get back to C++ again a little, i only write some stuff for our lib in C++ for Android cause their VM sucks.

Pages: [1]
anything