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.


Topics - Josh

Pages: [1]
1
Graphics / Random spikes in time taken to draw objects
« on: February 17, 2013, 10:56:48 am »
I've been noticing some stuttering in my game, which is kind of annoying. I did some profiling and it seemed to be centered around a section of code dealing with some render textures.

I've been able to form this minimal program:

#include <SFML/Graphics.hpp>
#include <stdio.h>

#define NUMLOOPS 2000

#define WIDTH 1600
#define HEIGHT 900

int main() {

    sf::RenderWindow  window( sf::VideoMode( WIDTH, HEIGHT, 32 ), "Lag Spikes" );

        sf::RenderTexture renderTexture;
        sf::Sprite renderSprite;
        renderTexture.create(WIDTH, HEIGHT);
        renderSprite.setTexture(renderTexture.getTexture());

        sf::VertexArray tileDetails;
        tileDetails.setPrimitiveType(sf::Triangles);

        sf::Clock deltaClock;

        FILE *fp;
        fopen_s(&fp, "timings.xls", "w");
        fprintf_s(fp, "Frame Time\tClear Time\tDraw Time\tDisplay Time\n");
        srand(0);

        float records[NUMLOOPS][4];
        for(int i = 0; i < NUMLOOPS; i++)
        {
                //Frame time for previous frame
                records[i][0] = deltaClock.restart().asSeconds();

                window.clear(sf::Color::Cyan);
                records[i][1] = deltaClock.getElapsedTime().asSeconds();

                //Draw to some render textures and draw them to screen
                for(int j = 0; j < 5; j++)
                {
                        renderTexture.clear(sf::Color::Transparent);
                        renderTexture.draw(tileDetails);
                        renderTexture.display();
                        window.draw(renderSprite);
                }
                records[i][2] = deltaClock.getElapsedTime().asSeconds();

                window.display();
                records[i][3] = deltaClock.getElapsedTime().asSeconds();
        }

        //Write out the times
        for(int i = 0; i < NUMLOOPS; i++)
                fprintf_s(fp, "%f\t%f\t%f\t%f\n", records[i][0], records[i][1], records[i][2] - records[i][1], records[i][3] - records[i][2]);
}
 


Essentially, opens a window, makes a render texture and then draws some data to the render texture. It keeps track of the time taken to clear, draw and display each frame and saves it to an excel file. Opening it up and making a graph shows tonnes of spikes in time taken for the draw step. The loop is there to exaggerate the lag spike. Also, my game requires 5 passes, due to some shaders, so I left it in in case it affects the answer.

Here are some example runs:
My pc:


Friend's pc


As you can see, every frame is dominated by the draw time, with occassional spikes in the display time. But then, sometimes this also happens:

Note from frame 1400 onwards, the display time jumps from almost 0ms to 0.7ms, and the draw time drops from around 1.4ms down to 0.7ms. I've noticed this behaviour in my game as well, where sometimes all the rendering time is reported under my render system, and window.display() happens almost instantaneously, and other times my render system renders faster, while the window.display() takes long enough to fill the gap.

Basically, I'm looking for reasons or explanations why both the spikes and the draw/display effect is happening. I'm guessing it might have something to do with interrupts to the graphics card, or some other hardware side effect that I won't have much control over. But figured I would post and see if anyone has any ideas.

2
Graphics / Question About GL States
« on: January 01, 2013, 04:00:24 pm »
I'm using an sf::VertexArray and a multisample shader to draw the tile map in my game. This works fine, unless I try to draw something before it. When I do so, it messes with the texture coordinates in the multisampler. I found this page, which mentions differing coordinates. Sure, enough, when I manually reset the GL_TEXTURE matrix before the shader, it works perfectly. However, I don't do any custom openGL rendering in my program, so why does SFML not handle the states properly itself? I'm happy to manually reset the texture matrix, but was just curious as to why I have to.

Aditionally I can call push/popGLState around each draw call and it works as well, however, calling resetGLStates does not work.

I also tried the fix listed here, saying I should use sf::Shader::CurrentTexture, but that also did not work.

Also, should mention, I'm using one of the latest versions of SFML (Checking commits since my copy and they're all API updates, or irrelevant).

Anyway, here is a minimal app that demonstrates the issue (full app including shader attached):
#include <SFML/Graphics.hpp>

//Only included/linked to manually set GL_TEXTURE matrix to identity
#include <GL/glew.h>
#pragma comment(lib, "opengl32.lib")

int main() {

        //Render window
    sf::RenderWindow  window( sf::VideoMode( 800, 600, 32 ), "GL States Issue" );

        //Multisampler textures
        sf::Texture prime1Tex;
        sf::Texture prime2Tex;
        sf::Texture prime3Tex;
        prime1Tex.loadFromFile("prime1.png");
        prime2Tex.loadFromFile("prime2.png");
        prime3Tex.loadFromFile("prime3.png");

        //Mutlisample shader - Samples from 3 repeating prime textures to significantly reduce tiling patterns
        sf::Shader multisamplerShader;
        multisamplerShader.loadFromFile("multisampler.sfx", sf::Shader::Fragment);
        sf::Vector2f size;
        multisamplerShader.setParameter("prime1Tex", prime1Tex);//sf::Shader::CurrentTexture);
        size.x = 1.0f / prime1Tex.getSize().x;
        size.y = 1.0f / prime1Tex.getSize().y;
        multisamplerShader.setParameter("prime1Size", size);
        multisamplerShader.setParameter("prime2Tex", prime2Tex);
        size.x = 1.0f / prime2Tex.getSize().x;
        size.y = 1.0f / prime2Tex.getSize().y;
        multisamplerShader.setParameter("prime2Size", size);
        multisamplerShader.setParameter("prime3Tex", prime3Tex);
        size.x = 1.0f / prime3Tex.getSize().x;
        size.y = 1.0f / prime3Tex.getSize().y;
        multisamplerShader.setParameter("prime3Size", size);

        //Random extra sprite to draw
    sf::Sprite proj;
        sf::Texture projTex;
        projTex.loadFromFile("randomsprite.png");
        proj.setTexture(projTex);

        //Vertex array storing tile data.
        sf::VertexArray tileDetails;
        tileDetails.setPrimitiveType(sf::Triangles);
        tileDetails.append(sf::Vertex(sf::Vector2f(20.0f, 20.0f), sf::Vector2f(20.0f,20.0f)));
        tileDetails.append(sf::Vertex(sf::Vector2f(20.0f, 500.0f), sf::Vector2f(20.0f,500.0f)));
        tileDetails.append(sf::Vertex(sf::Vector2f(500.0f, 500.0f), sf::Vector2f(500.0f,500.0f)));

        bool run = true;
        while(run)
        {
                window.clear(sf::Color::Cyan);

                //Removing all these push/popGLState calls causes the multisample shader to mess up
                //window.pushGLStates();
                window.draw(proj);
                //window.popGLStates();

                //Just using this works
                glMatrixMode(GL_TEXTURE);
                glLoadIdentity();
               
                //Just using this does not work
                //window.resetGLStates();

                //window.pushGLStates();
                //Even trying the fix of setting the first texture in the shader to sf::Shader::CurrentTexture, and
                //then passing it in through sf::RenderStates does not work. It changes how the shader messes up however
                sf::RenderStates states;
                //states.texture = &prime1Tex;
                states.shader = &multisamplerShader;
                window.draw(tileDetails, states);
                //window.popGLStates();
               
                window.display();
               
                sf::Event Event;
               
                while (window.pollEvent(Event))
                {
                        if ((Event.type == sf::Event::KeyPressed && (Event.key.code == sf::Keyboard::Escape)))  {run = false; }
                }
        }
}
 

[attachment deleted by admin]

3
I'm currently working on a metaball shader effect for a projectile system (Example here).

Essentially what happens is I render preblurred particles to sf::RenderTexture, then render that texture to the main screen using a shader which does the thresholding part of the effect (Checks if fragment is brighter than certain value, if so returns specific colour). I have to do this for 5 different output colours, so rather than draw to 5 separate sf::RenderTextures, and then draw all of those separately, I figured I'd pack the data a bit more efficiently.

Since I only need an intensity value to be passed to the threshold shader, I can store that only in the red channel of the sf::RenderTexture, and then store a value in the green channel so the threshold shader can determine which of the 5 output colours it should use. To do this, I use 5 different sprites, like this:

Where red = alpha and goes from 0 to 255 the closer it gets to the center. Blue channel = 0 for every pixel, and green channel = 51 for this sprite (Each sprite is some multiple of 0.2, so I can multiply the green channel by 5 to get an integer between [0, 4])

Ultimately, it would mean I could process all 5 colours of projectiles in a single sf::RenderTexture and threshold shader pass.

However, the problem is, when the projectile sprites are drawn to the sf::RenderTexture, I have issues with the blending. Using the default BlendAlpha, the green tag values get blended, resulting in incorrect colours being picked. Using BlendNone writes the the top most (last drawn) projectile's green channel correctly, but messes with the blending of the red channel.

I've searched a bit, and unfortunately it seems I can't use a custom blend function to do BlendAlpha on the r/a channels and BlendNone on the g/b channels.

So instead, I figured I'd use BlendNone, but write a shader to blur the sprite with what is already there on the render texture. So, I pass the sf::RenderTexture::getTexture() to the shader, along with the sprite and do the following:

uniform sampler2D projectiletex;
uniform sampler2D bgtex;

void main()
{
        vec4 spritepixel = texture2D(projectiletex, gl_TexCoord[0].xy);
        vec4 bgpixel = texture2D(bgtex, gl_TexCoord[0].xy);
               
        vec4 res;

        res.r = spritepixel.r * spritepixel.a + bgpixel.r * (1.0 - spritepixel.a);
        res.g = spritepixel.g;
        res.b = 0.0;
        res.a = spritepixel.a * spritepixel.a + bgpixel.a * (1.0 - spritepixel.a);
       
        gl_FragColor = res;
}
 

However, this blends the entire render texture into the confines of the sprite, not just the region the sprite covers, resulting in each projectile becoming a square texture on the sf::RenderTexture, and without any of the blending I wanted. So I figured I perhaps I had to use gl_TexCoord[1].xy for one of the textures, but this does not work.

I've tried setting the gl_TexCoord[1] through a vertex shader, but it did not work (I doubt I did it right).

So basically, how do I use two textures (of different sizes) in the one shader. (Or if anyone knows a simpler way to blend the red channel but just copy the green channel, I'd love to hear it).

For the sake of completeness, here is the threshold shader that takes the sf::RenderTexture and draws on the main game screen:

#version 120

uniform sampler2D imagetex;

vec3 colours[5] = vec3[5](vec3(230.0/255.0, 0, 0),
                                              vec3(1.0, 0.88, 0),
                                                  vec3(0, 0.16, 0.898),
                                                  vec3(0, 230.0/255.0, 0),
                                                  vec3(0.9, 0.9, 0.9));
                                                 
void main()
{
        vec4 pixel = texture2D(imagetex, gl_TexCoord[0].xy);
       
        float threshold = pixel.r;
        int currentColour = int(pixel.g * 5.0); // All green channels should be one of (0.0, 0.2, 0.4, 0.6 or 0.8)
       
        //If fragment is "bright" enough, set it to the defined colour, otherwise discard
        if(threshold > 0.5)
                gl_FragColor = vec4(colours[currentColour], 1.0);
        else
                gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
}
 

Thanks

4
Graphics / PostFX usage in SFML 1.6
« on: May 06, 2010, 10:35:30 am »
Hey,

I'm writing a game in SFML 1.6 that requires I colourise various images on the screen (i.e. platforms, enemies, etc). Each game object has a sprite which refers to common images in my resource manager.

What I originally tried to do was store the original, uncolourised image in the resource manager, then apply the PostFX to the sprite as I was drawing it (As I'd do when writing games with directx), however, I found out I couldn't do that. That is, according to the documentation I either have to apply the PostFX to the screen or to an image.

So I decided to just store a copy of each colourised image in the resource manager. I only need a handful of different copies anyway (As in, I'm only colourising to 5-6 discrete colours). So, I load the original image, create a new image which copies the original image, and then try to apply the PostFX to that.

However, I'm unsure how to actually tell the PostFX to apply the effect. For the frame buffer, you call a draw on the effect, and that applies it to everything drawn previously, but how do I do it for an image in memory?

Or do I have it wrong and applying the effect on the image does what I originally wanted (As in, applies it only to the drawn instance rather than the image in memory)?  If so, how do I do this properly, since calling sprite.GetImage() as the second parameter for the SetTexture call returns a const mismatch error.

The code that changes the images (in memory, I'm hoping) looks like:
Code: [Select]
tempEffect = ShaderManager::Get()->getEffectByIndex(tempIndex);
tempEffect->SetTexture("framebuffer", (&initial));
tempEffect->SetParameter("colour", (float)colours[red].r/255,(float)colours[red].g/255,
(float)colours[red].b/255);

App->Draw((*tempEffect));


Initial is the sf::Image& which contains the pixels I'd like to colourise. tempEffect is the sf::PostFX which does this. I'm just not sure how to actually "submit" it (i.e. I'm not sure what the last line should be)

Any suggestions would be much appreciated,
Josh

Pages: [1]
anything