Dealing with Errors in OpenGL

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's up guys my name is a Chan and welcome back to my opengl series so today we're going to be talking all about errors last time we were able to use index buffers and I even mentioned one thing that you might do wrong that would cause an arrow to appear and actually does not display anything on the screen check out that episode if you haven't already but today we're gonna figure out what we can do to actually be notified or how we can find out what has gone wrong with OpenGL when we do something incorrectly that's a very big part of OpenGL because open gel like a lot of things tends to be fairly fragile and we can upset it pretty easily that's okay that's just a normal part of development how do we know what we've done wrong and how can we debug this as quickly as possible and fix it so that we don't have to spend the whole day wondering why our screen is black okay so there's a few ways to deal with this we're not going to talk about using external tools or anything like that we're just going to talk today about what OpenGL actually gives us as a way of checking for errors and all of that so basically right now we have two main ways that we can check for errors in OpenGL one of them is called GL get error it's a function we can call it's been in opengl since forever since the very beginning and it's conserved because that's compatible with all versions and it works in a very simple way basically whenever we call an open shell function if an error were to occur a flag kind of gets set internally in open jails memory that basically says what type of error occurred and when we call GL get error that gives us back a flag right and by flag I just mean like an integer basically there's like an error code and if we actually keep calling GL get error it will kind of give us all the flags because sometimes it's possible for us to generate multiple errors if you're really bad at programming if you're really if you've done something really bad you might even have caused several different types of errors with a single open jail function call and so because of that jail get error will actually set like maybe three flags of three different types of errors that have occurred and when you call she'll get error it gives you one of the flags just an arbitrary flag so you kind of have to keep calling it to get all of them but basically that's just us kind of polling open GL us asking OpenGL hey is everything okay have I caused any errors now that strategy is okay the way that it's typically set up is that since pretty much any opengl function that we call might cause an arrow let's actually take a look quickly at what an arrow might look like and OpenGL to begin with so right now since the last episode we have this beautiful Blue Square rendering now what I'm going to do is just close this and inside my jail draw elements call which is my actual draw call I'm going to change this unsigned int enum which specifies the type of index data that we actually have to just say GL end this is one of my favorite errors to demonstrate because it's really simple I've made this mistake plenty of times and is it's fairly effective let's hit f5 you can see that all we get is well a black screen our square rectangle is just not rendering at all and you can see that in the console of course nothing gets printed at all except for our OpenGL version that we're printing we just get a blacks we just get a black screen and this absolutely sucks I mean I hate this getting a black screen and then having to figure out what you've actually done wrong in which line meant like maybe you maybe haven't set up your vertex data correctly maybe your index buffers not correct right maybe your shader doesn't work like what has gone wrong it's very hard to tell and in this case of course we're just sending jail and instead of jail unsigned and as the index buffer data type so it would be great if we could just get up and gel to tell us hey man your GL draw elements function you're passing an incorrect enum in there you should fix that right that would be fantastic so the typical kind of workflow that we have with GL get error is first of all before it pretty much before every function call that we give to OpenGL we clear all the errors so we're basically just called GL get error and a while loop until it equals no error which means that we've kind of retrieved every possible error from OpenGL that's just a way of that that's just a way for us to actually clear all of the pending kind of errors then we call GL draw elements and then we call GL get error again and that way we can kind of see has this previous function called GL draw elements has that generated any kind of errors and if story which ones and then we can kind of print that to the console or something like that to figure out what went wrong so that's one way of dealing with errors in OpenGL it's pretty much the most common way the most popular way because it has been in the specifications one point one since the very beginning and it's compatible obviously with pretty much everything now recently in opengl 4.3 so it is quite recent OpenGL added a function that basically lets us well it's called GL debug message callback if we take a look at the documentation real quick it's called GL debug message callback you can see that it's been added in opengl 4.3 and basically what it is is it allows us to specify a function pointer to opengl and opengl will actually call that function of ours when an error occurs now this is much better than jail debt error because first of all said of us having to constantly ask her in jail hey are you alright man is everything ok it will tell us itself when something goes wrong the only thing with this is obviously the fact that it's in opengl 4.3 and above so you can't use that in earlier versions but also another really good thing about this is it will actually give you more detailed information instead of just giving you an error code which is kind of useful you like completely useless they will even suggest things like it'll explain to you what you've done wrong in English like it's amazing it's kind of dependent of course on drivers and stuff like that but in my experience that generally has been very very good and much better than jail get error today we're just gonna be focusing on GL get era completely it's gonna be just totally about you get era that thought that way is still good and it's obviously compatible with every version of OpenGL but then in a future episode I will I want to devote it like a whole episode GLD about message callback and talk about how we can actually use that because it is much better but obviously it's not really compatible with every version network in jail alright anyway let's talk more about you'll get error so if I flip over to the documentation here for GL get error above and telling us of course what it does and the fact that Jill nor error is what it returns when we don't have an error which you can see also that Jill no error is guaranteed to be zero so that's useful information here it also tells us that calling it will return an arbitrary error flag so not necessarily all of the errors and because of that we should call this in a loop to make sure that all of the errors are reset and we kind of get information about all of the areas that have actually occurred with OpenGL so that's kind of like important information that you definitely need to pay attention to we should we should be calling this inside a loop to make sure that we get all of the errors because if you don't kind of do that then maybe you might be clearing an error just by calling GL get error then you call a function and then you get two errors back and you actually didn't realize that Oh actually one of them was from another function I called early on because I didn't clear all of the errors so just make sure you kind of read this because the worst thing about jail get error is if you can't even do that properly and then you don't even know what's happening all right so let's look back to our code and let's actually add all this so what I'm going to do at the very top is make a function I'm actually going to do this at the very top I'm going to make a function static void GL clear error and I'm kind of just writing GL and capitals here at the beginning of this function just to kind of let ourselves know that it is kind of an open GL internal kind of function that we're writing you know and the purpose of this function of course is just declare all the errors so all we really need to do is call GL get error just like that and while that doesn't equal zero so either the way you could kind of write this is does not equal GL nor error so while there while we actually have an error back from opengl let's kind of keep looping through this so I'm just gonna add a semicolon here and that's it we don't really need a body we don't care about error codes at this point in time we just want to clear all the errors that's all now another way you could write this obviously because Jill no error is guaranteed to be zero as we read earlier you could just kind of write this like that so while Jill get error is not zero then keep kind of looping keep retrieving all the errors but I am trying to keep this code a little bit more clear for people who are learning OpenGL so I'm just going to write it like this okay cool so we now have a way of clearing errors let's write another function here which will actually print an error that does occur or all the errors that do occur after a function call so I'm just going to make this a void function called GL check error or something like that and all this is going to do is basically what kind of the same thing is the other function is going to GL get error however instead of just kind of ignoring this we're actually going to assign it to something so GL get error as you know returns a GL e Nome which is just an unsigned int but because this is such a close that kind of tied jail for I will actually use GL enum here so while GL enum error equals jail get errors I'm assigning it here some kind of retrieving the error and assigning it to aro kind of all in one line obviously since this is inside a wild block this while loop will run as long as GL get error as long as this kind of error variable is not zero so it's not false so while we kind of have errors that we are actually retrieving let's go ahead and just kind of print them out to the console so I'm just gonna write standard C out kind of format this nicely so maybe Open GL error and then in brackets I guess we'll actually put that error code and that's it for now all right pretty cool so now we have a very very simple way of actually checking to see if we have an error let's let's just test this out so I'm going to go down here to my draw function now I have made a mistake here right you can see I'm using jail int and not unsigned and I've still got that code in the first thing I'm going to do is called GL clear error write to clear all of errors and then finally GL check error after the function so you can kind of see how this works we clear all the errors first to make sure that we don't have any other errors from other functions we then call our actual function and then we check errors that way we can kind of make sure that all of the errors are actually coming from this function and we're just getting the errors from our last function call alright so now let's just hit f5 and see what happens okay look at its check this out so in our rendering loop we're actually getting a bunch of pauses for SEC we're actually getting opengl error printing and then 1280 fantastic so we've we know that we've done something wrong and if we go back to our documentation you can see the following errors that we actually have defined now which one of these is 1280 is the real question to figure that out let's go ahead and actually go to our OpenGL add a file so this will be in blue for us if we type in 1280 when I'm actually going to get anything because OpenGL defines all of its emails in hexadecimal notation so what we'll actually have to do is convert that error code from decimal into hacks to do that I'm just going to take a breakpoint into this error function here I'm going to unselect this so that it resumes my program you can see we have 1280 if I just right click here I can go accidental display and you can see the error code is just 500 here in hex so if I go back to my Glu header file and sort and search for 500 maybe as 0 x0 at the beginning you can see that here we have all of our invalid kind of errors right all of our error codes so 500 means invalid email now at this point it will be very wise for us to actually write a function which translates these numbers into actual words so basically if our error code is Kjell invalid enum as it is here maybe print invalid enum to the actual screen that would be nice but I'm not going to do that here just not to waste time so you can see that our error code here is invalid enum which tells us that we've actually passed in an invalid value into an OpenGL function and specifically an invalid enum now in this case that of course makes sense because Jill int is the actual invalid enum we've passed it should be unsigned int you can see though that there are a few flaws with the with what we've done here and there's no I mean it's not some of it's flawed it's just it's not that great right I mean we know we're getting an error we know it we can kind of deduce that it is inside the render loop and it's not you know in all the kind of OpenGL initialization code or anything like that that we did such as creating our vertex or index buffer because it's where we're getting error is like every frame basically so we know is something here right what we don't know is actually which line of code it occurred on so that's kind of an issue and not only that but you can see the code is a little bit clunky I mean if we have to go back up here and basically call clear error and check error before every function like clear error and then check out that's going to just pollute our code and make it so much harder to breed like why would we want any of that that's going to just burden us with error checking which we should be doing just automatically all the time right so there are a few ways of dealing with this for our first problem it'd be nice for us to know the line of code on which this error actually occurred on now we can kind of do that manually ourselves if we kind of resume our program here so it is running it is still kind of spamming all this to the console I did place a breakpoint here in this GL check error function to figure out what kind of value this wasn't hex you can see if I look at the call stack I double click over here I know that it's happened on this line which is GL draw elements so I do kind of know what line of code it happened on however that involves me placing a breakpoint and all that it's not not too fun and also in this case we kind of know what line it happened on anyway because we're only checking one function for errors but what we can actually do is get our actual deep to just pause execution and break on the line that causes that error right and we can do that by just using an assert basically an assertion is when you assert if something is true or false and if that kind of condition is false you typically like either write a message to the console or just kind of stop execution of the program and kind of break on that line so it's kind of like placing a breakpoint but we can actually place a breakpoint through code right we can actually tell our kind of debugger hey if you run into this line of code here into this function that I'm calling break insert a breakpoint here and just break right it's pretty cool we're definitely gonna talk more about assertions in the C++ series I'll have a link up there once that video is made but let's take a look at how we can actually do that so to do that I'm just going to kind of rewrite this check error function a little bit I'm going to first of all get it to return a boolean let's just stop execution of that program as well and I'll call this GL log call okay because we'll add some more features to it in a minute now if this does return an error and in fact as soon as it does I'm just going to call return false here now you might kind of question the validity of this loop now why does this have to be in a while loop if you're returning from the first iteration but this is just kind of to demonstrate from now so I don't really care and then return true so basically if we return false from this it means that our GL pool was not successful I'm then going to come up here and I'm actually going to write a macro which is our assertion right so define assert X so all this is going to do is validate a condition so we'll have to write a like this if you kind of write it like this it won't really work because it's reasons we'll have a video about C++ macros and all that again I'll link it up there when that's actually made but we have to write our condition wrapped in parentheses like this and if this was false I'm just going to call a function which will basically insert a breakpoint at that and at that moment in code and just break the debugger so this is an intrinsic to your compiler which basically means that this function that you call here is going to be different for each compiler that you're using when I cover assertions in the C++ series I will show what the function is for like all compiles and all of that I'm sure you can google it anyway but because we're using Visual Studio here and this isn't really about C++ is about OpenGL I'm just going to use the MS BC function which is underscore underscore debug bridge like that the honest colonoscope kind of lets us know that is a compiler intrinsic which means that it's specific to M SVC and it won't work and clang or GCC or any of those compilers okay so that's pretty cool that's our session function so what I can do now is basically what we have this geo log call over here at the bottom and this GL check error will actually it's called a log poll now let's just rename it to the log call like that I can assert that so I'll write assert and I'll wrap this in parentheses like that and I'll hit f5 to run my program so if this returns false it should just break the debugger and you can see look at that open children AC has triggered a breakpoint on this exact line we know exactly now a program does not continue running and doesn't keep spamming the error the error into our console our console just reads Open GL error 1280 and we can see where that error occurred how cool is that much better but we can do a lot better than this the next step is actually going to be just to get rid of having to call clear error and then log call after every function let's invent some kind of macro that does that for us so if we go up to the top where we define all of our macros I'm going to define another one called GL poll right X is going to be the function essentially that we're calling and over here the first thing that I'm actually going to do is called GL clear error to make sure that we clear everything now I don't want to write all this code on one line because it'll be hard to read so I'm just going to add a backslash and then hit enter this basically ignores the newline character so that we can continue writing this macro on another line I'm then going to just write X comma which basically will insert that function we've specified so that we call it and again and on the backslash now one more thing I have to mention is make sure when you put the backslash you don't insert a space and then enter otherwise use you still got the newline character there you have to put a backslash and then with no spaces press ENTER and then finally I'm just going to call GL log call but this time actually inside in a session so like this Jill log pull like that and I'm not going to put like a semicolon or anything over here because we don't need to because after we call Jill call we'll end it with a semicolon all right so now let's scroll down to our code here and instead of having to call GL clear error and then asserting that local what we can actually do is just wrap this Jill draw elements with a GL call just like that how cool is that and now we can hit at five all right and we get the same result here but you can see now it's even more clear it's actually on this Childre aliments line so we know exactly what's gone wrong now the what now one more thing we can actually do is this error message doesn't specify which file or line the actual error occurred on we don't really need that because we're running through the debugger and it just breakpoints right and it's easy for us to see what file in line but if we're running it without the debugger or anything like that it would still be nice for the console right to somehow print out the file and line of code this actual error happened on maybe even the function name so let's see if we can do that let's go back out of here and what I'm actually going to do is make this jail lock Hall accept some more parameters so we'll add const char function which will be the name of the function we tried to call with all of its arguments and everything well add const char file which will be the c++ source file with a sack where this function was actually called from and then finally into the line so what I'll do to this error is I'll add all of those details so when we print our error message I'll also add in the function which will be just function I'll actually move this down here so you guys can see we'll add a space after that function then we'll add file then maybe a column like that and then the line of code okay great so now we need to fill all of that information in so inside this GL local we can actually use macros to figure out which file in line we call this function from and not only that but we can also since we are actually passing in this function we can actually turn in whatever X is into a string and that's how we'll print our function name and so to do that I'm just going to write hash in front of X which turns it into a string and then for the file its underscore underscore file in all caps now unlike the debug break this is not an intrinsic it's it should be supported by all compilers they don't worry about that and then finally for the line again it's just underscore underscore line like that okay pretty cool stuff let's try and run our code again now when there's breaks if we go back to our console check this out we see GL draw elements with our actual parameters your triangle six GL internal pointer and then we actually have a full path to this application or CPP source file followed by the actual line of code 166 which you can see is line of credit that breakpoint is on how cool is that so we exactly what's gone wrong where and what we've actually like what what code we actually tried to call that failed because we're printing it all so the console pretty cool stuff now well now I know this is a lot to take in if you're not familiar with C++ macros which is why that video when it's ready will be linked up there and all that and I should do that probably very soon but you can see with the power of C++ and macros we can like really improve this OpenGL debugging and specifically the most useful feature here to be honest is that actual debug break just being able to break on that actual line by that assertion that we've kind of created is really really useful because it kind of lets us know exactly where we've gone wrong now what you should do now with this GL call is actually pretty much every opengl function we ever call so like chill gen just everything we should be wrapping that with GL call so basically what I'm going to do here is just do that really quickly every single opengl function call so i surrounded in GL call now because I wrote this macro in a in a very simple kind of way there are a few flaws for example if we use a one-line if statement this isn't going to work because just the first line is going to be inside the if statement and these true won't be and all of that you can fix that by just throwing it this is gopal and like a do-while 0 loop and all that again I talked about that in the macros video so kind of test so kind of take this function of this code that I've written here is just a really kind of basic simple way of doing this also putting it in a scope isn't very preferable because if you actually have values being assigned like if you have the shader code that we wrote over here you can see that calling GL career program actually assigns it so we can still do that inside GL call just make sure you do it like this however the issue is that if we were to wrap that in a scope program would be out of scope over here so if we were to kind of put curly brackets like that around this to keep it all in one scope then we have the issue of of not really being able to use program over here because it's like if we're a program in a source code kind of like that it wouldn't be accessible anymore as you can see here so there are a few kind of setbacks that we that you have to be aware of and all that I'm going to go ahead and add GL call to the rest of all these functions that source code is available for patrons if you support the series we're going to patron 4/2 Cherno you will receive basically the source code to every single episode kind of episode by episode and that will have all the story written of course I'll do though I'm not gonna waste your time and just you know write jail call and every single function in front of you because you kind of get the point but anyway that is basically OpenGL error checking right that's how I like to do it I like to just wrap every single function call every single OpenGL function call inside GL call and basically that will just break point and whenever I have an error GL debug message callback we will talk about in a later episode because that is also very useful and kind of theoretically means we don't have to do all of this although I find I still like doing this because you do get the compatibility with earlier versions of OpenGL and also there's nothing really wrong with this method it's just that GL debug message callback doesn't give you more information and is slightly more useful and all that anyway hope you guys know the video if you did you hear that like button as I mentioned earlier you can support this series by going to patient icon for special journal and get some pretty cool rewards finally we also have a discourse server we can discuss this episode and all of that the Turner home safe discord is how you get an invite there it's a really cool community where you can actually talk about everything you've seen this in this episode let me know what you think about opengl error-checking or maybe if you have a really cool way of actually checking errors that isn't as cool as my way then definitely leave a comment below or just in general what you do to to to debugger OpenGL because I know that it can be a huge issue and that's why I'm not that's one of the reasons why I'm not the biggest fan of OpenGL anyway I'll see you guys in the next video goodbye [Music]
Info
Channel: The Cherno
Views: 73,353
Rating: undefined out of 5
Keywords: thecherno, thechernoproject, cherno, c++, programming, gamedev, game development, learn c++, c++ tutorial, opengl, errors in opengl
Id: FBbPWSOQ0-w
Channel Id: undefined
Length: 23min 41sec (1421 seconds)
Published: Sun Nov 12 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.