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

Pages: 1 [2] 3 4 ... 11
SFML development / Re: How should SFML handle non-legacy shaders?
« on: August 29, 2016, 09:23:12 pm »
Setting the bindings via the Shader class is all nice and good, however, the actual binding takes place in the RenderTarget. Therefore, either the RenderTarget has to check if custom binding is required by inspecting the Shader objects every draw, or the bindings become part of the RenderTarget instead. The latter variant would make the inner workings of the RenderTarget transparent to the users who don't care one bit about shaders in their application.

To clarify, is the concern here having a public function in the RenderTarget class for setting these shader param bindings?

If so, one way around that is to make those functions private (so not exposed in the public API), have the RenderTarget friend the Shader class, make a public API in the Shader class to set the bindings, which then calls the private function on the RenderTarget.
... A little convoluted, but it at least keeps the public API for the RenderTarget clean of any shader-related stuff.

(edit) For this to work, the binding vars would need to be static, and shared across all RenderTargets, but that's likely what you'd want anyway.

How does the user determine/control which rendering backend SFML chooses to use? If multiple are available, it would be nice if the newest were to be chosen, but there might be times when the user wants to suppress selection of newer backends for whatever reason.

I would think an sf::ContextSettings variable would do the trick.
Perhaps something like
// in sf::ContextSettings:
unsigned int maxMajorVersion
unsigned int maxMinorVersion

or alternatively
bool useHighestAvailableGLVersion

SFML development / Re: How should SFML handle non-legacy shaders?
« on: August 28, 2016, 08:00:46 pm »
Maybe a mixed solution containing both of the options is the best. SFML would use the pre-specified table of replacements as default values and allow the user to override entries as they wish.

This is exactly what I was thinking while reading up to this line.  Seems like a perfect solution.  Full flexibility either way, but still works in a "simple" (SFML) manner by providing reasonable default values.

The next question would be: How does SFML guarantee that the GLSL the user provides will work no matter what the target platform supports?

Again, the solution you've proposed seems completely logical. 

Yes, if the programmer wants to support both legacy and non-legacy shaders, he/she will have to write both.  No way around that, of course.

Yes, if the programmer wants to be a bit more lazy and only write legacy shaders, that's fine.  This decision of simplicity (1 set of legacy shaders) vs. optimization (2 sets of shaders) is completely up to them.
... which, incidentally, seems to mean you can continue using SFML exactly as you do today, and ignore this new functionality.  Or do some more work and take advantage of it.  That's perfect - I see no downside here at all.  Give us (SFML users) the power to do whichever, then it's our problem (or "non-problem" if I choose to ignore the new functionality). 

I assume SFML will be able to tell us whether non-legacy shaders are supported, right?  Is it as simple as a GL_VERSION check? 

If so, I could imagine things working two ways.

1.  SFML tells you whether non-legacy shaders are supported.  Then, in (non-SFML) application code, it is up to the programmer to load and use the appropriate glsl shader. 

2.  SFML actually has some kind of "unified" shader API which allows 2 implementations:  legacy and non-legacy.  The programmer provides sources of one or both, and then SFML takes care of everything from there by using the appropriate shader source depending on the opengl implementation on that machine.  Nowhere in application code do we have to think about legacy vs. non-legacy, except for providing the source of one or both.

Off the top of my head - I think I'd lean more towards solution 1:
  • It keeps the SFML API simpler.
  • It's not that difficult to deal with in application code, if you need it.  (Or you can just provide legacy shaders if you don't care).
  • Maybe the application code needs to know whether it's using legacy vs. non-legacy shaders anyway.  e.g. because those shaders are using different shader params which need to be set.  So trying to get SFML to do this shader-switching work for us under the hood doesn't actually accomplish it's goal of allowing the application code to be agnostic towards legacy vs. non-legacy shaders, anyway.

General discussions / Re: SFML 2.4.0 released
« on: August 11, 2016, 10:07:15 am »
Cool.  Thanks guys.

One thing I really enjoy about the distributed SFML team is you get to follow the conversations on github.  Particularly for things like the "New API to set shader uniforms" and "Multi-GPU preference" changes.  That's not typically something you'd get in a non-distributed environment, where many of these conversations would happen in person, and not recorded on-line.

Feature requests / Re: Vulkan Support
« on: February 29, 2016, 04:38:34 am »
Great post binary.

I recall in our previous debates, you held a lot of disdain for DirectX.  One argument you provided for not supporting it was that it didn't "deserve" to be supported (paraphrasing).  And that lack of OpenGL support on some platforms could only be blamed on people caving into the demands of Microsoft to write DirectX apps.  It was a very idealist argument, but one that I could understand.

Using this same reasoning, Vulkan sounds very much worthy of supporting.  Based on your description of and enthusiasm for Vulkan, it is certainly consistent with your stance that only well designed, open, cross-platform rendering APIs be considered for SFML.

General / Re: Texture coordinate interpolation
« on: February 28, 2016, 09:03:50 am »
Thanks Laurent.

I'd been looking at bilinear interpolation articles on google.  I was having some difficulty because many relate to rotating or scaling an image.  These articles assume the image is "square".  Unlike an OpenGL primitive or an SFML::VertexArray tri/quad, which may stretch the texture.

I'll keep checking it out.  But I was hoping to find an OpenGL function "shortcut".  ;)  I guess maybe there isn't one.

General / Texture coordinate interpolation
« on: February 28, 2016, 08:11:01 am »
Hi all,

A question for any OpenGL gurus among you:

Does OpenGL expose the functions it uses to interpolate a texture coordinate across a primitive?

I'm looking for a function which would do something like this:

sf::Vector2f InterpolateTexCoord(const std::vector<sf::Vertex>& primitive, sf::Vector2f p)
   // Examine the texture coordinates of primitive, and calculate the
   // interpolated texture coordinate for point p

... in other words, exactly what OpenGL does internally when calculating the color of a fragment from a texture.  Or when OpenGL passes the interpolated uv coordinate to a fragment shader.

Ideally, the function could handle either quads or triangles.  But I could get by with just one or the other, if necessary.


General / Re: MouseButtonReleased Event is spamming when moving the mouse
« on: February 23, 2016, 04:58:55 pm »
Pretty slick guru debugging, eXpl0it3r. 

General / Re: Yet another RAII discussion
« on: February 14, 2016, 11:26:18 am »
re:  aurora::CopiedPtr

Yep, good arguments.

There's no doubt that having to write some/all of a constructor, destructor, copy constructor, copy assignment operator, move constructor, and move assignment operator gets pretty burdensome.  I am highly sympathetic to simplifying this.

I recall reading an article (or maybe it was a video) by Jonathan Blow, who is a pretty popular and influential indie game developer.  He discussed the need for a new programming language for games.  He has many issues with C++ but these complexities addressed by aurora are one of them.  So it's not an insignificant problem.  It legitimately pushes some smart people away from C++.  Blow is actually working on a new language for games.  That link is off-topic, highly optional reading, but just figured I'd include it for those interested. 

Personally, I'd have to be dragged kicking and screaming from C++.  But I get people's complaints.  I would actually be interested in official C++ tackling this problem, similar to how your aurora stuff does.  If it were part of the standard, the "principle of least astonishment" argument (whether you agree with it or not) would completely disappear.

re:  C++ RAII

I definitely agree that shared pointers have disadvantages and their use cases have to be considered carefully. I've also noted this in my article and mentioned here that I consider it overused. Still, the article you linked is definitely interesting to read, as it outlines some lesser known problems in detail.

Cool.  Thanks.

So, it seems like we agree that RAII can greatly simplify and improve code, and abandoning it is primarily reasonable due to problems with concrete implementations, such as std::shared_ptr? :)


This is where I am at.  If I liked the concrete implementation, I would try adapting RAII in my code.  I can't guarantee I wouldn't find other issues once I got my hands dirty.  But I would be sold on giving it a shot. 


Here's the broader picture.  There's a series of mental steps a person has to make to go from traditional manual mem management, to adapting RAII. 

1.  Syntax
It is very common to dislike the syntax, which I readily admit is a very minor issue.  But hey, coders will argue for hours on where to put curly braces.  So while I might mention it, I wouldn't pursue it as a serious argument against RAII.  But the first reaction is often an "ewww" one.  ;)

I'm just talking about the thought process, not trying to make arguments to debate here.

2.  Old vs. New
It's very common to require convincing, by oneself or others, that RAII is actually better, and if so, by how much.  Memory management and memory leaks are issues programmers have dealt with for a long time.  We've written a lot of software using our old, non-RAII tools and techniques.  We are competent with them.  We trust them.  They have worked well for many of us in the past.  Arguments telling us that they don't work just annoy us, because we believe they do.  It would be like someone telling you you should use C# because you can't trust programmers to work with memory, with or without RAII (both of which are susceptible to programming errors, although RAII less so).

This is why people will mention things like memory leak detectors.  I readily accept that there are reasonable arguments against these older techniques.  But obviously, the argument for RAII would be far stronger if these older techniques did not exist.  That's why they get brought up.  RAII is a replacement to existing techniques.  So you must examine both together.  It's a debate between the old and the new.  Personally, I believe it is wrong to frame the memory management argument as "the wrong way" and "the right way" (and doing so will likely alienate those who require convincing).  Rather, I believe it should be framed as the advantages and disadvantages of different tools.  Both advantages and disadvantages exist.  "Familiarity", "existing, functional code", and "established technique" being some arguments for manual management. 

For example, I chose to use CEGUI for my UI library in my current game.  There are a lot of other GUI choices.  Maybe some of them are even better.  I don't know.  But I have a huge familiarity with CEGUI.  I have a lot of existing code.  I have a highly efficient workflow.  I just simply know how it works.  So it's one less unknown piece of tech, one less big learning curve I have to deal with in my current game.

Maybe some other GUI libs do things a little better, in certain ways.  That alone would not be enough for me to change.  It would have to be a lot better.

I'm sure one day I will change to something other than C++.  Some fancy new language.  But when that day comes, it will take a lot of convincing.  I'm good with C++.  It works.  Same kind of thinking.

Again, not trying to make arguments.  Discussing thought process.  ;)

3.  Examining reasons not to adapt RAII.
This is where we hit the implementation, and smart pointer issues. 

As discussed above, many programmers have to be strongly convinced to abandon tried and true techniques for something new.  This is reasonable. 

I'm pretty good with my existing techniques.  Changing requires me to abandon those.  Learn new stuff.  Rework my existing code.  Likely deal with new kinds of bugs and other issues as I adapt and learn.  Take for example the difficulties associated with debugging non-released memory from shared pointers (memory I expect to have been released).  That's a different kind of debugging than I've ever had to do before.  I'm starting from scratch.

So, if I see definite problems in the implementation of C++ RAII, that may be enough to weaken the "strong convincing" I need to adapt it. 

That's why discussing these problems associated with shared_ptr are such an important part of the process.  Or, instead, discussing alternatives to shared_ptr that do not have these problems.

This is why I was so interested earlier in talking about shared_ptr alternatives, using a combination of unique_ptr and raw pointers.

That's a big wall of text.  But I hope what it achieves is to be more sympathetic to those who either decide not to adapt C++ RAII, or are somewhere along the stages of accepting it, but not quite there yet.

Thanks for the great discussion, Nexus.

General / Re: Yet another RAII discussion
« on: February 12, 2016, 03:16:28 pm »
I should add that this article is written by the Lead Software Engineer at Wargaming Seattle, with an extensive history in the AAA gaming industry.  He also mentions consulting his peers in the industry on the topic. 

That doesn't mean he's necessarily correct.  I disagree with people in the industry all the time.  But it does mean he isn't some hack spouting off crap with no experience to back it up, as may have been the case with other articles you've dismissed.

In particular, I would draw your attention to these sections in his article:
  • Debugging References
  • Hidden Performance Issues
  • External Reference Counts (although he backs of this critique somewhat based on feedback)
  • Reference Bouncing
  • Atomics Overhead
  • More on Ownership

... but ideally you'll want to read the whole thing.

Here's a link to the article again.
Dangers of std::shared_ptr

General / Re: Yet another RAII discussion
« on: February 12, 2016, 02:18:39 pm »
That aurora::CopiedPtr looks pretty cool! 

The only objection I might have is "the principle of least astonishment"

You're obviously straying pretty far off the code people expect to see.  If you were to argue, "it's worth it, look how much less code I have to type!  That makes everything safer, too".  I'd say that is a good argument.

If someone else were to argue, "Listen man, I'd prefer to type more code than have some weird templated smart pointer black magic obscuring all my logic, and changing the established way C++ is written", I'd call that a good argument, too. 

This may again be an instance where I would be fine with either approach, whereas perhaps you might view the second argument as inadequate.  But I don't want to put words in your mouth.

Either way, nice work with the masterful smart pointer wizardry.

Onto RAII:

RAII as a principle I have no problem with. 

I'll leave that statement by itself, with italics for added impact.  ;)  We can base the rest of our discussion with that agreement in place.  All my arguments against RAII throughout have been related to the RAII implementation in C++.

If you don't want to discuss RAII implementation in C++, no problem.  We can stop now. 

For me, as a programmer, I would never accept the use of a programming technique without considering the implementation.  For example, I like OOP better than DOD, in prinicple.  But in practice, the implementation of OOP often has terrible performance.  My code usually ends up being a marriage of the two, focusing more on DOD principles where performance is critical.

Similarly, if unique_ptr had a 500% performance hit on dereferencing, nobody would use it.  Or, I certainly wouldn't.  That's not a principle thing, that's an implementation thing.  As you rightfully pointed out, it doesn't.  The implementation is important.

shared_ptr is also an implementation detail.  But there are legitimate concerns with the use of shared_ptr.  The advantages you list for shared_ptr are also cause for some significant concern.  These are highlighted in the article I linked.  It actually has a good section on resource manager-style use of shared_ptr, such as in your Thor implementation.

Thanks for sharing your thoughts on Thor.  Everything makes sense. 

A user of Thor is still choosing to use shared_ptr.  I would say that this decision is one that be made carefully, in light of both advantages and concerns with shared_ptr.  Again, I would argue there are valid arguments to be made both ways.  You may personally weight the evidence, and decide the pros may outweigh the cons.  I would hope that you at least recognize their are cons to be considered.  Again, these are highlighted in the article.

If you read it, and say, "I read the article, and found no cons to using smart_ptr", then I can try to point you in the right direction.

General / Re: Yet another RAII discussion
« on: February 12, 2016, 01:08:18 pm »
I should add -
my implementation above does not have an API to configure the sf::Drawable*.  Obviously you'll want to do that.

I would probably have some sort of GameObjectManager class, with a series of Create* functions that allow various parameters with which you can configure the Drawable*.  Different functions and parameters may be required for different Drawable subtypes.

Alternatively, if you need to configure the sf::Drawable* past GameObject construction, and you need to access specific functions of the derived class (e.g. sf::VertexArray::append), then a down-cast from sf::Drawable* is inevitable.  I believe this would be true in any other proposed implementation as well.  Although the DrawableType data member should allow you to safely do this with a static_cast rather than a dynamic_cast.

Now, here's how I actually do it in my code.

I primarily make use of sf::VertexArray, and sf::Sprite.  I don't use most other drawables.  I use separate GUI middleware, for example, so sf::Text is unnecessary.

I actually have 2 separate classes,
class ManagedSprite
class ManagedVertexArray

These classes create an sf::Sprite and sf::VertexArray in their constructor, and store this as a pointer data member.  It is copied and destroyed through all the usual constructor, operator, and destructor methods.  They also contain a data member which specifies their draw order in the game. 

These classes are owned by a manager.  I call it DrawManager.  The ManagedSprite are stored in one std::vector.  The ManagedVertexArray are stored in another.  They can be looked up by a ManagedSpriteID and ManagedVertexArrayID, which corresponds to indices in these std::vectors.  Game entities (actually the Drawable component of game entities) store ManagedSpriteIDs and ManagedVertexArrayIDs only, and never a pointer.  So, it's your typical "handle" implementation.  But the handle can be used to lookup and modify these drawables as needed.

The DrawManager maintains a separate list of ManagedSpriteID and ManagedVertexArrayID in draw order.  This is how I know which drawables need to be drawn overtop of which others.

The separate draw order list is necessary so that I never have to reorder the vectors storing ManagedSprites and ManagedVertexArrays.  As this would invalidate the IDs stored by components, which are indices into these arrays.

The point of all this is that I don't actually have any kind of GameObject which requires a polymorphic pointer to an sf::Drawable.

Some advantages to this approach are:
  • no downcasting is ever necessary
  • clear ownership of memory by the manager and the managed classes it employs(this is what managers do)
  • the DrawManager's std::vectors contain sequentially ordered data, not pointers to data, avoiding an extra level of indirection which thrashes the memory cache and slows the game update loop.  This is a common paradigm I use, largely borrowed from data-oriented design, which does have some strong principles for perforance
  • pointer safety, as no persistent pointers to any dynamically allocated memory (sf::sprites, sf::VertexArrays, ManagedSprites, ManagedVertexArrays) is employed outside of the manager.  Everything else uses handles.
  • an easy implementation of draw ordering

General / Re: Yet another RAII discussion
« on: February 12, 2016, 12:47:37 pm »
class GameObject
    explicit GameObject(?);
    sf::Drawable* mDrawable;

My approach would be somthing like this:
(pseudo-code, didn't compile).

enum DrawableType
   // ...

class GameObject
   GameObject(DrawableType type);

   sf::Drawable* mDrawable;
   DrawableType mType;

GameObject::GameObject(DrawableType type)
   mType = type;
   switch (type)
      case Sprite:
         mDrawable = new sf::Sprite;

      // etc

GameObject::GameObject( const GameObject& rhs)
   switch (mType)
      case Sprite:
         mDrawable = new sf::Sprite;
         *mDrawable = *rhs.mDrawable;

      // etc

// ... similar code to above for other operators like assignment ...

   delete mDrawable;

There is no question of ownership, as the pointer only exists within GameObject, and is allocated in the constructors.

What would your implementation be?

General / Re: Yet another RAII discussion
« on: February 12, 2016, 12:32:53 pm »

Indeed, the article argues against shared_ptr, not RAII. It would be really interesting to see some founded argument against RAII. What I've seen so far are mostly points along "I don't need it", "it's a matter of taste/code style" and "I can program correctly without it". And I can't take them seriously, as they're 100% replacable and generic.

One enlightening part of this discussion is that you are ok with using raw pointers for non-owning pointers, as we discussed earlier.  That would largely remove the necessity for shared_ptr, which is one of my primary technical objections to the use of RAII.  Out of curiosity, would you describe this as the dominant view of RAII advocates?  I would have thought not. 

For example, in the Wiki for RAII, shared_ptr is explicitly mentioned as follows.

Ownership of dynamically allocated objects (memory allocated with new in C++) can also be controlled with RAII, such that the object is released when the RAII (stack-based) object is destroyed. For this purpose, the C++11 standard library defines the smart pointer classes std::unique_ptr for single-owned objects and std::shared_ptr for objects with shared ownership. Similar classes are also available through std::auto_ptr in C++98, and boost::shared_ptr in the Boost libraries.

I wanted to check out how you implement RAII in practice.  So I checked out the Thor source code.  I'm not doing this to attack you.  Just simply to see and understand your approach to RAII.

I do notice in Thor, you do seem to stick largely to smart pointers.

If I search the Thor source code for shared_ptr vs. unique_ptr, you do make use of a lot of shared_ptr.  I think I found it in at least equal measure to unique_ptr, based on a rough count of the search results.

So, if RAII does advocate the use of shared_ptr (which I think it commonly does), and your implementation of RAII uses shared_ptr (which it appears to), then the article I posted critiquing shared_ptr is also a critique of RAII, as it employs shared_ptr.

I think that leaves us here.  Either:

1. You accept shared_ptr as part of RAII, in which case we should debate the merits of the article I linked critiquing shared_ptr.

2. You do not accept shared_ptr as part of RAII.  This would appear to be counter to your code, but I accept you may have changed your stance on things.  If so, this changes the debate considerably.

Is that fair?

General / Re: Yet another RAII discussion
« on: February 12, 2016, 10:58:33 am »
I just had a look at my use of new/delete.  I should add that I often use "new" in ResourceManager type classes, and this occurs outside of the constructor. 

I have never once had a problem with memory leaking with these classes. 

General / Re: Yet another RAII discussion
« on: February 12, 2016, 10:35:39 am »
How was that again with the sarcastic tone you didn't like? ::)

Actually, I don't believe there was a single instance of sarcasm anywhere in my post. 

If perhaps you are noting an element of frustration, I would agree.  I did resort to a "fight fire with fire" on the "Fallacy" thing, but that was to illustrate a point.

Anyways there's quite a bit of nitpicking going on while if one looks at the discussed points more broadly it would be clear what was meant even if it wasn't pedantically written that way.

I agree with this also.

Really, Nexus and I got caught up in who is right and who is wrong, when really we likely would have both quickly agreed on the following:

Some smart pointers have overhead.  Others do not.

The overhead is small for shared_ptr, but it exists.

The overhead of using smart pointers can be minimized, or eliminated by reducing or avoiding the use of shared_ptr.

We may have debated the merits of shared_ptr and it's use in RAII, but unfortunately we didn't get that far.

I would say to Nexus, your way of approaching things right from the start put us at odds.  I am partially to blame for getting dragged in, even though I tried not to.  Still, I would suggest you may reconsider the way you approach a debate here on the forums.  You're obviously a very smart guy.  I very much appreciate the work you and the SFML team do.  I think it is brilliant software.  But nobody likes being berated. 

Perhaps you don't think you did that.  This is very much how it came across to me.  You can decide whether you want to believe that to be valid or not.  I'm just letting you know.

As fans of programming, graphics, games, and open source software, all of us here have so much in common.  It's a shame we cant treat one another with more respect.  Yet somehow, small differences in the way we approach our craft leads to this kind of showdown.  I wish we could respect those differences, even if we do debate them.  I have tried to reinforce this message throughout my posts.  But readily admit I got pissed off eventually.

Does manual memory management introduce a higher probability of making a mistake (e.g. creating a memory leak)? Yes, due to the various ways a scope can be prematurely exited, the deletion of allocated memory requires more care and thus a higher probability in forgetting one or the other scenario.

Interestingly enough, I almost never have to "new" anything outside of a constructor.  In these cases, there are no concerns with scope. 

I do use tons of stl containers.  I normally prefer containers of data over containers of pointers to data, as interation time and cache use is far better.  So there is no concern here either for new and delete, and scope issues.

Should manual memory management be suggested as best practice to a new beginner or even "advanced" C++ programmer? No, because the gained performance (if any!) through MMM will not outweigh the high probability of making mistakes, in almost any application said programmer will write.

Well, I think many would use the same logic to recommend C# over C++.  Yes, I know garbage collection is far more sinister than anything I dislike about smart pointers.  But if the argument is to be absolved of memory management, C# is superior.  Many people choose C++ over C# precisely because they wish to manually control their memory. 

I understand you disagree with this.  I am just stating what I believe.

Since you claimed that there are many people advocating against RAII, I'd be interested in reading some sources (as long as they aren't just C advocates, we're talking here C++ after all).

Here's one.

Now, that article is primarily against shared_ptr, although RAII is mentioned.

RAII is not a panacea. shared_ptr is an application of RAII for example. It's simply important to note that RAII - if used correctly - allows problems to be solved that automatic GC does not.

I think that article is a balanced and insightful debate of the sort we were having trouble achieving here.

Nexus, while I was writing this up, you posted your response.  I agree with you on the meta-discussion.  I suggest we drop the fight over whose fallacies are, uh, more fallacioius.  ;) 

Regarding the example you posted - yes, I would agree this is a tricky (albeit manageable) example of memory management. 

Honestly, in the case of your example, if a person said they wanted to use a smart pointer to avoid the problems of scope and early returns, I would be fine with that.

I do not think that argument extends to insisting smart pointers be used in every single scenario where memory is allocated, a core principle of RAII.  If the allocated memory can be completely handled by the tried and true method of a proper constructor, destructor, and copy/move/assignment/etc operators as necessary, I do not see this as a danger except in the hands of an inept programmer.

This above pattern is by far the most common use of managed memory I encounter in my code.

Pages: 1 [2] 3 4 ... 11