Kohi #006: Application Layer and Entry Point (Vulkan Game Engine Series)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone and welcome back to the kohi game engine series today we're going to get started on the application layer and the main game loop and we're going to get started right now so before we jump into the video i would like to take a second and thank the channel's partners arsleya and wen chang the partners are the highest tier of membership on the channel so i thank you guys for your support i'd also like to take a quick moment and thank everybody under the supporters section all of you guys that are listed here on the screen thank you very much for your support it's greatly appreciated if you're interested in supporting the channel one way that you can do that is you can go ahead and click the join button below this video if you'd like more information on memberships i'll go ahead and post a link in the description below as well as a card in the upper corner of the screen with that stuff out of the way let's go ahead and jump into it okay so last time we set up our basic platform layer on win32 and linux so what we have currently right now is our entry point is in main.c and we are currently calling our platform startup and shutdown code from there which is far from ideal so what we really want is to set up an application that then references the platform itself and the application to be exposed not the platform because we don't really want to be up exposing platform specific stuff outside the engine if we can help it we are going to accomplish this via what i'm going to call the application layer and application layer is basically going to be you can almost think of it as a singleton in a manner of speaking it's going to be a state with a set of functions that go along with it that not only contains our game loop but also contains some basic state information about our application so that's what we're going to go ahead and do today but before we jump into that i wanted to kind of show you guys a little bit something special today i i was going to hold off on this but i think it might serve as a good motivator for you guys to kind of see visually where we're going with this so as i've mentioned in comments and i believe in in prior videos i have another sort of uh branch to this project that i am working on sort of in private so that i can test things out see what works see what doesn't that way when it comes time to actually record these videos i've got something worked out i've got a plan and a structure to go with and i can just go with that versus trying to figure all that out on the fly and having to do a bunch of refactoring so what i wanted to show you was a little bit of that what i'm going to call the development branch and it's not technically a branch it's actually a completely different repository but uh nonetheless um it is the kohi dev branch and so what i'm going to do really quickly is i'm going to run this application just so you guys can kind of get a visual idea of some of the things that are coming down the pipeline now if you pay close attention to what actually happens here when we launch the application you'll see some things happening so not only do we have assets coming up in the screen drawing we've got some basic lighting effects in here i still have a long way to go on that we have models textures all that stuff loading but if you noticed we also have asynchronous loading of these objects happening so we've got a full job system in place and so all of these things are elements that we're going to be building on top of so i'm not going to spend a lot of time on this but i do think that it might be somewhat useful to you guys to be able to actually see some of this stuff in place so i figured i would go ahead and just real quickly show you this with some test models in here so that you got guys could get a visual idea of where the project is going so anyways that is that let's go ahead and jump into our application layer so there's a couple things that we want to do first for the application we want to create a couple basic methods but we obviously need somewhere to house those so under core we are going to create a new file i'm going to call this application.h obviously we will begin with our pragma once and defines.h include okay and we're going to start off with two methods here we're going to start off with a application create and an application run and this is basically going to be the entire interface that runs when we start up the application so it's a lot simpler we don't have to worry about any platform stuff at this point now when we create the application uh currently if we look at what we're doing in maine.c we're actually passing in some information here right so we have uh the the application name and then we're passing an x y and a width and a height for our window position if we actually need it so what would be nice is a way to be able to say when we start up the application what some of this configuration is and so we're actually going to add that as well so above here just under defines.h we are going to go ahead and create a application config struct and this is going to take in our starting x and y positions and a starting width and height for the windowing and then of course our name of the application so uh this is of course the application uh the window title that's going to be used so with that in our application create we're obviously going to want to take this end so so we are going to want to take in our config like this okay so the next thing we're going to want to do is we're going to want to copy these two methods and create a new file under core call it application.c obviously we'll want to include application.h paste in our methods get rid of the k api and now we'll go ahead and start filling these things out there are a couple of things that we are going to want to set up here so the first thing that i mentioned is application state and i mentioned this is going to be sort of a singleton which means there's going to be sort of one instance of an application that runs so the way that we're going to handle that in this code is we're going to create a sort of static application state structure that we'll go ahead and hold on to and that is where we'll hold sort of our global application state as it were so we're going to create a struct of type application state and it is going to hold a boolean for whether or not we run it we're currently running a boolean for suspended we'll come back to that in a bit it's going to hold a pointer to the platform state and then it's going to hold width and height and the last time which is going to have to do with our our game update so we'll come back to that in a little bit and actually it's going to hold probably a lot more stuff this is going to grow in size as the application does but for now these are the basic things that we need to put everything in place now i mentioned that we have a sort of static state that we need and so that is going to be a static variable which makes it private to this c file and that is going to be of type application state we're just going to call it app state and it's not going to be a pointer we're not going to do any dynamic allocation anything like that we're just going to access this directly for any of the methods in here that we create or when we fill out these guys so there is one more thing that we want to do and we want one more sort of variable to track and we could technically add this to the state i suppose but i'm not going to do that i'm just going to add it here because there's really only one place we need to check it and that's it and i don't necessarily want to pollute the state with that so this is an initialized boolean and basically this is just a safety check that if we happen to call application create more than once this is actually going to cause a failure of that and what i mean by that is this so we simply look at that initialize and say hey if we've already initialized let's throw an error and return false now if this returns false uh that means that the application could not be started and the program will actually wind up exiting so uh with that we actually have a couple of things here that we have not imported so we have our k error here which is out of our logger and then we have the platform state which is out of platform so we need to go ahead and add and include for logger.h and include for platform.h so now that we have those things there is a couple of things that we need to bring out of here now one thing that i noticed is we're not actually doing this in main.c but our logger actually does have this initialized logging function that we're not actually calling anywhere so we actually need to make a call to this guy because we are going to be standing some stuff up here very shortly so back in application c the very first thing that we are going to do here is initialize logging and i'm actually just going to put this a comment here that this is where we're going to initialize our subsystem so we're going to have a lot of subsystems throughout the engine and we're going to cover those individually this is one of those subsystems and that type of initialization happens as we create the application so the other thing that we have is we have this test code here for our logging let's get that out of main and for now i'm just going to move that also over here to after initialize logging we can always remove this later in fact i'll put a to do in there to remove that but we want to leave it in place for now because we actually have some enhancements that we want to make to the logging system that we're going to want those for so i'll go ahead and move those over there and the next thing that we're going to do is we're going to set a few of these items for application state here so we're going to say first off that application state dot is running is equal to true and that's basically saying okay we're running the application now and we're going to say application state dot is suspended is false now suspended is sort of a state that our application can enter when we shouldn't be updating or drawing or basically doing work on the application and this can include things like when we minimize or maybe on certain platforms like android for example where we've switched to another task or another application where we can't be occupying resources we need to actually completely pause the application suspended is where we do that so it's going to be a bit before we actually tie that in but we want to go ahead and get the flag in there for it now so that when we go to actually tie that in later it makes things a lot easier so the next thing we want to do is back in main.c we want to strip out this platform startup code and i'm actually going to grab this platform shutdown as well so i'm just going to cut that out of here and go back to our application and paste it down here okay and we're actually going to get rid of this state here because we're actually going to use the one that's up here and actually i just realized i made this a pointer it should not be does not need to be a pointer so we're actually going to use this platform state here so we're going to change this to [Music] appstate dot platform and we're just going to pass the address of that and then instead of passing all this hard-coded data we are actually going to pass in our configuration so we have our config dot name our config start position x config start position y and this is actually going to get too long for one line so i'm going to split this up okay so we have our config start width and finally our config start height okay and this is where our our platform startup is going to be now we are actually going to move this while loop that is here which is technically sort of our first iteration of our game loop we're going to take this out of this if check and for right now we're going to paste it in application run we'll come back to this okay so if our platform startup is successful is basically what we're checking here we want to do something however we're going to invert this to say if it's not successful so if it's not successful we want to just return false okay so that is pretty much the platform startup change so the next thing that we need to do is take this platform shutdown code and we're actually going to put it outside our while loop here after that so whenever this loop completes we will go ahead and shut down the platform okay so the last thing that we need to do in our application create is we need to mark our initialized flag as true and then we need to return true now this is obviously a very simple setup this is going to grow quite a lot as the application does but this is where we're going to be doing all of our initialization from uh going forward except for game specific code okay so our application run as i said before this is where our game loop will actually be so when we call application run the application is going to [Music] continuously be in this function for the rest of its life until the user actually chooses to quit and that is because our game loop is right here however right now this is hard-coded to sort of just do a while loop forever while true basically and we don't want that so we need to change this to app state is running to say basically while our app state is running um we want to loop but if it's not running then we don't want to loop and so if for any reason if you recall when we set up our platform pump messages it returns a boolean so if for any reason this returns a boolean we actually want to break out of this message loop so or the game loop rather so we're going to go ahead and do a check now properly on this to say if not platform messages or in other words if it returns false then we are going to go ahead and set appstate dot is running to false now obviously this does not have the state so we'll need to pass it the address of app state platform which is our platform state and we'll need to do the exact same thing down here for platform shutdown the next thing that we will go ahead and do is if for some reason we exit this loop without having set this to false we want to absolutely make sure that that is set to false just so that anything that checks it at this point during the shutdown process actually has the right information there so we're going to go ahead and explicitly set that to false okay so next we call platform shutdown and then our application run at this point would return true and so for now that is our application setup at least for right now like i said we are going to be expanding on this but for now that is it so back in main we are going to get rid of platform.h because we don't want that anymore and in fact in platform.h before we update main i want to get rid of the export of platform startup and platform shutdown as well as platform pump messages because we don't want any of this stuff exported anymore we don't want to be able to call this from user code okay so back in main.c we want to include core application.h and this will actually be changing somewhat too but for now just to make sure that everything is actually hooked up properly we're going to go ahead and do it this way okay so the first thing that we need to call obviously is uh our application create but to do that we need an application config so the way that i'm gonna do this is i'm actually going to [Music] copy all of this go back to our main.c and just paste it all here and i'm going to just change this to a declaration right so we'll just call this config and to make this explicit we'll set our config start position x to 100 as before and then we will do the same for y now that we have our configuration we are simply going to call application create right and we're going to just pass through the config and then we're going to call application run okay and this actually takes a pointer so i need to pass the address of config and let's go ahead and build this okay and we'll run and we can see here that our application is running as expected so again uh it doesn't feel like we've accomplished much here but actually um we have set up a better structure for our engine than just directly relying on our platform code okay all right so the next big piece that i want to handle is i don't want user code to have to be concerned with creating and and dealing with input parameters and and so forth from a main function so i don't want user code to have to worry about creating a an entry point or dealing with arguments or anything like that i want to have the engine handle all that and be able to eventually expose some utility methods to user code to be able to handle those things gracefully so what we're actually going to do is we're going to take the main out of the user code and we are going to put the entry point of the application into the engine dll itself and then we are going to um create we are we're going to invoke that from our our executable by turning it so basically what this means is the only thing that the executable is actually going to contain is just functions specific to the executable that it needs it doesn't have to worry about calling application create an application run and all this startup code right this this is not a great way to to have to work with an engine right you shouldn't have to do all this you should be able to basically define some configuration and pass it off and say okay engine do what you need to do to start up and then we're good to go right we shouldn't have to call all this so we're going to change things up a little bit so the first thing that we want to do is as i mentioned we want to get rid of this main function out of here but to do this we're actually going to need to do a couple of different things in our so as we've discussed in our architecture of kohi the executable is basically going to be referred to as a game so we need a structure that is capable of giving us a description of what a game looks like or providing us an interface if you will as to what the actual game should contain in terms of what functions it should have to be called how it stores state configuration things of that nature and so that is the first piece we are going to create up here in directly in source so this is sort of the the root source folder we're going to create a new file and we're going to call this gametypes.h and gametypes.h is going to pragma once obviously and then it is going to include core application h and what we're going to do is define sort of what our game needs to provide in terms of how it needs to run so we want to we want to basically declare what the structure of our game should look like on a high level and to do that we are going to type def a new struct called game this game will contain our application config that we defined in application h and it's going to have some function pointers so we're going to have a function pointer to initialize so that the engine can call the game say hey do whatever you need to do to initialize yourself there's going to be an update a render an on resize which is basically for window resizes and then a void pointer to its internal game state so this is basically what a game will look like to the engine we want to keep this interface as simple as possible so that there aren't a lot of entry points into the game from the engine okay and that is it for game types we just need to sort of declare that okay so the next thing that we need to do is again under the source folder we need to create another new file and we're going to call this one entry.h and entry.h is going to include a couple things it is going to include the core application the core logger and gametypes.h next it is going to include the definition of a function that will be defined in user code and this is a little bit different than anything that we've seen up to this point so we have x turn b8 create game and then we have a pointer to a game that we're saying is out game so what this is doing basically is it's telling the compiler that we have an externally defined function to create a game so this extern keyword is basically saying we have included this in some other either library or executable that is going to be calling this code it's defined elsewhere so don't worry about its definition it's defined outside the context of this particular assembly that's being compiled just know that it returns this type and looks like this and so um this is going to be defined over on our main.c here in a moment so this is the critical piece to being able to move main this function out of our user code okay speaking of which back in entry.h we are actually going to take main.c and cut out the main function and we're going to place that in entry.h so this is now the new main entry point of the application i'm actually going to mark it as such and so this is where our main function is actually going to sit so that we no longer have to define it here in this code the next thing that we're going to do is we're actually going to have to move some stuff around here but before we actually do that we want to go ahead and request a game instance from the application by calling this right so the first thing we need is a game instance variable some something to actually hold our game instance and then we're going to go ahead and call our create game that's externally defined bear in mind you know again our engine dll does not know um how this is defined it just knows that it's defined externally okay and so we're saying um go ahead and call that externally created function and uh pass it the address of gameinst here to be filled out and if that returns false then we have a fatal error of some kind on the game side and we actually can't proceed so we're going to return negative one okay so this is going to set up our game for us all right so the next thing that we want to do is as i said before we actually have in our game types we have these function pointers here right so we actually want to be sure that the game has set these things appropriately because if those are null and we go to call them obviously that's going to be a problem so we want to ensure that those function pointers actually exist so we're going to check that render update initialize and on resize are all assigned they must be assigned by the game so that is really one of the only requirements that the game has is it must fill out these things so i'm just going to snip this out of here and move it down below here just for a second because we're actually going to be moving it out of here entirely okay so the next thing that we're going to want to do is our application create takes our config which i've just moved down here and said we're going to move out of here so what we actually need to modify this to take instead is our game instance right and so our application create actually needs to be modified a little bit so if we go to our application create definition or declaration rather right now we're taking a pointer to an application config which is not really what we want so now that we have the new game types we want to include gametypes.h and instead of application config here we are going to take a pointer to a game and say game instance okay and now in application c we're going to change this obviously to match that definition and so our application doesn't yet know anything about a game so obviously we need to add a definition for that so i'm actually just going to go in here to our application state and add a pointer to a game instance we'll call it gameinst right after we check our initialization but before we actually initialize our logging we're going to go ahead and set the instance right so we're going to say appstate dot game instance equals game inst okay and we'll just reference this going forward right so now we have our game instance okay down here in our config section we just need to change a couple things so the way that our structure looks now is we have app config as our application config right before we are calling just config so now we actually need to change this i'm actually going to call and select here and say game underscore inst and then app underscore config oh and then since our app config is no longer a pointer we'll need to change this to just dots all the way down okay so this is basically just a direct pass-through of those properties so the next thing that we're going to want to do is we're actually going to want to call into our games initialization routine so to do that we simply call appstategameinst and then since it has a function pointer to initialize we can just call that directly and then we pass through our game instance itself which if you recall is just a pointer to our application states game instance right and so if for some reason initialization fails at the game level that's a fatal fair uh fatal error here and so we log that out and return false and that aborts this process okay so the next thing that we want to do is go ahead and hook up our on resize event um or i should say our event handler and i know that we haven't actually declared this because we haven't handled window resizing but i want to put this plumbing in place now so that the game instance actually gets notification as to when the window resizes and as to what those new dimensions are so whenever we get to handling that part we're basically going to set the app state width and app state height to that new resized value and then we're going to go ahead and call this okay and then all the rest of this is the same here in our application is where we're actually going to be adding a little bit more stuff so right now we're basically just pumping platform messages and not really doing anything else so the next thing we want to do is actually do a check to say if appstate is not suspended then we want to actually do some other stuff most notably we want to call our game insta update point uh function pointer right so we want to run the update routine now we are passing a zero here this is for the delta time we don't have delta time yet that is something we are going to come back to shortly um so for now we are passing zero here because we don't have that and i'll explain what delta time is when we come back to that but for right now let let's just take that at face value and accept that we're just basically hooking up some plumbing right now so the game had a update routine it also has a render routine which looks almost exactly the same change that to render so again if render just like if update fails then we want to break out of the application because something has gone wrong okay so that does it for the application updates to support the game now we have a couple more pieces unresolved here in application create so first off application create actually returns a boolean but we're not actually checking it so that's the first thing we need to do and if it fails for any reason we want to go ahead and log that and return appropriately next we no longer have the config here so we actually want to pass the game inst instead or more accurately the address of the game instance okay and then our application run also returns a boolean and for if that returns false for any reason we also want to log that out as well so we're going to go ahead and check that and basically all we're saying here is if that returns false false it means the application did not shut down gracefully and actually i have these new lines in here i don't really need those because the logger handles that for us okay so um we have our application create our application run this kicks off our game loop um so you know what let's actually snag some comments in here just to make this a little bit more obvious as to the life cycle of the application okay and then of course we have our return zero here so now we're left with our application config which is sort of just dangling in place here what do we do with this well i'm going to cut it out of here because it no longer belongs in here because we actually get that from our game inst so we have this externally defined create game that so far we haven't actually defined so we need to do that in our user code so i am actually going to take main.c here and i'm going to rename it to entry dot c because that is more accurate as to what it actually is okay and we are going to define our b8 create game right which takes in a pointer to out game which is basically what we're going to be filling out and this is where our application config sits however we don't need this guy anymore because out game actually includes that so similar to how we did an application we're just going to change this to actually use the pointer so this is going to be out game app underscore config and then it'll set all these properties okay so we'll go ahead and mark this as our definition of our function to create the game and from there we need to we don't need this application anymore right that's our our test code we actually don't need the logger or asserts anymore okay we do need dot h and remember we're using angle brackets here to denote that we are importing from the engine not something locally okay and we're actually going to need to define a couple of other things right because if we look at our game structure one more time we've defined sort of what our our config looks like but we haven't defined these other things right we actually need to set some stuff up for this and so under source we are going to create a new file call it game.h and this is actually where we're going to sort of stub out our game code if you will so the first thing that we're going to do in our game.h is obviously pregnant once we are going to include defines and include game types because we need those things the next thing we're going to do is we're going to create a basic game state structure which we are going to come back to in just a moment but for right now it's just going to hold that delta time value that we pass in our update and render and we're going to create our four methods our game initialize game update game render and game on resize so if we take a look at our game types one more time you'll notice that you'll notice that these are called initialize update render and on resize so it may not be immediately clear but the name whoops the name of these functions doesn't actually matter what matters is that the return types and the parameter types all match and the number of parameters so as long as these things match which is known as the function signature the function pointers can be pointed to these functions right so even though these are called game on resize game render game update these could be called anything and still this could be assigned to point to those functions okay so i've just sort of named them this to make it clear that these are the game versions of those particular functions we need a new file and we're going to call this game.c obviously it includes game.h for good measure we'll go ahead and include the logger as well and i will go ahead and define those four functions now for now they're not actually going to do anything but we need them to actually be defined and at least return true so that we can actually start up the application successfully so we'll go ahead and fill these out in a bit so now that we have that set up how do we link these or these to our game function pointers here well in entry.c we basically fill them out as an additional property to out game our entry.c will now include game.h which gives us access to these functions here and to this i'll just add assignments for each one so game update is assigned to out game update for same is true for render same is true for initialize and on resize okay so we're almost done the last thing that we have to worry about is this state now this is a void pointer and our void pointer basically allows us to assign any type here whatsoever and the engine does not know or care about it it just knows that it exists and that's it so what do we do with this well we will need to if we recall here we created a struct for game state and this is actually what we are going to assign to our out game state right however if we look at our platform code which is how we do our allocation we don't have a way to point to this yet so what i'm going to do is this is very temporary because again we don't want to be pointing to platform code but we will be revisiting this as i'm temporarily going to expose platform allocate and platform free we are going to provide a nicer interface to these functions instead of having to go through the platform code but for now we're going to use these because we don't have that interface yet so back in entry.c we are going to create our game state by well first including oops platform platform.h and i'm going to put a to do there to actually remove that so we don't forget to remove it because we don't want to be again including our platform specific code here so our state we need to basically platform allocate and we want the size of game state and aligned we're just going to say false okay and again alignment is not something that we should have to care about in user code it's something i've not explained yet i realize that so just take this at face value for now so we're going to allocate a new game state of type gamestate and assign it to our void pointer here and that is it that's how we create our game state so last but not least we return true indicating that all of this has been successful now we can have some additional initialization in here it's up to the user from that point but this is basically um the only part that user code has to sort out for now eventually this interface will also be improved to just take in basically configuration file but again we don't have that stuff for now so for now this is how we're going to do it so i believe we've covered everything that we need to cover however just to prove that all this stuff works i actually want to do a couple of logs here okay so i basically want to say you know game initial game initialize was called which is a way for us to sort of validate that all this works so i'm going to build looks like we have an error so i made a couple goofs here first thing is entry.h needs a fragment once and then i accidentally created a circular dependency between game types h and application h game types h was included here because of this and this should technically be for declaring this instead so we're going to change this to a struct and then get rid of this include and say struct game there and that will allow us to forward declare that and then in the c file we can then include gametypes.h so if we build this now we should be good to go it's easy to miss things like that sometimes all right so let's go ahead and run and now we are successfully running and we'll see we have an additional debug message here game initialize called so our application layer is now successfully in place we've properly created the concept of a game and gone ahead and initialized that properly we've moved our entry point from our executable to our dll and are now pointing to that properly so now our application is starting to sort of resemble the original architecture that i had discussed and so with these things out of the way now we can move on to setting up some of the other systems and so forth next time so that's going to do it for this video please do me a huge favor and hit the like button if you haven't already consider subscribing it really helps me out hit the little notification bell to get notifications as to when the next video in this or other series drops and i will see you guys next time you
Info
Channel: Travis Vroman
Views: 2,690
Rating: undefined out of 5
Keywords: write a game engine, write your own game engine, make a game engine, how to write a game engine, learn to write a game engine, writing a game engine, make your own game engine, game development, game dev, game engine, game developer, how to, tutorial, programming, gamedev, vulkan game engine, vulkan, game engine series, how to make a game engine, vulkan api, 3d game engine, vulkan engine, vulkan tutorial, 3d graphics, learn vulkan, 3d vulkan, how to vulkan, vulkan renderer
Id: jEbqCf-MsZs
Channel Id: undefined
Length: 44min 10sec (2650 seconds)
Published: Fri Apr 16 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.