Jump to content

  • Log In with Google      Sign In   
  • Create Account

noizex

Member Since 04 Jul 2011
Online Last Active Today, 07:12 AM

Posts I've Made

In Topic: Horizon:zero Dawn Cloud System

23 August 2016 - 08:21 AM

Hehe yeah, this thread inspired me too. Couldn't force myself to rework my rendering system into something more flexible and after a week I'm done with a nice way to render such effects easily so I will also have a try. Stay tuned for questions coming from me soon :P


In Topic: Abstract Classes and returning a varying Type

23 August 2016 - 03:24 AM

Thanks a lot, everyone : )
 
Noizex, I understood the implementation but the key-identifier part. While I understood that std::strings can be a bit of a power consuming thing to compare, how would I do it with integers?
Well, example:
A game object gets created, it knows a file path of its texture. Now it requests the asset-manager if this has been allocated already. The asset-manager would iterate through a list of collected assets with all their <type>s.
What I thought about is to compare the strings with each other, as every asset-item knows its original path. Storing std::strings is not that lovely for such an undertaking, especially comparing them. If I would use something like you suggested, I could simply compare numbers, is that right?
 

The new game object would simply send the type it is looking for together with an ID, maybe send the file-path as well, to directly allocate if it does not exist.
But how would I define what asset would get what number? Or is there something better?

 

I think you're confusing several concepts here and complicate unnecessarily. When you load the resource you want to supply whatever ID you need to use to identify this resource and its version on a medium that you store it. It may be file path, it may be just a name or numeric id, it may be something more complex like a struct that contains both, filename and version. It doesn't matter what type it will be, as long as your loader knows how to deal with it. One thing matters - type should be hashable because you want to store references to your resources in a unique_map or similar container for fast lookup. You don't want iterate over a vector and look up for the resource comparing strings, as you suggest above.

 

So for resource key just use whatever type is most convenient for you. Strings are perfectly fine for this, as they're hashable. You can make it all compile-time even with some C++ magic, but that's probably only needed if you do a lot of lookups on thousands of resources.

In my case, I don't ask manager each time I need the resource (so, each frame just before rendering), I use a simple refcount and don't return plain pointer but a handle that can be resolved to a pointer (this also acts as a proxy and allows returning placeholder when doing async load). This resolving doesn't use a map but rather a vector, and a handle keeps index to the resource in that vector (this is the main container for my resources, map just holds references in case the key-lookup is needed through Get<T> method). This requires designing a good policy of when the handle is valid, what happens when resources in vector need to be moved to other indices and so on, but otherwise is fast where it's needed and allows easy lookup when it's needed.

 

In terms of code it looks a bit like this snippet:

struct Model {
   Resource<Shader> shader; 
   Resource<Texture> texture;
   Resource<Mesh> mesh;

   void LoadResources(ResourceManager& resources)
   {
      # Here it does the std::unique_map lookup on the Store class (mind that Store class is provided by default
      # but also can be user-defined with completly different storage implementation, so this unique_map is just 
      # what is the usual case)

      shader = resources.Get<Shader>(ShaderKey("base_shader", E_NormalMapped | E_Lighting));
      texture = resources.Get<Texture>("texture.dds");
      mesh = resource.Get<Mesh>("mesh.iqm");
   }
}

Now a bit on what happens when you Get<T>:

  1. Find appropriate meta data for resource type T - meta data contains load/store/retrieve function callbacks, Store object and Loader object
  2. Call Store->Retrieve(key) to check if resource exists in the cache
    1. If resource exists, Store returns Resource<T> handle to it and increases refcount
    2. If resource doesn't exist Store returns empty handle
  3. If handle is empty, proceed to loading the object, so Loader->Load(key) is called
    1. Depending on the loader, it either fetches resource from somewhere, or creates it using factory class, this all happens inside Loader and other classes like Store or ResourceManager itself don't know anything about it. Loader just has to return the resource (this time it's unique_ptr) or nullptr
  4. When loader returns and the handle is not empty, it gets stored into Store, increasing refcount to 1 (so the store callback is called with the unique_ptr, which is then moved into the Store, as the store is the owning entity for resources)
  5. Handle is returned to user

Later on, when I actually need to use the resource I do it as if it were a normal pointer to resource it holds:

RenderModel(mesh->GetVertexBuffer(), mesh->GetIndexBuffer(), texture->GetId(), shader.get());

When -> is called, the handle does the call on the owning Store (to which it holds a pointer) like:

m_Owner->Get(m_ResourceIndex);

where m_ResourceIndex is index into resource vector. This way it can return placeholder, or do all sort of things, because it has this indirection which you don't get if you point straight to Resource* pointer.

 

So to rendering system I usually pass straight numbers of resources (GLuints in my case), but sometimes I need resource object itself like in Shader case (I need to call few more methods and Shader keeps the state for this, like caching uniforms per shader type). This is why last argument is passed as shader.get(), which returns plain pointer to Shader*, just like std::unique_ptr and similar smart pointers. There is a policy that resource manager will never free any resource during rendering phase, so passing raw pointer and holding to it for a while (during the journey of RenderTask through queues, sorting, and into actual render call) is fine because we guarantee that the resource will stay alive during that time.

 

It's still a naive implementation so there may be some problems, but it worked for me so far and it's quite flexible so I hope it helps you a bit and not adds more confusion :)


In Topic: Abstract Classes and returning a varying Type

22 August 2016 - 08:28 AM

You may consider making it a bit more generic - with some template programming you could have resources of any type and not coupled with some common base class which is not really a good solution (like deriving from Resource class or similar). Not only it will have broader use (for example you could use some external classes that you can't derive from common base without modifying some library), but also should be faster because you won't use virtual calls for something so crucial as getting resource id, something that will happen a lot each frame.

 

Think of a handle like Resource<Texture>, Resource<Shader>, Resource<Config> etc. You could use resources that don't necessarily use the same key to fetch them (so for example instead of std::string some more sophisticated struct or maybe uint?). 

This way you don't have the problem you seem to describe - that you have several "semi similar" classes, but they all have their own quirks and data. If you have Resource<Texture> you know the object you're dealing with is a texture. If you have Resource<Shader> you know it's a shader and may have completly different interface than texture. You are tying your hands with a common interface for things that may be completly unrelated in terms of their functionality, and only share one thing - that they're loaded and managed in a way that minimizes duplicates. 

 

In terms of loading, that's where you specify what resource you're querying:

 

auto texture = resources.Get<Texture>("mytexture");

auto shader = resources.Get<Shader>(ShaderKey("myshader", shaderBitMask));

auto framebuffer = resources.Get<Framebuffer>(2); 

 

etc.


In Topic: Naive deffered pipeline

18 August 2016 - 04:29 AM

Exactly, you do one geometry pass (naively, not mentioning special cases here) and write to several gbuffers (textures) at once. I suggest reading some tutorials about deferred rendering to get a better grasp on this concept, there are several out there, even specifically about OpenGL:

 

http://ogldev.atspace.co.uk/www/tutorial35/tutorial35.html

http://learnopengl.com/#!Advanced-Lighting/Deferred-Shading


In Topic: Horizon:zero Dawn Cloud System

29 July 2016 - 12:49 PM

Hm, where does the buffer come from? This looks like C++ code, you do it on CPU? Do you run it for every pixel of the rendered image or only for those that are actually on "sky"? 

 

Thanks for the paper, I will read it, need to find some good explanations how raymarching works, as I see a lot of weird equations about the actual volume sampling, but nothing about how is this actually rendered :)


PARTNERS