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.  Is it working? Is chat happening? Waiting for them to... Waiting for them to...  Have we come up with a language policy for this, uh, this little show of ours? I swear.  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...  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, you know, the main point of this is the audio, not the video. So, um, and I never look at my camera anyway, right?  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.  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.  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 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.  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?  And if it's just like a mess, then you know right away, you don't want to use the library, right?  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.  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.  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?  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.  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?  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.  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 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.  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.  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.  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 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?  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.  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++.  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.  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?  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'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.   Collusion detection, broad-phase collusion detection. Oh, I don't consider that.  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's still all pretty direct mapping.  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.  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.  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.  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.  So I changed it to Z-order curve addressing.  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.  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.  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 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,