• Content count

  • Joined

  • Last visited

Community Reputation

1893 Excellent

1 Follower

About Giallanon

  • Rank

Personal Information

  • Interests
  1. Long launch reflections

    Great story, congratulations
  2. @alessio1989 sorry for the downvote, I hit the -1 button when trying to go back to previous page from my phone, it was not my intention to downvote
  3. Getting Rid of "char*"

    Maybe you should reconsider using char*. I think const char* is the best solution in many situations. Having a function that accepts a cost char* means you can almost always use that function without tricks; having a fn that wants std::string or whatever, means you have always to convert your data to a string object before you can call that fn. Say you load a file in memory, then you need to process it in some way. If you use plain char*, you can just use the file in memory as is. If you use strings, you have to convert a "natural" char* in a string before using it.
  4. Trigger Saint!

    nice :)
  5. You got it right. There's no need to store an end of command since, after you've read the command ID you should know exactly which params should follow,and then you should know how many bytes to read till the next command. You may instead want to use a special command and append it at the end of the stream so that, when reading the buffer, you can easily understand if you reached the end of the command list. This command list is API agnostic so you can use with dx/ogl, since it's up to you to define which command your list will support
  6. I use a buffer, with a counter that "points" to the first free byte ( starts at 0 ends at buffer size -1). Every command is a byte (sort of command ID) followed by additional bytes (ie parameters) if needed. If I run out of space, I simple realloc the buffer giving it some more memory than before. During debug, I break on this reallocations so to have an idea of how much memory a buffer needs and with this data, I prealloc the buffer at startup. To my experience, prealloc a few kb (2 or 3) will be more than sufficient and rarely will run out of space.
  7. Voxel-based lighting

    Couldn't you add a single bit for each voxel that tells you of the voxel is already in light list or not?
  8. For something as simple as this,I would go with simple brute force. After all, you only have to check 3 rows, 3 cols and 2 diagonals
  9. Resources handling - part 2

    To recap from previous post: 1) [color=rgb(40,40,40)][font=arial]There's a central hub that I call ResourceHub (resHub) that acts as the main interface from the app to the resources.[/font][/color] [color=rgb(40,40,40)][font=arial]2) You can add to the hub many ResFamily (like shape family, image family, shader family and so on)[/font][/color] [color=rgb(40,40,40)][font=arial]3) Every family can have many ResProvider which are used to load and to "specialize" a resource[/font][/color] So, when it's time to add a resource, all you have to do is to call resHub->addResource (family, resourceName, &resID) in order to obtain an unique resource ID that will identify this resource (and all its specializations) forever. The key point here is the word "forever". You have no way to remove a resource once it's added. This may sounds a little strange, but it has to do with the lifetime management of the resource and its ownership. First of all, an unloaded resource, once added, has a memory footprint of only 8 bytes (plus the resource's name string which is usually short, something like 8 - 12 characters as it does not include any path or extension). I call this 8 byte the "resource header". Resource headers are stored in tables of 256 headers each (named TableU8). A single table needs 8 * 256 bytes = 2KB which is not that much. A resource provider, store resource headers in a table of tables (named TableU16), which is a table that can hold a max of 256 TableU8. The maximum number of resources that a single provider can hold is 256 * 256 = 65536, which is a not bad for a game I guess. This limit is also limiting the number of resources you can add to a family. In short: you can have no more than 65536 textures, 65536 shaders, 65536 materials, 65536 whatever. Usually you don't need that much available at the same time, so TableU16 can load/unload a whole TableU8 at any time, just like a pagination algorithm. When you add a new family, TableU16 will allocate only one TableU8 (ie 2KB) and will allocate further TableU8 only when needed. It will also deallocate any TableU8 that contains only unloaded resources that are not used anymore (more on this later). So, keeping any added resources forever will not cost that much in memory, giving that a single resource header need only 8 bytes and that resources are paginated in blocks of 256 headers. What comes handy, is that once you obtain a resourceID, you can use it forever, at anytime. So maybe you have added a bunch of textures for your terrain and than you warped 10.000 km away and have to load a whole new set of textures... well, going back to the starting point (maybe after 10 minutes), will not require you to add again the textures and the meshes, you already have all the resourceID that you need. In fact, you could even add all the textures/meshes you will ever need for your terrain, get the resourceID and then forget about it and keep on using only the resourceID. The ResourceSystem will take care of loading/unloading every resource. So, who own a resource? Resource ownership is always a delicate argument. There's a very good post from swiftcoder that gives you a nice introduction; you can find the post here My solution is: the ResourceHub own the resource, and is the only responsible for loading/freeing it. Actually it's the family inside the hub that own the resource, but from a user point of view, the magic is inside the hub. So you can not delete a resource, you can't even free or unload it. You can maybe give hints to the hub, but he will decide on what to do. When you need access to a resource, you call resHub->getResource (resID) and the hub will give you back a status and a pointer. The status tell you if the resource is loaded or not. If it's loaded, you can use the pointer, otherwise you can't. The next time you'll need that resource, you'll do the same thing, call getResource() and check the status. If a resource is not loaded, calling getResource() will return a status of "loading" and will schedule an asynchronous load so it's quite possible that the first time you call getResource() you will recevice a "loading" status, and the second/third time you will receive a "loaded" status. Internally the hub use a Last Recently Used (LRU) schema to keep tracks of used/unused resources. Any resource lies in one of 8 LRU level . Level 0 is the most recently used, level 7 is the last. It's your responsibility to call resHub->onLRUTick() once every 1 second or so; it will "move" resources from Level 0 to level 1, from level 1 to level 2 and so on. Once a resource hits level 7, it will unloaded and will stay there on level 7. A TableU8 that holds only resources at level 7, will be unloaded as it contains only unused resources. Calling getResource() will always move a resource from whatever LRU level actually lies, to LRU level 0, just to indicate that this resource has been used very recently so it should not be unloaded very soon. This is how the hub can automatically unload unused resource and free some memory. From a performace point of view, the whole LRU thing is highly optimized and does not involve "memcpying resources from level to level" which will surely be a bad thing to do once every second... Also, "moving" a resource to level0 is just a matter of setting a byte in the resource header so it's not that costly, since you have to access the resource header anyway in order to getResource() and retrieve the resource status. So far so good, it's seems to work pretty well but, I admit, I still have to stress it to see how really performs under heavy load. It will take sometimes before I can stress test it, I'm now involved in writing with the GUI library which does not need much resources to work. I've also to refine and simplify some aspects of the main interface, but I'm satisfied of the results so far. See you next time