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

Author Topic: Another design question  (Read 3639 times)

0 Members and 1 Guest are viewing this topic.

masskiller

  • Sr. Member
  • ****
  • Posts: 284
  • Pointers to Functions rock!
    • MSN Messenger - kyogre_jb@hotmail.com
    • View Profile
    • Email
Another design question
« on: January 05, 2013, 05:14:45 pm »
This one's much simpler than the previous one, I made my own custom sprite meant to be used for groups of sprites that use the same texture and I am making the native container that allows me to handle it with ease.

The custom sprite has the four vertices and all you would normally expect with a sprite with the difference that it uses its center as a position point and that it isn't neither drawable nor transformable (self implemented transformations). The container works pretty much like a wrapper of a std::vector of vertices, however it differs from sf::VertexArray.

class AltSpriteHolder : public sf::Drawable
{

private:
        unsigned Quantity;

        sf::Texture Tex;

        std::vector<AltSprite> AS_Holder;;
        std::vector<sf::Vertex*> RefHolder;;
};
 


As you can see it needs to hold all my custom sprites and draw them accordingly. I use a vector of vertex pointers and not normal vertices because the respective vertices are already contained in the sprite and copying them everytime I need to update isn't really helping me with mantaining performances (the purpose of all this) high.

However after looking at many parts of SFML sources I got the doubt of  whether the vector of pointers will work or not at drawing time. VertexArray grants the performance boost due to reducing draw calls when using a single texture and having contiguous memory that helps it further. However while with pointers I won't have to copy anything I don't know if they'll work or not. And in the case the pointers aren't enough, the constant copying would betray the purpose of the container.

Here's the code for its draw function:

void AltSpriteHolder::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
    states.texture = &Tex;
    target.draw(RefHolder[0], static_cast<unsigned>(RefHolder.size()), sf::Quads, states);
}
 
 

Summarizing, the question is whether the pointers will actually work/draw (like a vector of sf::sprites) and be faster than a std::vector of sprites or the lack of contiguous memory will actually affect its speed badly or even make it not work.

If you have any ideas on how to make it better feel free to tell them, I'm pretty much out of them.
Programmer, Artist, Composer and Storyline/Script Writer of "Origin of Magic". If all goes well this could turn into a commercial project!

Finally back into the programming world!

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10819
    • View Profile
    • development blog
    • Email
Re: Another design question
« Reply #1 on: January 05, 2013, 05:55:58 pm »
So did I correctly understand that your vertex pointers point to array of vertices in the sprite?

Things should work fine, if you pass a valid pointer and the correct size, thus if you pass a pointer to just one vertex and 1 as size, then it will work nicely and if you pass a pointer to the first element of an array and give the correct size of the array it should also work.

If you meant to draw all the vertices within the RefHolder then you'd have to do it differently.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Another design question
« Reply #2 on: January 05, 2013, 06:23:57 pm »
It doesn't work.

RefHolder[0] is a sf::Vertex* which points to the first sprite's vertices, so:
- there are 4 of them, not RefHolder.size()
- you lose all the other elements of RefHolder

Your strategy won't work. To speed up drawing you need to do less draw calls. And a single draw call can only draw a contiguous array of vertices. So to make your code work you'd need as many draw calls as elements in your RefHolder array, and that wouldn't be better than directly drawing sf::Sprites.

Things are not that simple, you can't batch sprites without copying their vertices to a unique contiguous area. So they have to be static somehow -- batching together sprites that change all the time is not easy, otherwise SFML would do it ;)
« Last Edit: January 05, 2013, 06:25:28 pm by Laurent »
Laurent Gomila - SFML developer

masskiller

  • Sr. Member
  • ****
  • Posts: 284
  • Pointers to Functions rock!
    • MSN Messenger - kyogre_jb@hotmail.com
    • View Profile
    • Email
Re: Another design question
« Reply #3 on: January 05, 2013, 06:28:34 pm »
Quote
Your strategy won't work. To speed up drawing you need to do less draw calls. And a single draw call can only draw a contiguous array of vertices. So to make your code work you'd need as many draw calls as elements in your RefHolder array, and that wouldn't be better than directly drawing sf::Sprites.

Things are not that simple, you can't batch sprites without copying their vertices to a unique contiguous area. So they have to be static somehow -- batching together sprites that change all the time is not easy, otherwise SFML would do it ;)

Thanks, this is precisely what I wanted to know, I guess I'll have to change how I structured the two classes and do some crazy hack to make them work and give them a nice notation. At least I already have all transformations programmed.
« Last Edit: January 05, 2013, 06:40:30 pm by masskiller »
Programmer, Artist, Composer and Storyline/Script Writer of "Origin of Magic". If all goes well this could turn into a commercial project!

Finally back into the programming world!

FRex

  • Hero Member
  • *****
  • Posts: 1845
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Another design question
« Reply #4 on: January 05, 2013, 06:38:13 pm »
Quote
Things are not that simple, you can't batch sprites without copying their vertices to a unique contiguous area. So they have to be static somehow -- batching together sprites that change all the time is not easy, otherwise SFML would do it ;)
What about transforming the vertices like the cache in target does? And storing own 4 vertices and every time some method is ran, updating that to the pointer that would point to somewhere in the long array and pretransoforming them to there.
Back to C++ gamedev with SFML in May 2023

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Another design question
« Reply #5 on: January 05, 2013, 06:46:08 pm »
That would work, but it requires twice the number of points (transformed and untransformed), to perform all the transformations on the CPU, and to do that everytime something changes in a sprite. It may be faster than drawing the sprites directly in some cases, but it's definitely not the ultimate solution.
Laurent Gomila - SFML developer

masskiller

  • Sr. Member
  • ****
  • Posts: 284
  • Pointers to Functions rock!
    • MSN Messenger - kyogre_jb@hotmail.com
    • View Profile
    • Email
Re: Another design question
« Reply #6 on: January 05, 2013, 06:47:17 pm »
Quote
What about transforming the vertices like the cache in target does? And storing own 4 vertices and every time some method is ran, updating that to the pointer that would point to somewhere in the long array and pretransoforming them to there.

I just thought of something similar. I'll reprogram all my transformations and texture rect functions to work with an index, the index would tell the functions which quad of the vector of vertices it has to modify , it would end up using one class and not two. So that most of the notation is left unchanged for when I start refactoring my engine with this optimization.
« Last Edit: January 05, 2013, 06:50:20 pm by masskiller »
Programmer, Artist, Composer and Storyline/Script Writer of "Origin of Magic". If all goes well this could turn into a commercial project!

Finally back into the programming world!

krzat

  • Full Member
  • ***
  • Posts: 107
    • View Profile
Re: Another design question
« Reply #7 on: January 06, 2013, 08:04:16 pm »
That would work, but it requires twice the number of points (transformed and untransformed), to perform all the transformations on the CPU, and to do that everytime something changes in a sprite. It may be faster than drawing the sprites directly in some cases, but it's definitely not the ultimate solution.

I have XNA style SpriteBatch that rebuilds vertices every frame, and it's still 3x times faster than standard drawing(.Net version). So I wonder if it is necessary to store two matrices and 4 vertices for every sprite (that is a lot of wasted memory).

Test app: https://docs.google.com/open?id=0B74Xwz4um7akZjducDdOMUFOdkk
It uses my SFML.Net fork: https://github.com/krzat/SFML.Net

Usage:
SHIFT - move and rotate sprites
Any other key - switch modes

My results:
Standard drawing(mobile): 8,5ms
Standard drawing(static): 7,5ms //only 1ms / 15% gain from keeping vertices!
Batched drawing: 2,2ms

EDIT:
I have ported my code to C++ and these are results:
Standard drawing(mobile): 2,5ms
Standard drawing(static): 3,3ms
Batched drawing: 1,7ms

Also, I found out that sin/cos in C# are terribly slow. I replaced them with lookup table, and it is now 1,25ms (as fast as C++ with lookup table). Yay!
« Last Edit: January 07, 2013, 12:16:33 am by krzat »
SFML.Utils - useful extensions for SFML.Net

masskiller

  • Sr. Member
  • ****
  • Posts: 284
  • Pointers to Functions rock!
    • MSN Messenger - kyogre_jb@hotmail.com
    • View Profile
    • Email
Re: Another design question
« Reply #8 on: January 06, 2013, 09:35:11 pm »
In my computer I get 18, 17 and 30. I am nearly finished with the class, it was a pain to rewrite most to work with indexes (it goes against your common OOP approach), but I think it will pay off in the end, once I finish it and test it tomorrow I'll post it in the wiki and maybe I'll do a benchmark if I'm not too lazy.
Programmer, Artist, Composer and Storyline/Script Writer of "Origin of Magic". If all goes well this could turn into a commercial project!

Finally back into the programming world!

masskiller

  • Sr. Member
  • ****
  • Posts: 284
  • Pointers to Functions rock!
    • MSN Messenger - kyogre_jb@hotmail.com
    • View Profile
    • Email
Re: Another design question
« Reply #9 on: January 08, 2013, 05:07:21 am »
I have one last question. I am almost finished with the class, as soon as I fix a bug that's occurring I'll get it done and get to document and upload. I've minimized the problem as much as I could yet I know not why the bug is occurring.

When displaying the texture it gets folded like if the quad was a trapezoid, yet it's a rectangle defined by the texture's rect.

I'll post the minimal test program and the code that powers it.

Here's main.

sf::RenderWindow window(sf::VideoMode(1280, 800, 32), "Bullet Testing", sf::Style::Fullscreen);
unsigned MaxNum = 1;
AltSpriteHolder ASH(MaxNum);
sf::Texture T;
    if(!T.loadFromFile("C:/Users/Carlos/Desktop/Sprites/White Mana.png"))
        return -1;
ASH.setTexture(T);

window.setFramerateLimit(60);
    while (window.isOpen())
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        window.draw(ASH);
        window.display();

        ///Events
    }

 

And here's the remaining code:

AltSpriteHolder::AltSpriteHolder(const unsigned amount)
  :VertexHolder(), PositionHolder(), Quantity(amount), ScaleHolder(), AngleHolder(), Tex(nullptr)
{
    VertexHolder.reserve(amount*4);
    PositionHolder.reserve(amount);
    ScaleHolder.reserve(amount);
    AngleHolder.reserve(amount);

    for(unsigned i = 0; i < amount; ++i)
    {
        for(unsigned j = 0; j < 4; ++j)
        { VertexHolder.push_back(sf::Vertex(sf::Vector2f(0.f, 0.f), sf::Color(255, 255, 255, 255))); }
        ///Filling the remaining containers, not relevant.
    }
}

void AltSpriteHolder::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
    states.texture = Tex;
    target.draw(&VertexHolder[0], static_cast<unsigned>(VertexHolder.size()), sf::Quads, states);
}

void AltSpriteHolder::setTexture(sf::Texture& T)
{
    Tex = &T;
    setGlobalTextureRect(sf::IntRect(0, 0, Tex->getSize().x, Tex->getSize().y) );
}

void AltSpriteHolder::setTextureRect(const unsigned index, const sf::IntRect& IR)
{
    TexRectHolder[index] = IR;
    updateTexCoords(index);
    updateVertexCoords(index, false);
}

void AltSpriteHolder::setGlobalTextureRect(const sf::IntRect& IR)
{
    for(unsigned i = 0; i < Quantity; ++i)
    { setTextureRect(i, IR); }
}

void AltSpriteHolder::updateTexCoords(const unsigned index)
{
    float left = static_cast<float>(TexRectHolder[index].left);
    float right = left + TexRectHolder[index].width;
    float top = static_cast<float>(TexRectHolder[index].top);
    float bottom = top + TexRectHolder[index].height;

    unsigned I = index * 4;

    VertexHolder[I].texCoords = sf::Vector2f(left, top);
    ++I;
    VertexHolder[I].texCoords = sf::Vector2f(left, bottom);
    ++I;
    VertexHolder[I].texCoords = sf::Vector2f(right, bottom);
    ++I;
    VertexHolder[I].texCoords = sf::Vector2f(right, top);
}

void AltSpriteHolder::updateVertexCoords(const unsigned index, const bool Reset)
{
    sf::Vector2u S;
    S.x = TexRectHolder[index].width * ScaleHolder[index].x;
    S.y = TexRectHolder[index].height * ScaleHolder[index].y;

    unsigned I = index * 4;
 
    VertexHolder[I].position = sf::Vector2f( -(S.x/2.f), -(S.y/2.f) );
    ++I;
    VertexHolder[I].position = sf::Vector2f( -(S.x/2.f), (S.y/2.f) );
    ++I;
    VertexHolder[I].position = sf::Vector2f( (S.x/2.f), -(S.y/2.f) );
    ++I;
    VertexHolder[I].position = sf::Vector2f( (S.x/2.f), (S.y/2.f) );

    if(Reset)
    {
    PositionHolder[index].x = 0.f;
    PositionHolder[index].y = 0.f;
    }
    else
    { move(index, PositionHolder[index]); }
}
 

I've attached the bug image and the image as it's supposed to be. Note that the good image is the clean png, not a displayed image so it clearly lacks background.


[attachment deleted by admin]
Programmer, Artist, Composer and Storyline/Script Writer of "Origin of Magic". If all goes well this could turn into a commercial project!

Finally back into the programming world!

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Another design question
« Reply #10 on: January 08, 2013, 08:02:55 am »
Your texture coordinates don't match the positions.

Positions (Y only): top bottom bottom top
Texture coordinates (Y only): top bottom top bottom
Laurent Gomila - SFML developer

masskiller

  • Sr. Member
  • ****
  • Posts: 284
  • Pointers to Functions rock!
    • MSN Messenger - kyogre_jb@hotmail.com
    • View Profile
    • Email
Re: Another design question
« Reply #11 on: January 08, 2013, 03:24:32 pm »
Thanks, I had checked it many times yesterday as I thought that was it, but lack of sleep had already taken it's toll and I didn't realize regardless.
Programmer, Artist, Composer and Storyline/Script Writer of "Origin of Magic". If all goes well this could turn into a commercial project!

Finally back into the programming world!

masskiller

  • Sr. Member
  • ****
  • Posts: 284
  • Pointers to Functions rock!
    • MSN Messenger - kyogre_jb@hotmail.com
    • View Profile
    • Email
Re: Another design question
« Reply #12 on: January 08, 2013, 04:36:27 pm »
Now I've realized something quite interesting, while drawing now takes short time when compared to as it was before, the FPS lag on a large amount of vertices is still there. After profiling I've realized that most of the time (with a massive 25000 ms as average, draw on 16000 vertices takes 5000-6000 tops) is taken by the display call. 

It's obvious that run-time is bound to be slow in this computer due to the fact that it's low end, 5 and a half years old and with Intel Graphics, however 25000 ms is quite something, and vertical sync does nothing to salvage it. This lag caused in display pretty much makes it as slow as a vector of sf::Sprites.

The container clearly has better overall rates (12000(vector of sprites) + 2200(transforms) vs 5500 (selfmade container) + 5000 (transforms, needs micro-optimizations) ) than a vector of sf::sprites. The display function on a 16000 vertices test case lasts from 25000ms on my container and 30000ms on the sprites.

I can't use sf::Transform because if I'm not mistaken it would only work for transforming all vertices and not an specific quad (only one draw call vs N amount of draw calls gives you this limit).

The lag display() creates is most likely that big due to my crappy graphics card, however I want to be sure that I can't do anything before giving up on rising performances.
Programmer, Artist, Composer and Storyline/Script Writer of "Origin of Magic". If all goes well this could turn into a commercial project!

Finally back into the programming world!