{"id":23605,"date":"2014-07-09T05:21:30","date_gmt":"2014-07-09T10:21:30","guid":{"rendered":"http:\/\/www.shamusyoung.com\/twentysidedtale\/?p=23605"},"modified":"2014-07-09T18:15:22","modified_gmt":"2014-07-09T23:15:22","slug":"project-unearth-part-5-speed-boost","status":"publish","type":"post","link":"https:\/\/www.shamusyoung.com\/twentysidedtale\/?p=23605","title":{"rendered":"Project Unearth Part 5: Speed Boost"},"content":{"rendered":"<p>Let&#8217;s talk about these speed problems I keep alluding to. The framerate is half of what it should be. I&#8217;ve mucked about, looking for inefficiencies. I&#8217;ve made some minor changes but haven&#8217;t seen the framerate change all that much. <\/p>\n<p>I suppose I&#8217;ve been spoiled by my days at Activeworlds. Back then I used <em>Microsoft Developer Studio 6<\/em> to write my code. It was pretty old (1998) but it was the Professional Edition, which had top-notch tools for profiling performance. You could just fire up the program, run it for a couple of minutes, and then it would show you where all the time went. <\/p>\n<p>This is far more accurate and convenient than manually measuring time from within the program as its running. You&#8217;ve got to put this timing code everywhere you want to measure, then you&#8217;ve got to surround it with additional lines of code to disable it when it isn&#8217;t needed. Then you have to run the program again and again, measuring performance and adding more and more clock-checks to zero in on the source of the problem. <\/p>\n<p>But now that I&#8217;ve gone indie<span class='snote' title='1'>SUPER indie. I&#8217;m so indie, I&#8217;m not even working on an actual game!<\/span> I&#8217;m using <a href=\"http:\/\/www.microsoft.com\/en-us\/download\/details.aspx?id=40787\">Visual Studio: Hippie Freeloader Edition<\/a>, which doesn&#8217;t include the profiler.<\/p>\n<p>This is an interesting look at how we perceive prices<span class='snote' title='2'>Or at least, how <strong>I<\/strong> perceive them. But I&#8217;m willing to bet I&#8217;m not the only one.<\/span>. I would say that $1,200, while <strong>steep<\/strong>, isn&#8217;t unreasonable for a robust tool like Visual Studio. You can use it to make retail-ready software. Companies can and do use this thing to make millions of bucks. But since Microsoft gives away the free version and the free version does nearly everything the pro version does, it doesn&#8217;t feel like I&#8217;m paying $1,200 for a development environment. It feels like I&#8217;m paying $1,200 for a profiling tool. And there&#8217;s no way that&#8217;s worth it<span class='snote' title='3'>Seriously. This is NOT me shaking my tin cup or beating around the bush for more donations. No matter how much money I had, I&#8217;m not sure I could ever bring myself to spend that much for the profiling tool.<\/span>. I keep hoping MS will discount some old version and I can pick up the pro edition for a few hundred<span class='snote' title='4'>I do see offers here &#038; there around the web, but they always strike me as being sketchy. It&#8217;s always some company I&#8217;ve never heard of and I&#8217;m always worried I&#8217;d be buying a bootleg version.<\/span>.<\/p>\n<p>The point of all this bellyaching is that we&#8217;re going to have to look for our performance bottleneck the hard way. But before we get to that, I need to talk about my normal map some more. You&#8217;ll see why in a bit.<\/p>\n<p><!--more-->Normal maps are kind of a pain to make if you don&#8217;t have the right tools. There are plugins for Photoshop and such, but I&#8217;m using Paint Shop Pro 8. Which came out in 2003. Which means it&#8217;s older than normal maps<span class='snote' title='5'>Not older than the IDEA of normal maps, but it certainly pre-dates the ubiquitous use of them in games by a few years.<\/span>. I don&#8217;t think I&#8217;ll be getting any normal-map plugins anytime soon. I know it&#8217;s old as hell, but it does exactly what I want and it&#8217;s already paid for. Also, it doesn&#8217;t have any DRM. I literally just copy it from one hard drive to the next when I migrate to a new computer.  And like I keep saying: <em>Convenience is everything.<\/em> <\/p>\n<p>But even if I had modern Photoshop and a nice plugin, I&#8217;m betting it&#8217;s a hassle to use them on atlas textures. The problem is that your typical atlas texture looks like this:<\/p>\n<p><table width='512'  cellpadding='0' cellspacing='0' border='0' align=''><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/unearth_minecraft_atlas.jpg' class='insetimage' width='512' alt='unearth_minecraft_atlas.jpg' title='unearth_minecraft_atlas.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>That&#8217;s the texture atlas for Minecraft. (Or was. I&#8217;m sure this version is ages old. It&#8217;s just one of the first results from <acronym title=\"Google Image Search\">GIS<\/acronym>.) And if Minecraft used normal maps then it might have one something like this:<\/p>\n<p><table width='512'  cellpadding='0' cellspacing='0' border='0' align=''><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/unearth_minecraft_normal.jpg' class='insetimage' width='512' alt='unearth_minecraft_normal.jpg' title='unearth_minecraft_normal.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>I&#8217;m assuming that the average plugin doesn&#8217;t &#8220;get&#8221; atlas textures. It wouldn&#8217;t respect the boundaries between grid elements, which means textures wouldn&#8217;t tile right. (Or at least, their normal maps wouldn&#8217;t.) Instead it would do what we see above, which is create all these goofy edges around every element, making EVERYTHING behave like this tile with a steep lip. To avoid this, you&#8217;d probably have to put it together by hand. So if I changed the brick texture in column 8, row 1, then to update the normal map I&#8217;d have to run the plugin to make my little patch for the bricks, then copy &#038; paste the result into my normal map. <\/p>\n<p>I&#8217;m <em>sure<\/em> a proper studio with mature tools has a better way of doing this. But here at Twenty Sided we&#8217;ve got old ideas and older tools. We&#8217;re also cranky and set in our ways. So here is what I came up with:<\/p>\n<p><table width='512'  cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/unearth_atlas.jpg' class='insetimage' width='512' alt='Reduced. The original is much larger. You know, just in case I suddenly need to add a couple hundred more textures to the scene.' title='Reduced. The original is much larger. You know, just in case I suddenly need to add a couple hundred more textures to the scene.'\/><\/td><\/tr><tr><td class='insetcaption'>Reduced. The original is much larger. You know, just in case I suddenly need to add a couple hundred more textures to the scene.<\/td><\/tr><\/table><\/p>\n<p>This is the atlas texture I&#8217;m using now. You can see there&#8217;s not much in it. I&#8217;m not even sure where all the bits came from. I&#8217;m pretty sure this is mostly a leftover from <a href=\"?p=15742\" title=\"Project Octant Part 1: Introduction\">project Octant<\/a>. I don&#8217;t think this project is going anywhere, so I&#8217;m not in a big hurry to make a bunch of new graphics for it<span class='snote' title='6'>Note also that this atlas texture doesn&#8217;t match what was used to create the screenshots in this write-up.  I saved screenshots as I went, but I didn&#8217;t meticulously back up each and every iteration of my texture. So if you&#8217;re the sort of person who obsesses over little details like that then in this case, don&#8217;t.<\/span>. <\/p>\n<p>I take my atlas texture and make a &#8220;relief&#8221; version of it. It&#8217;s just a greyscale image where the parts that should stick out are white and the parts that dent inward are dark. <\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align=''><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/unearth_relief.jpg' class='insetimage'   alt='512' title='512'\/><\/td><\/tr><\/table><\/p>\n<p>Back in my <a href=\"?p=22995\" title=\"Frontier Rebooted Part 3: Act Normal\">previous project<\/a> I wrote a shader to take a heightmap (in that case, terrain) and turn it into a normal map. The exact same idea applies here. Instead of reading height values, I&#8217;m reading the brightness of individual pixels. The only change I have to make is have it respect the edges of each element. So when it gets to the edge of the first little square of texture, instead of looking at the neighboring element it will look at the opposite edge of the element it&#8217;s working on. That way the elements will tile. <\/p>\n<p>This doesn&#8217;t create <em>beautiful<\/em> normal maps or anything. These are kind of rough and half-assy. Sometimes the contours are too severe, or not severe enough, or the contours just don&#8217;t feel right for the kind of surface we&#8217;re tying to emulate. But that&#8217;s all fine. It&#8217;s bad from an artistic standpoint, but it&#8217;s correct from a technical standpoint, and that&#8217;s what matters here. <\/p>\n<p>I probably don&#8217;t need to post the final product. I think one blob of lavender pixels looks pretty much like any other. You get the idea. I run this shader on the relief texture at program startup to generate my normal map. Then I can forget all about it and just render everything normally<span class='snote' title='7'>Pun not intended. I didn&#8217;t even notice this until the post went live.<\/span>. <\/p>\n<pre lang=\"c\" line=\"1\">\r\nvoid DrawUpdate ()\r\n{\r\n  \/\/Convert relief map to normal map...\r\n  if (!normal_map_ready) {\r\n    ShaderDataNormal  s;\r\n\r\n    \/\/Load the shader data and switch to our normal-building shader.\r\n    s.texture_size = tx_terrain->Size ().x;\r\n    s.tile_size = s.texture_size\/16;\r\n    s.texture_heightmap = tx_relief->Id ();\r\n    ShaderNormal (s);\r\n    \/\/Render the result into our normal map and we're done.\r\n    RenderBlitFramebuffer (gl_normal);\r\n  }\r\n<\/pre>\n<p>Hang on a second here. I think I found my &#8220;mysterious&#8221; speed problem. <\/p>\n<p>On line four I check the flag &#8220;normal_map_ready&#8221;. If it&#8217;s false, then I run my expensive normal-building shader on the relief texture and continue on. Except, I don&#8217;t change the value of normal_map_ready, which means next frame we&#8217;ll do the whole thing again. And again.<\/p>\n<p>Head. Hit. Keyboard.<\/p>\n<p>Well at least we&#8217;ve figured it out. Too bad I wasted a lot of time looking for actual problem areas and overlooked this flagrant waste of power. I feel like I spent a bunch of time meticulously tuning up a car without seeing any performance improvements and then realized there&#8217;s a half ton of bricks in the back seat<span class='snote' title='8'>It&#8217;s been too long since our last Terrible Car Analogy.<\/span>. <\/p>\n<p>It&#8217;s a problem with my graphics card. It&#8217;s apparently bad at processing raw stupidity. <\/p>\n<p>So with one little change&#8230;<\/p>\n<pre lang=\"c\" line=\"13\">\r\n    RenderBlitFramebuffer (gl_normal);\r\n    normal_map_ready = true;  \/\/Hey dummy, THIS ONLY NEEDS TO BE DONE ONCE.  \r\n  }\r\n<\/pre>\n<p>The framerate shoots up to 60fps. <\/p>\n<p>A huge portion of my graphics muscle was spent repeatedly re-creating the normal map every singe frame. A giant 2048&#215;2048 normal map. It&#8217;s only got 8 texture elements in it, and the rest is empty space. And I think I&#8217;m only using two of them.<\/p>\n<p>Well, our speed is back up and we&#8217;re free to move on. Technically I&#8217;ve completed my major goals. We&#8217;ve made stencil shadows and bump mapping. The speed is good, even when doing a rough VR approximation. <\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/unearth_vr1.jpg' class='insetimage'   alt='unearth_vr1.jpg' title='unearth_vr1.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>Looks kind of bare. I mean, obviously. I haven&#8217;t done anything with it yet. But hey, I just recently made that <a href=\"?p=23354\" title=\"Frontier Rebooted Part 7: What Have We Learned Today?\">grass shader<\/a> and that worked out pretty nice. Let&#8217;s import that and just drop it into this project. <\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/unearth_grass1.jpg' class='insetimage'   alt='unearth_grass1.jpg' title='unearth_grass1.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>Eh. Better than a poke in the eye.  There are a few more experiments I want to do here before we move on. We&#8217;re not quite done yet.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Let&#8217;s talk about these speed problems I keep alluding to. The framerate is half of what it should be. I&#8217;ve mucked about, looking for inefficiencies. I&#8217;ve made some minor changes but haven&#8217;t seen the framerate change all that much. I suppose I&#8217;ve been spoiled by my days at Activeworlds. Back then I used Microsoft Developer [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[66],"tags":[417],"class_list":["post-23605","post","type-post","status-publish","format-standard","hentry","category-programming","tag-idiot"],"_links":{"self":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/23605","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=23605"}],"version-history":[{"count":0,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/23605\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=23605"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=23605"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=23605"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}