C++ 3D DirectX Tutorial [Bindable / Drawable System Part 2]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
so we've created our bindable drawable system and it is good but there is tons of room for improvement so the first thing we can notice is that well I mean all these boxes we're creating 80 boxes we're creating 80 vertex buffers 80 vertex shaders but they're all the same vertex buffer they're all the same vertex shader everything is the same basically between these boxes so it's pretty wasteful to create separate instances of all of these d3d resources when they're all the same thing we'd like to share them between boxes every object of the class box should be able to share its common bindable now when I ask you how do we share data between all of the objects of the same class hopefully you'll say well Chile you know we use static right we use static variables for that and that indeed is what we are going to do but there's a question where do we put the static variables and you might say well Chile there's two places we can put them we could put them in the base class drawable or we could put them in the child class box so if we put it in box we'd have to put you know a vector a static vector in here and then maybe some static member functions to add binds just like we had in drawable but there's a problem with that first of all you'd have to do that for every different type of drawable that inherits from the base class you'd have to repeat copy and paste all that code the other problem is that the draw function here it only has awareness of the per instance bindable x' that are in the drawable class here in that vector doesn't have any awareness of the static ones those two problems to solve and you might say well then we'll just push this static data up into the base class so we'll put our static vector of static bindable x' the bindable x' that are the same for all instances of box or all instances of sphere or whatever but that's that doesn't work that doesn't work right because if we put the static data up in the drawable class there will be one copy for drawable as a base class that means that box fear pube they will all share the same static bindable so we don't want that we want separate static bindable z' for pubes fear and box so we're at a little problem here we've we basically we've got to do copy and paste if we want separate static bindable z' for each one of these derived classes now let me show you one weird trick C++ programmers hate them so what we're gonna do is we're gonna create a class in this inheritance hierarchy between drawable and the actual concrete classes and we are gonna call this one it doesn't really matter what we're gonna call it we're gonna call it draw double base and what we're gonna do is we're gonna make it a template class T and for every different type of class that inherits from drawable we're gonna have it inherit instead from drawable base and it is gonna inherit from drawable base with a different T so box is gonna in here with templated on X and sphere is gonna inherit templated on Y and pube is gonna inherit template it on Zed now because they're templated different parameters that's going to create three different instances of this drawable base class and they're all gonna have their own separate static data so if we put the static vector in here we're gonna get a copy and pasted by the compiler instead of manually copy and pasted by us now the only problem is we have to give each one of these guys different template parameters what parameters should we choose well easiest one is to choose the parameter of the actual type that we are defining so we can do box from public base and templated on the type that we're creating and it works and we'll just do that for everyone you'll see you'll see it's gonna be good trust me it's cool all right so let's take a look at the code all right so I was threatened I create the class drawable base and we can see here it's a template class T and it inherits from drawable and a static member data which is the vector I talked about and here you have to declare it outside or define it outside of the class because that's what you do with static and in template you put it in the header file just below the class basically and then you've got some functions you've got functions to add bindable x' to the vector for an index buffer and for non index buffer you have a function here that checks to see if the static data has been initialized because you only want to initialize at once basically if you're doing box right the first box will initialize all the static data and the rest of them don't need to initialize it again because it's already been created so you need a way of checking to see whether it's already been initialized in my way is very simple if the vector here is empty that means it wasn't initialized so we're assuming here it's a big assumption but we're assuming that there is going to be at least one static bindable and that's indecent exception for right now although it's probably not one you want to make in a super perfect system I mean anyways so we've got this function here to check if it's been statically initialized add binds and here is the tricky here's a little tricky dicky so in draw do two things I make drawable base a friend of drawables but has access to the the privates and I also create this pure virtual function get static binds because drawable needs access to these static binds that are going to be declared by its children so we need something to bridge the gap between our static templated world and our dynamic virtual world and that's going to be this virtual function here and if we look at drawable that's cpp we see now in our draw function we bind all of the instance binds and then we bind all the static binds by calling this function here get static binds it'll get a reference to the vector then we can loop through all the elements in a vector called bind then we finally issue the draw command and in drawable base the promise is fulfilled here we implement the get static binds and we just return static mines then in Box H the way we now declare a box at HS we do class box public drawable base templated on box and in box that's CPP there's a little bit of a change here now we check to see if we are statically initialized if not we do all of our static initialize calling add static for all these binds and then at the end we do our non static initialization and there's only one bind that is not static and that is the transform because every box is going to have its own individual transform right if we gave them all the trans same transform they just all stack up on the same space in the world wouldn't make any goddamn sense would it now if we check out a branch at this commit and we run this you're gonna see a big old crash big old exception this was null pointer that's never good right well let me show you something neat if we go in the app dot cpp and we changed a number of boxes 1 and then we run it works fine what if we change the number box is to obviously 80 was too many but maybe 2 will be fine right all right crashes okay so this is a fun a little bit of a fun one to debug but it's not super hard the problem as you can imagine lies in the new logic we added for static initialization for those bindable x' that are shared by all instances a box so we're only calling and adding these functions once and that's good that's what we want except one of these functions is not like the other add static index buffer right because when we add an index buffer we're not only adding the index buffer to the verb the the vector of bindable x' we're also setting the pointer to the index buffer that exists in every instance of the drawable class so what happens is the first box that is created it does the static initialization it adds the index buffer it sets up its own index pointer the second box does not do the static initialization does not set its index buffer pointer so that pointer is no pointer and then when we call draw on that bad boy we try to access the index buffer to get the count and it shits the bed which is rightfully so so we need some way of getting all those other boxes getting their index buffer pointer set up but all we have is a big vector of bindable x' we don't know which one really is the index buffer and the solution this problem wasn't that difficult to come up with in drawable base we're gonna add another function and we're gonna call it set index from static so if the index buffer is in the static binds then for all of the boxes or whatever that aren't doing static initialization we've got a call set index from static and all it does really is it loops through all the static lines it uses dynamic cast to find the one that is the index buffer and then it sets the UNIX buffer pointer and I just got a couple of asserts here just sanity checks to make sure you're not trying to do something incredibly dumb and then in box dot cpp the logic is very simple if you're doing static initialization you proceed as normal before if you're not doing static initialization you should set your index buffer reference or pointer from static and doing this now it works perfectly fine as before only now we don't have all that duplication of the identical you know vertex shader pixel shader all that stuff it's all being shared between the instances of box the only thing different for each box is its transform constant buffer everything else is shared all right so our system is shaping up pretty nicely here there are of course many more improvements we can make but let's make one more and then call it a day so we've got our transform constant buffer and this is the only one that is being stored as a per instance bindable all these other bindable x' are being stored as per class and static data so here and that's good because every instance needs its own transformation it needs a separate matrix because it's got to have a separate position in the world but here's the thing does the hole does the whole of this transform constant buffer object need to be separate because think about it there are two parts in the member data here one is the reference to the drawable now every transform constant buffer needs to have its own reference to a different drawable right because they're all every different drawable will give you the different transform that will give you the different position in the world but do they all need their own separate constant buffer I mean if they're just updating a constant buffer and then binding a constant buffer to the pipeline which is then used to draw the vertices couldn't they all use the same constant buffer and just update that one constant buffer every time the answer is yes let's do that again the solution isn't that difficult all we do is we make the the constant buffer here we're going to make it static and we're going to make it dynamically allocated and that's so that we can lazy allocate it later on then in the CPP file here we check to see if the constant buffer has been allocated yet and if not we allocate it and then in the bind function here we use the static constant buffer and because it's a static variable we've got to declare it down here like this and there you go in drawable base what am i doing here now this is just a minor housekeeping I decided that these functions here like add static bind and so forth they don't need to be publicly visible they can be protected they only need to be seen by the children of this class and I can make these function here static changes message in here just a little bit and in drawable i also what did I do here well I made this protected because again these add bind add index buffer those they don't need to be publicly visible now there's one last minor details want to quickly go over here and that is the no accept specifiers here I made some of them conditional so here depending on whether the build is a debug build a release build it might or might not throw in this macro here I defined it myself you can do the same if you go into the properties go into the preprocessor and you can set for example in release here I said ease debug equal to false and in debug I said is debug equal to true and there we have our bindable drawable system everything now all of these different bindable zarnow organized into their own separate files your own separate objects they can be mixed and matched added to any kind of object any kind of drawable that we want to create and render on to the world we've eliminated that loading of resources every frame we've eliminated duplication redundancy of resources and it's clean its dynamic the top-level interface is super simple and it makes my dick real hard but yeah there's still plenty of things that we can do there's plenty of problems one probably the biggest issue right now is that we are binding these resources for every single instance of a box even though the resources between instances of boxes they don't change the vertex shader doesn't change we don't actually have to bind it every single draw call and that's an improvement that we can look at in the future but for right now we have improved our system enough so that we can continue on with the coils continue learning about the API and we'll look at improving this system in the future as it becomes an issue last thing I want to add is a little bit of a disclaimer I this design here it has two purposes number one is to solve the problem that we currently have in the tutorial which was reloading the resources every frame the second purpose of this design here is it to expose my viewers to new ideas new design spaces that they might not have been aware of now that doesn't mean and that the one thing that I just want you to understand is some people they see me do a certain design or show off something to say oh this is the way it must be done Chile showed me how to do it this is how I'm gonna do everything from now on but no that's not that isn't completely not right this is just an idea one out of many ways that you could solve the problems don't take this and run with it as if it was set in stone this is how things must be done because that's absolutely not what I'm saying here I just want to show you guys some cool and interesting and get moving on with the tutorials and that's gonna do it for today thanks for watching hope you enjoyed the video if you did please click the like button helps a lot and I will see you soon put some more hardware 3d [Music] [Applause] [Applause] [Music]
Info
Channel: ChiliTomatoNoodle
Views: 6,660
Rating: undefined out of 5
Keywords: 3d game programming, c++ 3d, game programming tutorial, c++, C++ tutorial, c++ game engine, how to, 3d game engine, Directx programming, direct3d programming, how to program 3d games, DirectX, Direct3D, D3D, programming, game, lesson, cpp, guide, code, tutorial, coding, software, development, windows, visual studio, game dev, chili, winapi, windows sdk, win32
Id: ZoNCFe9FgG8
Channel Id: undefined
Length: 16min 25sec (985 seconds)
Published: Wed Apr 24 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.