 Hey guys, welcome back skits on series episode 20 topic today is the frame buffer and how to go about rendering to that device I'm always honest when it's a boring topic. I let you know so you can skip the video This video topic is very interesting and very cool And I'll first start off with the examples which are also very interesting and cool And today we're going to be in a virtual machine for the examples. Why is that? I'll tell you in a bit Example number one I want to show you is this screen saver I made that changes the flag whenever it hits the boundary Obviously prefers grease don't know why that is it should be random Which it is I don't don't wait around for your own country almost seven countries in there right now If you want to add your own country download the code add your own remove the ones you don't like Ireland for example That's very cool. We also got This is a cool one. Basically, this is a three-dimensional rendering So basically it's a three-dimensional cube model that we parallel project onto a two-dimensional plane We have vectors that define that plane I See we move that plane around space so we can orbit the cube like you're seeing here that parallel projection is then Arrasterized onto this two-dimensional bitmap you can see the orange lines on a black background very cool And then lastly I want to show you this if you have epilepsy do not watch the next 10 seconds of the video It's really really dangerous Yeah, pretty cool. So with that out of the way, let's talk about the theory. There's not much theory to explain in this video It's gonna be pretty quick. So question number one. What is a frame buffer? What does that even mean? well Wikipedia has a good example a good Explanation thanks Nigel But basically it's a bitmap file that your monitor is always displaying long story short very simple explanation to that About the frame buffer It's literally a file. So it's it's a file on your machine. Where is it on Linux? Oh, I should say this is also possible on BSD But it you have to jump through some hoops to make it work, but it's a file located at this directory dev FB zero There might be an FB one. I don't know could be You can open and write to this file like any other file using syscalls We've covered that in so many videos and you can get details about this frame buffer, which you have to by the way Like the resolution the the color depth the size of your screen that kind of stuff using an IO Controls this call and then once you know all that details you can treat frame buffer like any other bitmap That we discussed in episode 12 only difference is that the y-direction is flipped But once you know that everything seems to work out all the same. So very simple stuff Some tips here about the rain buffer from Donnie Thornberry So yeah, the thing is we x11 Wayland these types of things they don't share the frame buffer device They have complete access to it and you're not able to write to it Without you know them letting you so the easiest way to do that is either well two things one is to just not bother With like x11 don't even start that or Ultimately you can use that But then when you want to actually render something to the screen switch to a different, you know virtual tty That's not using X11 or whatever so for example on my computer that's control alt Function key. I know on virtual machine. That's just you know the host key plus an F key You can Google it figured out for your computer and Your install, but yeah, so you have to know that before you start Now to get the info about your actual frame buffer again It depends on your resolution for your monitor and so the way that works is We're gonna first use our file open function that we covered in a previous video And we're gonna pass in the file name of this File and that file name is as I explained before dev FB zero So you pass that null terminated file name into our file open function Open it with right permission so you can actually write to the frame buffer very important and Then yeah, you open the file now once you have it open you can get some details about this using the IOC control CIS call and this you know frame buffer IO get vscreen info Thing that's just a basically a number and we've defined that in our CIS call listing here So you kind of can see the value of that. It's probably different on BSD You can check I haven't implemented this functionality for BSD because I think a very few of my viewers even use it But if you want to this is the process So yeah, you can call this IO control CIS call and it will basically fill up This array of memory. So you have to have initialized 1280 bytes somewhere that you can drop in all your frame buffer info and Basically, you only care about 12 bytes from that chunk and those are basically three of these four byte ints and The first one is offset zero from this Array start and that's how many pixels you have in the x direction Then four bytes from that at an offset of four you have the pixels in the what y direction and then You have four bytes at offset 24, which is the bits per pixel. So for your RGB Let's say it was 32 you would have eight bytes for our eight bytes. Sorry Eight bits for our eight bits for G eight bits for B and eight bits for alpha I guess that's transparency or something. Maybe it's not even used So yeah with that information you can kind of figure out how big is your frame buffer. That's very important Because you need to be able to write to some memory location that is that size So let's say your frame buffer info pops out and saying hey your monitor is really bad It's 640 by 480. Let's say for example. Well, then you have 640 by 480 times Bits per pixel. Let's say it's 32. That's four bytes So you have to have four times six hundred forty times four hundred eighty bytes in memory that you can constantly be Manipulating to whatever pixels you want them to be you want this pixel to be red this pixel to be orange whatever then you can take that buffer and You know that that frame buffer buffer that your own memory chunk that you're using to write to the frame buffer And then you can write that with a right syscall To this file descriptor rax. That's the idea So basically you're making your own buffer That you will then dump with a right call to this file name In a loop Assuming you want to have a loop. Maybe you don't So here's the process that I use which is just you know, probably bad. I'm not very good at this stuff First thing that I do is I make a heap again. We don't use like the c runtime We don't use like linux, you know heap management stuff. We have our own thing So I make my own virtual heap where it has to be at least big enough for that frame buffer. So if it's Let's say my computer is 640 by 480 by four bytes per pixel I have to have a heap that's at least that big Plus however much extra I want for my math for my computations, whatever else I'm doing Then I'm going to get their frame buffer info and then allocate memory on that heap to be big enough for my frame buffer So I'm making a frame buffer buffer as you can see here. And that's all done in this Assembly File you could check it out if you're curious And then you just start drawing stuff you change pixel values at this location in that file We covered this in episode 12 And then if you want to have a rendering loop like for example We had in that screensaver and we had in that orange cube spinning thing We have a rendering loop if you don't want To have a loop just render once and you're done So it's pretty easy I highly recommend giving it a try all the code is available. So check it out With that out of the way not much through you to cover to be honest I have six examples. We already talked about three of them, but I'll talk about how they work in more detail right now This might be a bit scuffed because I have to kind of go back and forth between My computer and the virtual machine, but we'll make it work. So Example a this is the Well, let me run it first to show you what's going on So if I run this code It pops out my monitor Happens to be 1920 by 1080 with a 32 bit color depth per pixel That would mean that my frame buffer would be in bytes 8000 some odd bytes in size So this kind of represents what I was talking about In that slide when I said You had to do this and it would give you these pieces of information And you can use those to determine how big your buffer needs to be that you're going to dump to the frame buffer Um, that's what we've done here. So let's show you how that works. It's pretty much just what I said Let's take a look at the code And so what are we including? We're including pretty much nothing just the ability to open files, which we have to do Exiting our program and then something to print. So What do we do first we open that frame buffer device that dev fb0 device With the read and write permissions need and write permissions, obviously To draw things then we run that iocontrol assist call getting the details about the pain buffer device Dumping those in memory and then you can see here. I'm basically just printing out Those values. So at offset zero, that's the x resolution offset four was the y y resolution Offset 24 was the bits per pixel. You have to know these are four byte values. You have to move them into a four byte register Or you can do tricks to To make that work, but it's easier to just do move into esi as opposed to rsi Yeah, and then here at the bottom you can see I just basically multiplied all those three things together I've divided them by eight to convert bits to bytes. That's what this means shift write esi by three that divides it by eight And then I print out the number of total bytes for the frame buffer. And then we just have to know that when we Initialize our heap It's got to be at least this size And of course if you were using the c runtime you wouldn't have to worry about this because you always have An infinite amount of memory that you could access We don't we have to make our own heap and so Yeah, it gets a little bit harder. But as long as we make sure our heap is at least 8,200 and whatever It's our eight eight million bytes Yeah, then we should be fine So next example, I want to show you is example b. This is Let's run it. See what happens Okay, obviously I have to be in the virtual machine to see anything Let's do that oops We got the flag of mexico Oh, I missed the the emblem. What am I? Oh, well, let me deal. Um, so yeah, what is this doing? This is basically doing what I just said And it's just drawing a couple rectangles to the screen. So let's see how the code works Uh Here so How does this work? Well First thing we have is we have to have a heap that is big enough I said I had to be at least 8 million bytes. So here our heap size is initialized at 16 meg. That sounds about right to me Um, so we have a heap that's big enough. So what do we do first? First it looks like we open the frame buffer and we're saving that file descriptor somewhere great Then we are grabbing all that frame buffer info as before In this location, but you can see we've done it on the heap. I didn't actually make a slot of memory In the binary. It's it's in the heap. I know realistically speaking. So that's that saves memory in the binary um We call that iocontrols. Let's call we get the number of bytes at this point. We now have uh We're calling this heap alloc to get a A new buffer I believe yep that we can then write to here You can see we're looping through rows and columns and we're changing the colors to green and white and red respectively Uh, yeah, so that's how that works And at the very end you can see once we've got that Buffer completed, you know, it's got green and white and red or wherever it has to have it Then we have one last sys call here that is a sys write call That just dumps our entire contents from our heap allocated memory chunk That is our frame buffer buffer and we dump that to the actual frame buffer Filed shifter, which we saved in r15 So that's how we're able to draw that. So it's very simple Very simple process and i'm just curious how big is that that file So that entire program the entire ability for us to Generate a heap and draw things and then dump all this the frame buffer get all the stuff the entire program Is 545 bytes to draw to draw the flag of Of ireland with a different with the incorrect color, you know so very very small file size What's next uh example c This was a frame buffer clear. So This is uh, basically Let me show you at this point, we're kind of combining our functions our our Our listing our our code into functions So if I go to example c Open up the code you can see now Our code is going to be much smaller and cleaner than before So here you can see i'm initializing the heap I have a new function here called frame buffer in it as I explained before that function Hey, it was all the nitty gritty stuff. It it checks it makes a you know heap allocated space for the frame buffer info it grabs info from a no io control sys call then it um Makes a another thing on the heap. That's exactly enough bytes for the frame buffer buffer um, so that's what that does then I have this Clear function that basically just makes every pixel on the entire screen a single color So you can see we move that color value rgb value into rdi We call that function and you can see here the entire screen was green. That's what it did And this last function is that sys write thing I just showed you but again contained in one function So this flushes our frame buffer buffer to the frame buffer And then you can see the program ends So everything is now contained in its own little function And this is I guess in my opinion the best way that you should go about Drawing things to make functions for each part of the drawing process To make it simpler and easier to execute and it's more modular and you can change things Very easily and it's also more applicable. So this frame buffer clear can be used. Let's say for example in our screen saver We're using frame buffer clear black To constantly set the background to black before we draw the flags And then in the the cube spinning every loop of our rendering loop. We're clearing the screen to black So yeah, very cool stuff If you didn't clear the screen to black you would basically have that Ghosted flag and you'd have a ghosted cube. You'd have a circle at the end of the day You know or an orange circle after a few seconds. So yeah, it's not what you want um, what next Example d. This is the epilepsy warning thing. I'm not going to show it again unless you really want me to fine I'll do it again fine fine fine I just got to do it, you know to be five minutes to make I got to do it Yeah, this is awesome. I love this Um, and what is this you can guess right? This is just basically us changing the The the color using that clear function Red and blue red and blue red and blue red and blue with some delay Let's see. Let's make sure Are we doing that? Here's our loop We have a delay. We make the screen cyan Flush it to the screen another delay make the screen red Jump to loop. So yeah, that's what I said With what's next it's the flag one. Okay. This one's pretty cool So this one the way it works is yeah, you can see here I've got these countries to fine if you want to add your own go ahead I added grease because it has a lot of you know, rectangle stuff So it's it was you know a lot more involved just to show you that you can draw anything with rectangles very easily Let me have some simple ones here as well Um, and what happens is basically I'm pretty sure we let me show you the top so We have these functions that we're using um In net clear and flush from before then we have these two functions that We've made in a previous video. We have this set filled rectangle that we made in episode 12 I believe and then we have this random interfunction that we made in a previous episode as well and so what happens is basically You can see I've defined the flags of Greece of Italy Poland all the rest France in terms of the rgb values in each rectangle and then the locations of each rectangle Um, yeah, so that's all defined here belgium romania Ireland feel free to add your own and remove the ones you don't like um Then you can see when the function actually starts the program actually starts here Again, we initialize the framebuff for all this stuff We randomize our x and y locations for our screensaver to start off with Randomize the start country and then we have a loop that constantly checks yet Have we yet hit the border when we have we check pick a new random number pick a new country Etc you can expect, you know, how this would work and I'll show it again Just because it took me some time to make I don't want to Minimize the amount of effort it took probably like a few hours um So yeah, it's pretty cool. Um, if I were to change this I would make it so it couldn't pick the same country that it currently was I'd add that check So you'd always have it change whenever hit the border. You would never have it stay the same Or you could just add more countries. For example, we have italy three times in a row. That's not very, you know Pleasant to look at in my opinion. Oh, sorry. Mexico might be might be. Um, what next? um The last example was that cube rendering in in 3d That was a lot harder that probably took me like the better part of a week to implement Um, and again, it's not my full-time job. I do like half an hour a day Um, but yeah, let's check this out It's pretty pretty cool So how does this work? Well A lot of things so again, we have these inputs for the frame buffer in it clear and flush I have this function that I added here again. We have sine and cosine That's to calculate our current orbit around the cube as we're spinning Um, then I have these other functions that I made. So one is perpendicular eyes Is that a word? I don't even know. Um, basically I'm I'm when you're When you're rendering something I'll cover this in a later video You have to have your your vector as the viewer So you have to have your y in your x direction And then you have to know where you're looking and you have to have that normal Right, so you have to be able to pick what's going on And so this function basically Subtracts off a component of one vector From another and so you can guarantee that two vectors are perpendicular In this way. So basically if we're looking from here to there I can figure out What my normal vector is to that with this function And then lastly the the real heavy hitter here is this rasterized edges function This function does everything this basically takes your three-dimensional Edge set of vertices and edges And it projects them onto a two-dimensional plane and then rasterizes them in a way that you can then print And so you have to pass in a bunch of stuff The size of your total array and some pointers to things. I'll talk about that in a second Um Actually, I'll talk about it right now at the bottom here. So you can see We have two functions two structures here. One is a perspective structure and one is um an edge structure Which then contains pointers to the actual vertex locations in 3d space And kind of a pairing of which vertices are part of which edges And so you can see here. We have eight points and 12 edges Point address is at points as addresses at edges And you can see here our points are basically a A two by two by two cube Um, and then you can see here the edges of the cube I've drawn out here. And so you can see that one edge is between point zero and one Another edge is between points two and three, etc. And this would trace out all of the different Edges of the cube And so basically you have to pass in this perspective structure and the edge structure into that rasterized edge And what is this perspective structure? Basically this contains where you are looking from Where you're looking at and also where your up direction is and ideally well doesn't matter what you put in Um, this has this will generate You know basically what you put in but this these three points should be normal to the Looking vector that you've established with these six points or at least two points. I should say So this vector has to be perpendicular to this this vector here And then we have a zoom value to basically zoom in and out when you have a so there's two types of projections I'll talk about this later, but uh in in real life, we see a perspective so things that are further away are smaller but in In engineering typically you want to use a parallel projection That will kind of like keep things the same size. So just about how far something is away It won't be smaller. It will just be projected parallel It's very common for engineers to always change settings in their software to ensure things are parallel For example at work we use CAD software and everyone I know Changes the projection from perspective to parallel the first thing that they do on the job So yeah very important stuff So yeah, basically actually you can see here our cube is a 2 by 2 by 2 cube with the point at 111 And our look vector you can see here is that you know, basically 111 And so how is that possible? How can we see something on the screen that's On our face? Well, it only works with a parallel projection That wouldn't work with the perspective With the perspective everything would be super zoomed in and you couldn't see anything wouldn't in this fashion So it has other uses as well So yeah, how does this work basically? In our loop you can see here we make the screen black then we are You know, we're rotating around the cube ideally. So I'm Calling cosine and sine to kind of pick out. Hey, where are we now? And so if you saw down below we had a memory Address dedicated for rotation angle rotation increment as well as tolerance for those sine and cosine and again sine and cosine are Taylor series expansions we implemented those in a previous video and so every frame we're recomputing our position using two different Taylor series expansions to a tolerance of point zero zero zero zero one And so you say well, that might be slow. Really. It's not though the limiting factor on the rendering is not the math And the cpu usage to put all the pixels in the right spots The real render the real rendering limit is the sys call that we have to use to constantly dump our entire contents To the frame buffer or we have an eight megabyte Array basically of pixel values that we're dumping every frame And there there are better ways to do that. We might cover that in a future video But as of right now, that's what we're doing. And so that's the limiting factor on the performance And so yeah, that's what these things do here. Signing cosine that changes our our looking direction We'll cover this more in a future video and then Um perpendicularize that again like I said before make sure our up direction is normal to our looking direction Which is again required And then this function is the heavy hitter. This basically does everything so We pass in our frame buffer address for passing in the color We want to rasterize as for all our edges here. You can say I passed in orange as a color and then You pass in the width and the height of the frame buffer as well as those two structures I mentioned before with the edges and the points and the perspective all defined and then we call the Function and that basically does all the work and fills the frame buffer buffer with the rasterized edges And then we flush to the screen And then you can see here every iteration of the loop We are incrementing our perspective angle around the the orange cube. And so I will again show this example Yeah, you kind of can see how this how this is working And we'll cover a lot more about 3d rendering in a future video. I have a lot of plans In fact, this is how we're going to actually render our three-dimensional models and I should say This so you want to do fea or whatever you can render it like this You can render the mesh right and you can form the model and then you can see how the mesh changed A lot of cool stuff we could do And the the really cool thing that I think we can do with this is because it's a bitmap We can also write this to a file So we can take this capture Write it to a file Also, we can put that in an embedded html report You know, we can do our own 3d rendering for many different purposes even as images So we could draw like You know a car a hot air balloon a bus Anything you can model anything and you could draw it in this fashion. So Pretty cool stuff. I hope you like this video There's so many possibilities when you can write to the screen like this I didn't know this was possible while I did but I don't know it was so easy up until like three or four weeks ago And it's been crazy ever since if you guys want to hang out We have a discord server link in the description. Check it out. If not, I'll see you in the next video Thanks for watching. Bye