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 - Nexus

Pages: [1] 2 3 ... 391
1
SFML development / Re: SFML Move Semantics
« on: August 03, 2020, 11:10:48 pm »
I can be biaised by what I need, but I suggest to first add move semantic for simple classes, that is which do not need custom logic.
For those, the compiler-generated move constructor/operator= would already do the correct thing, or am I misunderstanding you?

Which method to use may be important for consistency, but maybe there should be no more than few tens of classes that supports move semantics, so rewriting style from one method to another may take not long.
Yeah, since there hasn't been much feedback so far, I would suggest we choose one style and go for it. Doing it consistently still makes sense though. It's confusing for everyone if every move ctor/op= is implemented with a different idiom, and it creates needless work to refactor.

2
General discussions / Re: Thor and Animation
« on: August 03, 2020, 11:06:50 pm »
Regarding first post: the API changed a bit between Thor versions, in case of doubt generate the docs directly from source (Doxygen option in CMake).

Regarding second post: you're doing it extremely complicated, and with a large amount of boilerplate.
The normal algorithm is this:
sf::Vector2 vel;
if (up)
  vel.y -= 1;
if (down)
  vel.y += 1;
if (left)
  vel.x -= 1;
if (right)
  vel.x += 1;

// account for diagonal movement

3
SFML development / Re: SFML Move Semantics
« on: July 26, 2020, 11:20:36 am »
Regarding my PR, I'd like to get some feedback, also from other  SFML team members ;)

Anyone? :)
(also SFML users, not just team)

4
General / Re: The shortest possible way to resize my image?
« on: July 15, 2020, 07:31:00 pm »
SFML doesn't provide resizing, because there is no single algorithm for all purposes. There are in fact loads of scaling algorithms, all with different trade-offs. If you need one of them, you might want to look for a specific library.

As Hapaxia said, if you don't care about image quality/information loss, simply render a sprite to a render-texture and then save its contents. Don't forget to call display().

5
SFML development / Re: SFML Move Semantics
« on: July 09, 2020, 10:27:53 pm »
Those suggestions sound good. There are fortunately many classes in SFML, for which the default semantics work well.

But we need to be careful: sf::Image for example requires no special member functions at the moment (although there's a destructor doing nothing). However, a class with no special member functions declared is subject to implictly generated special member functions (aka Big Five). This would break in C++11, as a newly introduced move constructor does a member-wise move of each field, leaving the std::vector and sf::Vector2u fields inconsistent to each other. Ironically, the no-op destructor -- since declared -- prevents exactly that: sf::Image has no move ctor, even if compiled with a C++11 compiler.

Regarding my PR, I'd like to get some feedback, also from other  SFML team members ;)

6
SFML development / Re: SFML Move Semantics
« on: July 05, 2020, 01:25:15 pm »
A note worth mentioning is that if the internal handles were unique pointers then default move constructors would be perfect. Maybe this is a more valid approach for move semantics in some cases.
In sf::Shader, the private members are those:

    ////////////////////////////////////////////////////////////
    // Types
    ////////////////////////////////////////////////////////////
    typedef std::map<int, const Texture*> TextureTable;
    typedef std::map<std::string, int> UniformTable;

    ////////////////////////////////////////////////////////////
    // Member data
    ////////////////////////////////////////////////////////////
    unsigned int m_shaderProgram;  //!< OpenGL identifier for the program
    int          m_currentTexture; //!< Location of the current texture in the shader
    TextureTable m_textures;       //!< Texture variables in the shader, mapped to their location
    UniformTable m_uniforms;       //!< Parameters location cache

The two std::maps would move fine (emptying the source), but the integers will be copied. So, the default move implementation would leave a half-working instance behind.

As mentioned, in SFML we need to decide whether we give guarantees on moved-from objects or not.
For the sake of least surprise and simplicity, it might be more user-friendly to make moved-from objects behave like default-constructed ones.

In that regard, your current implementation is inconsistent:
sf::Shader a = ...;
sf::Shader b = std::move(a); // a is now empty
however:
sf::Shader a = ...;
sf::Shader b = ...;
b = std::move(a) // a now has contents of b

Since you already implement Move-and-Swap (but backwards), you might as well reuse swap() to always empty the source. In general, it's better to call the copy/move constructor in the copy/assignment operator, not vice versa.

A generic approach is this. It does a bit more than necessary, but this pattern can be applied everywhere, and the advantage is that the actual logic is in the swap() and only there --- move ctor/op= don't need to be changed when the fields change.
class Shader
{
    Shader();
   
    Shader(Shader&& moved)
    : Shader()
    {
        moved.swap(*this); // empties source
    }

    Shader& operator= (Shader&& moved)
    {
        Shader tmp(std::move(moved)); // invoke move ctor, empties 'moved'
        tmp.swap(*this);
        return *this;
    } // destroy 'tmp',

    void swap(Shader& other); // actual logic is here
}

We should also establish a naming convention for parameter names. For copy constructors, SFML currently uses copy. This is slightly misleading, as the parameter is the object being copied (origin) and not the copy (replica). In Thor I used origin and source, but I think we can be more precise.

What about copied and moved?
This makes it immediately clear that you're dealing with a copy or move ctor/op=.



[Edit] I made an example how I would probably implement it here:

https://github.com/SFML/SFML/tree/feature/move-semantics

One commit with unified copy/move op=, the whole branch diff with separate ones.
Not tested -- those things might be a candidate for unit tests.

7
SFML development / Re: SFML Move Semantics
« on: July 05, 2020, 12:46:57 pm »
However, I really want behaviour to be logical, and I don't think that swapping internal state for resources like windows during move operations is logical.

A classical implementation of Copy-and-Swap would be like this:
class Image
{
        Image();
        Image(const Image& copied);
        Image(Image&& moved);

        void swap(Image& other);

        Image& operator= (const Image& copied) {
                Image tmp(copied);
                tmp.swap(*this);
                return *this;
        } // object 'tmp' (with previous contents of *this) is destroyed

        Image& operator= (Image&& moved)
        {
                moved.swap(*this);
                return *this;
        } // object 'moved' (with previous contents of *this) is destroyed
};

Since the temporary object is destroyed after a copy operation, it does not matter what it contains. A moved-from object is typically destroyed after the move as well, but there are exceptions like std::unique_ptr which define a behavior. So, unless SFML would explicitly guarantee that a moved-from image is equal to an empty image, we can put any valid content into moved-from objects. Otherwise we would simply involve a default-constructed object to "reset" the image after moving.

The reason why this idiom exists in the first place, is because the naive (or compiler-generated) implementation does this:
        Image& operator= (const Image& copied)
        {
                member0 = copied.member0;
                member1 = copied.member1;
                // ...
                memberN = copied.memberN;

                return *this;
        }

        Image& operator= (Image&& moved)
        {
                member0 = std::move(moved.member0);
                member1 = std::move(moved.member1);
                // ...
                memberN = std::move(moved.memberN);
       
                return *this;
        }

Now imagine that during "...", an exception occurs while copying/moving. The assignment operation is aborted, but both objects are in an inconsistent, "half-moved" or "half-copied" state. This is mostly a problem for copies, as moves should be designed to not fail (however, this is not always possible, e.g. when moves fall back to copies, for raw arrays for example).

What Copy-and-Swap does is -- instead of overwriting the object in-place -- first creating a new valid object, swapping with that and then destroying the old one. Since both swap and destroy operations should not fail, only the construction of the new object can fail, in which case the object is left in the previous, valid state.




So much about the theory. What I mentioned above is absolutely crucial when designing generic building blocks like containers, smart pointers, graphs or other data structures. You don't know what the user will put inside, so you have to assume copies can fail, and design the code accordingly.

Now, in practice, you often have better knowledge about what types are involved, and can exploit this knowledge to simplify code. If SFML resorts to not throwing exceptions during copy/move operations, and has no APIs where user-defined types could trigger exceptions, we can avoid the extra effort and try to use the compiler-generated methods where possible.

Pragmatically, I would attempt to use default move (and even copy) constructors/assignment operators wherever possible -- it's just so much easier to reason about code, to add new fields, etc. But since SFML operates with resources in several places, it involves custom ownership semantics here and there. These are the cases we need to analyze and determine what is the greatest common denominator.

8
SFML development / Re: SFML Move Semantics
« on: July 04, 2020, 11:10:12 am »
First, there are different levels of exception guarantees, specifying how involved objects behave in the presence of exceptions:
  • strong - every object is always in a semantically valid state
  • basic - every object is still destructible and doesn't evoke UB
  • none - no guarantees

There are several idiomatic ways to implement the special member functions with C++11, to sum up:

1. Rule Of Five
Overload copy/move ctor, copy/move op= and dtor.
This is the "stupid" approach which requires a big amount of boilerplate, code duplication and thus is rather prone to errors when extending a class.

2. Copy-and-Swap
Overload all 5 special methods + swap(). Use swap() in copy/move op=.
Avoids some code duplication, but still a lot of boilerplate.
Achieves strong exception guarantee (each object is always in a valid state).

3. Rule of Four-and-a-half
Overload copy/move ctor and dtor as usual.
Overload op= taking a value (not const- or rvalue-reference), which means it acts as both move and copy assignments.

4. Rule of Zero
Let the compiler generate the Big Five.
Code complexity wise, this is the best solution, as the behavior is clear and no member can be forgotten. It does not work when there are complex ownership rules, or custom copy logic (for handles).
I've found my smart pointer aurora::CopiedPtr to be a very nice fit for pointers which require deep copies. It can easily reduce the number of methods needed from 5 to 0. For example, in Thor I need it for quite a few things.
Downside: with compiler-generated member-wise copy, the exception guarantee is often reduced to basic (when the copy operation aborts in the middle).

I'm wondering if strong exception guarantee is something we really have to go for in SFML. Generally, I'm more in favor of readable/compact code. Having to implement the Big Five for every 2nd class is also often an indication of bad ownership design or lack of internal abstraction (too many types have custom ownership rules).

9
General / Re: thrown exception to draw?
« on: June 27, 2020, 01:09:42 pm »
Based on the address, it looks like you're dereferencing a null pointer.

Also read this post and follow the instructions.

10
Feature requests / Re: why sfml not support dds image type
« on: June 27, 2020, 01:02:33 pm »
SFML doesn't just support every possible image format for the fun of it. These formats are not very common in our domain, so few users would benefit from them.

If you disagree, maybe elaborate common use cases and argue why those would be a great addition to SFML. Also, it would definitely help to present a strategy towards implementation, as resources are limited ;)

11
Graphics / Re: Maximum number of quads in a sf::VertexArray ?
« on: June 21, 2020, 09:43:17 pm »
@Nexus: in your example you choose 100x100 cells because 10^4 quads is what the average gpu can handle easily at once?
No, it was just an example with arbitrary numbers. I don't know how many quads can be handled.

You may need to experiment to find what works best. With smaller regions, you are on the safer side, but it costs you some extra drawcalls.

12
Graphics / Re: Maximum number of quads in a sf::VertexArray ?
« on: June 21, 2020, 02:59:20 pm »
To extend eXpl0it3r's answer:

Quote
Put it in an other way, is there a maximal size above which it is not safe/performant to use sf::VertexArray ? Will SFML or OpenGL automatically split my sf::VertexArray if it is too big for the buffer of my GPU ?
No, SFML/OpenGL will not split the vertex array.

Apart from VRAM, you also are limited by RAM -- since sf::VertexArray is backed by an std::vector, you need enough contiguous memory to store all the cells. Even if modern machines come with lots of RAM, you need a big enough chunk of memory at once.

What I would probably do is hierarchical rendering. Split the world into regions, where one region is e.g. 100x100 cells. Render each region separately, and only if it's in view.

There are futher optimizations you can do:
  • Obviously, only update sf::VertexArray if the cells in that region change
  • Do not recreate/assign the vertex array, instead use clear() + append() or resize() + operator[]
  • Depending on your graphics, it may be enough to render the active cells (often a sparse subset of all cells)
  • At some zoom level, you may end up with so many tiny cells that the quad representation is inefficient, and you can e.g. render dots instead, or use a shader

13
SFML development / Re: SFML Move Semantics
« on: June 21, 2020, 01:12:12 pm »
You mention some good points. I also think a modular approach makes more sense -- could even be on a class/group of classes basis, to have smaller incremental steps that can be reviewed in reasonable time.

I expect that after the release of SFML 2.6, the master branch will start targeting SFML 3 and new features will be merged against that. eXpl0it3r probably knows the approach in more details here.

So, what's left to decide is really:

are move semantics pressing enough to introduce them into SFML 2.x
or should we prioritize making a development branch available for SFML 3, so that PRs with new features can be processed?

14
SFML development / Re: SFML Roadmap
« on: June 21, 2020, 01:04:51 pm »
Error Handling
I don't like the idea of having resources constructed through factory methods. Once we get move semantics implemented, you could implement your own factory methods that handle errors in what ever way you like. Forcing an error handling method would be an opinionated debate with no right or wrong answers.
The library still has to provide this functionality. "Opinionated" in this sense mostly means "consistent" and "that's the recommended way of doing it".

Currently, this API is
sf::Texture texture;
if (!texture.loadFromFile(...))
{
    // error handling
}
and this is equally opinionated, but has several technical drawbacks: easy to forget checking the return type, no const variables, no way to get error message/code, unclear semantics when re-loading (and failing), dealing with invalid (unloaded) states, etc.

Of course it can be discussed if this would remain the best API for SFML 3, but keep in mind that the original reason to do it like this was because SFML doesn't use exceptions, so constructors cannot fail.

Constructors on the other hand are not a good idea, as they cannot specify the source (file, memory, stream...), and this would need to be dispatched solely based on parameter types. There would already be conflicts now, e.g. with sf::Shader.

Hence the suggested named constructor idiom (factory) -- it's an established idiom and shines especially in combination with move semantics.

15
SFML development / Re: SFML Move Semantics
« on: June 18, 2020, 11:33:41 pm »
Keep also the SFML Roadmap thread and its ongoing discussions in mind, when providing additions possibly targeted to SFML 3.

Some things may still change :)


Quote from: Laurent
What exactly is a huge change? To stop supporting non-C++11 compilers? I don't think it's that huge ;)
I was thinking the same here, and maybe this is a part that should be discussed in the roadmap. The biggest risk I see is that SFML 3 is again several years away, and people have to wait even longer for useful additions like move semantics.

Projects that want to do "everything right" at once are often problematic. But as eXpl0it3r mentioned, SFML 3 might be rather short-lived, hoping that we can iterate faster on possible design mistakes.

Pages: [1] 2 3 ... 391