Jump to content

  • Log In with Google      Sign In   
  • Create Account

Juliean

Member Since 29 Jun 2010
Offline Last Active Today, 01:01 PM

#5298047 Is using one the switch statement better then using multiple if statements?

Posted by Juliean on Yesterday, 05:59 PM

Better how? Switch-statements can be way faster then if/else-chains, specifically if there are many items to check for.

 

The compiler is free to optimize the switch-statement as he pleases. For a small number of items, the compiler might just produce an if/else-chain in the background. However, if you have like say 20-30 items, then the switch can ie. be converted to a table-lookup, which is way cheaper than having to check perform up to 30 if-conditions.

 

A reason for using multiple if/elses is mostly if you cannot use a switch, ie. if you are comparing strings, or other non-literal types.

 

But keep in mind that unless you are calling that block of code many many times per frame, this won't matter at all. At this level, always prefer code clearity over performance. So if you are asking if switch-statements are better in this regard: Largely depends. For many items, switch can easily be more readable, and safer (since you cannot accidentially check for the same item multiple times).

However for just 1 or two items, if/else tends to be more readeably, if not for being shorter. Consider this:

if(x == 2)
    foo();
else
    bar();

// switch is moar awesome!

switch(x)
{
case 2:
    foo();
    break;
default:
    bar();
    break;
}

So to sum up, both have their uses. Worry only about efficiency if you know you are working on performance critical code and/or  if you did proper benchmarking. Otherwise, choose whichever is more appropriate to the situation in terms of readability and clearity - just don't got around and throw switch-statements at every single if-statement and you should be fine :)




#5296677 Batching draws - keeping buffers up to date

Posted by Juliean on 15 June 2016 - 10:12 AM

The problem that you have is even if only 10% of your data actually changes, its not quaranteed that this data is in the same area in memory. You could have 100k sprites, and 10k are dynamic, but those 10k are scattered around the buffer so you actually require 10k bufferSubData-calls for updating it (which is pretty bad compared to just one update that discards the entire last buffer).

 

But if you have static vertex data that can be identified or marked as static, than you can put those into a separate buffer that isn't updated on a per-frame basis, only when needed. You than have a separate buffer that is refilled on each frame, and you draw from both of them.




#5296490 How to avoid Singletons/global variables

Posted by Juliean on 14 June 2016 - 09:30 AM

There's only know one way to not use slot of not use global variable , but it does not make sense to me

 

Event in your second example, you use a global variable, you just moved it to the other class. A common way to avoid this is by doing depenceny inversion injection (oups), by passing in your texture manager to the texture class:

class Texture
{
public:
   Texture(string a_name, TextureManager& textures){
         m_name = a_name;
         m_ID = textures.GenTexture(a_name);
   }
}

Now you have TextureManager as a regular non-static class somewhere, and pass its instance in whenever needed.

 

Or better yet, remove the depenceny of Texture<-TextureManager completely, and have your texture just take an int, while having the TextureManager have a CreateTexture-method:

class Texture
{
public:
   Texture(string a_name, int ID){
         m_name = a_name;
         m_ID = textures.GenTexture(a_name);
   }
}

//Texture Manager.h
class TextureManager
{
 public:

    Texture& CreateTexture(string a_name)
    {
        for(int i =0 ; i<m_textures ; i++)
        {
           if(m_textures[i].name == a_name)
           {
             return m_textures[i].Texture;
           }
        }
        int id = OPENGL_genTexture(a_name);
        
        auto texture = new Texture(a_name, id);
        m_textures.Add(a_name , texture);
        
        return texture;
    }
    
private:

 //this store the name of the texture and the ID of the texture
 Map<string, Texture*> m_textures;
}

Note that I've also changed the TextureManager to store a "texture" pointer instead of just the id. I'm not sure whether your Texture-class is actually representing an handle? In this case, this change doesn't make much sense, but on the other hand it would be very bad to store a string inside an handle since this makes passing it around/storing it very costly. So eigther create one Texture-object, store it in the map and return it on subsequent calls. Or remove anything but the ID from the texture class, so you can use it like a primitive type if you plan on using an approach like that.




#5296109 whats the "Big O" of this algo?

Posted by Juliean on 11 June 2016 - 12:37 PM

So do you mean this?

 

https://en.wikipedia.org/wiki/1_%2B_2_%2B_3_%2B_4_%2B_%E2%8B%AF

 

Ie. for 5 elements:

NumIterations(5): 1 + 2 + 3 + 4 + 5 = 15

If so, the runtime is O(n^2). Looking at the formula given on the page, its n * (n+1) / 2, but since we do not care for constant factorss in big-o notation, n * n is whats left over.

 

We can also deduce this if we look at the number of elements growing vs the number of iterations when doubling:

 

NumIterations(10): 55

NumIterations(20): 210

NumIterations(40): 820

 

And now lets look at how they scale in comparison:

 

55 / 15 = 3.66666

210 / 55 = 3.818181

840 / 210 = 4.00000

 

So whenever we double n, the amount of iterations roughly quadrouples, which means O(n^2).




#5295999 Trivially trivial?

Posted by Juliean on 10 June 2016 - 11:43 AM

- the class logs how often it is moved, probably for debugging reasons
- the class somehow logs where it is being moved

I'm sure I can think of others, but I admit that they're mostly odd corner cases.

 

Well RVO is already allowed to bypass any additional constructor by standard, so one should not rely on those things anyways. In the same manner, I would think it is perfectly fine for a container to exhibit a similar behaviour of skipping unnecassry ctors, at least for those two cases.

 

@samoth: Thinking about it, why not just give an additional template parameter for the container for users to specific whether they actually need ctors being called? Or something along the lines of a custom is_trivially_copyable, that someone can overload to specify whether they really need those ctors by any means, for like some corner cases like those mentioned by oberon_commander.




#5295993 Trivially trivial?

Posted by Juliean on 10 June 2016 - 11:05 AM

In short, can you trivially imagine something non-trivial being a problem when being treated as trivial in this context? Something real that might actually happen, that is, a case where this might realistically break something.

 

In other words, I'm inclined to just write memcpy, and I'm wondering if there's a valid reason not to.

 

I've been doing the same thing for a while now in my entity/component-system (for component storage), and even after heavy testing by different people/machines there has not been a single issue (and my component types are anything but trivially copyable, with vtables, storing smart-pointers/dynamic memory, etc...).

 

I quess you shouldn't generally do this because someone might write a class that relies on some very specific behaviour in their ctor/dtor. But in general I belive this is safe to do so if the user of your code knows what it does, because in reality you are not actually copying the object but as you said just copying/moving the bytes.

 

Also keep in mind that in modern C++, you would not want to have a copy-ctor called there even without memcpy, but rather a move-ctor, since you are not making a copy of the object but rather moving it to a new location. So this makes it probably even saver to just use mem-copy because as far as I have seen, move-ctors are less reliant on specifc object management logic (ie. you general don't need to do ref-counting of owned objects in move-ctors, and raw dynamically owned memory would just be moved and nulled out in the original class).




#5295375 Get the amount of draw calls in runtime

Posted by Juliean on 06 June 2016 - 04:07 PM

There actually is a thing for this, called "queries"

 

https://msdn.microsoft.com/en-us/library/windows/desktop/ff476515%28v=vs.85%29.aspx

https://msdn.microsoft.com/en-us/library/windows/desktop/ff476191%28v=vs.85%29.aspx

 

You need to look at the different types of queries to see if there actually is one for draw calls though (or someone tells you that knows this for sure).

 

Your other options include:

 

- using a 3rd-party application like nsight, perfstudio, or the build-in visual studio graphics debugger, which all can show you those metrics.

- if its about an application that you write yourself, just make a wrapper for draw-calls. Instead of calling ID3D11DeviceContext::DrawXXX() in your code, you wrap this inside a custom function that also increased a counter, which is reset before each frame. This gives you the metrics you want on your side of the application.




#5295353 use ID or Pointers

Posted by Juliean on 06 June 2016 - 02:42 PM

You also do not have to use eigther - or. There is nothing wrong with having a pointer in your code, and when its about to be serialized, you can convert the pointer to an ID (for example via lookup). On deserialization, you perform another lookup to see which pointer to get from this ID.

 

I'm not saying that its the best option, but it certainly is a thing to consider. It still keeps the speed of pointers (in case you need the last inch of performance) and relative easeness for debugging (inspecting an object via handle in debugger can be more difficult), while being able to safely serialize/deserialize where needed.




#5295134 when do you load new shaders?

Posted by Juliean on 05 June 2016 - 02:38 PM

How do you deal with shaders that might have different hlsl code? If cube 1 is shiny and cube 2 is flat that would require different shaders, correct? Can you load multiple shader code, or do you do that? Its really a workflow question, how do you manage it when there are a lot of shaders? Do you load each one before calling draw?

 

 

Okay, so for your question: Yes, you can load as many shaders as you want. You usually do this at load-up time, doing it before drawing would be ungodly aweful for performance.

Shaders are just like textures, they are resources being bound to the device. So you load & compile the shader once, and for each object that needs a certain shader, you bind this shader before drawing.




#5295094 when do you load new shaders?

Posted by Juliean on 05 June 2016 - 11:39 AM

However, what's the flow for having the shader pick one texture vs. another?

 

A shader does not directly use a given texture. A shader has a slot or binding point, to which you can assign any texture you want, in your application code. Note that you don't actually bind the textures to the shader, but rather to the device (so if you change shaders, the texture still stay bound).

Texture2D BoundByApplication;


float4 pixel = BoundByApplication.Sample(Sampler, vTexCoords); 

As to how this is done, each shader stage (vertex, pixel, ...) has its own binding points, and you can assign a "shader resource view" generated from the texture-object to that. For SharpDX, have a look at this code:

 

https://gist.github.com/axefrog/b51b4e149c329608eae6

 

Line 145, SetShaderResource(slot, resource). This should give you the right idea.




#5294658 2D Tile Engine, should I change my current Design.

Posted by Juliean on 02 June 2016 - 10:07 AM

There can be more than one Node in a cell, they're designed to be able to look at more than a single object. As for your guard and door example. In games like this I've played. They usually have 2 guards, or a guard that's scripted to quickly move in front of the player to block them. The game has no fast action or movement though, so projectiles and spawned objects have no issues fighting for room on the grid. I have pathfinding already in place as well, working fine with no issues.

If you have a specific case that fits this, its fine :)

 

I do have all my level textures contained in a single texture. The string is just the key in the dictionary to pull out a certain texture. Etc "WOODWALL_TOPLEFT" would return me the source rectangle of the section I'm looking for, on my tilemap texture.

Well but how do you actually build the tilemap? Do you really have to type WOODWALL_TOPLEFT in the specific cell to select this tile (I'm using you have or plan on having an editor)? I don't know how exactly your tilemaps are laid out but for anything more than like 20 tiles per screen, this sounds like an enormous pain. Might not be an issue with the design, but if you plan on making a content-heavy game with lots of different locations, you are going to spend a huge ton of time remembering and typing in repeated names of tiles, unless you have the proper toolset to automate this.




#5294652 2D Tile Engine, should I change my current Design.

Posted by Juliean on 02 June 2016 - 09:31 AM

I have a Grid at the moment that is 200 by 200 of a class called Cell. Each cell is 32x32 pixels big. The grid is stored as a 2D array[,]. 

Cell contains:

  • A pointer to a Node class, which can be an NPC, Player, Furniture etc
  • A base layer, Floor/Wall etc which are enums and a string, the string is a key for pulling textures from my TextureManager Dictionary
  • A bool for checking if this tile is occupied or not. This helped for A* path finding etc

 

Some thoughs:

 

- If I read it correctly, there can only ever be one "Node" in a cell, right? Does it really make sense in your case to have it this way? This can be a major restriction, and while I know some tile systems handle it this way (Rpg-Maker, ...), I find it a major bummer in most cases.

What about pixel movement? What about if you have a 2-tile wide door that you want to guard by a 1 tile-wide guard? What if there ever need to be multiple objects per tile (objects without passability, objects spawned by another object, projectiles, ...).

So unless I'm mistaken about how you though about that, this is an unnecessary restriction. If you ever need something like A* or a grid-like movement system, you build it on top. Other than that, you should basically allow objects to be places whereever they want (in an editor, add a button to allow toggling a grid-lock), and as many as they want.

 

- What type of tile-maps do you want to build? Generally, having one texture identified by string per tile is a major waste of productivity. Most tilesystem (Rpg-Maker, Tiled...) Allow you using a Tilemap (composition of multiple tiles in one texture), and then have some paint-tool to allow to easily paint a tilemap like you would a picture.

Ok, depending on how many tiles you have you could even have them as separated textures (in my case, most tilesets have 500+ different tiles so thats a no-no),. But even in this case you *need* a system for abstracting this concept away. I'm not going to fill a 100*100 tilemap with all strings to different textures, and so is nobody else. So a brush tool also  makes sense in this case, just instead of storing a tile-id, you store the texture-reference.




#5292731 My C++ gotcha of the day

Posted by Juliean on 21 May 2016 - 12:29 AM

Huh? i dont see how the macro is related to this in this case, I generally do tend to avoid them but in this case it is just a shorthand for "extern ACCLIMATE_API template class XXX", and I do know what it stands for so whether seeing the macro or the written-out version doesnt seem to make a big difference to me.


#5292595 My C++ gotcha of the day

Posted by Juliean on 20 May 2016 - 03:28 AM

Huh, when its unclear where the issue is coming from, thats the worst :/

Had something along those lines for some time now. So in my runtime-type system I had a SFINEA-expression to check if a registered object has a reference-counter, or not:

template<typename ObjectType>
static auto AddRef(ObjectType* pObject, int) -> decltype(pObject->AddRef(), void())
{
    if(pObject)
        pObject->AddRef();
}

template<typename ObjectType>
static void AddRef(ObjectType* pObject, long)
{
}

For certain types under certain conditions, this simply was not working. Even though the object had a reference-counter, AddRef was called with the empty implementation, in certain parts of the code. When debugging explicitely, this behaviour somehow depended on which files where included, whether I forward-declared the type in question or not, and so on. There was no clear pattern to it, and I thought it was a compiler bug for the longest time.

 

So than the other day, I noticed that at a certain point I was doing this:

template<typename Type>
class Asset; // this is forward declared in another header

class MyObject
{
	// this is a fully declared class
};

 // exports dll-symbols for the type system for MyObject and Asset<MyObject>
EXPORT_ASSET_OBJECT(MyObject);

At this point, I was generating the export-symbols for the refCounter<Asset<MyObject>>, one of the types that were making difficulties. Turns out that because "Asset" is only forward declared here, it doesn't have an "AddRef"-method as far as the compiler can see it, so the empty SFINEA-expression is generated -.-

This explained why it depended on whether I included specific files, and/or was using the DLL symbol in the test project or not, and so on.

Thats really weird, I expected for SFINEA to eigther give some compilation error that the class was not defined at this point, but not just silently tread it as if it was an empty class the whole time.

 

 

 

 




#5291680 Templated Function results in Circular Inclusion

Posted by Juliean on 15 May 2016 - 04:16 AM

So since I also got it to compile the way you posted, I assume the following:

 

- Component and IApp are actually in different header files

- Your code at some place usinng the Component::registerEvent-function only includes "Component.h", without including "IApp.h"?

 

If this is true, you can do it this way: Since apparently you cannot have circular-inclusions of inheriting classes, you need to create a non-templated function in your Component-class, and define it in the cpp. There, you can include "IApp.h", and that is where you call its functions.

// Component.h

#pragma once
#include "IApp.h"
class IApp;

class Component
{
	IApp* app;

	// Error: Invalid use of incomplete type 'class IApp'
	template<typename T>
	void registerEvent(const int& evtId, int(T::*func) (int))
	{
		auto res = std::bind(func, (T*)this, std::placeholders::_1);
		registerAppEvent(eventId);
	}

	void registerAppEvent(int eventId);
};

// Component.cpp
#include "Component.h"
#include "IApp.h"

Component::registerAppEvent(int eventId);
{
    app->registerForEvent(eventId);
}

If my preassumption was wrong, this might not present a solution however.

 

On a side-node, any reason why you pass evtId as const int&? In case of small primitive types, you should always pass them by value instead of const-reference unless totally require otherwise, since there is absolutely no speed-gain by doing so.






PARTNERS