 I have started the stream. I think it redirects them. I'm not positive though. I've never done it. Yeah, I'm nothing's too. Oh, right. Yeah. Of course. Oh, sure. Yeah, that makes sense. That's clever. Yeah. Yeah, I'm... Okay, they can't hear you, so I have to fix my OBS settings to pass this through. Okay, now they should be able to hear you. Uh, okay, I'm speaking. And then you're chewing and going to fool them even though you're not. Is it working? Is chat happening? Waiting for them to... Waiting for them to... The stupid Twitch delay. Have we come up with a language policy for this, uh, this little show of ours? I swear. Fuck it then. All right. Okay, so, uh, for everybody out there, uh, hi, this is a quote-unquote podcast. It's gonna be super informal. Um, so... Fuck it. Um, this is a total technical nightmare. My stream is... My camera is really shitty because it's... Uh, I'm capturing my thumbnail from Hangouts. Um, and because I don't know how to get Hangouts to work, we spend a little time on it and fuck it. Um, you know, the main point of this is the audio, not the video. So, um, and I never look at my camera anyway, right? And so it was all... none of us ever look at our cameras, so it's just all dumb anyway. Um, so, uh, yeah, we're gonna talk about programming and game development, and I'm not the host. It's, I mean, I am the Twitch technically, the Twitch host or the OBS host, but it's just both of us. There's no power structure here. That's your cue to speak, Sean. Oh, shit. Uh... This is good because we can both call each other Sean, and there's no confusion, unlike when we were with Abner and he kept saying Sean. Right, right. This is much better than that. Um, yeah, so how do you want to start? Do you want to just, because we have the list of things that people were... Yeah, I want to start with a topic that you have a lot and I don't have a lot to say about, and that way I can walk out and I'll be listening, but I won't be talking. So I can get my lunch. Okay. So, the thing that somebody said on Twitter, and I'm just gonna quote it word for word, and I didn't write down the names, fuck you. Recently, Jonathan Blow ranted on graphics being broken. What would a solution look like? And you said you had... You said there's too much to say, but I'll probably have an opinion on your opinion, so just, you know, throw some stuff out. Right, I mean, like, it's a really not... Like, there's way too much to say, right? So let me see where to start with how are graphics broken? What would be a solution? A solution would be like a lot of people float the idea of, or a lot of people explicitly say that a consistent, like, ISA for all video cards that would just solve it, right? Like, if all GPUs were running on an agreed-upon ISA that would solve everything. I don't think it would solve everything. I don't think it would... I don't think that's a bad idea, necessarily, but I don't think that's happening. Let me interrupt and expand on that for the audience, just because we're gonna get a mix of people with different skill levels and stuff, I think. And I don't want to spend too much time on it, but... So, the basic issue is that graphics APIs are trying to wrap a really complicated problem. And the complicated problem that they're trying to wrap is that, you know, there's this pipeline and this is classically more true. There's this pipeline, there's a rasterizer, there's all this... What else is there besides the rasterizer? Texture fetch? Texture fetch? There's all this stuff. The cache, and they're just trying to hide that all and give you something to program in that's the same across everything. And nobody's really ever tried to do that level of portability across heterogeneous platforms to that level. We have lots of languages that are portable, but they are all exposing the same sort of van Neumann machine like idea, like we figured out how to do that. The level of abstraction, what we're talking about in graphics APIs are hiding this sort of very radically different kinds of graphics architecture implementing the pipeline. Now as we've gone to programmability the stuff has converged a lot more. You can when the shader is programmable, you can write no language and it can translate it to multiple different, you know, in the same way we have the PowerPC and the x86 and the ARM and they can all run the same high-level code. They compile to different low-level code. The same way you can do that in the shaders and that's mostly fine like the shader the internals of the computer hardware have some way to express that stuff. The shaders being expressible is maybe not actually the hard part of the problem because the that is a solved problem. It's annoying because you have to send bytecode. It's a solved problem for Java. For everything else we compile natively. Your C program has to be compiled separately on ARM and x86 and PowerPC to be able to be run. Java has this idea of a bytecode that gets all those architectures and compiled on the fly. That's what we're doing with the shader bytecodes. So my version of your argument then is that the single fixed isa solves that part of the problem but that's not even a problem that we necessarily need to solve. That's not necessarily where the pain of the graphics API comes from. Which is why I was agreeing with that. Yeah, I just want to everyone to know what we meant by that. So that's all. And now I'm going back to my lunch. Alright, so yeah people want a standard ice. It's like, okay fine you can do that but who cares. That's not really a big deal and I don't think graphics vendors are going to go for it. There's very almost everything else is converged to though. I mean as far as I know but like there's like tiled and non-tiled memory and stuff like that but it's pretty much converged and all of those differences can be attributed in the same way that you know how a cache on one particular Intel CPU is going to be different than maybe an AMD CPU or something like that but there's going to be little differences like that but we still programmed it the same thing. So I don't think that the difference in video card or GPUs hardware implementation of things is actually different enough to even really worry about it in that regard. The tiled and non-tiled memory, like the difference between all mobile like if you treat all mobile GPUs as one thing and all like desktop GPUs and console GPUs that's a group and then mobile GPUs are a different group. Those two groups I think are quite different from each other in a lot of regards but the variation of GPUs in each group is not as big as it used to be at least. Like a standard ISA I don't think that's the problem. I think the problem is just the APIs are really bad. OpenGL in 1995 or 1994 or 1995 whenever it came out was actually quite good and it was quite good because it sort of abstracted what the hardware Silicon Graphics hardware or software renderers were capable of at the time and it did a very good job abstracting that, right? Is this OpenGL or IrisGL? Well, they're the same thing, right? I don't know how different or similar they were. Were they actually the same? As far as I know they're the same because OpenGL the original OpenGL spec was by SGI. Yeah. I'm just going to assume that I'm using them interchangeably basically. Somebody in chat will correct us if we're wrong. OpenGL started programming at all. Was in like 94. Yeah. My knowledge of history back then I wasn't around doing anything serious back then. Yeah. I think OpenGL was very, very good to solve the problem at the time. It's evolved into it's disastrous but its evolution had very good aspects about its evolution and very bad aspects about its evolution but it clearly needs to be replaced and it seems that the companies or the people who are interested in replacing it don't have the same commitment as OpenGL had or at least the same idea that SGI had when doing OpenGL of what are we trying to do and let's do the minimum number of steps to abstract the current rendering hardware or rendering hardware in general that's going to be around for 10 or 20 years and make an API based on that. The console APIs I think are actually quite okay. I've heard that Apple's API metal is a metal or mantle whichever one is Apple's I can't remember between Apple and AMD. I heard the Apple one is quite good but I haven't used it like Vulkan is it's just it doesn't all I could probably come up with what an API would be for a modern graphics API it would be like 8 function calls and 1500 enums and that would be it there would be like free memory allocate memory create I wouldn't even necessarily create a command buffer just free memory and allocate memory and that well no because there would be CPU allocate GPU memory free GPU memory create command buffer push thing on the command buffer push a command on the command buffer with some number of parameters and then execute command buffer that would be almost all you need for a modern graphics API but that's not the approach that graphics people are taking and the whole idea behind modern like DX12 and Vulkan is minimal driver overhead right but they're making the exact same mistakes that OpenGL made they're making these assumptions on like render passes and how memory is worked or how graphics memory works right now how different things like how we batch together state changes and things like that there's a ton of assumptions that are being made in the modern Vulkan in particular about current hardware and expecting that to be the same for a long period of time they're making the exact same mistakes that OpenGL made and I think it's I think it's quite disastrous and I don't know what the answer of what would a solution look like is exactly what I described a really really lean API that just exposes malloc malloc and free on GPU memory and like execute these commands and that would be basically it now maybe there would have to be an agreed upon thing at the hardware level about how memory gets virtualized which is kind of only important on PCs because of like multitasking but if you could be like at the start of the thing, at your start of your app you say how much GPU memory is there give me this much right like reserve this much and then you can commit later to it or whatever and then other apps could be paged out by the driver and that would be the only overhead that the driver would have would be paging committed and uncommitted or committed versus reserve memory for the current process that would be basically the only job of the driver in that case and I think it would make everything much simpler we would have much clearer performance characteristics everything would be a lot easier to debug drivers would be much smaller much simpler they couldn't fuck around and put little optimizations in there to give them a higher benchmark score on one particular game it would just be everything would be really really lean easy to program for and if one video card happens to do one particular thing better than another video card then you could just say if you're running on this code path do this like you do that all the time right you have ldr pass and hdr pass a 16 bit hdr pass a 32 bit you have all of these things or like what kind of options do you want you have to program that anyway so you might as well like just allow the programmer to program for different slightly different characteristics of um a video card performance and all that's going to happen is those are going to converge too because everybody's work everybody's going to be like well this is how it does it so it did it this way so their video cards are going to be or gpu vendors are going to be like well we have to make it as fast as possible for id so everybody else is going to program that same way too and it will actually converge where the optimizations happen I think that that's probably like the best way to go but so I don't know if that's going to happen so there are some things that have to change in how the graphics cards works maybe like so right now one of the things is that the texture formats um you know they swizzle internally the textures rather than oh yeah that has to be made public yeah and um and there's some crazier things with render targets in terms of probably I don't know the details but you know in terms of like certainly there's hierarchical z and stuff like that where they're trying to do that stuff behind your back hierarchical z is exposed now but um I think it just you have to make everything explicit to do that and so it does mean and I think you were just assuming this you were taking this for granted and I'm probably just enumerating the things you were assuming you need a stronger convergence of the graphics cards to really all work the same way they don't necessarily have to all use the same swizzle format but they have to document their swizzle format and expose it as a query or whatever but also like why not have the same swizzle format like it doesn't matter like to us it doesn't matter to them AMD maybe thinks they have a competitive advantage because they have 1% better swizzling we need to get past that which is fine like that was sort of my point of my premise is that like we need the hardware people to consider themselves um interchangeable whatever the word for it commodities like they just how many cores you have you know the equivalent of Intel how many cores you have really needs to be the only significant differentiating factor or you can have extensions or whatever like you were saying that you can explicitly make use of but nothing behind your back basically I mean I think that's why the graphics APIs are complicated because they try to hide the stuff behind your back because that way you don't have to implement everything for every hardware vendor you can just implement to the generic path and the hardware vendors responsible for mapping to that to their unique features um and we're just saying or you're just saying that just yeah we've done that we we've innovated for 15 or 20 years on the graphics cards on that stuff and they've stabilized to a reasonable enough thing that why we just lock it all down and I don't know why they don't want to like I guess I can understand why AMD might not want to because then Intel will probably just crush everybody right but Nvidia can sort of compete with Intel now so I don't know I think maybe it'd be the death of AMD I'm not sure but um I kind of just don't care right like I just I want something good so just uh because people who see this on youtube archived or hear a podcast if we somehow make it into a podcast aren't going to see the twitch chat let me summarize a little bit of stuff from the twitch chat so Fabian says iris gl and open gl were actually radically different doesn't it's not relevant to our discussion but I just wanted that on record uh and then he he also says swizzle format massively matters it's intimately tied into their memory architecture but again that just means we're arguing that yeah then the memory architecture needs to converge right exactly uh and like you already said maybe that means AMD has to die or whatever but like well I I mean like I understand that the swizzle format is different but I meant I meant in like performance differences right like no I don't think like you said one percent was your like thrown up number right like one percent difference right it's like yeah maybe it is one percent like who cares though right like I get that it's gonna it's obviously gonna have different implications on texture fetch like all of that hardware and then all of the memory hardware but even then it's like I as somebody who just wants to write something that works on as many things as possible the best result for the consumer like if you're buying a game you want the best result possible the way to get the best result possible is to not have to worry about that at all yeah right and and but like you can do like even now when you do like a shader compile pre-pass on game launch right you can do that with if if swizzle format is just so ingrained into the texture fetch hardware and all of that stuff then you can just have a thing we're at the start it says you query the swizzle format and then then you maybe it provides no wouldn't have to provide a function you just query the swizzle format you can take your internal textures bake them out to that swizzle format the same way that you do for shaders on game load so you it's just another thing you could do yeah they don't they don't expose that finding says the swizzle format performance is 10 to 20 percent of course I don't I don't know if that's comparing between two different swizzle formats or comparing swizzle to linear but also that the swizzle format basically is dependent on like your chip configuration and memory type like if you're using gdr5 versus hbm2 like you'll want the swizzle format to be different to be optimal but whatever so but we're arguing but what we're saying is that even if that is a 10% even if the best swizzle format is a 10% difference and it's you want to expose that to consumers then your argument is to push that onto programmers that they have to query that and if they support the optimal swizzle format you get the optimal swizzle format and if you don't you don't get it yeah but even then it could you could have a function that's like upload linear download swizzled right like you could just do that for all your textures on game load right so the first time you load the game it takes an extra five minutes anyway yeah it's an you could add that api thing and keep it off of the critical path of the rendering the core rendering loop because it's meant to only be used for this baking step or something right exactly and like what Fabian just said about like gdr5 versus something else if a new swizzle format comes out on the next generation of hardware then you still have that same api which is upload linear download swizzled yeah um that's actually what OpenGL had I don't know if they've stayed supporting it for the compressed texture formats is they just had a mode that you're supposed to be able to upload your linear texture and it automatically converts it to the compressed format for you but of course since it's doing it at runtime as part of the upload it's not going to be a very good algorithm and I don't even know if they actually supported it but that was the premise when they originally did the extension for that stuff um gotcha but like I said they they were putting it in the not the critical path but the the main upload path and you're arguing like obviously you maybe want to have some facility to you know at runtime you're baking fonts and you know you have a font cache that you need to upload or something like that so you still probably um want something for that but maybe that's a case where you can say hey you can accept the performance loss some of the time that there are other things you can do you can have this low performance baking and then if you want you can query through what the swizzle format is in a data defined way and have a generic algorithm that tries to swizzle to that which is never going to be as fast as a hard coded one but you know there's there are things you could do if your goal was to keep the API keep it from making the API a nightmare right so anyway and that's that was a deep dive into swizzle which is just one of the many things the hardware does and the argument that we're making with that maybe Fabian should be having this discussion because well he's a little more knowledgeable than at least I am on this actually implementation details he'd be able to be responding to you point by point here and going well no the reason why they have to do that is because blah and I don't know those reasons um yeah but like even if there's a reason like the proposed solution to that particular reason I think is just fundamentally wrong sure yeah yeah that's the problem right like yes there's lots of reasons for everything most of which I don't know but like the the solution that we currently have which is the will let the driver take care of it is not the correct solution we'll hide all the implement detail implementation details and let the driver handle it is not right that's not going to that's not going to work uh so anyway Fabian says they already have the upload linear download swizzled he's just explaining why you don't have direct swizzled access because it keeps changing so yeah so okay so they do have upload linear download swizzle okay that's good I think it is part of their main path like I think it's actually I think they have that upload as a it's a upload linear and then memory to memory GPU memory to GPU memory blit that converts it to swizzled so that you can just upload a linear texture and then at GPU speed still convert it um well can you like do it you'll read pixels or whatever the equivalent is but but then he did say download as well so presumably you can then do some kind of read back to to bake it but I don't know I've never looked at Vulkan and and you know I it's a 20 second latency or whatever for Fabian to respond so uh I don't actually know for sure but presumably there they may have done the right thing there I think they maybe they maybe in one this one particular situation they have yeah yeah exactly but the point is is that the API complexity is still there they've they some of these things they may have addressed but overall the API is still this mess is oh yeah it's like the quick reference cards like 16 pages or something yeah it's like no it's one page Vulkan Vulkan we all know is just a nightmare but um alright anyway but I feel I feel like the early open GL it was it was probably like one page maybe two uh I don't know if it's really that simple I mean I'm trying to think back because I have I have like the red book like 1.1 I think which was 96 or 97 well I mean the promise early open GL I didn't have texture maps or whatever so everything started getting more complicated once texture maps went in and but and that's really just hard to argue about because so much of that is historical artifacts like the the badness of that is historical artifacts so um I maybe we've hit on that topic enough I said we move on well yeah like but do you more or less agree with the fundamental idea of stop doing things in the driver and just expose everything I don't really have an opinion like okay I can I mean I just intentionally just stick with old school open GL because I like that programming style and I noticed I haven't I didn't look at it closely but I noticed in when John was coding his animation stuff it looked like he had written his own little immediate mode wrapper for when he just wanted to do immediate mode graphics um and it's all fine if we have to write our wrappers and it lives on our side but it's just funny that that's like for a lot of programming you don't want the fast API you want the simple API yeah like 99% of it or 90% of the time at least you do and whether that lives in the API or is a wrapper around it like in my opinion actually should be part of the API because but that's about deployment that's about like getting it in people's hands automatically and stuff so I'm always of the opinion that this stuff should actually be in the API but that makes the API more complicated so that's just like a whole perpendicular orthogonal argument so you know whatever I'm just checking the stream yeah Fabian was getting a little overwrought perhaps he was asterisking his response as we were into that so alright so let's move on to a new topic maybe yeah I think Fabian was like um harping on like the details of the swizzle thing because I'm gonna look into the fact that you could do that but like I was just talking about the overall thing yeah we were trying to just use that as an example but there is a valid point maybe he could sit here and every example we could name he'd go well no the reason they have to do it that way is axed like and that's why I don't have a super strong opinion about this is because I've stayed on this simple open gel stuff and I've ignored the whole nightmare of the stuff of the new stuff because I know it's a nightmare so I avoid actually coding in it and you know at rad we've the product I do is on you know what has been on 8 or 10 platforms and the only graphics layer that I wrote was the open gel one and then Fabian wrote all the others so I never actually used the console except the 3ds I wrote the 3ds one so I never used any of the other console apis so I don't really know anything about them so I don't have super strong opinions about this stuff and I don't aware of a lot of it because I've never programmed it so I that's why I don't have super strong opinions I quite like the console apis relative to the modern open gel sure um I can't hear you I wasn't saying anything oh okay oh you know why because I'm looking at your the video on your stream not very smart yeah no I have three copies copies of you on my screen right now because of all that stuff alright well so that was one of the ones that I knew you had a lot to say and I didn't have a lot to say I didn't really have a lot to say it was just like well you had strong opinions you wanted to talk about it I want a simple fucking api yes but okay moving on so so another one that I feel like I feel like I don't have a good answer to this which is why low prioritized it and we were not going to do it but I'm ignoring our discussion about that shit somebody tweeted at me specifically said I feel like one of your strengths is translation of problems into classical algorithms um and then he had a couple follow ups but I never got him to actually say what his question was or like what was the interesting thing about that so the implication is that it's not a strength for other people um which would argue that their curiousness on this topic is how do I do it or how did I get to be that way or something like that and I don't really know which is why I prioritized this topic because I wasn't sure clear to me that there was much to say but I have an exact I have a very specific example about you doing something okay well let me let me just add one thing before you get to that example which is I've been doing this do hangouts with a bunch of people who donated to the ACLU as a reward I'm what's doing hangouts with them and this kind of question I'm up on totally different topics and it's clear I just don't have a lot of introspection into how as a programmer I accomplish things at that level but what I did find in talking to people was that it was useful for me to walk through examples of problems I've solved and they are they're like how did you approach that problem and I'm kind of like well this was the problem this was my solution and how did I get from the problem the solution I can't necessarily tell you but by walking through some examples like people seem to get value out of that as long as I wanted to say so I was originally saying I didn't really want to talk about this but we can't talk about maybe just talk about examples and hey it sounds like you have an example so well I was just thinking about there was one time that Pere was doing his stream on his Btree implementation yep and I remember you were in chat you and Fabian were in chat and you were talking about I remember you mentioned something about I can't even see this is what I can't even fucking remember is I think a Btree is the same as a 343 343 table or was that the red black tree that is I don't remember right but you you were very specifically like pointing out the differences between AVL trees red black trees and how they were balanced versus Btrees and then how there are a couple of different ones are equal to each other and like I remember reading about that sort of like a while ago and if I had to think about it I could look it up or whatever but like the fact that you remembered the differences between all of the trees and how you would use them differently like that's knowledge that's just in your head where for me that's knowledge that's like I look it up if I ever need it but I'm never going to fucking need it so the fact that you were taking these like really classical algorithms and like you remembered them and remembered like their properties I was like Jesus I can't do that and I thought that was interesting so that specific example so that's just partly that is like different people learn differently and remember differently so I have these I have this friend Mark LeBlanc who was used to be at Looking Glass back in the day and one of the things in hanging around with him that became very clear is he could often drop you know some line from a movie that you know is a kind of a classical line but not one that people go around quoting and just word for word he just like remembered it word for word and when I wanted to make a reference to that line in that movie I'd be like oh you know when that guy said the thing about and you know I have a couple of the words from the line or whatever that part of my memory doesn't doesn't work right I don't retain that stuff like that those kinds of things don't stick for me I'm terrible at trivia like classical trivia but obviously this algorithms and data structures trivia sticks in my brain for some reason so I think that's a lot of it so there's just not there's no that's just predetermined or how wherever that comes from nurture a nature some of that is because you know I had a college education and you took data structure classes and for various reasons a lot of things things I actually got exposed to in class and like a second class or outside of class for some reason encountered it and that just like helps it stick in the memory so the specific thing you were talking about is the fact that the red black tree is also a two three four tree and part of why that stuck in my head is because somebody made another tree called the a tree which I don't remember why this stands for something Ardman or Ardman the that's the animation people right the some somebody's initials were a or something and that tree is a two three tree so you know the two three four tree meaning that it's a tree where each node has two three or four children and then the two three tree meaning it has two or three children and the idea there I think Fabian is the one who told me that the red black tree was derived did actually it wasn't like in hindsight it was a two three four tree it was invented by saying hey if we make a two three four tree and then represented as a binary tree it turns out to be this red black tree and that makes sense because the red black properties like when they prove the red black properties how that makes sure the tree is balanced or whatever it makes no sense like there are these proofs and you like they're doing the stuff and you're just like what how does this make any sense no fucking sense at all but apparently the proof through the two if you treat the proof as a mapping to the two three four it's probably intuitive so so that's an example of a thing where it might be that the two three four like I heard the red black two three four tree equivalence at some point and didn't quite stick and then when I encountered this other tree where the guy was like the red black tree is really complicated you've got all these different directions of rotations what if we take a two three tree and make a red black style tree with a just a two three tree it turns out you need a few the code is much smaller because you only need half of the cases basically and and that's in there's an a tree in sdb.h that I've never used and in reading about that like that's when it probably when it finally stuck for me the toll two three and two three four tree because now I had just enough information that to form a whole will cosmology of this whole class of trees but it's possible the two three four tree had stuck and I just don't even remember like because this was ages ago and yeah all I can say is hey some of those details stick with me without me ever implementing that was the thing I was gonna get to is I've never you never implemented I don't know if I ever implement a red black tree I guess I might have implemented a red black tree in college but I definitely implemented a B tree because I implemented B star tree and a B plus tree but there's a there's a red black tree in numerical recipes I think that's just the standard that everyone uses anyway yeah although they like don't include delete or there there's some in numerical recipes sorry I was thinking of the core code I'm thinking of CLR that's what I was thinking I think it's numerical anyway it's the one that everybody uses CLR is like the standard algorithms textbook yeah I know I'm but I think I don't remember now maybe I'm wrong another thing though is you have two checks from Donald Knuth that's kind of crazy like I have the art of computer programming and I have gone through and done all the less than 40 exercises or the number for like the difficulty scale 40 exercises in the first book and it took over a year and you managed have you gone through and done like the whole have you read all four of the books can you finish your thoughts and then I'll reply well my point is just like that's a serious commitment to just reading it all or going through it and doing it is a serious commitment to like understanding fundamental algorithms right that I just don't think most programmers do or willing to do I mean I know they aren't right like it would take me another it would take me five years and I still might end up doing it to go through the art of computer programming and do it but like the fact that you found two errors I assume that you've gone through it all that's a commitment that most people just don't have yeah I just want you to get that on the table before I expose the truth right so I actually found three errors one of those checks was actually for two errors like if you check it it's not a power of two it's the sum of two powers of two and but I believe I read all of the three first three books I haven't looked I haven't got the fourth but I didn't I think I didn't do any of the exercises at all I just read through and retained what I retained and didn't retain what I didn't retain or whatever but by the time I started that I know I wasn't at the point where I had to do exercises to force me to understand the material I could take my time with the material and understand it and playing of it I skipped or skimmed you know the proofs the the proofs by there's this whole mathematical induction step no it's not induction it's this whole like function I can't remember the terminology for it where like converts one class functions to a different kind of function I can't remember what the generators or something I don't remember what it was and I just would skip all that stuff the three errors I found we're all like very minor errors the most minor of those the one I barely got any money for was there was a missing close quote for an open quote so I just like I was like well I might as well mention it you're paying enough attention to read that's something where you that's something where you just like notice it it's like why didn't that I'm waiting for that to end but the other two were pretty minor kinds of things and one of them was in the tape sorting section which I don't think anybody bothers reading these days and I was just reading it you know because why not and there was just some little minor mistake it wasn't like an algorithm had a typo or I mean even a typo it wasn't like an algorithm had a mistake or even a typo and it wasn't like the proofs had an error in the proof it was just like it was just you know like something was misstated or something like that I don't remember because that was a long time ago and I was just like nobody knows what these are for so I'll go ahead and post the checks so they think I'm brilliant but I'm not it doesn't actually reflect the the brilliance people assumes it does were there even any just a side question were there any errors in any of the proofs or algorithms ever found I have no idea yeah as far as I know there weren't yeah I doubt it I mean presumably there's Arata but I've never looked at them so I don't know well there's also like the fact that you actually because this was a discussion about sort of classical algorithms and using them as in applications to solving everyday problems if you are able to read through like those books are dry right canoes books are hard they're hard to get through yeah if you have the dedication to get through it and you actually do it and if you do the exercises you just you understand it sort of well enough that you don't need to do the exercises once you get that level of knowledge I think you can just solve you could just you have such a fundamental understanding of these algorithms that you can just sort of apply them to everyday problems right I think that that's that's sort of the difference between you know people who aren't able to apply it or people that sort of don't and people that can right well I mean it may be the difference but I don't know if that's enough so yeah to get to that question the translation of problems into classical algorithms like it's clear that experience is a huge value like knowing all that stuff well enough to sort of see but what I'm trying to get at is that I don't know that that's efficient maybe there is also additional skill maybe just exposing yourself to that stuff to that same level isn't enough maybe there's something else you have to have done or already have I don't know um so in terms of how do you get to that same level of skill I don't know that that's sufficient that's that's just my caveat there which is why I hesitate about talking about these things because I don't want to over promise anyone I want to go into a little anecdote it's a totally unrelated topic but it's that same pattern of like how do you do things and how how did you get there when I was at looking glass so this would have been I had been programming in C since probably since I was 19 so probably seven or eight years of cc plus plus programming and I had programmed in basic before that although one day I'm in the office of the project leader I think this must have been Terra Nova I might have been thief but I think it was Terra Nova pretty sure it was Terra Nova and he's looking at some problem he was a programmer and he was looking at some problem we're looking at the screen and there's something wrong there's a bug I don't remember I don't remember any of the details of what it was and we're looking at it and I'm like trying to think how that could go wrong and he's looking at and trying to think how it could go wrong and we already knew at this point that we had this very different debugging style and it wasn't an intentional experiment but um I wandered off to try to think just think in my head about it and he sat down and went into the code and started putting in printfs and tried to track down what was going wrong and I went off and I just thought to myself well what kind of I don't think I looked at the code I might have looked at the code but I know I in general solve some problems this way so I was probably just sitting there thinking well what kind of behavior of a program could produce that bad result and from that I'm inferring what the program is doing is trying to do and what it's doing wrong and thinking like okay then that's the bug if you can like kind of figure out that pattern of what's a thing a computer might be trying to do and might do wrong and get that bad result and after I don't know 45 minutes or an hour I finally thought of a way this thing could happen and I walked back into his office and I said I figured out what I bet it is I'll bet you it's this because that's always how I felt about these things it's like I could be wrong I have no evidence at all but I bet you it's something like this and knowing that is like kind of sufficient that then you can look in the code and find the spot that should be doing that thing and look and see if that bug happens and I came in and I said that and he's like yeah I just figured it out I just did my printfd bugging and narrowed it down and here's where it's going wrong and it was exactly the thing I thought it was and it took us both exactly the same amount of time to figure it out but we used two entirely unrelated methods to solve it so that's just a funny anecdote two entirely different methods but the thing I want to say about that is at that point I was already known like at Looking Glass I don't know if people really said this but I thought of myself this way at Looking Glass as a person who could debug problems that way it's like you want some extra eyes on a problem that you're trying to debug get my eyes on them I don't even have to look at the code a lot of the time so I can think of something that's worth looking at and that skill it's not like a skill I was born with I don't think that was something I came to over time and I think of it now in hindsight as sort of a pattern matching kind of thing it's just like having my brain has retained enough you know it has neural network deep learned enough of these sorts of things that I can kind of intuitively find this stuff not necessarily rationally find this stuff and that just comes I like that you have applied a fake brain algorithm to a real brain but go on it has some analogies like it's why I can't introspect it because it's not it's an opaque process to me and the way to get there is to train it a lot to just keep doing stuff to debug a lot of things to implement a lot of algorithms to use algorithms when you're doing stuff don't always do stuff the simplest way do use algorithms some of the time etc. I think is what's going on there and so I think like the like if it's true that I'm good at mapping problems to classical problems it is probably largely just due to doing that an awful lot and having sort of trained a neural network to solve those problems so to do that mapping for me without me necessarily consciously doing I mean kind of our latest like how do you retrieve memories like when nobody can tell you like you think about a topic or whatever and the memory comes to mind and maybe it doesn't and etc. and that's a little bit about what's going on when I'm like how do I solve this problem and I'm like well I'm trying to recall related topics that seem to match this problem in some way and then once they come to mind if I can remember enough about the algorithm and the data structure I can you know do the finish the mapping in my head and decide whether that algorithm solves that problem or not and I do want to be clear like I did not retain everything out there many many years ago I wrote a very convoluted implementation search that's on my website and one of the things I had to do to solve it is you lay out the the regular expression and you convert it into a finite state machine and I needed to do a network flow sort of algorithms on that state machine I had to do there's like a single short short as Bellman Ford relaxation I think was the solution to it I didn't know any of that like I had all this knowledge and stuff but when it came to solving network problems I had no knowledge and I had no knowledge of how to solve it or look in CLR or whatever and go there must be some algorithm that can solve this problem and you know hunt around and go that oh here's one that solves the thing even if you have negative pathways that's what I need and boom so yeah that's my thoughts on that topic yeah I just wanted to say I don't have that skill at all like your debugging skill I can't even understand code by reading it. I don't understand what it's doing. I just, my eyes gloss over and I'm just like, fuck it, I'm not even gonna bother trying to understand how this works. I cannot understand source code by looking at it because I found many, many years ago that if I just stepped through a debugger, I will understand it much, much faster. So I don't even, I just, my way of understanding code, like you were just like, oh, I'm gonna like look, you don't even need to look at the code or whatever, but like, take that aside, like you would look at the code and you're like, okay, I can think about how to solve this bug. I can't even look at code and understand how that, how that code works. So I find that to be interesting that you have that very, very different approach. Well, and that's a great segue into our topic that I wanted to talk about how you were stepping through code in the debugger to look at code you hadn't, that was other people's code. So I don't remember which stream it was that I watched where you were doing this, but it was some stream where you were trying out some library and you were stepping through the initialization and cursing it endlessly because it was, maybe because it was boop or maybe just because it was deeply nested. I think it was my object oriented programming rant, actually. Yeah, it might have been. And what struck me as the reason I brought it up was because I like that doesn't really cross my mind as a way to approach the problem of integrating somebody else's library into my code. Now, to be fair, I almost never integrate other people's libraries into my code. That's kind of the reason the STB libraries exist is because I am so not invented here that I just have to reimplement everything myself and then try to give it away to other people and tell them not to be NIH, which is surprisingly working. You would think people would go, well, wait, that doesn't make any sense, but. Well, I'm the same way. I have to implement everything myself too. But, you know, for OBG, I used Crunch, Rich Gelderich's texture compressor. And I largely did that not so much because I wanted his texture compression or because I wanted the texture being compressed on disk or because I wanted to use a third-party library so much as the fact that I wanted to use DXT textures and the canonical way of doing that nowadays is to find some tool and I didn't even know, at the time, I'm like, I don't even know what tool people use now because like this, the compressinator from back in the day doesn't seem to be maintained anymore. And I'm like, and regardless, they're all gonna output in DDS format, so now I'm gonna have to write a DDS parser. And I was like, doesn't somebody just have some solution to this problem that just gets me the DXT compressed texture in memory ready for me to download it? And I'm like, oh, Crunch does that, so I'll try using Crunch. And I actually had to get it to compile in VC6. I had to do severe work to that library, which is not his fault, like my problem for using VC6. And that's the other reason why I'm a not-invented hero person is 10 years ago, people were still using VC6. It was a little less crazy, but now I literally can't use anybody's libraries because they're not gonna compile in VC6. I guess if I got binaries, maybe I could, but. I see my future. Yeah, but maybe I'll switch to Jai and Forecoder and be, jump from it. I actually thought what IDEs might be an interesting thing to talk about. Jumping from the far past to the far future is always possible. But yeah, so anyway, I wanted to talk about that process because one of the things is that if I thought I wanted to know what was going on inside Crunch, I would want to know what's going on in the part that's running most of the time. In other words, I'd want to like, I'd run it and I'd go into the debugger and hit break and find where I am and I'm like, okay, here's the hotspot probably in the code. Let's take a look at what this code is doing. But the idea of stepping in through the initialization seems crazy to me. That's why I wanted to talk about this. Like, why do you care about how they initialize at startup for some library that you might want to incorporate in your code? Why is stepping in from the beginning? Or was that just because you wanted to do the OOP brand? That was mostly because I wanted to do the OOP brand. But the initialization thing is, there wasn't like an explicit initialization thing, right? That's one of the problems that I was trying to highlight with OOP is because like the initialization is the constructor of like the thing that runs it and calls the actual execute function that it was a triangle splitter, I think. That actually called the split triangle routine or take polygon soup and generate triangles from it. Or no, it took a polygon and then generated internal triangles from it. The thing that actually called that function and did the work was the constructor of an object, which is why I was doing the step through of the whole initialization thing. And I was kind of pointing about how ridiculous it is to couple of these things together. But I do generally do that, yeah. I generally will step, I will run it and step through every single line. And I think one of the- Wait, how hyperbolic are you being there? Like if it's a 20,000 line library, are you going to step through every line of that library? Yeah, eventually, not at the start, obviously, right? But like, you know, how you start through the initialization thing and where it starts and you can make a very quick and easy judgment is this thing concerned at all about how the memory is being allocated, right? Sure, yeah, yeah. And you can see that for the first like 10 lines that you step through, right? Because you can, you know, you search around a little bit and you watch the variables and you're like, well, how is it handling memory, right? And if it's just like a mess, then you know right away, you don't want to use the library, right? So it's a really good like the Van Halen fucking colored, single colored M&M thing. Oh, yeah, yeah, yeah. It's a very good one of those, right? So you just step through it, like how is it initializing, right? Like is this sane? And then most things aren't. So you're just like, okay, I can't use this then, right? So I think, sorry. Do we need to explain the Brown M&M for the audience? I don't know if that has modern currency. I mean, I guess maybe it does because back when I was young, the whole Brown M&M thing was known, but nobody knew why. And so it was just used as a thing to make fun. No, no, and that's, and now in modern times, we've heard why and it's a cool, awesome thing that connects to what you were saying with. But I don't even know if most people nowadays have even heard of this thing or if we need to explain that whole rider thing. I mean, it's not, we don't have to explain it if that was just an analogy or whatever. But that's just kind of an- It'll take two seconds. We might as well explain it. I was just amused at that. So yeah, you go ahead and explain the whole rider. You don't have to explain all about riders, but yeah. So when a band comes into a, when a band is a show, they have a set up crew who runs, who works at the venue or whatever. It's all unionized and bullshit. And you have a rider, which is like a list of demands, not demands, but a list of things that the band wants, right? Water, horse, et cetera, right? And one of the things that was on Van Hellen's list was a bowl of only brown M&M's. I don't remember the color, we'll say brown. I don't even remember where it was on M&M's. Could have been Sparty's. Whatever it was, they were like, we only want brown M&M's, a bowl of them, right? And the reason for that is because they had very- Wait a second. It was actually better than that. A bowl of M&M's with all the brown ones removed. Okay, all the brown ones removed. Okay. So the reason for that is because they have a very precise, like choreographed, I don't want to say dance routine because they weren't dancing, but they had a very precise stage movement system that they had. And once the stage wasn't built to their exact specifications, and I think it was David Lee Roth, fell off and broke his arm. Because the stage wasn't built correctly. He was expecting there to be something there, a foot place, step down, fell, broke his arm. So after that, they put in the rider, they put, we want a bowl of M&M's with all the brown M&M's taken out. And the reason for that was it was a very quick way for them to look and see if the people actually read the rider in detail. So they'd look, they'd see all the brown M&M's are gone. They didn't need to check the stage. If they didn't see it, then they, if the bowl was fucked, then they had to go and physically measure every single part of the stage so that they wouldn't fall off. So that they could trust that the stage was, was, or trust that all the other things were done correctly. And it became like, yeah. It became like a thing. It's kind of funny. Yeah. Anyway, so I use the, stepping through the initialization to be like, it's a really good guide to be like, how sane is this library written? And, yeah. Yeah, it's like, it's a canary in a coal mine in a sense of, of, it's like, there's gotta be some other term for this of like the first, a little, an indicator that you can use to represent something else or whatever. That is representative of something else. Yes. Yeah. Maybe we should explain what a canary in a coal mine is because I'm not sure everybody, no, I'm kidding. There's gas buildup. Yeah, so I do that. But my general way of understanding any code is run in debugger and step through line by line. And I think this is a skill, this relates to a skill that I have that I think is probably better than most people or I don't wanna say whatever. In the same way that your skill of translating things into classical algorithms better than most people is due to experience in what I've worked on, I've written several multi-hundred thousand line programs in C that were more or less bug free entirely by myself, right? And that's given me the skill of I can keep very large complicated systems in my head pretty well. I think I'm pretty good at it relative to most people. That's probably my best skill as a programmer is keeping large diverse complex systems in my head. So when I step through somebody else's library or somebody else's code, I can keep its running state and expected state in my head, I think probably better than most people, which is because I've always understood code by, and I'm just sort of realizing this now is because I've always understood code by stepping through it in debugger. And I think that's given me that skill whereas you have the skill of being able to abstractly think about a bug or a problem like that and then sort of reason about how the code might be conformed because that's how you've always done debugging. I think it's kind of an interesting contrast. Yeah, I mean, to be clear like some of what you're saying is sort of core to programming at the level we're talking at the level we all are in terms of having a model of what's going on in your mind and that the program is updating, like that can model how the program is updating. Yeah, sure. I'm not using the word model there, but like that is sort of like a fundamental skill. I'm not denying like your specific version of it being this branch off of that that's more superior. And Jeff Roberts is another person who spends all his time in the debugger, his way of thinking about how his own program is working is by watching it in the debugger as well. Yeah, me as well. Yeah, yeah. So yeah, that may just be a programming style thing. I mean, it's not like I don't debug. Obviously I use the debugger all the time. I love having the debugger tell me things. I don't love printf debugging. That's part of that other anecdote was that the way to solve that problem was not to go into the debugger and like watch what was happening. You really had to gather information across multiple frames and other stuff that made it resistant to debugger style debugging. It really had to be printf debugging. Some people love printf debugging. I'm not one of them. I will printf debug all the time if that's the best way to solve a problem but I don't like doing it. Yeah, and that's like, I remember Tim Sweeney came out and said that he doesn't even use a debugger and then Mike Acton sort of agreed with him. If I remember the Twitter exchange correctly, it was like Mike Acton and Tim Sweeney basically both sort of agreed and I could be misremembering so don't. Yeah, yeah. Maybe I'm wrong. But like that you can't solve difficult problems in the debugger. It's only meant to solve simple problems. And I was like, what? Like that doesn't make any sense at all, right? Like to me, I can solve really like the only way I can solve a problem is in a debugger, right? Like I had this nasty thread bug a couple of months ago, right? It took me like four hours to solve and it only showed up in release and it was a pain in the ass so I had to use the release, do release debugging. There was no way I would have solved that without a debugger, which absolutely no way. And it's like, I don't, I just fundamentally disagree with the standpoint of debuggers only solve easy problems and you shouldn't rely on them. Like I think that those two things are completely false. And I think that for me at least, and I don't think I'm entirely unique is the best way to understand code and the best way to understand what it's actually doing is to step through it and run it in debugger. And then you can very easily or very quickly build a mental model of the entire system doing that. Way better, way more accurate and way better than you can any other way. Yeah. So yeah, I just, people do have different ways of thinking about things and ways of working problems. So shall we move on? Sure. So the tweet was how to program in C in a good way. Mostly talking about games, I think. Yeah. So I kind of, I kind of wanted to rate this low to not do it just because it's such a, I feel like it's a topic that our peers and we have addressed a lot already. So, now do you- I wanna put out just a one quick thing before we start about this topic. Sure. I program in C++. Yeah, I was gonna ask. Oh, okay. I program in C++ because I like to be able to add two vectors together, right? Like, and sometimes I like a function where I don't have to end it with R and F or F and I or whatever to take an intro flow for actually, that's a bad example. But in general, I like function overloading and I like to be able to add two vectors together, right? If my choice was use all the C++ features or go to C, it would obviously be go to C but there's enough C++ features that I'm like, I don't wanna restrict myself. Yeah, so, which I have total respect for. I think me and Jeff Roberts are the C programmers. Most of the people in our peer group do use C++, John Blow uses C++, uses some- When John Blow uses inheritance and stuff, right? Yeah, exactly. He goes a little further but he doesn't do deep inheritance. No, I know. It's just as an automated function dispatch. And I mean, one of the things, one of the bad things in fcb.h is there's a hash table in fcb.h that is a 500 line macro so that you can instantiate the hash table for different data types. And- Oh, yeah. Yeah. And a 500 line macro is not debuggable. I think there's nothing, there's no way to debug that. I debugged it without it being a macro initially and then I converted it into macro form. And it's just a poor man's template. It's clear I'd rather be using C++ template to solve that problem if I didn't think C++ was such garbage that I just don't even wanna get near it. The- Right. But not, I don't think it's such garbage that you shouldn't get near it. Like that's just for me. I think if you were using my compiler, you'd probably think differently just because I don't think, like my compiler doesn't support at C, not even 99. Maybe it is C99 because I used 2010. So it's probably C99. But it's not like, I don't think there's a difference anymore or at least that I know of between the C and C++ path in Visual Studio. It's not an issue of the compiler. It's an issue of knowing that the facilities are there. It's just, So this was a thing I commented about, many people commented about like 20 years ago, which is that everyone uses a different dialect of C++ because they subset it and have a different set of features that they use from everyone else. And it's really, that's terrible for like team programming. You can't, you know, yeah, I'm an experienced C++ programmer when you get hired. And it's like, oh, you use a totally different subset than I. Of course you can adapt and learn. It's not actually a big deal but it's this weird ecosystem where we pretend it's one language and really everyone is using kind of a different language if they subset it differently. Absolutely. And that's sort of the kind of thing I mean when I'm like C++ is a garbage language that I want to stay away from. It's like, yeah, I could pick the subset of C++ that I'm happy with and stick to that. But in a sense, I'd rather avoid that temptation and just stay away from it. And once in a while, I have to write the 500 line macro, but it's pretty rare. And yeah, part of it is because the C++ and VC6 is not great. But if I just turned on C++ and VC6, I'd be able to use Declare anywhere. I could write C programs and use Declare anywhere. But I give up the void start casting by going to C++. That's really annoying. That's super annoying, the void start thing. And it's in one sense in C++, it's fine. If you use new for everything, then your mallocs all work. Whereas if you're in C and if you try to use malloc itself, rather than using new, in C++, it's a void star and you have to cast. So if you go with the grain of the language, if you in C++ use new, you don't run into the void star for malloc specifically. You might run into it for other things. And I do run into it for other things. The standard dictionary type that I use for all my programming is a string to void star mapping. And that just lets me stick anything I want in for the void star and I never have to cast. And it's not type safe, but normal in a lot of small C programs that I'm doing, I have one of these dictionaries. So it's not like there's a lot of doubt about what type is being stored in the dictionary. So where was I going with that? So yeah, so you are setting up the whole C++ question before we address the question of how to program in C in a good way. I just wanted to clarify. Yeah, yeah. So yeah, I don't even exactly know what to say. So one thing that everybody should remember, because this has come up in my ACLU Hangouts, is I have not shipped a commercial game since 2000. And I've never shipped a commercial game using modern engines. I've never shipped a commercial game that used 3D hardware. I've never shipped a commercial game that on a console. I do ship this thing at RAD, like a library, but it's not a game. Like the work that I'm doing at RAD is not game like at all. So my knowledge of game programming comes from writing small indie games, not from writing large indie games or serious projects. Like you're talking about 100,000 line things. And I think the largest indie game I've shipped is probably 10,000 lines. Right, but I've never worked, I have worked on AAA games, but only on like very short time limited contracts. So like I've never worked on a team of like 20 people. Yeah, well, I mean, the largest team I was on it, Looking Glass was five programmers, I think. So I don't have the modern AAA experience at all. In fact, we weren't, technically I'm not sure we were actually considered AAA. Certainly when we were doing the Underworlds and System Shocks, we were not actually considered AAA by our publishers. So that aside, we were obviously considered a major mainstream developer. But so all that knowledge is historical and et cetera. So anything I do say about this, you have to at least have that grain of salt that it's not based on, it's just some opinions of some guys. Right. But actually, I think that that actually is, I think that's good advice. Don't work on these huge projects. And a big reason I have for that is like, let's say you were to go work on a team that had 100 programmers, right? Which I don't, is that, that's, you know, like Bungie probably has 100 programmers, right? Maybe, maybe 50, 50? Certainly there are some of these games like the Frostbite games or whatever that where I certainly think the 100 programmers across the entire thing, you know, they'll have five UI programmers or something like that. So yeah, I think 100 for the whole thing is probably plausible. Well, you're going to be miserable if you're working with the 99 other programmers, right? Like you're, at least for me, my productivity would be so shot. Like right now I program quite fast. And if I had to work on a team with like 100 people, my productivity would be 10% of what it is now. Maybe even less. And it's just like, that's like not fun. Like I don't find that fun at all. And that's good advice. Don't work on those projects. Fortunately, the question was not where should I go in the game industry? The question was, how should I write C programs? And you're not going to be writing in C if you go anywhere in the industry. It's all C plus plus. So we already know that the person asking the question actually didn't want to do that. Well, I think Insomniac is mostly C. Oh, really? Well, Mike Acton keeps talking about C99 and all that stuff. So yeah, I don't know. I don't know how much Mike Acton is accurately reflecting the state of the codebase of his 30 programmers or however many they have. I think the engine stuff is C, very C like. It could be. But I don't know about the gameplay stuff. It wouldn't surprise me if it was C plus plus, but with that they're only using a tiny parts of C plus plus in the way that everyone does. Right, right. So, I mean, the question is a little tricky because like is that question asking what parts of C plus plus do I stay away from? Like, do we only care about the delta between how do I write a program in C and how do I write program in C plus plus? Or is this from first principles? I mean, you know, make everything make everything an array of structs. That's, that's my answer. Like that's how you program a game. You use arrays of structs everywhere. Yeah, I sort of agree. I think like, like we were just talking about like, you know, you should learn algorithms and apply them to things. My advice is don't just make everything an array make everything an array and have lots of, have to have lots of for loops. That's basically it. Okay, so you were asking on chat whether, what about the C and C plus plus stuff? Yeah. Yeah. Cause Fabian is there. Fabian mentioned that it was, they were C plus plus. So I, I'm misunderstood then. I see. Yeah, I don't know. I mean, like, I think, I think if you're gonna use C, a big thing would be be a not invented here person, right? Like don't expect to be using a whole lot of libraries. Don't expect to skimp over details and things like that. If you're gonna use C, it's because you want to really understand what's going on and to want to be able to sort of do everything and have, do everything that a game engine would require, do everything that the game would require and have the control of everything. Like if you, I assume that if somebody's asking they want to have the control. And I think like just understanding what you can, what like the trade-offs are is really important. Like it's in many ways, like I could put together, I don't even know if this is true. I was gonna say I could put together a game faster in Python or whatever, but I don't actually think that's true once the game scales past like the most basic, you know, a really minor complexity game. Like I actually still think C is probably the most efficient way to develop games. Well, the thing is, part of that thing I was doing about that, my talk, the programming, small C programs. One of the things that showed in that was an example of someone who was solving some toy problem. It was using, looking at a user dict words, a dictionary, just a flat listing of words and finding which words were anagrams of each other. And he was like, you know, I've only just started learning. I think it was Python, I don't know for sure. I've only just started learning in Python. So here's my, I'm a Pearl programmer writing in Python. So it's not idiomatic Python. It's just like getting stuff done. And it's only, you know, 15 lines to do it or whatever. And here's the Pearl thing. And like, yeah, and then somebody else comes back and here's the, you know, the idiomatic Python way. And it's a little shorter. And there's like a one liner, but it's the, you never understand it way. And one of the things that, like, it was a little side note and I couldn't measure the performance and actually compare the performance, but I just, you know, said, this is a problem that I would not have wanted to try to solve with before I had stb.h, before I had made that my standard lib. And I mean, it's a problem I could solve easily, but it would be, you know, a bunch of lines of code because getting anything done in raw C is a little painful. But I was able to write the idiomatic stb thing in a pretty similar number of lines of code as his non idiomatic Python version. So it didn't look that bad. It wasn't like, oh, this took me twice as long to write as it took you to bang out the little Python thing. No, it's like I banged this out really easily. And that was, you know, I used the stb has a file loader so I can load a flat text file as an array of strings directly instead of having to parse it because that's something you do often enough that I made it a shared function. And I use it often enough that I remember its name so I can just type it in, as opposed to a lot of the stuff in stb.h that I never use, so I don't remember the names. STB string file. Yeah, exactly. And then, you know, I have some Qsort helper functions to make Qsorting easier because you always have to write your own comparison function. And so the point there was that that became, that library moved that into the space of things that I found intuitive. And now what was I replying to that you said because I had a point about that in replying to what you were saying, which was- I was talking about once- Oh, you were talking about, you were talking about like you could write some small game in Python faster maybe, but once it gets large, it stops working. And my point there is that, if you have the right libraries for C, I don't even think that's true. I think you can write the C or C++. And we just don't normally have the libraries for them. Like the whole universe of Python and like Perl Cpan and the equivalent things for Python, they just have this whole wealth of high level libraries. So if you look at the GoStandard library, it's got all this shit that C doesn't have. And if C had those things, that stuff would be just as easy. So it's not something really inherent to the language. It's really about the libraries. That's starting the slow start-up and the ramping. Yeah, you have to worry about your memory management and stuff like that. But it's so easy to write the first version, the dumb version that you have a bunch of static global arrays of things. And if you're doing a small scale game, you declare the array to have 1,000 entities, you're never gonna have more than 1,000 entities in your game, you're done. You never even have to go look at that and mallocate or do anything. So, yeah, like flat arrays, when you're doing a game, flat arrays solve most problems. And John Blow has talked about this multiple times. So that was why I was saying, I don't know that we have much to add. Obviously, there are some things that are not that. There's all sorts of stuff about writing your game loop and how you structure things. You know, one thing, I was talking, I talked about this to somebody in Hangouts, so if he's watching, this will be redundant. It wasn't Hangouts, actually, it was the same thing. It was the ACL that you think. And a result of talking to him, I ended up kind of enumerating all the ways I know to do the array of objects for a game. So, the naive classical way is that you make a base class and then you inherit it from it for your different object types. And now you have all these different classes, and so then you have to make an array of pointers to them because they get all different sizes. So you have an array of pointers to your objects and you knew your objects and they're in a class hierarchy. That's sort of like the way you might do this if you got out of college with an OOP education. You might think that that's how you do it. You might use a list abstraction or you put them in a vector or whatever to make that container. There's gonna be a factory somewhere. Possibly. And then the C way of doing the equivalent thing, there are a couple of things that you can do. You can still do that pointer in direction and then you make a union instead of a base class. You just define all the struct types that you need for all your different things. You make a union of all of them. That lets you mallock each one to the size that it actually is and then refer to it using the union and keep an array of pointers and you're done. But unless you're size constrained and we used to be size constrained and I might have been more likely to do that back in the DOS days. But once you're not size constrained you might as well just allocate that union like everything allocate the same size and then you can just have an array of the unions and you don't even need that other stuff. But if you really don't care about size you can go one step further and you don't use a union you just put everything you need into one struct. All the different types, everything just goes into the one struct and now that struct is really big you're gonna be cache inefficient and stuff but if you're making a small indie game that none of this may matter. Every indie game I've released I've never even worried about performance. I mean, I've worried about performance at the way I always do when I'm writing code I never write dumb inefficient things but I never like said I really need to optimize this size thing down for performance kind of reasons. I just rolled with whatever I had and it was fast enough on my machine and it's a free indie game. So I'm like, I'll ship it and hope it's fast enough on everyone else's machines because usually it's not doing very much and so it's got 20 entities in the level not 20,000. So what I have done is actually slightly different and I've done this on the three games and I'm convinced it's the only way to go. And that's you write a code generator and what your code generator does is it takes some sort of you define your entity hierarchy in some way. Basically there's a base class, class entity and then all your subtypes of your entity. And what it does is it generates what's essentially a discriminated union. So you have a structure where the first element is a numeration for the type and then you have a union of all the other entities that are inside of it. So that the size of each entity is the size of your largest entity. And then you automatically generate in your metadata that you define these structures in you automatically generate a function dispatch table. So for whatever functions you have. So you can have an update and render, right? So you say, oh, I want an update and a render function for this. And rather than it generating, rather than it like generating the functions for you, it generates a dispatch update that takes the base pointer to the base union thing in it switches it writes, generates all the code for you automatically that switches on the discriminator and calls update underscore that type of entity. And then you make them and then the generator automatically makes a macro for what that update signature should be. So then you go into a file, you write macro name and then your entity type and then you just start coding the update function for that. And it's done. So all of your type updates or all of your like dispatches happen automatically and just sees a switch statement on the discriminator. All your entity allocation is the same size. And yes, your largest entity is the size of that entity. Too bad. So you throw some space away, right? And I have found that to be far and away the best way of doing things. All right. So to be clear that is except for the code generation that is what I meant by putting everything in a union. You do need to discriminate them. And I always just do that by making the first field in the structure be the enum in every structure. Right, but it's the automated dispatch thing that's the best. Right. Well, I was gonna get to that. So, and it has that same property of the size being the same. You could do your thing and add a pointer interaction and actually let them allocate to different sizes if you wanted. Because if you do have a union of the things. So that actually reminded me of another way of doing this. So the way the guy was proposing to me that he was doing things was he was keeping a separate vector for every single different type he had. So he had different structures for everything and he had different vector for each one. And then he was code generating the dispatch of all. So like when you wanted to do for all things in the level, he would code generate all of the four loops for each of them with a different function call for each one, which is equivalent to the code generation you're doing. It's just looped in a different place. But it has obvious problems when you don't have an array of all of your objects so you can't loop over all of your objects or issues and I don't wanna go into them. So what I wanted to say though it's the code generation thing is something I've experimented with and decided against. I generally try to keep code generation out of my code base. Why is that? Because I find myself using it more and more. And I don't know, right? It's just I had whatever experiences I had and they're lost in the sands of time now. I think of some of it is sort of deployment mess of the integrating it, having to integrate it into your build system but that extra step. Some of it is that the code that you're manipulating is not the code that's running and that can be annoying in the debugger. You can make sure it passes things through. You could force it to pass things through in your code generator, but you don't always want to. Like there's an equivalent thing. I don't know if this is gonna strike. It's gonna be familiar to you. It's something that's not gonna be familiar to most people, I think. Sometimes I would have these things that were data driven and they're kind of heterogeneous. So I would have this big array of like, it's a struct but the structs are just defining this data that's gonna drive some other process. Like you can imagine, it's not an interpreted language but it's kind of like that. It's something that's saying, like here's what a mission looks like in this game and it's got these elements but they're disparate elements in different things. And so there's just this structure that's got this different stuff. And so there's maybe a struct and it's got 12 fields in it to define the various capabilities or whatever. And so then I need this big array of all the actual data. And the thing is that each line of that thing only needs to fill out some of the fields because this only cares about this thing and this only cares about this thing. And so I would make macros that expanded out to all 12 items and like say, okay, here's the macro that represents this data item and here's the macro that represents this data item and each one would only have the parameters it needed. And if I changed the structure I would just change the macros to have add extra fields or whatever. Does that make sense what I'm describing? Oh yeah, no, no, this is like a very common pattern that you see. What I decided over time was that I didn't like that. And even though it's a little more error prone I preferred to directly, I ended up expanding out those macros and leaving everything expanded out in the things I could see what was going on better. It's a little bit the whole C low-level programming mentality. It's like, I wanted to actually know what was in the memory not I wanted to see the high-level abstracted version of it when I was reading the code. When I was writing it maybe I felt more comfortable just writing the part I cared about but once I wanted to see it. But actually the thing I end up doing now most of the time for something like that is even though that data is statically defined I don't statically define it. I make an empty array or an array with all zeros and then I use code to fill out the slots of it and the code can look the same now. The code can look like those macros did. It only has a few parameters. And then in that function that it calls it fills all the others with zeros. And I can't tell you why that's better. It's just like that I've moved to feeling that's more comfortable for me. If I'll use the filled out data array but a lot of the times it's too hard to see what's going on in the filled out data array and the macro data array just doesn't feel right to me. And so I end up doing basically the same macros but as code and it at initialization time has to fill them out it's slower but in a way nobody cares about. Sorry, so are you done? Yeah, well that was all my analogy to get back to the code thing. So yeah, your comments on that data case. Well, I actually I think that you just described as the biggest problem in C. It's you want to specify data because data is the most obvious way of specifying the problem and you want the code to just exist to deal with this data but you want it all to be static code. You don't wanna switch on name at runtime or whatever, right? And I find that to be the worst thing about C. It's like C's biggest deficit is I specify data and I want code that can operate on this completely non-homogeneous chunk of data and just know what to do, right? And that's really, really annoying in C. I've been, I just wrote a parser this morning for assets and assets have a different, each type of asset has a different bunch of fields that has different number of sub assets or whatever, right? And then so I wrote a parser and like I'm trying to hack X macros in some way that I can easily specify the data only one time and then have the code just work with it and it's just not fucking happening. And it's just like, this is a problem that if I just had, if I just decided I was gonna use a code generator, this problem would be solved. And I actually think that for this sort of stuff, my, I've tried a lot of things and my opinion is code generation is the only way to go. Right, well, so I was trying to use that as an analogy to get back to the code generation and hey, you've just brought code generation directly into it, so I'll try to address it there. I mean, I've already established that the code generation doesn't appeal to me for various reasons and other languages have what's called metaprogramming where your program itself can do that code generation internally. Like Jai has it as a sort of a first class thing of you can just generate some code and push it back to the compiler. And, but Lisp pretty famously has that kind of facility. That's what the Lisp macros are. They're not macros that we think of. That's what Lisp designed for, right? Yeah, but the thing is that metaprogramming is this feature that's very risky to put in your language because it really encourages people to make their own dialects of the language, even more so. It makes it harder to understand what the program is doing because they can be using these macros. Like you can look at some Lisp code and think it's doing one thing, but macros are rewriting it behind your back to do something totally different. And I've never worked seriously in a language with metaprogramming, those issues, people's description of those issues aligns with some of the problems I have with code generation stuff to the point that it does feel plausible to me that I would have that reaction to using a language with that kind of metaprogramming. So I think of those two, the metaprogramming and the code generation as for different reasons, mostly for different reasons, probably not being satisfactory solutions for me. So that's another interesting thing though is part of one of the advantages of working alone is I get to decide what features and where to use them, right? So for code generation, for things, like what you're talking about is like, oh, I want to be able to see what's going on. I'm talking about for like asset parsing, like loading code, right? Or something like that, right? Where it's like, I don't care. Like it doesn't matter, right? I just don't want it to switch on strings because that's annoying too, right? So I think in certain situations, for me, fuck it. I think that's a good way to go. But yeah, I totally get the point of I don't know what it's doing necessarily, so I don't want to do it. In one sense, I think the code generation and the case where I said I prefer the data to just be laid out flat in the array, both point at the same thing, which is I want to look at the source file and have the shortest path to understanding what's happening. Right. And the code generation adds this hidden step that you need to go look at the source of the code generator or you have to know the code generator, and normally you would know the code generator. You wouldn't have to go look at the source. You'd know what it's doing, it's job. Like I would not probably be that opposed to the lightest weight possible code generator for doing introspection of structs, right? So, you know, you have the thing where you want to have a way of visiting all of the reflection. It's what most people call nowadays. Yeah, yeah. To have the, I want to know what the names of the fields in the struct are and the types and the address of them. And that way I can serialize or I can pop up a dumb UI that just exposes sliders for every variable, you know, or whatever, those kinds of things. I probably would not be very opposed to using a code generator solution to that other than the deployment, the build system problem. Like I've tried and bounced off of it in the past. John talked about that in one of his recent streams. Or no, it might have been at handmade, it might have been the handmade con talk and which I just watched, the old, whenever. Oh, last year? 2015, whenever it was that he did it. Yeah, it might have been in that, but that he was talking about that for this exact problem he was saying, he took the hard step where you have to go do some extra, extra stuff. And part of that is just because it's like, if you have 100 structs with 20 fields each, like that's a lot of work. But if you have six structs with five fields each, writing another 30 lines of markup to describe them. It's just, for me, I'd rather have the 30 lines of markup describing them than have the extra step that hides what's going on inside my program. But it is definitely at 100. If it's more code than I would ever want to look at anyway, then I might as well have a code generator generated. I guess it's fine. Also, once the code generator's done, C is, this is a great thing about C versus C plus, is C is a simple enough language where you're not, once it's done and you know it's bug free, it's not gonna have any bugs with it, right? Like, so, I don't know. I think that the code generation, I think C actually lends itself quite well to code generation for that exact reason, just the simplicity of the language versus something like C plus plus works. It's definitely easy to parse. I mean, that's one of the things when you're doing a lot of these kinds of code generators, is you don't actually parse, right? You either add some kind of markup in the comments or something like that. Or you just parse. Give it a C, you could just parse it. Yeah, yeah. But even, you're often just lazy anyway, because writing a parser for C is still a pain. So, if you can just do a line by line parse, you probably would. Certainly, like, so that rad, the most of us use systems that we generate our documentation. We write most of the documentation, but we also have API documentation that is just directly generated from the header file and comments attached to the thing. And we parse the thing. And I wrote one and it doesn't do a full parse. It parses header file type constructs and it doesn't try to parse the full language. I think if you're not parsing the macros, I'm pretty sure you could write a C parser in a few hours. If you're not parsing the expressions and you're not parsing the macros, you're only parsing types. That's not, I don't think very hard. Well, right, you have to only parse. That's the, that was exactly what I meant. It's like parsing the types. Well, and so like, so one of the things is I was using somebody else's originally and one of the types I had was a struct with another, with a union inside it or it might have been a union with a struct inside it. And his parser didn't handle that. He didn't handle nested declarations, right? If the union inside it had been a separate union, his thing would have handled it, but having it directly embedded inside it, he didn't handle. Gotcha. You know, and so it's just stuff like that, right? It's like, you usually just kind of do that. You hack it until it's working for your case and then somebody else comes along and actually has some other cases and you might discover, oh, I didn't make, I didn't bother writing the full recursive parser for the exact spec because I didn't bother bringing the spec up and reading it and thinking it through at the full level. You know, it's really easy to half-ass this stuff. You know, it's the, you might have, you could imagine somebody, for some reason they're trying to parse the code anyway, even though they don't need to parse the code or they have some problem with the parsing code and they barf on register because they didn't, you know, that nobody uses that keyword anymore. So they didn't even try to parse register because they didn't think about it. You could easily have those kinds of cases come up. But yeah, see, it's way easier to parse than C++. My point was just that like, most of the time, if you're just half-assing it anyway, like, you know, as soon as you try to parse templates, you're gonna have extra annoyance. You're done. That's your every year. Good luck. But so it's fine. If you're using C++ instead of C, you just say, hey, for the stuff where we generate code, you're not allowed to use templates and you just skip all the stuff with the templates or something like that. I don't think it would actually be that much worse to do the code generator for C++ if you did that kind of restriction on it. Yeah, probably not. Oh, actually, this is just a side question then because you mentioned register. Are near and far still C keywords? I don't think they were ever C keywords. They were only keywords on the DOS compilers. And so MSVC may still have it as a reserved keyword. I don't know. But certainly, VC6, I think VC6, I think does still have them reserved. But I would hope they'd be gone in the modern ones. Okay. I didn't know that it wasn't a C thing. I'm fairly certain. Fairly certain. I would have been more certain 10 years ago, but I haven't even thought about it in so long. So anyway, so yeah, the question was writing programs in C and my main answer was the struct array thing. And then I talked about the array of objects. And that was the whole thing. I was drilling down into the arrays of objects and we were talking about the code gen. Okay. So, I mean, I don't know. What else is there involved in writing programs in C? I mean, so I have a whole... I think code generation is a big one. I actually think that that's a good thing to be doing. Yeah, and I think it's a terrible thing to be doing. So there we go. Great. So I mean, I have a whole separate talk about writing small C programs that you could always go check out. If you want to see some stuff that's less gamey, that's more about using algorithms and data structures. But the main point there was to... Some of it is about build stuff. So, but the main point there is just to get some standard data structures that aren't built into C and have them at your fingertips and be able to use them always. Jeff Roberts has just gone down this path now. We'll see if he sticks to it, but he came away from this. I'll explain what I mean by this. He came away from this like, oh, this is awesome. Programming C is fun again, or he didn't say that, but it was that kind of attitude. And it's almost exactly the same as what I did and also totally different, which is what I've said in that talk and about STBIH is that the two things that I care about are the dynamic array type, which is the STB array, which is equivalent of vector in C++ and the string dictionary, the map between char star and void star. And just having those two data structures just simplifies everything. And I can do so much with that. It's great to have STB string file and the QSword helpers and all the other things that are in there, but those two are like the workhorses. And he wrote some new libraries. He was just writing some program and he wrote these things as libraries for them. And it was a string to, he wrote a generic hash table that you can wrap in a smarter way than I did it. And I can't remember if it was string to void star or not. I think it might have been string to void star. But then instead of arrays, he actually did a stretchy buffer that is a chunked allocator. You know, it allocates a chunk and then allocates another chunk and keeps a linked list of the chunks. And so the array is actually split into these multiple segments, but it never relocates any item in the array. But he couldn't address it with the square brackets though, right? Exactly, you can't address it with the square brackets. And I was like, so you have to use a macro to access and it has to do some linear search or whatever potentially defined a given thing. And it's like, yeah, for the things I cared about, I was actually basically only iterating over everything. So he doesn't even have a macro for find me the 37th element in this thing. It's not even really treating them as arrays. It's really just treating them as lists. But could you just do that with on-demand memory virtual memory committee? Yeah, if you are willing to consume enough of your 64-bit memory on multiple array, right? Like it's that whole nightmare of, you know, sometimes I do put a dictionary and every item in the dictionary is an array. And if I have a dictionary with 100,000 items and I have 100,000 arrays and each of them has reserved a big chunk of virtual memory, it could be a problem, right? So I think what he could do, if you wanted to do it the way he's doing it, I think what you do is you allocate fixed size chunks and you keep a separate array indexing the fixed size chunks. And so when you want item 5,033, you divide that by 1,024 or something to find which chunk it is. And you can turn it all into an order one thing. It'll still be way slower than an array lookup, but it can be an order one thing. It's a doubly indirect array lookup. I think that would actually be, and that would have that benefit of what he's talking about is that you can take the address of an item and it is permanent, it will never relocate and be a reasonable performance, but you wouldn't, you'd still have to use a macro to do it. And that's, for me, was always the issue, was the crucial thing was being able to just use array notation. Yeah, that's sub array with the, you know, divide by 1,024 and then and it or whatever. That's how I do like bit fields for anything larger than the 64 bit field, like all the time, right? Like if I have single bit flags and I have 1,000 elements or whatever, that I do that all the time, I just split it into 64 bit numbers and then do that macro thing. That's a project strategy, but here we're talking about actually having multiple chunks of memory that are. Oh, no, no, I know, I'm just saying, this is a, that's a common thing. It's a related pattern, yeah, absolutely. Fabian says there's an STD deck, you know, DQ, that is array of fixed size chunks, but I didn't actually know what API it provides. Well, if that's true, is that, wait, array of fixed chunk sizes, how STD? It's not, is it spec to do that or is that just how everybody has done it? Yeah, that's, there are two separate questions. One is the spec, but what the other is, I don't actually know what a deck is. I've never. It's a vector where you can insert at the start or at the end, so push, push, push back, and then it can resize in both directions. I think, well, double-ended Q, right? So that would be that, but I don't, I don't know if it's spec, Fabian will know for sure. Is it spec to do that or is that just how it every implementation is? We'll have to, we'll follow up once we get past the flag. He's just repeating exactly what I just said. Anyway, so anyway, that was just a side note, was that Jeff, he didn't actually end up with the same thing because his set of problems is apparently different from my set of problems. I mean, to be fair, I actually like taking the address of the third item sometimes. And to get around that, if I'm using STB array, then I have to make it an array of pointers to things and I malloc each item separately so that they have stable addresses. And so it's not that I don't ever have that pattern, it's just I do the slightly more painful thing to deal with that pattern because that pattern is way more rare for me than wanting the random access array. And the advantage of the regular random access array that I kind of mentioned in passing about the talking about that anagrams in the dictionary problem is that when I write the small programs in C I'm just trying to write them simply but I use the order one hash table and the order one dynamic array and my solutions for those things are usually if I were comparing them in these speed competitions they're fast, they are as fast as the fast ones. Somebody did a thing where they were doing some optimization thing and they were like, see here after I've done a little optimization to the C sharp thing, boom, I've got this down to this performance. Now let's get it down a hundred times faster and then he starts doing all the crazy shit. And I'm like, okay, here, I just did the first step. I didn't try to do the crazy optimizations. I was just like, let me do the basic thing. And I'm already at the speed he got when he did some optimization to the C sharp, right? He had to like, I don't know, fix garbage collection thing or something, he had to do something to work around something dumb in the C sharp thing. And I'm like, yeah, I didn't have to do that optimization. And I didn't do optimization. I just used my standard data structures that I didn't, I mean, I just picked them because they're the standard data structures. I mean, yes, I use a hash table for my map not a binary tree for my map. But that's not, I mean, I guess it's because the hash table is faster, but it was just, I chose to make that be my standard type and I used that. All right, anyway, what was the point of all that? We were just talking about how to write simple programs in C. So. As you know, that was another thing though is you just brought up in that I think is related to this question is a question I get on stream a lot is why don't you optimize X, right? Like I'll be writing something and I'll be like, fuck it, I'm done, right? Like I'm done this part, right? And somebody will inevitably ask me why I didn't optimize it. It's like, well, it's already like a thousand times faster than almost every other programmer's implementation just by virtue of me doing it in C, right? Like it's just like, it's just so much faster than everything else. And that's a huge thing that you get when you write games in C is that you really don't have to optimize anything. Pretty much ever. Yeah. Like maybe, but not really. Yeah, and that may be different for different languages and different problems. I'm sure there are problems where the Perl and the Python solution are competitive with the C solution out of the gate. It does depend on the problem, but for a large class of problems. For video games. And for writing indie video games. Yeah. I mean, like I said, almost the only thing you ever have to optimize in anything other than the hardest core tree, anything other than triple A game is draw calls, right? That's basically the only thing I've ever had to optimize is draw calls. Collusion detection, broad-phase collusion detection. Oh, I don't consider that. I just consider that like, if you don't do that, you're just an asshole. Well, I'm already doing a broad-phase narrow-phase collusion detection like, and I'm already. But the implementation of, but even the implementation of the broad phase has to be sane. I mean, the naive implementation of broad phases and squared, like the moment you start to do a real broad phase, you've already had to start deploying a serious data structure or something. Right, but you're gonna have to do that in another language anyway. Oh yeah, absolutely. Yeah, if we're just talking about the language comparison. I'm talking about just optimizations that are very like, when you think about, when you program it and see you don't have to optimize anything unless it's quite hardcore, except probably draw calls. Cause you know, you could have 100,000 draw calls per frame if you're not careful. Right, well, and on PC's, you can get away with a lot of draw calls too, but. Not 100,000, I don't think. Probably not, but. You can get away with tens of thousands. Yeah, these days, they are now to the point where tens of thousands, I think is fine. I think N plus plus, each frame had six draw calls, total. Yeah, well, and that's the, that hardcore thing that you can get into. This is one of the differences between Iggy 1 and Iggy 2 is that Iggy 2 is better for draw call obsensations. In Iggy 1, we were really stuck by the flash model that to just, to tons of draw calls. A combination of factors. Like we couldn't atlas all the textures together, which meant every texture change was a different draw call as well. Yeah, I have actually a question. I have a question for you about something then. Not really necessarily related to that, but some hardware platforms, specifically the PSP and the Vita, maybe not the PSP, the Vita, they don't do, they don't allow fine grain state changes, i.e. you can't just change the blending state, you have to sort of batch your state changes together. Have you worked on a system like that? Well, it was all, like I said, the only one I did was 3DS. The on Iggy 1, all of that stuff was wrapped away from my site. The way Iggy 1 worked is, I implemented it in OpenGL, and then I wrote a wrapper around all of the graphic stuff I was doing, abstracting it, but not deeply extracting it. It was like, hey, I want this scissor-rect and I want this, you know, whatever. It's still all pretty direct mapping. You know, draw some triangles. And it included stuff like, hey, I have a texture, I want to upload, and I'm going to give you the data to upload, but I can give it to you a little bit at a time if you want, in the API, so that if on a console that needed to do some processing to it or something could accept the texture data a little bit at a time. And, but I mostly just wrote that all based on my knowledge of the OpenGL implementation and what I needed for that. And then Fabian had to come along and make that work on every console. And what that means is that every console implementation of the graphics layer for Iggy basically implements OpenGL style semantics for resource uploading during the middle of rendering. Like you're in the middle of rendering a frame. You've already used this texture somewhere in this frame. And then we come in and we say, well, we have this fixed amount of memory allocated for a texture usage. And Iggy says it has another texture it needs to upload. You've got to make space for it. And it has to free out that texture even though that texture's already been used. So it has to fence and wait till that texture's done. And then it has to free that memory and then it has to defragment potentially to make room. And now it starts accepting the texture upload and all that works. And he had to implement that on every console except the most recent ones we didn't bother supporting that. If you don't have enough memory, you're fucked. That sounds like fun. Yeah. And so I didn't deal with any of that shit. That was what was what your question was. Yeah, my question was specifically about how you abstract certain parts. But that was the thing, it was that we did totally the wrong thing. I mean, so there were reasons for that, which was, we were respecting what the flash file requested and we had the flash file up front. So you could, in theory, bake an Atlas of all the textures in the flash file, all the bitmaps in the flash file. But the way flash things could potentially use it is like, well, I have four compressed background images that are big, but they're compressed in the file. So they're not that big in the file. And I only use one on each screen that I switched to. So having them all take up texture memory and they have to be RGBA eight bit textures, you can't use Dexy compression on them. So they're pretty big when you decompress them and store them as textures. So we're like, that trade-off doesn't feel right to force you to have all of them always loaded. So we are like, the trade-off, we, this is all me, trade-off was like, let's go ahead and leave them compressed and download them on the fly and have a cache. Hey, that just works in OpenGL semantics. That was the entirety of the process I was thinking about when I designed that system. And it has this advantage that you can throw any flash content in it. And even though you don't have enough memory to display that flash content, to have all that flash content impacted once, you can even do this thing of uploading the resources during the frame and manage to show you the thing. It'll drop to a frame a second or three frames a second or something because it's thrashing this memory uploading during having to re-upload textures every frame, but it would display correctly. And we're like, ah, correctness. Hey, it sounds like a good trade-off. It's totally the wrong trade-off, but it was the trade-off I made. You can do like a double buffering thing where rather than having three or four or five or whatever, you just had two of them that were always available. And then you could swap one out, keep one in the cache, and then, you know what I mean? Well, it's just, you pick any fixed amount of memory, like the, you're given a certain amount of GPU memory to work with, like that was the premise, is that the game says here, you get 50 megabytes for your texture memory. If the flash file has 250 megabytes of textures in it, there's nothing you're going to do. There's no way you're gonna make that work, except we make it work, we're just slow. We make it work by just thrashing that texture memory. It would be perfectly fine to just say, yes, you're not allowed to have that much texture memory, being, you know, you have to increase the texture memory allocation if you wanna run that file. Would have been fine, it was just not the choice we made in Niggi One. And there are reasons for that. It's because it's all hidden by flash, so like it's the, when they're in the tool making the thing, they have no way of knowing how much memory that thing is going to use or anything like that. Whereas in 8.2, where we have our own tool, we can be telling them upfront, hey, this is how big all this stuff is going to be. And it's predictable in a different way. Anyway, so that was all very roundabout way of discussing whatever topic it was we were discussing. Something about writing C, writing in C. Yeah, well, there was a specific subtopic you had asked about the graphics APIs or whatever. Oh yeah, I asked about the differences between like, never mind. Oh, the Vita and the... The Vita works one way, completely different than everything else with regard to issues and changes. How you guys wrapped it, because that's always a problem when something works. When one thing works one way and 25 other platforms work the same way. Yeah, I don't remember what the Vita PSP difference is and NDA, for NDA reasons, we may not be able to really talk about it, but... Might not have been the Vita. The thing, the big change that we made was in the recent consoles, or at least on the PS4 was people would like to do multi-threaded rendering. And basically that means we need to be able to build a command buffer that will draw the UI scene. Maybe it's a UI thing. So we need to be able to build a command buffer and say, okay, we're done, we built the command buffer, go run this command buffer and that'll draw the thing for you. But the whole process I just described where we have to be able to fence and wait for the thing to finish rendering so we can upload the next texture doesn't work under that constraint that you're producing a command buffer that you just run. Because you can't fence. Like you can sort of fence. Well, so the point that I'm getting to is that you need to store all of the texture data that you need to upload in that command buffer to make that work. Like you can still store in a command buffer, upload this texture and then upload this texture. Oh, and then it turns out we'll have thrashed the texture memory so now upload this texture, the first texture again. Like that could happen in theory. And it's really dumb because that means that command buffer is storing like multiple copies of this texture and your texture memory that you're trying to use as your cache is smaller than your command buffer. So like it doesn't actually make any sense to go down that route. But that is the way our new stuff works. We like don't ever fence and we don't block. And so you can get in that state. If you allocate to us a large enough command buffer and a small enough texture memory we will actually generate as far as I know generate that sequence where we upload the textures multiple times. Maybe we don't. I don't know the details of how the stuff works. He may have just made that stuff not work at all if that happens. Well, thankfully memory required like maximum memory usage isn't really a concern so much anymore. Right. Well, that was why the switchover happened was because the PS4 and the Xbox one both have much more memory and something that so and because the PS4 basically we kind of had to go the command buffer route. We had to go the non had to go that way. So because of the way their API works. So that forces down that path. But that's closer to the right thing anyway on the way stuff should work now. So that was just an amusing thing. And so that was where our big switchover happened in terms of dealing with the APIs doing different things. I mean, the other obvious difference is just that the mobile the thing you mentioned early on the whole tiled memory which is not so visible at the API level because they're just batching everything for you. But has radically different performance characteristics for certain workloads like our workload. So we're at two hours. Well, we started a little late but basically at two hours. Yeah, so do you want to keep going or? Well, do you want to take some questions from people because we said we're gonna do two hours. So we can take some, I don't mind keeping going though. First of all, I don't think we said we were gonna do two hours. I don't remember a time ever being said. Oh, okay, I'm fine, I don't care. So I'm just thinking, do we want to take a break to answer some people's questions? Because I know there's been a lot of chatter that we haven't really responded to. Yeah, sure. Go ahead and chat. No, I'm gonna leave that to you while I go pee. I'll be right back. All right, chat, go ahead and address questions to me. Like put an at nothings too at the front so that I make sure that I see them. Or apparently Insubot didn't put an ad at the front and it still worked and nobody has any questions. There you are. No, nobody has asked any questions since you left. Well, none that I wanted to respond to. Okay, I just thought of something that was funny. So you specifically, when I was talking about never having to optimize anything and you specifically mentioned the broad face collision thing. So in my game that I'm working on right now, I did do an optimization, a quote optimization on broad phase. I did it mostly as like I could spend a couple of hours and teach people something. So I changed my linear, I just do a 256 by 256 grid, uniform size cells. And I did a linear addressing for them. And I was like, oh, I'll teach you guys how, I'll teach you a Z-order curve or Morton curve. So I changed it to Z-order curve addressing. And for some fucking reason, it was slower. And I was just like, ah, fuck, that's why you don't optimize. There was one question that I skipped, that I accidentally skipped actually, I'll get back to it. I wanted to say about broad phase collision detection. It's because I mostly make these indie games with 20 entities, I don't do any broad phase collision detection. I've done it in school or I did, the first thing I did at school, I've talked about this before is I implemented plain sweep rectangle collision for the first company I worked at, but it wasn't actually for games. And at looking last, I wasn't the person writing the collision detection stuff. So I never wrote the broad phase stuff for them. I think I've done it maybe, just like experimented, played with it on some projects or something. But when a game I have right now, an indie game that I've been working on off and on for a long time, for a couple of years, a little 2D game, for various reasons that I will not delve into, I want to be able to simulate about 40,000 frames per second. And that means that all the physics and stuff needs to be fast. And it's a 2D platformer with like moving platforms, so things need to collision detect with the world and with the moving platforms. And because there's only 10 or 20 entities in the scene, I don't bother doing broad phase. It was like, I didn't, it was just, oh, I'll just do the dumb implementation first. And once I've like really need to have the speed, I'll come back and implement some kind of broad phase thing. You know, just because, hey, collision detecting and each entity against one other entity is gonna be faster than collision detecting against 20 other entities. But computers are so fast, like it was fine on my dev machine running single threaded. And, you know, by the time the game is done in two or three years, or whatever, if ever, you know, maybe everyone's gonna have machines as fast as it was when I wrote it. And I just never need to optimize it. And it can run 40, simulate 40,000 frames per second. And because the levels are small, it's just not a big deal. So yeah, part of it is see as fast and computers are fast. So for a lot of, for the problems that a lot of the problems that exist are at a scale that's small enough that the naive C program is pretty fast. Yeah. And like that's the thing about using C versus another language is that the naive anything else isn't fast enough, which is actually remarkable if you consider how fast computers are. Yeah. Like it's crazy that you can't make like a Java program or a Python program run at reasonable speed. It's just insane to me that that's even possible. Don't tell the programs, don't Java programs run at reasonable speed nowadays? I don't think like heavy UI type stuff. Well, right. But I think that's a failure of the UI library is probably not a failure of Java per se. Yeah. Like the server type stuff, like data processing type stuff, I'm sure runs fine. Or like probably if you're using processing, you know that weird Java wrapper thing that's for doing graphics programming that was probably 10 or 10 years ago. That's probably fine. Because again, you're probably not, I mean, when you try to write the 500 million particle SIM in it, maybe you are losing some performance to Java that's noticeable, but probably most of the time you're not. All right, well, let's go to the questions from the chat. So here was one that I don't know if I really have any answer for. What is a clue for when abstraction becomes an implementation detail? I don't even know what the question is. That's the thing is I don't even understand that question. Good. Then we'll move on and he can clarify it if he wants. Okay. Have you ever thought of writing a software renderer with OpenCL? Well, so a bunch of people have tried doing software renderers in compute of various kinds and I haven't paid any attention to that work. It hasn't ever hit the mainstream, so I assume they're not performing it to a sufficient level to be interesting. No, I don't think a polygonal one would make sense. I think maybe something that's like non, like polygonal rasterizing might make sense. Right, well, there's the, I guess, media molecule that has the dreams for the PS4 that's not out yet, but where they're doing non polygonal rendering and I think it is mostly compute. Yeah, I think it makes sense for that and it's interesting, but like anything of just re-implementing OpenGL or something in compute is kind of stupid, I think. Well, I remember 10 or 15 years ago, people were experimenting, even before we had full programmability. Like when we just had the earliest shader programs that were really limited. People were experimenting with trying to implement render man on the GPU by turning the render man shaders into these multi-step things that had to compute temporaries into the render targets and then reload them from the render targets on the next pass and do all this crazy stuff. Right. And I know some people have looked at the idea that rasterization on our current graphics hardware is really targeted at this assumption that like triangles are 10 pixels on average or whatever. Like there's this whole assumption that you can do the shader, the quad shading of pixels or fragments that breaks down performance wise when triangles get really small. And so people were experimenting with things where I think there were papers with the results that are like, we actually are faster with our compute shader doing rasterization for really small triangles than the native hardware is. What do you say really small? Do you mean like sub-pixel triangles? Yeah, exactly. Like Pixar or Pixar style? Exactly. So when you want to start, because we do want our image quality to get higher, we should be going to Pixar style eventually, right? We should be doing something that produces that image quality. I mean, Pixar is moving away from that stuff and doing global illumination and stuff now. So who knows? But that whole idea of having the sub-pixel, well, I mean, Render Man actually tries to optimize the triangles to be one pixel-ish size in reality. But... I thought they were, I thought when I read on Toy Story 3, I think it was like the average triangle size was a tenth of a pixel or something like that. Yeah, okay. Maybe my understanding was wrong, but you know, because the whole idea is that they have these parametric surfaces that they can test late to whatever density they want. And my understanding was that they tried to adaptively tessellate them to about one triangle per pixel, but it might be that what you're saying is true. Because they are, they do have finer than pixel shading, higher resolution, right? They're doing like eight-by-eight super sampling or whatever, something like that. So it would make sense to allow the polygons to go a little smaller than a thing and maybe down to a tenth, yeah, I don't know. Well, did you see their most recent, like publicly released, everyone's gone up and everyone's gone insane about it, where they had like the great individually models or grains of sand that were 5,000 triangles each. Yeah, yeah. So yeah, they've clearly gone smaller. Anyway, so I do believe there was a paper that was doing that kind of thing where it kind of maybe makes sense, but it was still a research-oriented paper. It wasn't like a production kind of thing. And you know, the graphics APIs probably really don't lend themselves to hooking that stuff up to the shaders. Opponly at the current, the way current hardware is. So who knows? But anyway, so yeah, so the short answer is that I haven't really paid any attention to what people are doing. And the long answer is all the stuff we just said. If you aren't using introspection, how do you normally handle serialization? So you just write a function that, or write some data in an array of structs that describes the introspection data my hand. You just say, like I would tend to do this as a set of functions where, so for each struct, there's a function that defines the introspection data. It just says there's a little macro and it says here's the name and I had to write it out by hand. Here's the name, you know, maybe I make the macro do some of this work for me. So I type in the name of the struct and the name of the field and the name of the type of the field. And the macro goes and expands and does an offset of macro so it can find where the offset of in the thing is and does a string convert thing to turn the name into a string so that I have the name at runtime. And I just have to manually update that when I change the structure enough I forget to manually update it. I'm fucked. But then I can append extra data like what should the initial state of this variable be if I'm loading an old file or something like that? Which you can all do in other ways. Like I'm not saying this is a superior thing but it all does fall naturally into that. And I believe that was the thing that John was talking about in, I believe the handmade con or maybe it was in a giant thing that I don't remember now. Where he, I just remember he was saying that A, you can do this to cogeneration or B, you can have to do a little extra work and you just do the little extra work. All right, I assume you have nothing to say about that because you use cogeneration. Yeah, for serialization almost all like 99 out of 10 M-Map, M-Copy, done. Yeah, and I should clarify. Yeah, for the thing I just described is how I do, how I simulate introspection but almost all of my serialization I either go through the struct field by field and think about what I want to do with it explicitly and handle it or I do what Sean just said and just write it to disk. I've leaned, write the struct as is to disk. I've generally leaned in the other direction because I was trained on writing portable code in an environment where the base data types and the endian-ness were not guaranteed across different platforms. I learned to program C on Unix machines and C at the time back in those days which is the 90s, people were like, C is the only language that is portable and what that meant was that you had to have a whole bunch of if-deafs in to handle all these different cases and you had to recompile it on every platform and that's what portable meant because you could actually do that whereas in Fortran or something like that there was no way to express any of this stuff. You couldn't write one program that worked in all the places and C you could write one program that worked on all places if you did all of the crazy magic voodoo to explicitly handle all of the cases. Obviously it's something like Java that's way better and just in general that's better now and in general it's even better because everyone has settled on lindial-endian so you don't even have to worry about lindial-endian disk conversion. But for serialization explicitly because that went out to a disk format I had a real habit that I learned early on of explicitly defining what the byte layout on disk was that was totally separate from the representation in memory. Yeah, if I cared about that and I used to because I mean I should have been on page three which is big-endian so that used to matter and I worked on it and I had a Mac and stuff too. So it did matter before back when it was part of BC and all you would do is the first four bytes you would put zero, one, two, three, read them first and then if they were zero, one, two, three then you knew that they were a little-endian because that's what you originally wrote it on and if not you've just flipped everything. And it really wasn't, sorry. But to flip everything you need all the introspection data because you need to know the size of everything. The sizes. The sizes, yeah. If everything is just an integer, a 32-bit integer or a 64-bit integer or a 16-bit integer then you could just do the offset of I guess you would have to but. Like you said you don't need, you need the size. Like you don't care if it's a 32-bit integer or a 32-bit float, you-endian swap it either way. Right. You do the same endian swap for those. So you do just need to know the size of each thing which you can do with the macro by doing a size of the field. I think there's a, is there a size of for? Size of the field. I don't- Size of the null pointer. Yeah, you have to do the half. You have to do the half to null pointer to the field minus or not minus anything, that would just be it. The offset macro of macro exists and just solves that problem for the offset calculation. And the naive way when there wasn't an offset of macro is to do exactly what you just said but do that to compute the offset of the thing. And that's funny that they put in the offset of macro so you wouldn't have to do that hacky thing that is like in the spec not guaranteed to work. It's one of those dumb spec things. But they didn't, I don't think they did a size, yeah, the equivalent struct size of or whatever, like offset of. Oh, I don't know. It's the obvious- I never did that anyway. No, yeah, it's just the obvious symmetric thing to do. If you're gonna have an offset of struct comma field, why not have a quote unquote size of struct comma field? But oh well. Yeah. Yeah, I wonder why. Because then you'd have to pragma pack and then one above to figure out the size. That's yeah. Have you ever found a way of programming in quotes a way of programming? Stupid in the beginning, but later realized that it is very useful. I mean, probably, probably. So, right, like I've always been a pretty good programmer. Not always when I was 13, I wasn't a pretty good programmer, but you know, I started programming at 13 and I spent way too much of my life programming, writing basic and assembly up until I was 18 and went to college. And so by the time I got to college, I was already a pretty good programmer by some standard. And so I've probably had a lot of hubris over the years of thinking I'm a really good programmer. And so I would not be surprised if there was something that I was like, that's a really dumb idea. Why would you ever do that? And then backed off from that opinion. But I can't think of anything specific. The one case I can think of is not my story is John Blow's story from when he gave a talk about how to write independent games, which amounted to do everything the simplest way, use arrays of structs, that same kind of argument. And the story he had in that, which I cannot give word for word accurate, so this may not have been exactly what the story was, but he was looking at Doom, how Doom looted some data file and was on a mailing list or something and commented that he thought it was really dumb or maybe he didn't comment in public, I don't remember, but comment that he thought it was really dumb that they were doing this inefficient way of loading this thing and they could do this much smarter thing that would be super, it's the way you're taught to program in college, apply the smart algorithms, do everything right. And that later he realized that that was dumb of him to think that because none of this matter because it was the loading time stuff which was already plenty fast and just doing it the simplest way is way more important because it reduces your bugs, lets you move on to the next task sooner, you get more done on your project overall. And I'm sure I've done the exact same thing about stuff. Excuse me, of looking at some system and thinking this could be done much more optimally, I'm sure back when I was younger I made that exact mistake that he described. I cannot think of any specific cases. But that's kind of the opposite of the problem you actually were, I think you were trying to pose which was like some sophisticated way of approaching a problem that seems dumb at first and turns out to be smart. Like I've only seen the opposite which is like thinking that you should do the sophisticated thing and realizing that the dumb thing was the right way to go. I think I completely 100% agree with that. Like, because you hear about this is the new programming paradigm, this is the new way to program, this is what all the cool kids are doing. This is gonna save so much time. Every single one of those things has been completely, whatever they said was the case, I have found it to be the complete opposite. And the only thing that's ever worked for me for programming is to do the dumbest thing possible and that's if a, do be, else do see, et cetera, et cetera, et cetera, right? Just everything, I think everything other than the simplest thing possible has always been wrong for me. I do wanna kind of contradict that. It's not a real contradiction. You're not gonna disagree with anything I'm saying here but our peer group does have this habit of making that kind of pronouncement. And I wanna say that historically there has been this path of people saying here's a better way of doing things and that we have that criticism of. But if you look at the historic history of our long enough period going back for enough they were right in the early days. Going from a similar to a high level language was the correct choice. And the most important thing to me, the reason I thought of this whole thing is that, and another example, let me use one more example first before I get to my valuable one is the whole structured programming instead of using go-to statements. It's huge, like using if, then, and while loops instead of writing everything in go-tos is definitely advantageous, definitely superior. And the one that for me is the one which is where I stopped, I think. It's the last one was abstract data types which is a thing that's akin to object-oriented programming, but object-oriented programming just takes it way too far. And then abstract data type is just the idea that you have some data and some operations that operate on the data and abstract it away and from the outside you don't know the details of what's going on inside of that. And so when I was talking about having the dictionary mapping strings to void stars, like that's an abstract data type. Yes, I implement myself and I know it's a hash table, but when I'm interacting with it, it is a dictionary. I put things in it and I get them out of it and I don't care that it's a hash table. And I learned that in college. There was a class where they taught us abstract data types in the 90s that I went to or whatever. And it wasn't like nobody ever knew about this. It was a decades old idea or something, but it had to be explicitly taught. And that has gotten subsumed into object-oriented programming I feel like nowadays people are familiar with that idea but only through objects. You do see the terminology occasionally. But anyway, none of this actually relates to your question of seeing a thing and thinking it's dumb and then later deciding that it's a good idea. But I wanted just to... And again, I assume Sean doesn't actually disagree with me about anything I just said, but I just wanted to say that historically there have been developments that were improvements that you definitely wanted to get on. Yeah, basically just read everything by Dykstra and then stop there. So... That's pretty good advice. So first I'm gonna scroll down and see what the replies to our most recent stuff are and then I'll go back to the things that we're still pending. But I actually, I wanna sort of ask you about this. Do you think that's it? Is that it? Like is this as good as programming is gonna get? Because the last 20 or 25 or less 30 years have been mostly regressions. Yeah, let me answer, there's a comment here that I just happened to scroll by and chat that wasn't even a question or it wasn't even directed at me but I happened to see it so I wanted to comment on it and which kind will connect to what you just said. So Ginger Bill's comments, I wonder if built-in Indian specific types would be a good thing. Maybe U32, U32LE, U32BE, Big Indian and Little Indian. And yeah, I think a systems programming language which is what C aspires to be ought to have built-in facilities for, not to say types, but ought to have some kind of built-in facility for dealing with Indianness and dealing with alignment and things like that. And currently in C, like there are some things you can do for alignment but one of the things you need to do when you're sort of manually implementing a memory manager, you're like, I wanna replace the malloc for this, I wanna do something smarter for my memory so I'll write a little memory manager for my allocations for this. So I'm gonna have a pull of memory and pull things out of that pull. You need to align your pointer accesses in that and to do an alignment operation on a pointer you have to convert it to an integer, do the alignment math on the integer and then pull it back into a pointer. And modern C now has the UIT pointer type that is guaranteed to hold the pointer size. So you can do it on the UIT pointer but you need some way to do that conversion operation and nobody is even sure what in the C specification allows you to convert a pointer to and back even though it's supposed to be a systems language. Right, we used to do this cast, we would just cast between them and then the strict aliasing people came along and we're like, okay, casts aren't safe, normally between pointer types but I believe it applies to the integer conversion as well. So we all switched to using a union, you have to store the pointer in the union then you fetch the integer out of the union, do your math, you store it back in the union and fetch the pointer back out, okay, now that's fine. And then somebody on John Reger's blog or maybe it was John himself commented that actually it seems like the spec isn't clear that that's actually safe. And so some future optimizing compiler might actually make that thing say fail and there was some even more convoluted way of doing it that is safe and I don't remember the details of this. A systems programming language should just have something, like just fricking define a function, a pseudo function that converts a pointer to an int and back and then the compiler knows for sure and is done. In the case of the little indian big indian thing, the thing to do would be have an extra type tag or whatever you call those modifiers rather than build it into the type, just be able to take any integer type and declare it little indian or big indian and it's the compiler's job to do the conversion and you don't expect it to be fast, you expect, it's not systems programming, like it's weird to say that that's systems programming because we think of systems programming as meaning it should be C and bear to the hardware and do that stuff but there are things that system programs need to deal with and things like indianess are one of those things and there should be facilities in the language to do it. That's my opinion. Well, I just wanna add two additions to that. One, regarding the indianness, stop making big indian CPUs, stop at IBM. No one cares about power, stop. Little indian, just give up. And then the other thing is just have an align qualifier. Align to whatever, right? Just have that. Don't make me pragma fucking pack the struct and then calculate the alignment, man, you just fuck off. Just give me an align. Like you want a big indian qualifier? I want an align qualifier. Yeah, and there's some chance that John's, John Blow's language is going to have some of these features because he wants them, he's making a language for game programming specifically and I don't actually even care that much about game programming as systems programming. I actually care more about systems programming than game programming to some extent but I think those are, so it may turn out that that's a better language for systems programming as well as games programming because games programmers care about performance. So that kind of stuff might show up there. But anyway, so then the actual question that you had raised, right, was I don't even remember what it was. I said that this was related. So you had said, oh, is this as good as it's going to get? Is this what programming is? I mean, like, yeah, I mean, clearly it's not, but like- No, no, no, that was the point. That was the point is you don't mean at that level. Like, oh, there's these little tweaky things we can do to fix it. It was intentional, I was always like, yeah, there's these little tweaky things we can do to fix it, but you look at Jai, you look at John Blow's Perkin Lane, and you're like, that's not very different. Like, there's like a lot of the complaints on Reddit or Hacker News are like, yeah, there's nothing really new in this language. It's like this incremental change over the old thing. And it's like, yeah, that's, as far as I know, that's all we're going to get is the small incremental stuff. You know, that is the topic of that old Fred Brooks No Silver Bullet essay. And it's very out of date in some sense, but, and I cite it a lot when I'm talking on Twitch and I haven't read it in a decade, but yeah, I think it's true. Like, I think he was already right when he said, yeah, we've back then, it's like a 30 year old thing now or 40 year old or whatever. It's like, he was arguing, we've gotten these giant productivity increases, order of magnitude productivity increases in the past by improving these things. And he's like, it's called No Silver Bullet because he thought at that time there was no Silver Bullet left. We weren't, there was nothing we were going to find that was going to give us an order of magnitude productivity gain. And, you know, back then the thing people were always talking about is that there would be programs that would write programs for you. So you would just tell it what program to write and it would just magically produce the program. We might get that actually. That was the hope for an order of, that was the way you could see getting an order of magnitude, but there wasn't anything else obvious that you could do. And yeah, I don't know. I don't know if I've got to trust the neural net that writes a program for me. Like, that seems- I've read a very interesting sci-fi book about that. I don't want to talk about it now, but yes. Like if we do get programs to write, if we do end up with systems that can write programs for us, those systems are going, the way the world is currently going, those systems are going to be deep learned neural nets. And given the flaws in those kinds of systems, I really worry about something. I mean, I guess our programs are buggy too. And they'll just be producing bugs, weirder edge cases than we would produce edge cases on. But yeah, that just seems frightening to me. But yeah, let's probably not rattle on that. So yeah, that was your question. Did you have an opinion on, are we at the limit? Well, I think so. I mean, I hope not. But like everything that I've read post-Dijkstra has been more or less regressive. And it's like, yeah, JAI, maybe programming in JAI is 50%, 25, 50% faster, but it's certainly not an order of magnitude faster. So I don't, maybe not. I only expected to be like 10% better experience. And that's partly because I have like the STB.h stuff that has brought C up a lot higher for me. Right, and that was the whole thing I was talking about with the Jeff Roberts using the, the map and the stretchy buffer that he was kind of saying, he felt like he had that kind of, it's not necessarily an order of magnitude or even a doubling of productivity or whatever, but maybe it actually is for him, it might actually be a doubling of productivity because he tended to rattle on optimizing things. And part of the experience for him has been letting go of optimizing everything because you don't need to, because C and computers are fast. But yeah, for me, I don't expect a doubling from anything. I think the STB.h might have been a doubling for me, for small programs, maybe not for larger programs. I think for me, I think it was a doubling, not necessarily the library itself, but the technique of how the library was made. I think that probably doubled my productivity. Do you think a 100,000 line program you would have written twice as fast though, a 100,000 line game? No, but no. Yeah, I mean, so that's, all of the support tools and stuff, like my current game has 22 programs just to support its development, right? Like publishing, code signing, asset checking, all this shit, there's 22 programs and all of those reference a single file that takes care of all of the common code between them. That saved months. So you think you doubled, hand-wavy, you think you doubled your productivity on those programs? Oh yeah, for sure. And that's more than double my productivity on those programs, and that's probably 25% of my time budget on this game so far. All right, so that's interesting. Because a doubling is huge. So I don't want to, the Silver Bullet, I think was arguing about basically orders of magnitude. And I do want to say that. I do think the stba.h stuff, especially just those two core things, probably did double my productivity in C because at the time I did those, leading up to those, I was writing my own language because I wanted a new language for writing those kinds of smaller programs in. I didn't care about writing a new language for writing games in, because games are large and C seemed adequate for that, but I wanted a new language for writing small programs in. And so I really do think, yeah, like a doubling of productivity for small programs. It's like that example I gave about the sorting, the anagrams in the dictionary, where I was a 15 or 20 line program and probably would have been a hundred line program or something like that. Those don't, the amount of productivity, like it doesn't multiply in the same way. But. It's probably actually more exaggerated because there's gonna be a lot of bugs in those hundred lines. Yeah, quite possibly. So yeah, like I definitely got, but to some extent, that's the same productivity everybody else got by going to Python, right? Like, it's, but the difference obviously, I'm not trying to say it's, there's no advantage to being in C. Obviously it's often faster or whatever. But to be fair, like the rest of the world got that kind of productivity gain they did get by going to things like Python and. I'm actually not convinced about that productivity gain. Because the rate at which there seems to be new frameworks and new languages, the learning that just the upfront cost to learn all of these new things just seems prohibitively expensive. Like it can't actually be more productive when you have to learn a new framework every six months. Well, the frameworks are pretty specific to the JavaScript ecosystem. Like people writing 50 line programs in Python are not confronting that stuff. They're not all learning new frameworks. They're not learning new languages. They, somebody out there picked Python and stuck with it and got the productivity gain. Well, I've heard Python two and three are like drastically different, but. Yeah, but they just stuck to Python two or something. The people who did get caught on the upgrade trend treadmill. Yeah, they're fucked. Like, but I'm sure there are people out there who aren't that dumb, who are advantageously using Python. I mean, I'm sure there are two, but. This is a little bit of a difference in our philosophy, at least our philosophy of our public statements and streams is that I try to be a little more open to the possibility that other people know what they're doing and aren't total idiots. I am not open to other people. Yeah, and we're just gonna have to accept that in this podcast that we'll have that, we'll express these things differently. But to be honest, I'm just joking. Like I am obviously, right? It's just nothing I have ever seen indicates that any of these things are good ideas. But if somebody proves me wrong, I would love it. Please. Well, John Blow gave a talk, I talked about John Blow's talks a lot. He gave a talk where he was complaining about all the programmers at Facebook or whatever and how crappy the output of what Facebook and Twitter produce. And so his conclusion was those people just don't know how to code. And, or the entity, the company as a whole doesn't know how to code or something like that. And that is, I do not, I do not trust that I understand the problem they're facing well enough to make that judgment call. And, you know, maybe in private, I would be willing to go out on a limb and say they're probably all idiots. But on the public stream, other than that statement I just made, on the public streams, I generally try in an honest, not out of like a deceptive thing, but you know, like I'm trying to be supportive and a little less cynical. And I don't think all people, are brainwashed idiots. So there are reasonable odds that some of the people there know what they're doing and are doing it. Well, again, Collette is at Facebook, right? There is definitely smart people at Facebook. But not just that, but they're doing the things that they're doing for good reasons, not just for, oh, the institution fucks them. And that's why the whole thing's like, one way of getting away from blaming all the programmers is to say, well, institutionally they have a problem. And it's not because like they're solving problems at scale. Like the example, the obvious example with something on Twitter is, Twitter needs to handle both the case of a tweet that a million people see and a million tweets that one people see. It has to handle both of those cases efficiently. And a lot of people, when they're talking about these problems, don't know anything about distributed systems. I don't know very much about distributed systems. And just kind of don't even indicate even the vaguest awareness that that sort of problem exists. Like in John Blow's talk, it's not clear that he's aware of that problem. He's a smart guy, so he's probably aware of that problem and has already discounted and isn't saying anything. It just isn't mentioning it in his public discussion. But it makes his arguments weak because those kind of issues don't seem to be addressed. So because those arguments are so weak and stuff, I don't make them publicly. And I don't make them privately. I was half joking when I was saying all that stuff about, I don't say what I say privately is different from what I say in the stream. Like it's more like that everyone's racist, deep in your heart, you're racist, but you don't just toe the line in your words. You toe the line in your words and deeds because you know you're wrong deep down to be racist. You just, everything about you acts non-racist in all the best ways that you can, but you can't help the little colonel in you that was trained to be racist. So in that same way, I'm racist against the Facebook programmers in that same way. I do think they're idiots deep, deep down, but it's just this colonel that I don't believe in. I don't trust it. I think it's garbage. Like if somebody proved to me that all the Facebook programs were garbage, I'd be like, oh, okay, they're all garbage, but I don't go into it. I think there's empirical evidence that there is an institutional problem. And I think there's empirical evidence that they are on average garbage. I think some of the best programs in the world probably work at Facebook, but like they also have 10,000 of them, right? They're just, isn't 10,000, you're just not gonna get 10,000 good people on the same project. It's just not possible. Not every, not 10,000 people, 10,000 really, really good programmers are not gonna be interested in working with that scale of project, right? Most good programmers are not gonna be wanting to work on it, right? So I just think it's like a fact, like a provable, demonstrable fact that Facebook and those types of problems are not going to get all of the best programmers. But there are very, very smart and very good programmers at Facebook. Well, but it's different to say good programmers, great programmers and idiots, you know, or have no clue how to program, can't program their way off a paper bag kind of comments because like a lot of software engineering practices and even OOP is, seems to exist in part to allow programmers who aren't that great to make forward progress on a large team. Okay, that's fair. That seems to me, and so they practice these practices and you have all these non-good programmers, non-great programmers making forward progress and eventually the app gets done and you ship it and it has more than 65,000 classes in it. And you can point at this more than 65,000 classes as empirical evidence that it's a bad program or whatever, however you wanna phrase that stuff. But if in the end it gets the job done, it's like, I don't have enough innards to that. Like I wouldn't ever wanna deal with a program that has 65,000 classes, but I'm not sure I can say a priori and it's not 65,000 classes, I think it was 65,000 methods. There was this whole story about how they had to patch over the thing that was the function, the Objective C method dispatch or something like that, I think it was. And I think it was actually methods, not classes. I think it has 5,000 classes though or something. It's still an insane number of classes, but the patching thing was because some number was exceeded and I think it was the number of methods globally. Anyway, let's get out of that rat hole, but I don't wanna spend my day defending Facebook programs, but I think our peer group has a very know-it-all attitude towards classes of programming that we know very little about. I think that's certainly true. And so I try, and I'm okay with you and John Blow and me or whatever, denigrating them. Like I'm not morally offended, but I just try to be a little more cautious about, well, I don't really know anything about that, so I'm gonna try to not do that. And I probably in the past was not very good about that. I probably have said Facebook programs or shit five years ago, but I'm nowadays trying to be a little more careful about that. I don't use it or know anything about it, so I've actually never specifically said that about Facebook. And I actually find like a lot of people bitch about Google and Twitter and stuff like that. I don't know, they're fine. Like, it's fine. I don't really have any major complaints about them, so, you know. Well, the normal thing is- I've never said anything about those programmers. The normal thing is just to say, but there are 20,000 programmers at Twitter, how is that possible? What the fuck are they doing? It's sort of the normal comment that you get in this. Yeah, but I don't think there's 20,000. Well, it's a thousand or whatever the number is, but. Right, but no, but like, things at scale are complicated in ways that I don't think a lot of people understand. All right, anyway, I don't even remember how we got on that tangent. Do you remember how we got on that tangent? Nah, but I just read something in the chat, 18,000 classes in the Facebook iOS app. Yeah, okay. All right, so I'm going back to back, I'm scrolling back and looking at the questions. How often are you able to bring code from an old project into a new project, and how much effort does it take to get to work with a new project? Do you want to answer first or shall I answer first? Yeah, so basically, two years ago, I purposely broke off from all my old code, and whenever I bring anything over, I make sure it's one file and one file only. And after something has been converted to one file and one file only, it is incredibly easy to bring everything I've ever written across. That's it. Yeah, so the recent history is definitely the same for me. If I want it to be reusable, I put it into an STB lib. I may never release it publicly, but it's usually actually, I usually do the extra work to not merely make it a single file library useful for me, but I try to make it usable for everyone. And all of that is mostly selfish because in 10 years, I'll be, no, I have no idea how the thing works either. And so it needs to be as written as much for them. The me in 10 years is a stranger as much as anyone else using the library is, so. But in the past, the thing I wanted to mention is, almost never other than the framework that I use for writing games, or the frameworks, because I had like Lost in the Static was written, it's the GDI thing. It just displays pixels directly through Windows. So I had a sort of one thing for 2D games like that and one thing for things that used OpenGL. I would just, every time I wanted to do a new game, I would copy the old game and then delete everything from the main except for most of the Windows setup. And eventually I was like, why am I always doing this? I'll copy the game into a framework directory and delete everything from that. And then from now on, I'll just copy from the framework directory. And I did eventually do that. But then I think I only maybe started one or two more games since I made that change. So most of my time has been spent, that's the entirety of the sharing that I would get is I would copy around how to set up a window so that I didn't have to learn how to set up a window or type that by hand every time. And that's it. I didn't try to preserve much of games until I hit on the single file library idea. You know, maybe once in a while I would like copy a hash table around or something, but I think, okay, I'm gonna totally lie. But I bet you in my life, I've written a double hashing hash table 50 times because I would just write it from scratch every time. Iggy has probably three separate implementations of a double hash table, just for different types. And I'm not using templates, I'm in C, so I just have to write it three times or whatever. Oh, you can do policy-based design with macros. You can. I've done it. I'm a red-black tree. Yeah, Jeff had a reusable AVL tree, right? AVL tree that you just include it multiple times. You include it once for each implementation. So you define a bunch of macros and then you include it. Yeah, that's how I do it. I do it for lots of things now, actually. And that was why I did the 500 line macro instead was because I don't really like that stuff. I used to do some stuff that way and I eventually decided I didn't like the multiple includes. They kind of felt clumsy to me and I particularly don't like that you could type out the pound-defines and not know that you would type out them. I mean, as soon as you tried to use the thing and it didn't have a definition, the old definition was there and the types wouldn't match or whatever. You'd catch it, but it just didn't feel right to me. No, at the end of each header file, you just force, undefined, everything could have been fine. It didn't feel right to me that it's inexplicit, that these are effectively parameters to this data type and it's not, and it's in this indirected way. That's why I chose to make a macro that's a 500 lines that takes an explicit list of parameters. Right, like I'm completely on board with Apollo, like that book, Modern C++ by Andre Alexandru who's at Facebook now, if you read it or know it, you know it, right? I know of it, yeah. Yeah, so that book is kind of great and kind of bullshit at the same time. And it's kind of bullshit because it does policy-based design through types or through, yeah, types through the C++ metaprogramming system, right? Which is awful, but the idea of policy and his examples of policy-based design are quite convoluted and bad too because they're all for sort of C++-y things. Like you can have 45 different ways of implementing a singleton, right? Like who cares? But for things like common data structures, like a hash table, a red, black tree sort, all of these things, like do you want stable sort, pound-defined stable sort? Then if you want a non-stable sort, the next time you include it, include it all again without the pound-defined. I have found that to be really, really useful and that's only over the last couple of years because I had the same aversion to it that you had where it's just like, this doesn't feel right. Ah, fuck it, it feels great now. Well, that's a perfect example of the problem that I have, which is that you misspell the stable sort thing and you get a non-stable sort, but none of your test data actually happens to hit the bad cases when you're initially developing and eventually you like far down the road realize that the sort isn't stable and that was fucking you. That's one of the things I don't like about that way of defining the macros. If you forced you to always say either stable or unstable explicitly, that would obviously address. That would solve it. I also, I think, always copy and paste anyway because I can never remember what the parameters are, so I just try to list them all at the top of the file and then just copy and paste. You know, one of the things is that the GL extensions, I've used macros to define my GL extensions rather than using them on the libraries to do it and I used to do it with multiple inclusion so I would make a text file containing a list of all of the extensions, extension functions that I wanted in some kind of macroized format and then include that in multiple places and in one place that would define the function types and in one place that would define the global variables for the function pointers and in one place that would fill out the function pointers. So would you mean like an X macro type thing? An X macro? Like where you define like a macro list and then like just a list of all the things and then there's an operator on each and then you define the operator? Yeah. Do you know what I mean? Yeah, you define the operator multiple times, right? Yeah, okay, yeah, yeah, I do that shit all the time. Right, well, and so when I was originally doing that, I did that by putting the list of things, the list of X macros, I guess, not in another macro, but just a plain flat list in an inl file and then I would include that inl file in three different places to get the list instantiated multiple times. That's fucking great. And now I take that exact same list and I put backslashes at the end of all of them and define a single macro that contains all of them. Instead of including it three times, I now just instantiate that macro three times. It does the exact same thing, right? Right. I just have to add, I have to do more work because I have to add all the backslashes, but otherwise it's identical. And I've just veered over to a system where I'd rather do that. I think it's that same kind of like the cogeneration expliciteness is like the including multiple times is cute and I originally didn't have any problem with it, but over time I was just like, you know, it's just cleaner if I don't have to have another file and I just can do it inline this way and not have the multiple includes. So again, like, I mean. Yeah, I wouldn't do the inline thing like you said. I do do it as this sort of standard macro thing. Yeah, yeah. But for the other thing, it's like imagine there was STB hash and you just like, this is doubly hashed. This is, you know, you pound to find STB hash. Fuck, I don't know, Robin Hood. Pound to find STB hash, max size or whatever, whatever it is, all the parameters you could have. There is stuff like that in STB right now. I can't even think of them offhand, but there are a few that have some config variables and they define an STB static thing that you can do that forces all the functions to be static, which allows you to instantiate it several times in different C files, because there's no way to, I've forgotten the word, to change the names to, you know. Sure there is. No, in my header files, there's no system for appending a different, you know, a tag, appending a unique thing that will make each one unique. There's no way to do that the way I have them set up, but you can, in multiple C files, instantiate copies over them and then wrap them in global names that you have affixed a prefix to or something to distinguish them. So it's possible to do it in the STB libs, but I haven't made that like an, I haven't put the effort, for example, to automatically adding the prefixes because then you have to wrap all of your function names in macros and they no longer look like regular functions and I just don't like that. Like I like to be able to read through the code and not have the names of functions wrapped in macros because now they're syntax no longer matches the cliche idiomatic syntax for a function definition. Right, it's got some extra parentheses in weird places. And so that just slows down my reading. So I'd rather not do that. So like, if I thought it was an important thing that you needed to do all the time, then I would go ahead and do that work and add that ability. The STB SprintF, which Jeff wrote, actually has that facility because he put it in and I was like, I'm not gonna take it out. He did the work, but I would never do it. But so I don't even know what we were talking about. Well, I was just saying, I only do that for things that are explicitly meant to be used for multiple types like different hash tables, different. And like I said, I just don't have enough of those. I have the tree and the hash table that I did the ugly macro thing. And in hindsight, I think Jeff has an approach to it that is maybe better. Like, this is not maybe exactly what Jeff did, but the basic idea is that the hash table needs, like if you were doing it in C++, this was a whole thing I never commented back when we were talking about this, with a part of C++ you do use. If you were using C++, you could do operator overloading to get you comparisons. For hashing, you just need a quality comparisons. And you need a hash function. And you could do that with C++. You could have a virtual method or you could use templates or whatever to expose the hash functions. And in mine, I'm writing this thing that's a generic hash thing and you have to pass in the hash function to it. But it's a macro, so it expands out and it can inline all that stuff and it can do the hash table efficiently. It's not like Qsort where you're passing in a function that has to get called at runtime. And- Right, I actually, I wrote the dumbest Qsort just to test how much that overhead was and it was quite significant, where I used a macro and then did it with the multiple include thing. And my Qsort was like 60 or 70% faster than the C one. And it was the dumbest implementation of Qsort you could possibly do to it. It was faster just because of the overhead of the inline. STB.h does actually have a Qsort, a templatized Qsort in that same way. It's a big macro. And yeah, I think it was 2x faster. Yeah, it was quite significant. I was like blown away to the point where I was like, well, I need to write my own sort library now. It was 2x faster eight years ago or whatever. I mean, who knows on a modern machine. And I was like, that's not enough that I care about it. I mean, it's enough to be interesting. It's enough to be interesting. And it's still in STB.h, but I never remembered to use it. It's just a little more cumbersome to use than the Qsort because I have the Qsort helpers that make Qsort easy. And for most things, that's just never big enough for most of the things I do, right? Especially when I'm writing a small app that I'm going to run once and it's going to take five seconds. And it's like maybe at one second of that is the Qsort. And so the half second difference is not worth the half second of my, it would take me more than a half second to switch the sort implementation. So it's not worth it. And there are other cases obviously, there are cases of programs that I run hundreds of times or it's part of a game, then it starts to matter more. And that's just more a matter of the fact I've never gotten used to it. I don't need to deploy it usually. So I'm not even, I don't even really remember that it's there. I only remembered it's there when you said you implemented one. And I was like, oh wait, I did that. It's also not, it's a bad Qsort in that it's the adversarial thing. It can go into the n squared thing. Oh yeah, mine could too. Yeah, whereas the C plus plus sort does have that correct. They have the- Well the C plus plus sort is actually not Qsort. Yeah. C plus plus is inso-sort. Right, which is- Which is like Qsort with a finite depth. And I was, Jeff was writing his own sort and spent a lot of time on this. What about American flag sort? No, let's not go into weird sort. But Jeff was writing his own sort and spent way too long on this and it was like, oh, I better fix the adversarial case than he did and all that stuff. But it was this giant mess where he was like in the end, he really didn't beat the, he beat Qsort, but he didn't really beat the standard sort. I think it was like, well, if you have a lot of duplicate items, then his was faster than Qsort, not Qsort, faster than standard sort. And maybe he was like 3% faster than standard sort or something like that. But the other shot was, hey, they did actually do the correct real work in whichever implementation of standard sort he was looking at. Well, that's the thing is when I wrote, I wrote, I spent a little like months on this just in my spare time writing better sorts. And it's just like beating the C plus plus sorts really fucking hard. Yeah. It's really quite good. So anyway, that was all, I think we were answering the question about bringing code from old projects to new products and we kind of rattled into that. Oh, right. So about one quick thing, before I was doing single file, starting in 2004, I started writing a game engine that I was a sensibility to use for every project, right? Which obviously that doesn't work. But like at that point, I had like 30 or 40 files, right? Like I had a VEC2 file, a VEC3 file, all my math, like it was brutal. And now it's just like having everything in one file is just like, I have a, I include my platform library, I copy and paste like 15 lines of code. I have OpenGL initialized, I have a game loop, I have an init function, I have a config function to configure the platform. Everything automatically gets called, the win main gets called and my game loop just starts and OpenGL just works. And that's like, and it's like literally 25 lines of code that I have to copy. And it's the best fucking thing ever. It's like, just one file makes everything amazing. Yeah. I've mentioned before on stream that I have a new OpenGL framework that I'm just going to be switching to that is a single file lib format. Like that replaces SDL? No, I mean. If you're interested in mine, mine replaces SDL and it also abstracts like IO completion ports versus ePoll on Linux and all of that shit. Yeah, I mean, I'll go down that path eventually probably, but like right now I don't generally port my games to lots of platforms and the main thing it's doing is providing the OpenGL, the whole core versus compatibility context stuff. You have to query the context. There's this whole mess to like get a debug context and none of my framework stuff we're doing. And then you can ask for a core context and people care about that more and like on the Mac that you can either get a really old compatibility context or you can get a new core context and you don't get both. And so the main thing that this has is that like it provides all those facilities so you can just query whichever version of OpenGL you want and you can just get it. And then the big, big part of the library that's not done yet is I'm basically re-implaning all of, a large subset of the compatibility, the old immediate mode rendering and Also you're implementing some of the function. I'm implementing all that on top of core myself. Not fixed function I'm not gonna do. It's gonna, I'll have like some trivial fixed function E shaders or something built in but it's not gonna do all fixed function. But doing immediate mode. So how would you do the fixed function just like write the actual shaders in the thing and then just switch to those? It's already got two shaders built into it which do a blit and I can't remember what the other one is. One does a pixel to pixel blit and one does a scaling blit and recolors or something like that. It has, there's an STB easy font library which is a way to really easily do text rendering that's crappy text and that's built in and so it can like built in display the frame rate by using that the built in shader. I've got one of those two same sort of thing. And just has a built in debug print kind of thing or whatever. And it has some weird stuff because of the application I was using it for you can call a function that binds a key to mean zoom and then when you press the key it shows you a zoomed box of wherever the cursor currently is. So you're keeping track of, like you're internally keeping track of some sort of a projection matrix. Well, no, it's just of the screen. So it just uses a shader, the shaders bypass the projection matrices and stuff anyway, right? So it just uses that scaling blit shader to scale up a certain region of the screen. And it's just as I was like I was doing some detailed rendering and I really needed this functionality of can I look at, because I'm running I have bad eyesight and I'm running on a 2650 by 1600 monitor that's how 20, 30 inches, whatever it is and which just means the pixels are really tiny and I just can't see what the individual pixels are doing. So I really needed to see what was going on in this thing that I was doing with some character rendering, text rendering. And so I needed this feature and I'm like, well, why don't I just put it in the lib because it might be useful for other things to do it. So I'll write it in a way that is generic because it sounds like a generic thing. Once you already have a framework. So one of the other things the framework already has is this idea that your canvas size and your window size are different. Like you're like, for this game, I just want to render it 800 by 600 and you scale it up for me or scale it down or whatever, do you meaning the framework? Right. And so I already had this idea of rescaling the screen. And so it's like adding in a zoom thing that rescales a part of the screen was really easy. And I was like, yeah, if it's my framework that I use for writing small little openGL things, yeah, one of those I might need to want to zoom in. So let's just have this in there. And it's not hard coded. You have to enable it. You have to ask, intercept this key and do that with this key and you'll never see that key again. Or I think it still gives you the key as well and you have to ignore it yourself. So it's only intended for debugging kind of stuff. But anyway, so the idea is to maybe put some helper features like that in that are kind of esoteric, but don't hurt. I was, the other test app that I started writing was, was, you know, just looking at some terrain. And so I just need the basic mouse keyboard movement. And so I wrote helper functions that generate the, I don't think they generate the matrices, but like tracks the mouse movement for you and gives you Euler angles. And you can just ignore them. It's just always doing this. Like just the framework is like, oh, I'm keeping track of some Euler angles for you if you want them. And that makes the easy, those apps really easy to write and you just ignore that extra data if you don't care. But anyway, so that's been my focus. It could eventually supplant all the rest of SDL, but right now it just does, creates windows and does that other stuff. And in my old library that for this stuff, I had like, oh, you can create multiple windows and you know, it has to do a windows function for each one or however that works. I don't even fucking remember. And you know, you have to tell it which window you want to interact with in the function calls to it, but there's a default global one so you can pass null for everything and get the default global one. And the new thing I'm like, there's one window and just get rid of having that parameter. And occasionally you want to write an app where it's like, well, I really want a pop-up dialogue that is a separate window. And it's just, yeah, you're fucked if you're using this framework, don't use this framework for that. So, you know, I'm not, whereas SDL does try to cover all those bases. SDL wants to do everything. And I'm like, eh, I'm just gonna do the things I care about the most. So it is gonna replace SDL in the sense of OBBG uses SDL, but everything else I've ever done doesn't use SDL. They just all use my little dumb framework, my own little, it's actually an STB lib that wraps windows messages and turns them into internal messages. But I've never really like sat down and solved that problem until now. And so now it's my replacement for that old system and for some of the boilerplate I still had to have in my code, like creating the window and stuff. Yeah, it might be, sorry. So the point is that it is so that I don't use SDL, like clearly, like so it is replacing SDL in that sense, but it doesn't have the vast majority of the function SDL has at this point. And I don't make lots of shipping games. So I'm not sure I want yours that has a bunch of other stuff and that has poured it to lots of platforms. Like maybe mine turns into yours in the end and I'll regret it, but. It will. That's the thing is like when I did mine, I was like, oh, it's just gonna be simple, right? And then like three months later, I was like, fuck it, I'm taking like Casey's approach for like in his talk on implementing APIs. He talked about like, you know, you start off here and you want to get to here and there's all these little points in between and you want to try to fill in all those points. I basically took that approach to the extreme. It does everything from like the simplest, create a window and draw something and in 30 seconds you have a full functioning OpenGL app to my entire game and my game server and my game master server, which outputs HTML and does encryption and all of this shit and all of my tools. They're all written in this exact same thing and they have various degrees of how crazy like you hook into the implementation. So I went like fucking all out on it. It's pretty good. Shall we move on? Should I move on to more questions? We've been doing questions for an hour already. Because. Sure, I don't care. I have to go in 45 minutes. That's how much time I have. All right. Well, let's try to not rathole as much because there seem to be a lot of questions. Well, we always have next month. Yeah. And we don't have to answer all of them, but. So, okay, old project. So quick answer. Any resources you recommend for physics body simulation? Me? No. Physics and body simulation? I think it means rigid body simulation. Yeah, yeah. Yeah, we can just stop there. No. Well, I mean, I think everybody should read or have Christopher Erickson's real-time collision detection. Yeah, but that's not so much about the physics and just the collision. No, but you should have, if you're asking that question. I don't actually have it and have never read it, but I know all about collision detection from having picked that up from Osmosis before that book existed. So, yeah. Yeah, you have to have that book. Well, you don't. There's an online site that has every pair of, here's how you do intersection detection between any pair of things. And it's this big table with one side has all the things and one side has all the other, you know, sphere versus sphere, sphere versus ray, sphere, blah, blah, blah, blah, blah. Maybe it's got good stuff on broad phase or something. I don't know. It's kind of good, it's good, but anyways, yeah. All right, let's move on. Hey, here's a question related. What books do you think all programmers should read? I can list books that programmers shouldn't read, but should read, they're not. Didn't you have that, what was the C++, the thing that had the policy stuff? No, I don't think you should read that. Oh, okay. You just thought the policy idea was good, yeah. Yeah, yeah, no, no. I mean, I could say something like Art of Computer Programming, whatever, but like, no, not really. Yeah, I mean, I do think it's, perhaps, here's a, perhaps a useful answer. Perhaps, but maybe not, but perhaps if you want to be able to do the things that I do, the things that people ask, Sean, how do you manage to do this thing? Maybe the answer is read all three volumes of the Art of Computer Programming. There's four now. Well, the fourth one is about fucking bullshit, who cares, right? There's two parts to it too, but it goes over in a second. Parsers and Compiler-y stuff, I assume. Is that what that is? I can't read it from here. I don't remember. Well, the point is, I didn't read them. So, and I'm still where I am. So if you want to get to where I am, you read the three. Maybe that's useful. All right, have any of you used the Intel Compiler? I haven't used it. The Intel Compiler, no. Okay, great, we move on. Easy. I've used their other tools, but not the Compiler. The giant, giant question with a quote from Gabe Newell that I'm gonna ignore. I skimmed it, and I was like, I don't know. Serious question. I've recently gotten interested in making emulators. I've been suggested to make a chip eight on first, but what would be a simple real console CPU whatever that I could make next? I have no idea. 68,000. Okay. It's the best fucking assembly language ever made. I have had more joy programming 68,000 than probably any other assembly language. I just don't know that I'd enjoy making a CPU. Emulator for that because then there's nothing I've run on it without a lot more infrastructure to actually, like, what's a 68,000 machine that you could then go emulate once you wrote the 68,000? Oh, I know, yeah. Yeah, like, do you want to get Mac boot ROMs? I mean, ugh. So yeah, I mean, that wasn't. The Apple II was 68,000, right? Apple II was 65 or two, but the Mac, the original Mac was 68,000. But yeah, I mean, if just answering his question, sure, that's valid. I just don't know how much value he'll get at the end once he's got a working 68,000 emulator, and he writes the three line assembly program and emulates it correctly and goes, okay. So, but yeah, if that's what he wants to do, that 68,000 is definitely, I did do a little 68,000 program, and it was definitely a clean CPU design. Java runs at good speed, whatever. Is there a speed difference between C++ and C? No, not really, unless you use C++ badly, which everyone does. Not everyone does, you don't use it. Like none of what I keep calling our peer group does. But if you do OOP, you probably hit a speed difference. You definitely hit a compile speed difference, which I probably care about more personally. Yeah, compile speeds are a big thing that people don't really take into consideration. Turnaround time is like directly correlated with happiness. Have you ever looked into some of the newer, lower level languages like Rust or NIM? And if so, what do you guys think of them? I have not. Not really. Just reading things that get posted to Hacker News, get halfway through and then be bored. Somebody's asking about what graphics cards we use. I don't even know and I don't care. Some NVIDIA because N site's nice. Somebody's being helpful, but you tagged me and I didn't need to be tagged on it. Oh, now here's a long block without any question. Okay, here we go. I'm going to, once again, I'm just going to say these and reject them, but then you're free to not unreject them if you want to. How do you feel about C++ auto? I don't care because I don't program in C++. I think it's pretty bad. I think it solves a problem that C++ has, which is an artificial problem created by C++. Crazy template, crazy template, definitely. Well, I mean, just don't have that problem and you don't need it. Well, so here's a thing I will say is that Jai has type inference. And from using ML, which is the sort of canonical type inference language back in the day, when I was trying to make a programming language for myself for small programs way back before I made STB. IH, as I mentioned previously, one of the things that my language did have was type inference. Because I was like, yes, you can do this thing where the types, when the types are simple, you don't have to explicitly type them. It's pretty obvious in the code what types there are. And so it's just less code to read. So it's probably going to be easier to maintain. So I did not think type inference was a bad idea back when I was writing that language. However, I eventually hit the 2x performance productivity in C without ever having to finish my language. So I dropped my language. So the type inference that Jai has is perhaps, that's part of the reason why I might think Jai is a better system for programming languages. It does have the type inference. It is optional. You can have full type notations. I'm concerned about the regressness of his type inference because real type inference is a hard problem. And I don't know how he approached it. And I'm concerned that the definition of his language is going to be whatever his implementation does, rather than it being definable separately. In specifically in the case of type inference, maybe he chose his type inference in a way that was very explicit. Like C++ has a very specific type inference rule, whereas ML has a very complicated type inference rule. Well, not very complicated. There's this whole thing called unification where you have to make a tree of types and map this graph of types. And you have two graphs of types and you have to map them to each other and resolve the types in them. That's part of the ML type resolution process. Which is why you can get grotesque type errors from ML. And even though ML is basically a very simple bottom-up type inference. And C++ tries to use a very, very simple rule and avoid that stuff, as my understanding. And I assume that's what he's doing in Jai, but I've never looked and I don't know how easily the type is inferred from the right hand side. The type of the left hand side is inferred from the right hand side. So that's a thing to be cautious about in type inference. But it is a thing I do not object to in principle. And if that problem is avoided, if you have a coherent rule for how the type is inferred that is objectively and separately defined from a particular implementation, I think it's probably a positive. There is a downside. I think it's only a positive in... I actually don't agree that it's really that big of a positive. The only positive that I see is it's less typing. But in C, or C++, you still have to type the word auto. So I actually don't think it's actually that. Well, yeah, and I'm only talking type inference in general. Yeah, in C++, it's an advantage in C++ because it's solving that problem that C++ induced on itself, the ugly template stuff. Which the auto is way shorter than the type you would have to type. So clearly it's valuable for C++ if you're coding that way in C++. But once you're not coding in C++, that way it's not that valuable. In Jai, it does save, it's a single care. It's simply the omission of the type. If he sticks with the syntax, which was a syntax I suggested, because it came from my language that I had written for this, you emit the type literally and you don't put any extra characters in. Well, just the colon, right? Yeah, well, the whole thing is that you need the colon equal anyway in the syntax and therefore you leave out the type between the... Normally the type goes between the colon and the equal. By simply emitting it, you get the type inference. So... Yeah, I think that's great. So yeah, I think that's fine. The only question is, it does have a possibility on the maintenance that when you're looking at code, it's harder to understand the code because you can't see the types there, right? And I feel like in a lot of circumstances you know what the types are and it's straightforward, but I could be wrong about that. It could be that when you come back to maintain that five years later, you really have preferred to have explicit types. But I do think it saves more than just the typing. It's when you're reading that code, if you can tell easily what the types are, when you're reading that code there's just less stuff going on visually and it's easier to read the code. That's my feeling, is that it's more than just saving the typing. But who knows? All right, are we done with that? Should we move on? Yeah, it's pretty simple. The Twitch web client is being really shit on me. I'm looking at the scroll back and it keeps scrolling, it's supposed to be paused because I'm scrolled back in the scroll back and it keeps advancing it anyway. Do you have focus on that sub window? Yeah, I don't know why it's doing it. It's just I'm losing my place. Like, what, why? Maybe you're all the way back and it's scrolling off the screen now. No, it's not, it's just, I don't know what it's doing. Okay, there it was. How do I feel about C++? Okay, here's a big question, let me skim it. I don't care. Okay. Although he ended it with pound feels bad, man. Just so he knows that that's the question I don't feel like answering. Okay, that was a whole bunch of commentary and some stuff we talked about half an hour ago. Okay, maybe people haven't been asking questions lately. Rigorous, Fabian said no, directed at me, but I don't know what he was directing it at and it was probably 15 minutes ago or maybe it's five minutes ago, so. Oh, actually, there's one more thing I just thought about now that sort of ties together a couple of topics we have. Oh, I know what it is. It's what the fourth book of of your program is. It's Cominatorics is what the fourth one is about. Let me just finish catching up. I'm almost there. Okay, okay. I'm always caught up to now. I think the original Mac was 65, 816, not 68,000. Yeah, we met the 68,000 family. We didn't really, really trying to name the processor. Bunch of them. Which of you is nothing's two and what is the Twitch name of the other? Sorry, so nothing's two. Somebody already answered it below that. Oh, okay. Then I don't care. It was important, but if it's already answered there. Where are you? Yeah, yeah. It's what I claim to see as faster than C++ who gives a shit. Auto solves problems, these aren't, none of those requests were directed at me. Everyone's talking to Fabian. Okay, great. So what were you going to say? What were you going to say? So I was going to say, we were talking about, before somebody asked, we were talking about our peer group and in general people just say, just do the simplest thing possible, right? And then I got onto the, or we got onto the tangent about sort. And the reason why I had to write a sort is because an algorithm that I was using to solve some geometric problem required a priority heap. And I looked at it and I figured, well, you don't, I don't need a priority queue, I guess. I don't need a priority queue because I could just sort the list every time. And if I wrote a really fast sort, I could probably write a really fast sort in a little bit longer than it would take to write a reasonable priority queue. So that's probably a better use of my time. And then I could reuse the sort on a whole bunch of other things. So I did that, and I actually think it was the right, right choice in that situation. So, and that's another example of doing the simplest thing possible. Like you could use a priority queue or you could just sort every frame or as often as you need to. Yeah, I mean, an interesting, I sort of round about kind of do the simplest thing that I've done recently. Well, and in fact, like it depends on like how your priority queue is updated, right? You can just linear search, you can just store them all unsorted and linear search it. Like that, if you do it, if you do an update every time you do a, if like, if you're inserting and removing from your priority queue at about the same rate, then that means that you're finding the minimum element about as often as you're inserting into it. And if on every insert you have to do a sort, then you're doing an N log N sort out every insertion and removal. You may be just better off doing the linear search because that's order N instead of N log N, right? You might have even gone too far with that. It might be the simplest thing. I will say that. Not the way it was used, but yeah. Yeah, yeah, okay. Just as long as that's clear. Yeah, it totally depends on the algorithm. I just want to mention because that's an interesting case that I very rarely implement the heap style priority queue but I have implemented it. Iggy2 has that implemented. Was it for the triangulation? No, Iggy2 has it for animation, for once the next animation event that I need to update. Because I use it for my triangulation, which is why I asked. Because that's a data-driven problem that is data set dependent. So different artists can produce vastly different sets of data. So I don't know, like somebody might produce a thing that has 10,000 pending events and that they're updating constantly. I don't, I can't predict it. And so there it's worth, well, let me go ahead and deploy the right, the good data structure for this. The thing is that the place where I encounter the priority queue the most is in doing pathfinding or pathfinding style operations. A star search, breadth-first search, that kind of thing. I guess breadth-first doesn't have a priority queue? Yeah, it does. Yeah, it needs a priority queue if you have different weights. And because you need the decrease key operation is the important thing there too. That's Dijkstrich, right? breadth-first is Dijkstrich? Yeah, I don't keep track of the name of that. Yeah, there's a different name for like, depending on, there's like breadth-first is if the edge weights are all one and then Dijkstrich is this thing and then there's something else if you can have negative edge weights or some, some like that was that Bellman-Ford thing or what I don't even remember, right? Whatever. I just hand wave those all as breadth-first search. They're conceptually doing the same kind of thing. I just call everything Dijkstrich. So yeah, the Dijkstrich thing, there's this classic solution, classic trick that you can use which is if all of your edge weights are integers and they're all small integers, like bounded, you know that all your edge weights are one to eight, let's say, then you can keep eight queues, one for each possible integer weight at the wavefront. So you're like, currently your distance 13 away. And so that means any node out of your node that's at cost 13 is going to be 13 plus one to 13 plus eight is the possible range of them. So actually you need nine priority queues. So you need a priority queue that has a priority queue or it's just a stack, a stack, whatever. You don't care about the order. So you need one that contains all the 13s because 13 is what you're currently processing. And then you need all the ones that have the 13 plus one to 13 plus eight. So you need 13 up to 21. So you need nine stacks. And those are just order one operations. You just push on whichever one. And then once you've consumed all the 13s, you move on to 14 and 13, it's a ring buffer. The 13 now becomes the 22s. So you have a ring buffer of arrays or of stacks. And this doesn't meet the order, the big order N for the general problem. Like if you have floats, you can't do this. Or if you have really sparse, like if the range of your integers is one to 100 but your entire data structure only has 100s in it, then you're gonna have all these empty arrays from zero to one to 99 is gonna be empty. You're gonna have zero to 100, 200, 300. And you're never gonna hit any other ones. But you will have to traverse them all, looking for what's the next, once you've exhausted all the zeros, and you're like, okay, now let me check the ones. Oh, the ones is empty. Let me check the twos. Oh, the twos is empty. And you go all the way up into 100. So as the range of weights grows, you are paying some time. So the cost of the operations on this is it's order N, for N priority queue operations, it's order N plus or it's order N plus M where M is the size of the weights, or something like that, right? There's, if you allow the weights to increase, the thing gets harder. None of that matters in practice. So like the classic thing that I do is I have a grid that I wanna path find or breadth research on. And so I want the orthogonal steps to the cost one and I want the diagonal steps to cost 1.4, right? Like that's the 1.2, square root of two, right? So you can approximate as 1.4. I go ahead and approximate as 1.5. And so once I've approximated as 1.5, I just double them. So my orthogonal costs are two, my diagonal costs are three. So now I just have two different integer weights and I can apply that thing I was just saying and I need four arrays and they're all order one operations. So they're faster than the heap priority queue, right? They're the heap priority queue is an N log N cost log N per operation. And this costs order one per operation. So there are a lot of places where it turns out that the theoretical priority queue isn't what you want in practice. Right, actually that's another, that's something interesting and worth I think talking about briefly is Casey had, well, Fabian's here. He had Fabian and Jeff on, he released that video from Handmaekon and then he had Charles last week and they were talking about, they were talking about compression and basically all compression always comes down to is understanding your data and the probabilistic model of your data. And I think that's actually the case for all algorithms in general, right? Because like the priority queue example, like my implementation was just to do the quick sort and it ended up being American flag sort or whatever I did the sort because I knew how often I was gonna be sorting versus looking up and I knew what my data range was and I knew all of these things, right? And you just gave the example of using the ring buffer of queues over, or a ring buffer of stacks over a finite range because you know what your data is and you can actually make a much, much better decision about what algorithm to use just because you know like the distribution and the sort of statistical properties of the data. And I think that's something that's actually really important and not, at least I've never heard it codified or talked about very much is that understanding the statistical distribution and model of your data is more important in almost all algorithm decisions than what I could, like this general like, oh, you're doing a priority queue, it should be a heap or whatever, right? Like it's always better to, at least what I have found and not talked about a lot, it seems to be always better to understand the statistical model of your data and then write something that's appropriate to that. And when you start doing, like what you were saying, you wrote three different hash tables for AGI, it's like, yeah, I rewrite my hash table, not anymore because I have the policy-based one, but I rewrite my hash table all the fucking time or used to just because this one I wanna use, you know, open addressing or this one, I can pack the index lookup in the high bits of the key because I know how big it is and I can use only a 32 bit key or whatever. And there's all these little things that I would do on every single time I would implement it, I would just do something a little bit different because I knew the data a little bit better. And I think that's something that isn't really talked about a lot, at least I haven't seen anybody talk about it, have you? Well, I think in our, again, in our peer group knows it, so it's probably talked about in Handmade Hero, it's probably talked about in Handmade Con. Like there's definitely people who know this and it should be talked about, but yeah, the academic delivery doesn't talk about it. I did have this discussion with somebody in one of my ACLU Hangouts. I think it was there, it might have been on Twitter, which is I want to resist that the way you expressed it is what I think of as an overreaction. I think you need to know both. I think you want to know about discerning of data and you do want to know all the academic theory, explicitly what this guy said was like, the big O stuff doesn't matter. You know, the big O was being the academic way of talking about the reforms of the different algorithms, data structures. And the thing is it does matter. Like if your end gets big enough, the argument, and I do see this all the time, regular blog posts just from regular Joe Schmo web programmers or whatever, it's like, hey, pay attention to your end because if your end isn't that big, you don't need to do all this crazy shit. Like not everyone, like plenty of people are dumb about that, but there are perfectly normal programmers who have noticed this problem and are aware of it, which is like, yes, that if your end is small, you don't give a shit about the big O. But I, and so all I'm saying is one step further past that, which is like, you need to know about your end and when your end gets big, you do want to know the academic shit. You know, you in the end, in the long run, you always have to measure, when you care about performance, you basically nowadays always have to measure the performance of your program. Somebody was asking, somebody asked a question that I skipped, which was about how do you measure cash behaviors and stuff? And you know, I've bemoaned this, I've gotten out of optimization programming because it feels to me a lot of the time, like what you do is you write it three different ways and you measure them and whichever way is fastest is the way that's fastest and you can't predict up front which way is going to be fastest. First off, like the cash thing or whatever. And you know, one of the things of that is like the array of structs versus structure of array stuff, it can be a pain in the ass to write stuff both ways unless you're using Jai. And you maybe don't do that work very often. And that's kind of why I've stopped thinking about optimization as much, why a lot of the work I do doesn't involve organization is that that's just so painful. In the old days, we could actually kind of model the problem and have some idea. It's like, well, I know this is like gonna take like 10 instructions, so it's gonna be like 20 cycles per thing and it's got one memory operation in there that slows it down a little, but it's consistent or whatever. And as the caches have gotten more and more complicated, that stuff comes harder and harder to predict. In micro benchmarks, you can look at and go, oh, well, I know this is gonna touch this many cash lines or whatever, but a lot of the time it really doesn't work. I have a question for Fabian if he's listening then. Because I'm in the same boat as you, right? Like, fuck it. Like thinking about like how the cash is gonna behave or how things are gonna be scheduled in different ports or whatever or how things are gonna be pipelined. Like, I don't care, right? Like, there's no fucking way I can keep that shit in my head and actually reason about code. But can somebody like Fabian actually reason about looking at C code? I guess you'd have to look at the assembler. If you look at the assembly output. Sorry. Yeah, he had to do that for the Oodle compression. Like, I don't know if you were paying attention to that, you know, we announced these compressors where we're like, oh, we've really pushed the speed compression trade off. Right, but can he reason about the code? My question is, can he reason about it? So yeah, he goes to the assembly and reasons about it. And it's very hard because there are many critical, many independent systems in the machine which could all be the critical path. And so he has to reason about that and figure out which one's the critical path and then figure out how to optimize that. And some of that maybe by using V2 and A kinds of stuff to measure the numbers. I don't actually know to measure the actual counts. And some of it is V2 and A style analysis where it just looks at your thing and tells you what the interactions of the things are. But I don't really know much of the details, but I know he's done that. He's talked about doing it. He's like, oh, I figured out that this processor claims to issue three instructions per cycle, but it can only retire. This is totally wrong, but whatever. But it can only actually retire two instructions per cycle. So it's the total fiction that it can execute three instructions per cycle because it can only retire two anyway. And that's not actually what it was. I'm super oversimplifying because I don't remember the details and even if I did remember the details, it would involve some parts of the chip that most of our listeners have no idea about. So even that, I don't even know if they know about the retirement versus whatever. But anyway, he'll probably correct it in the chat. But even if it doesn't matter because my point was that's not exactly what he says. But like he has that kind of encounter. In trying to measure it, he realizes that the documentation is not actually, is misleading or there's some undocumented thing that they haven't mentioned that he's able to get to that level where he's like, why is this thing not taking the number of cycles that the thing predicts and narrow down that there's some architectural limitation that's not documented? So he's definitely doing some of that stuff. But yeah, I have no connection to it and don't really know. This is just like discussions over lunch and occasionally tweets, this stuff shows up. But I have lunch with him a lot. And so I hear a lot of these stories at lunch. Yeah, I find it like it's interesting. It's like, I used to do like the crazy optimization stuff like back in the nineties, right? Like that was the thing, like how fast could you get this thing to go, right? Now it's fast enough. Fuck it. And now it's too hard. I can't even reason about the CPUs anymore, right? Like I have no fucking idea what's going on. Yeah, he just described the thing I was describing but he just described it in a general way instead of trying to use a dumb example. Yeah, I read what he wrote. But I wanted to repeat that out loud for the archive. So here's what he says, I need to do some data gathering but mostly I can tell from looking at the assembly code how long it should take. And then it's usually not the result I get. And then I figure out why it's not the result I expect. So I refine my understanding of the code and my understanding of the processor until prediction and reality agree. So there you go. It's a lot of work for on modern CPUs and modern systems, very little gain. Yeah, well, the thing is that depends on what you're doing, right? But for our case at least. Yeah, no, exactly. I was gonna say like, let's say he does this and he's able to get a loop to be 20% faster. And maybe that makes the whole compression, decompression step 10% faster. And then you drop that in a game and that makes your load time 2% faster. Nobody gives a shit. But when we are selling that to game developers, we can say our compressor is 10% faster, like or decompressor is 10% faster. That's a substantial thing that they may take seriously. They probably won't do the architecture work necessary to expose that to their user, but that's their fault, not our fault. But yeah, if I were writing a game, I would never put in the work necessary to get a 10% improvement in my decompression time because I know it's not gonna show up to the user. A 10%, a 50% reduction maybe is worth it. It's all a trade off. Yeah, okay. He says, it's actually bigger than 10%. He says he got a 1.5x gain. So the speed went from 100 to 150, I assume is what that means. So which, you know, you can view as like, if we're talking about time, that would mean the time went from one to two thirds. So it's a savings of 33%. And a 1.25x out of over, oh, no, sorry. That was the sub part. It was 1.25x of whole code, codec perf. So that's five fourths, invert that those four fifths. So it's saving 20% of the time. I said 10%, so, you know. Well, I mean, that's quite, if you're like a triple A game and your low time is at, you know. Yeah, yeah, if you're a triple A game, if you're triple A game and your low time is at a minute and that's because of your decompression or you're not doing any compression and you can drop our compressor in and that doesn't slow you down. Yeah, it's definitely worth it. And the triple A game people are the people who will pay enough attention to this to get it right unless they farmed that part out to an intern for some reason. Probably the parts that Oodle is facing are not intern facing. A lot of the things that Rad does are intern facing which have different constraints. And are what I'm interested in. Well, what I actually, I find interesting is that Oodle's not trying to sell to Facebookers or they're not trying to sell Oodle to Facebookers or something like that because that's where it would be huge, right? That's where a one or 2% gain in, which is I guess why like Yann Collette is there. It's like a one or 2% gain in compression is like massive when you're storing petabytes of data. Well, so our business model normally for selling these things is per product or whatever. And so it doesn't translate to that market very well. But I do know we did look at selling it to somebody. I don't know. So I know of one thing. So for all I know we did try or are trying to sell it to Facebook. I know of one large company that we did talk to them about selling it and I don't know what ever happened with that. It like might still be in progress or it might have died, I don't know. Yeah, it just seems like that would be a really good case for it, but I don't know. And I was vague about that because I don't remember whether we talked publicly about it at all. I assume we didn't, but let's see. Okay, so there was one question that I skipped way back in chat that I didn't mean to skip it. I was like, oh, I'll come back to that in a second and then I forgot to come back to it. It was a question I didn't quite understand what he was asking, but it wasn't so far removed from reality that it wasn't worth trying to figure out what he was asking. Okay. I mean, it was just like he phrased something awkwardly or something, but I assume we could decipher it. Where was it? I don't know how far back I need to scroll. I guess I need to scroll back farther down. What headset am I using? I'm using some Bluetooth headphones and a high quality music microphone. It's not a headset at all. But they're like stereo music headphones rather than like a headset style headphones or whatever. All right, still scrolling back. I swear, I swear I saw this, what? All right, well, I'm all the way back and I never saw the question I was thinking of, so I don't know what. Somebody just said LZMA is terrible for every case I've seen it in games. I don't think LZMA is terrible. It's decompression is pretty slow, but it's just really good compression ratio and open source and the library's actually quite nice and it's public domain and all that fun stuff. What would you use it for? I wouldn't use it because I just used broadly, but asset compression. What, compression? Asset compression, like large asset compression. But you don't think the slow decompression would be a problem? Not if you, I mean, I don't know, I haven't benchmarked it versus loading raw data or whatever, but if you just decomp, it's like if you're, if you're distributing a download game or you're downloading like an update or like what I'm doing where I'm like streaming content off the internet, I care about download like file size a lot, right? Cause I could just decompress it and then save it, save the decompression once, right? So at LZMA is reasonable or quite good. So for the, so the download, so for the case where you have a download and then install step and have your installer decompress it. Yeah, yeah. Or it could be the first, sorry. Or it could just be the first thing you, the first time you run, you do it, it's the equivalent. Yeah, yeah. But I just wanted, but so you could argue that, well, and a game installer is not what he was talking about by in games, but you know. Well, at the moment, the largest thing I'm using right now is actually uses bitnit compression because it's granny files, but yeah. Which is fine. It seems to be working well. How can this, I swear, I should not, I swear it was not before these. It was definitely after these. So I must just be looking for the wrong name when I'm skimming this. Or else he didn't tag me and I thought he had tagged me and I'd seen a question that wasn't tagged cause now I'm only looking at the tagged. When I definitely skipped intentionally, or he got banned, I guess he could have gotten banned and now the question is gone. It's conceivable. There's just no questions at all in that range. Well, there's two new questions. All right, well, go ahead. You can read them. I don't have to do. I'm reading this one, but I don't think it, I don't have anything to say about it, but maybe you do, probably not. What are your thoughts? Well, actually no, you do have something to say about this, because this is something you've talked about. What are your thoughts on AI navigation pathfinding versus physics-based movement? And how do you reconcile the two devoid scenarios like monsters not being able to path to a player? That question, that part of it, I don't think. Like the physics-based movement thing is something you've been working on, right? I can't see it, and I just found the question that I was looking for, so. Okay, go ahead on that. Okay, so should I do this one first? Okay, so I think this was the one. Yeah, the question was, has the STB library style influenced how you structure file layouts for non-library game-specific code? So I think what it means is when I'm writing a game, do I lay out my files differently? Do I put things in different places in files due to what I've learned about doing single file libraries? I'm sorry, I don't even understand your interpretation of the question. So, let me go specifically. Okay, so OBBG has two header files. One header file has all the functions and one header file has all the data. And you could argue that maybe the single file library style has, that's an experiment. It's an experiment I'm trying. I'm like, oh, let's see what happens, but all the functions in one header file, all the data in a different header file. And you could say maybe the single header file library idea is what prompted me, or contributed to me, design, oh, let me just try piling all that stuff like that. That's how I'm interpreting that question, is that that's the kind of thing he means. Okay. So, that's an answer. Well, there you go, that's the thing I'm trying and maybe that was influenced by that. I don't use header files. And I don't really, I just, the way I think about files is stuff that's kind of related together. And I don't care where things show up in files because I just use Visual Assist X to jump around. So, files are completely meaningless to me. If I could put everything in one file and it didn't bog down Visual Studio, I would just do that. So, what do you mean by you don't have header files? So, you're doing a, and include all of those C files into one. Yeah. Yeah. You need to build. It's so much faster, it's crazy. But then you, but you don't put everything in a single file just because of the IDE problems. Yeah, at about 15,000 lines, Visual Studio craps out. Okay, because that answers a question that somebody asked in the, originally on Twitter that we skipped about doing all the programming in a single file. But when you use Visual Assist X, like they're go to and then they're go to function and then go to definition and declaration. Once you like master how to use that, you get around, it doesn't matter how many files you have, you just get around your whole code instantly. Yeah, it makes sense. So like putting everything in one file would be ideal, but you can't. All right, so there were the new questions. So, was this the best practices? Is that the question you were? No, I didn't see that, but go ahead with that. Well, what was the question you read? It was about physics-based movement. It was like, what are your thoughts on AI navigation, pathfinding versus physics-based movement? But I don't think those two things are. I'm just not seeing it. Versus. Oh, it's because he's doing the Q format, okay. Oh, yeah, yeah, yeah. What are your thoughts on this? How do you reconcile the two to avoid scenarios like monsters? Oh, it just scrolled away on me. Fuck me. Well, I think the two things that he's talking about aren't contrasting with each other, but like he asked about physics-based movement, which I'm kind of curious about how your little experiments, because you're doing some experiments with that, right? So, I've never seriously done enough physics-based thing. Well, that's a suckwork kind of is, but then it didn't have any pathfinding. So. Yeah, I don't think the pathfinding has anything to do with it, to be honest, but. Well, no, so maybe there's two separate questions. Like the pathfinding thing, the interesting thing of the pathfinding is what I call path following, which is that you've set a path and now your physics system has to somehow follow the path. And the way to cheat that is you don't use the physics system when you're pathfinding, path following. You just move the thing along the path. And like when you write a 2D tile-based game, you just do that. That's how a 2D tile-based game works. You find a path that's a sequence of tiles, and then you move the guy along the sequence of tiles and there's no physics at all. Like maybe you're allowed to lock a door on the guy, and so you have to check that whether the path is clear and that you could think of that as physics, right? But it's not really. And so right now in OBBG, there's these characters you can drop in the world and pathfind, and they just move along the path without running physics. Well, what do they do? Do they run with physics? I don't remember now. I don't remember what I did for that. I think they do run with physics. I think they just try to turn towards the next point. But their physics is not, they're running in the physics system, but they're directly controlling their velocities. They're not trying to accelerate and decelerate, whereas, so like in System Shock 1, we had this physics system and the creatures, and I think Teranova had this too. Creatures had controllers on their physics, and so they would not have instantaneous accelerations. Like they had to come to a stop, it was very quick. It's the same as the one that the player used. So it had the feel of the player controls, but they had to come to a stop and they had to accelerate. And that actually interferes badly with the pathfind. If you're pathfinding along a narrow ledge and you just try to follow the path and you don't know there's a narrow ledge there and you like, oh, you came in from this direction so you accelerate and you try to get on the path and you're a game programmer writing this thing, so, oh, it overshoots the path, and so now it steers back to the path and eventually converges onto the path and it's like a narrow ledge and you fall off the ledge when you try to pathfind along it because the game programmer wrote that pathfinding. So there's some interesting thing that goes on in there. And if you cheat and you don't use physics or you do this direct velocity physics and you just go along the, you turn off physics and you say, while I'm following this path, I'm turning off physics and I'm just going along the path, you get the perfect pathfinding. But it's not really shippable. It's what's in OBBG sort of, but it's not really shippable like they're not, if you do literally what I just said, then they don't collide with anything. If there's another character on the path, they don't collide with them and that wouldn't work. So you really have to run the physics there. And so you just need a controller in there that tries to follow the path as well as possible. And so there's an interesting problem in the sense of if you look at what I'm doing at OBBG, I'm just ignoring that problem, but there is a huge problem that I haven't solved just because there's not enough else going on in the system for it to matter currently. The characters don't collide with each other. So it doesn't, there is no character versus character collision at all. So there's nothing, there's no reason they can't just follow the path. Make sense to me. I ain't got none, dad. Okay. So I'm just going down for more questions, but we're at near the end I think here, right? Yeah. I could probably do one, one more question. I got like 10 minutes. Yeah, that was my question. Pathfinding with 2D tiles, you can just cheat, but in 3D environments that would potentially look quite weird. So there's another thing that goes on there, which is that a quality improvement in 3D is, and in 2D, if you have full continuous motion, like in 2D you can have full continuous motion. You don't have to be locked to tiles. And if you path find like I'm doing, everything moves along these orthogonal or perfect diagonals. And like, so if something is three units to the right and five units up, and it's on an open thing, you'd like to be aligned directly at it. You don't want to go part way diagonally and then go straight up to it, which is what my system does. And nobody has a good solution that works to this, but nobody has solved this problem. So the normal thing that you do is you do raycasts, physics raycasts is what you think of the masses, like, can I move unobstructedly in a straight line between these two points? And you take your current point and you take all the points that you have in the path in front of you and you say, can I go directly? So I skip my path and just go in a straight line to the thing. And, excuse me. And if the furthest query that returns true, you do that. And then you do that at every point that you step. And so over time it's an n squared thing. You're doing an n squared set of tests, trying to find that. So people use nav meshes instead. And there's this very smart thing you can do with nav meshes, which is when you return the path result, you return all the polygons that you cross on the path. Rather than like a line, a sequence of points, you're actually returning all the ground polygons that you traverse. And those ground polygons tell you how big the ledge is, how big the walkable space is. It might be a ledge or a wall or whatever that defines the edge of that walkable space, but you know that you're allowed to go on any line over those polygons. So now you can do your shortcut raycasts directly on these polygons in the nav mesh. And those are typically gonna be much simpler. They're not the world geometry, you're not doing a physics raycast. You're doing this simplified walkability polygon mesh that you're doing this 2D mesh that you're doing these AI raycasts, walkability raycasts on, then it'll allow you to do these direct shortcuts. But you still have to do those casts, like nobody has a good solution to that problem. And even the nav meshes, I critiqued the, what's his name, Mikko Minonen raycast? Recast? Is it recast? Is that the name of his? Yeah, so nav mesh library. And it starts by dividing everything up into little squares and then merges them back together into polygons. And then there's a runtime thing that does the thing I just said and returns to the list of polygons and stuff. But he had one demo and I was looking at it and I was like, these guys take a suboptimal route. And he's like, yeah, that's, eventually when we drill down into it, I mean, in its blog post, he's like, yeah, that's the nav mesh. The nav mesh happens to have a narrow polygon here and a wide polygon here. And when you try to do the A star, you have to assign some weight to it and it computes the wrong thing. And it's this obviously suboptimal path. And I'm like, oh, okay. So nav meshes aren't the perfect solution that everyone says they are. You can still get the bogus data from them just like you can get from the grids, from doing grids. So that's why I've always stuck with grids is because it's simpler to stick with grids than to build the nav mesh structure. And it's more data to process. So it's going to be slower to path find on. But they all have feelings. So I'm like, whatever. All right. Hey, we need one more. Yeah, yeah. All right. So there was... What is the biggest mistake that unexperienced developers do? I don't know. I don't know what inexperienced developers do. I have no answer to that. Okay. No. I don't know. Not writing code. Are there best practices for how not to get in the way of how highly pipelined or out of order modern CPUs are? Don't worry about it. Yeah. Yeah. I mean, their job is to make your C++, your bad C++ code run fast. All modern architectures are designed to make your dumb shit go fast. So, unless you're Fabian, you don't worry about it. Yeah. Like there's like five people in the world who actually give a shit about that stuff. You don't need to be one of them. I think it'd be fun. Like it'd be a fun thing to work on, but like it's just, it's not, if you're writing in a game, it's not practical to worry about that stuff. How do you handle collision physics over every object one by one that affects physics, then one goes before another? Then how does the other object know to apply collision physics? How do you deal with this? I understand what question he's asking. I don't know if you do from that. So, I do, because I'm insane, I decided it would be fun to multi-thread the shit out of everything. So, what I do is I do a big threaded update that does all the collision stuff, and then I append to a list all of the collisions that happen, and then at the very end of the, at the end of the multi-threaded simulation, I pair the list down for when duplicate collisions happen, and then I just go in arbitrary order. So, the collisions will happen to two objects at once. But so now, if you resolve collision of two objects, and one of them bounces away in a new direction, do you just leave it for the rest? And that should have happened halfway through the tick. Do you just leave them in that spot, and then on the next tick, they'll start from there? Yeah. Yeah, okay. So, the one of the things that you can do is to try to make your simulation correct, which is what a lot of people worry about in physics research, is you have to go, well, now that object needs to bounce back in the other direction for half of the tick or whatever. And I've talked a little bit about that. If you're trying to do variable time step simulation, which is what we all used to do, or a lot of us used to do back in the software rendering days when it was very unpredictable how long your frames took, and every cycle counted, and you didn't wanna do multiple simulation steps. And you want the game to run about the same no matter how long the tick is. You actually do have to start worrying about that stuff. And on thief, I'm pretty sure in thief, we took all the objects, figured out when all the collisions were going to be, and then put that in a priority queue, took the first one and resolved that collision, and then took the objects that were involved in that collision and recollided them against everything else to determine why now their newest collision was. And that could change the collision times of other things. Other things might no longer collide with them, and add that to the priority queue and then cycle through the priority queue and do that stuff. Most of the time you have no work to do because you have no collisions. Most of the time you get one collision, or most of the remaining time you get one collision and you have to resolve the one collision and none of this matters. So like this system works fine for the kind of game physics we had in thief. We weren't stacking 5,000 objects where everything's colliding all the time. One thing is that you have to make them not collide all the time. You have to actually, you know, real physics and you need to say they are in contact and they're no longer triggering collisions than this kind of system becomes viable. But if they're actually constantly triggering collisions that the way you solve, resolve contact is by having everything always colliding and detecting and responding all the time, you know, and microscopically sort of, or with an implicit buffer around them or something, then that kind of system is probably a bad idea because you're probably going to do a lot of work, do that work or don't do it. In games, but I have talked about how I do sometimes do that kind of thing in 2D games where the character bounces off a wall or whatever. I go ahead and like, you know, I'll simulate or I think I do this in the OBVG player sim when the player bumps against the wall and now pushes along the wall. I go ahead and simulate what's left of the tick pushing along the wall. And I just, it's just like, ah, you know, I might as well. But what I don't do is resimulate everything else. I just do the one at a time, you know, a lot of games I just do the one at a time thing. That everything else is frozen, this object moves for the entire time. Yeah, I do that too, for statically. And that's just not correct, but it's not the way it's incorrect doesn't matter. Nobody like the kind of areas you're going to get from that are too subtle to matter. Like if you had two bullets going perpendicular and you'd set up their guns perfectly, like they're matched guns and you trigger them both simultaneously. So the bullets ought to collide with each other in midair. Like that case might not work, right? Like that case where you set up that system, it's going to move them all independently and they move forward far enough in a frame that they just miss each other. And I'll miss that case. But as long as I don't make a game where you can set that up, that experiment up and expect it to work, that case, it's hard to find places where that case matters. At least that was my opinion, especially in 2D games. I'm just like, whatever, just make it all consistent. Yeah, my opinion is just whatever. And if it doesn't work, just double the collision frame, just double your physics step, like do it at 120 Hertz. If that's not good enough, do it at 600 Hertz. It's like, it doesn't matter. The kind of case you can run into that I don't handle is like I bump into this block, which pushes the block, which runs into a wall, and so the block can't move anymore, but my character doesn't know he shouldn't move anymore. Or you're pushing two things, a chain of things. And I just don't make 2D games where I have chains of things that way, where that matters. And when you're writing a real, that's normally what you call writing a real physics sim, is starting to worry about those problems of chains of contact and chains of collision. And that's where you normally bring out the big guns. And I'm always like, yeah, if I ever really needed physics in a game, I would get bullet or one of the other free libraries and try to use it. And I probably hate my life because I can't make it work in PC6, but. You're module. Sorry, are you done? Yeah, yeah, I'm done, go ahead. Oh, I was gonna say, I find that this sort of pattern comes up a lot, actually, and not just physics simulation, but like this sort of question of, what's the correct way to do it? It's like, don't do that, right? Like it does, like just don't, just do the easiest thing possible, like literally, like you shouldn't ever need that level of physics sim for a game. And if you do double, like I said, double your physics decorate, right? Go 120 hertz, 240 hertz with six, dyad did physics at 3000 frames per second, just because that was the easiest way to do it. Which is interesting because that's also John Blow's proposed solution from writing your game to be future-proofed or even today-proofed of being able to run at on a 120 hertz monitor and a 70 hertz monitor. It's like, hey, like the greatest, the least common multiple of 70 and 120 is fairly big, seven, it's 120 times seven. And of all possible frame rates, all monitors might be in the future. And really the solution is just run at a high enough simulation step for the whole game simulation, not just physics. A high enough simulation step that the fact it doesn't divide perfectly just doesn't matter. It's, you know, as long as you're getting like 10 similar, if it should be 10.3 simulation steps, so you end up getting 10 or 11 steps every frame, that's probably not gonna be too objectionable. Certainly once you get to 100 versus 101 steps, that's totally, you don't, the fact that your character's movement is slightly varying depending on whether it got the short from the long frame just isn't gonna matter. I don't know if 10 and 11 is actually large enough, but the difference there might be too large, but. Yeah, and if you're doing, that's another advantage to see, you're just trying to multiply, do more. You're doing things fast enough. One, like I said, I have that game where I'm simulating 40,000. Well, yeah, why did you do 40,000? Is like, did you try doing it with like continuous, like continuous function physics? No, it's simulating multiple worlds is in a complicated way that I can tell you offline, but I don't wanna reveal it to the world. Okay. But like, did you try a like continuous function approach? No, I mean, it doesn't, it's not what it's, it's not the problem it's trying to, like imagine if you were trying to simulate, imagine that what it's actually doing is simulating a thousand worlds at 40 frames per second. Oh, okay, okay. Like there's just no other solutions. So as soon as we stop the stream, I'll tell you before, if you have time before you go, I won't, I'm gonna have to go. I actually have to go now basically. Well, remind me sometime and I'll tell you. All right, well, we're done there. So there you go. All right, guys, thanks for tuning in and we'll maybe do this again sometime, but not soon. Well, I think, do we, are we gonna try to do our plan? We proposed monthly, but I'm not gonna hold us to it. Like if in a month we don't feel like doing it, we won't do it. So, you know, maybe it'll be a month. We'll see. Okay, sounds good. All right, guys, thanks. Thank you very much. And this will go up on YouTube for those wondering. I don't, of course, if you're watching it now, yeah, if you missed the beginning, it'll be on YouTube. And you can just go watch the VOD right now. All right, bye-bye. Bye, see you later.