From 2d741c272947fa677c82dbd76abcc3e9af1f0602 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Mon, 24 Oct 2022 11:04:36 -0400 Subject: [PATCH] Add drafted articles --- content/blog/camera1/index.md | 2 + content/blog/deepvulkan1/index.md | 113 +++++++++++ content/blog/deepvulkan2/index.md | 188 ++++++++++++++++++ content/blog/deepvulkan3/index.md | 30 +++ content/blog/deepvulkan4/index.md | 23 +++ content/blog/deepvulkan5/index.md | 10 + content/blog/deepvulkan6/index.md | 9 + content/blog/deepvulkan7/index.md | 96 +++++++++ content/blog/deepvulkan8/index.md | 35 ++++ content/blog/deepvulkan9/index.md | 48 +++++ content/blog/figure3/index.md | 30 ++- content/blog/linux-graphics-stack.md | 227 ++++++++++++++++++++++ content/blog/steam-offline-achivements.md | 49 +++++ 13 files changed, 859 insertions(+), 1 deletion(-) create mode 100644 content/blog/deepvulkan1/index.md create mode 100644 content/blog/deepvulkan2/index.md create mode 100644 content/blog/deepvulkan3/index.md create mode 100644 content/blog/deepvulkan4/index.md create mode 100644 content/blog/deepvulkan5/index.md create mode 100644 content/blog/deepvulkan6/index.md create mode 100644 content/blog/deepvulkan7/index.md create mode 100644 content/blog/deepvulkan8/index.md create mode 100644 content/blog/deepvulkan9/index.md create mode 100644 content/blog/linux-graphics-stack.md create mode 100644 content/blog/steam-offline-achivements.md diff --git a/content/blog/camera1/index.md b/content/blog/camera1/index.md index 9108308..dc5322e 100644 --- a/content/blog/camera1/index.md +++ b/content/blog/camera1/index.md @@ -25,6 +25,8 @@ This is a ## Image Quality +The only frame of reference I have for this section is phone cameras, so apologises. + ## Included Lens ## Video diff --git a/content/blog/deepvulkan1/index.md b/content/blog/deepvulkan1/index.md new file mode 100644 index 0000000..bdfd7be --- /dev/null +++ b/content/blog/deepvulkan1/index.md @@ -0,0 +1,113 @@ +--- +title: "Vulkan Deep Dive: PCSS" +date: 2022-10-05 +draft: true +tags: +- Vulkan +- C++ +- Deep Dive +--- + +Hello and welcome to the first of many "Vulkan Deep Dives" I plan on writing this year. What are these? + +Well to put it simply, there is _a lot_ of Vulkan content on the Internet now. Which is great, but what ends up +happening is that someone gets knee deep in writing their tutorial and end up losing steam once they get past the +"really basic 90%". This is stuff like initialization (boring), device creation (snooze), render passes (eugh) and pipeline creation (woo?). +There is a **lot** of these kind of tutorials out there, and I don't want to add to that pile. What I want to do instead, is show +you the 10% of your Vulkan renderer, the one that implements these cool techniques! I will also cover how +these techniques work in detail. If you wish to suggest topics, you can contact me through e-mail or reply to the Knockout thread. + +Today we'll be covering PCSS, or Percentage Close Soft Shadows. + +## Theory + +For most naive soft shadow mapping techniques, there is usually a PCF or other similiar kernel +applied uniformly to the whole map. This works if the shadows are far away, and not supposed to be +extremely detailed, but as games are becoming more "realistic" and the camera zooms farther and farther in - this becomes an issue. + +Not only does it not like realistic, it also just doesn't look pretty. Because the kernel is applied +uniformly, it looks great as the shadow gets farther away but brings in artifacts and accidental peter-panning +as it the object gets closer to the shadow. + +This is where PCSS comes in, which was originally pioneered by NVIDIA (https://developer.download.nvidia.com/whitepapers/2008/PCSS_Integration.pdf). +I recommend reading the paper beforehand, as it's an incredibly simple technique and the paper is very short. There is three basic ideas: + +* Using high-precision depth textures. +* Implement blocker searching to vary PCF filter size. +* Use manually adjusted light sizes. + +The first and last points are extremely easy, use a 32-bit floating point depth map, and you must be able to +pass another light parameter to your shader. Where it get's interesting is how PCSS actually determines how to make +shadows soft, or hard. + + +_Note:_ Example of PCSS in action, Prism. + +Before we continue, it's important to refresh how shadows work in real life. In short, when you point light +at an object, it "blocks" light. This is the essence of shadow. However, why do shadows get softer the farther away you get from a light? +This is because shadows bounce, and so does light. This creates a very noticeable transition: +TODO MENTION PENUMBRA + + + +So back to PCSS, the algorithm is extremely simple. First, we want to figure out (from the light's point of view), what is +blocking the light and what is not. If you remember when you first implemented shadow mapping, how it works is simple: +you draw a depth map from the light's point of view, and then you reconstruct the depth map from the camera point of view. +If you discover something is "above" your object from the light's point of view - then it is obstructed and needs to be sheathed +in darkness! + + + +## Implementation + +Alright, that's enough - we care about soft shadowing. If you remember, the farther away from the object, the softer +the shadow should be. Actually, let's call that object the "blocker". PCSS is broken up into these steps: + +* Find the blocker for that pixel/fragment. This is of course, impossible so we get the average. + * The search width is dependent on the light size, which is tied to how large your shadow map is. +* If there is something blocking, then we must figure out the proper shadow penumbra, based off of the (average) blocker depth + and then we calculate a proper filter radius. +* Pass this new filter radius to PCF, or your favorite kernel . + +And the results are _amazing_, let's take a look at a comparison between hard shadows, PCF shadows, and then PCSS+PCF: + + + +Just this small change to calculate the blocker average depth gives a ton of realism to the scene, even +when nothing else has changed. However, there is a couple of drawbacks: + +* The PCSS whitepaper only covers sun-type, infinite distance lights. How does PCSS integrate into say, point and spot lights? +* What is the cost of running PCSS compared to PCF? +* What is the correct "light size"? + +Unfortunately the PCSS paper only covers light sources that are assumed to be infinitely far away, which is fine +but this technique is useful for things like spot lights. Thankfully, I have the relevant code below: + + +There are a few changes related to removing the parallel plane estimation, and the changes you have to make +to the shadow mapping in general, but it's pretty simple. The same can be said about point lights, as shown: + +However, a word of warning, this is _extremely_ expensive. Let's do some really naive math: + +* Assume we do 4 blocker samples, and 4 PCF samples. +* If we run on a 640x480 screen, assume this fragment shader is running on an object that covers the screen entirely. + * We will assume the object is covered entirely by another object, or multiple of them. PCSS will always be run and cannot early-out. +* Sun and Spot lamps + * (640 * 480) * 4 + 4 = 1,228,804 estimated depth map samples +* Point lights + * (640 * 480) * 6 (4 + 4) = 14,745,600 estimated depth map samples + +PCSS is _not_ cheap, and should be used sparingly. If you only apply it to just the main sun lamp in your +scene, it would easily be real-time - but scaling it to every kind of light will easily slow everything down. As shown, +point lights increase your depth samples by 1200%! Of course you might turn down the samples, but imagien having even one more +point light in your scene. This is a similiar problem with PCF applied to point lights, +but it is exacerbated with PCSS since we are easily doubling the samples or more. + +When it comes to light size, it is not physically based but instead another knob or artists. + +## Conclusion + +PCSS is an extremely easy way to increase realism for your shadows, but at a possibly heavy +cost. I highly recommend implementing it into your engine, but definitely watch how many samples +it's using. Using it for point lights and spot lights result in great +looking scenery but is still not really possible in real-time. \ No newline at end of file diff --git a/content/blog/deepvulkan2/index.md b/content/blog/deepvulkan2/index.md new file mode 100644 index 0000000..7992da7 --- /dev/null +++ b/content/blog/deepvulkan2/index.md @@ -0,0 +1,188 @@ +--- +title: "Vulkan Deep Dive: Debugging, Logging, and more" +date: 2022-10-05 +draft: true +tags: +- Vulkan +- C++ +- Deep Dive +--- + +This topic is +something that trips a lot of developers up, and they don't really know where to look, or don't know how to use existing +tools properly. Even worse, is that a lot of +the underlying validation stuff has changed a few years ago, so now there is a divide between the "old but used to work" +information, and the very little "stuff that actually works" info that's online. Let's solve both issues today. + +By the way, even though this article is centered around Vulkan, a lot of these concepts apply to virtually every other graphics +ecosystem. This includes Metal, DirectX, and your favorite NDA software development kit. + +## Graphics Debugger + +For graphics debugging, I usually choose RenderDoc. Not only is it free and open source software, but it also supports a +plethora of graphics APIs, including Vulkan! + +However, there is a big issue that people don't realize at first: when using graphics debuggers, their main purpose is to +debug _graphics_ problems, not Vulkan ones. If your Vulkan code is not spec compliant, Renderdoc +will not save you. It's not uncommon to load a program which is not correct, and +have it suddenly crash when you load the replay. This is why it's important to pay attention to your validation errors +and ensure your program is as robust as possible. + +_"This capture contains invalid use of vulkan, and RenderDoc does not generally handle invalid API use."_ + +From https://github.com/baldurk/renderdoc/issues/2738#issuecomment-1265432684. + +## Shader Compilation Logs + +Logs are very important, but some don't know that you can get more information from shader compilation. + +Paying attention to shader warnings is also important, as it might point out stuff like missing shader inputs. + +How you enable logs depends on how you compile your shaders. If you use GLSlang, simply get the log out: + +```cpp +``` + +## Validation Layers + +For Vulkan, missing even one parameter is crucial to the robustness of your program. Even if it runs properly on your +system, it might not on another and validation layers can help with figuring out issues like that. + +Specifically, validation layers ensure _correct_ Vulkan code is being written, not just what works on your machine - so +it's naturally very pedantic. Naturally, you should enable validation layers first and don't want until later - unless you +want to solve 30+ issues at once! + +### Debug Logging + +The first thing to set up is debugging logging, so the layers know how to print debug messages. What +you want to use is `VK_KHR_debug_utils`. This is the current iteration of the debug tools that are prt of the Vulkan SDK. + +I would recommend enabling logs in this way, because sometimes messages can appear _before_ vulkan is initialized: + +### Enable layers + +Now you can enable the validation layers. I recommend _not_ hardcoding them, but you can do so. Instead, use `vkconfig` to +enable layers on-demand when you so choose. Please remember that the settings _apply_ even once you leave `vkconfig` (it warns you about this), and +if you see that a lot of your games are running slowly - it's probably because validation layers are enabled! + +## Command Buffer Markers and Regions + +When debugging or checking timing of your Vulkan program, you might find sifting through your hundreds of +draw calls, color and depth regions really gruesome. Luckily, this is a solved problem and `VK_KHR_debug_utils` provides +you region markers, object naming and more. + +## Shader Logging + +This is a relatively new concept, but ideal for debugging shaders where you need to know specific values. +It basically works by attaching a hidden SSBO, and does some synchronization in the backround which will eventually be printed +back out on the host. + +## Example 1 + +Now let's dig into a real-world examples of how to use all of the methods above to our advantage to solve +tough graphics issues. For this first example, lets begin with the most common issue - nothing is displaying on +the screen! If you can, run the example code and work through it yourself. If you're new to this, I recommend attempting to find solution first before spoiling yourself. + +### Solution + +Alright, so first let's fire up the program first to figure out the issue. I first enable validation layers, and they +are printed to stdout. As you can see, I already see an issue: + +```shell +validation error: blah blah +``` + +Alright, so let's dig into the source code and figure out where this error might be coming from. Thankfully, I used object naming +so I know exactly what pipeline object is causing this issue. It is on L1221: + +```cpp +pipeline.alrisaffa = VK_FALSE; +``` + +Let's fix this by changing that line to this, as suggested by the validation layer: + +```cpp +pipeline.alrisaffa = VK_TRUE; +``` + +Alright, so we fixed that issue - but it's still black! Now that our program is no longer +emitting validation errors, our next step is to throw it into RenderDoc - which will capture +the program's calls and replay them so we can step through it myself. + + + +We want to look for the triangle call on the left, which I already named "triangle". If I haven't +hammered it in yet... name your objects and commands! + +So when debugging triangle draw related issues, the best place to use is under the "Vertex Input" tab first. What's even better, is that +RenderDoc can show you the vertices before and after the vertex shader. Let's take a look: + + + +Alright, so as we can see, the triangle _is_ being inputted correctly, so there's no issues with +the vertex buffer or the stride. And even better, it seems to be transformed correctly too, cool. So we can keep +moving down the pipeline. Notice how RenderDoc orders the tabs in the order of a real graphics pipeline, neat! + + + +Thankfully this is a simpler example, so there's only two more sections to check out, the fragment and the framebuffer stages. + + + +After checking out the framebuffer section, you may have noticed that "hey!", the "viewport looks wrong!" And you'd be absolutely correct. +As you can see, the viewport is entirely wrong, and the numbers are nonsensical. Let's check out the code: + +```cpp +viewport.width = false; +viewport.height = 400; +``` + +After changing this to be correct: + +```cpp +viewport.width = 640; +viewport.height = 400; +``` + +The triangle now renders! Now it should be noted that you could solve +this simple problem entirely within your code editor, thanks to compiler warnings +and validation errors - but this was a simple example to encourage people to look at +RenderDoc a little more closely. + +## Example 2 + +This is a much, much more complex example which showcases an example of an issue that can +only be solved through RenderDoc. Download the example application source code, +compile it and you will discover a very obvious graphical artifact: + + + +Like before, I highly recommend trying to solve it yourself at first, and come back if you fail or +succeed. I do hope you succeed :-) + +### Solution + +Now, you might think to turn on validation layers and see if that reports anything (good!) but +as you can see, it passes: + +```shell +engine is running! +``` + +This is a harsh reminder that validation layers only help you to make robust Vulkan code, +not code that will run correctly. Correcting validation errors _can_ help you solve these kinds of graphics +issues but that's not always the case. Alright, let's jump straight into RenderDoc: + + + +As usual, I took the liberty of naming absolutely everything so debugging should hopefully be easier. + +Now the graphical artifact has to do with + +## Conclusion + +Hopefully if you followed the article until this point, you have a much better understanding +of how to use these debugging and logging tools to help you solve even the hardest of graphics issues. + +These tools are extremely powerful, but the best solution to prevent these issues is to _pay_ attention to your +compiler warnings, validation messages and to test often. diff --git a/content/blog/deepvulkan3/index.md b/content/blog/deepvulkan3/index.md new file mode 100644 index 0000000..ac539f9 --- /dev/null +++ b/content/blog/deepvulkan3/index.md @@ -0,0 +1,30 @@ +--- +title: "Vulkan Deep Dive: SMAA" +date: 2022-10-05 +draft: true +tags: +- Vulkan +- C++ +- Deep Dive +--- + +In this Vulkan deep dive, we'll go over a simple but important technique - anti-aliasing. FXAA is a popular +and cheap choice for a post-processing AA solution, but SMAA is another popular option used in many games. Not only is it +cheap (but not as cheap as FXAA), it's also extremely easy to implement. It has a HLSL and GLSL version, but of course +we're just worrying about a Vulkan implementation here. + +## How SMAA Works + +Compared to FXAA, SMAA requires extra post processing passes, let's go over all of them and their purpose. + +## Implementation + +### Getting Files + +### Choosing Passes + +### Setting up new Render Passes + +### Testing + +## Conclusion \ No newline at end of file diff --git a/content/blog/deepvulkan4/index.md b/content/blog/deepvulkan4/index.md new file mode 100644 index 0000000..de8aa14 --- /dev/null +++ b/content/blog/deepvulkan4/index.md @@ -0,0 +1,23 @@ +--- +title: "Vulkan Deep Dive: CPU & GPU Culling" +date: 2022-10-05 +draft: true +tags: +- Vulkan +- C++ +- Deep Dive +--- + +Culling is an important part of modern graphics, you definitely don't want anything you aren't looking at wasting +valuable resources. Now wait... whats the purpose of doing it ourselves? I think I read somewhere that GPUs will cull +objects itself, and won't try to rasterize them... + +## Theory + +So let's go over that naive approach, and how it doesn't work. Let's take this scene +with a lot of McGuire models, which if you don't know are stupidly high poly so thse are perfect for testing. + +If you remember, GPUs will check if your triangles are even in the viewport, and will +dynamically cull them and prevent them from rasterizing - right? + +## Implementation diff --git a/content/blog/deepvulkan5/index.md b/content/blog/deepvulkan5/index.md new file mode 100644 index 0000000..73cc969 --- /dev/null +++ b/content/blog/deepvulkan5/index.md @@ -0,0 +1,10 @@ +--- +title: "Vulkan Deep Dive: GPU Instancing" +date: 2022-10-05 +draft: true +tags: +- Vulkan +- C++ +- Deep Dive +--- + diff --git a/content/blog/deepvulkan6/index.md b/content/blog/deepvulkan6/index.md new file mode 100644 index 0000000..d27a5c4 --- /dev/null +++ b/content/blog/deepvulkan6/index.md @@ -0,0 +1,9 @@ +--- +title: "Vulkan Deep Dive: Optimization" +date: 2022-10-05 +draft: true +tags: +- Vulkan +- C++ +- Deep Dive +--- diff --git a/content/blog/deepvulkan7/index.md b/content/blog/deepvulkan7/index.md new file mode 100644 index 0000000..fb8e1e8 --- /dev/null +++ b/content/blog/deepvulkan7/index.md @@ -0,0 +1,96 @@ +--- +title: "Vulkan Deep Dive: Building your own editors" +date: 2022-10-05 +draft: true +tags: +- Vulkan +- C++ +- Deep Dive +--- + +Building editor and mod tools is something I never expected to find so enjoyable, and +it's another rabbit hole of endless programming goals. However, there is a distinct lack +of editor-focused graphics tutorials. Transform handles especially are troublesome, but we +will go over more than just that here. + +## Wireframes and Debug Lines + +## Billboards + +## Transform Handles + +When I first attempted to implement transform handles, I _struggled_. Since then, there has +been a few articles going over how to implement transform handles but none of them I really liked, +as I could copy and paste code from them all day but I still don't understand the math behind it. + +### Theory + +Let's break down what's exactly going on when you use a transform handle. For this example, I would like to use Blenders: + + + +When you click and hold down the mouse button, the object _appears_ to move with it. You lock it to a specific axis, based +on which handle you grab - or do it all freeform. What's going on here? How is it able to associate movement on the screen with +movements in the scene? + +The keyword is _view matrices_. Remember that for view matrices, it transforms from NDC to camera space. However, think about how transform handles work - it's somehow associating +the screen position of the _object_ with the screen position of the _cursor_. So we need some way to reverse the view matrix, and +get the location of the object on the screen, or to put it more bluntly, in the _NDC_. + +### Implementation + +## Selecting Objects + +Selecting objects on the screen is something else that is widely used, but another rare +talent alongside implementing transform handles. It's practically required for any kind of editing software, +much less those needed by 3D game engines. There are three main techniques for +handling this: + +### Depth-based + +### Color-based + +### Occlusion Queries + +## Graphical Interface + +This is something not strictly graphics related, but I want to point out some common choices +for building the user interface outside of the viewport. + +### Qt + +Qt is still a popular choice for building editor programs, for example - Source Filmmaker and the Source 2 SDK. + +However there is a huge upfront cost related to Qt, it requires a _lot_ of code to get running. Once you do the hard work though, +it's an extremely robust, supported and battle tested user interface. Performance is almost never an issue, and it supports +a lot of platforms out of the box. + +It should be noted that there is a common misconception that Qt forces your programs to be FOSS, which is +a non-option for many games. This is FUD, because as long as you link Qt libraries externally you are free to keep your +source code as closed as you want. And let me tell you, you will be using tens of megabytes worth of Qt libraries - there is very +little reason to link it statically. If you're really paranoid, you can pay the Qt +company an insubordinate amount of money for a proprietary licensed option... but why? + +### Dear Imgui + +Using Qt might not always be the best option for you, and "dear imgui" is becoming an increasingly popular option for many games, from indie to AAA games such as the upcoming GTA VI. + +While "dear imgui" is suited to debug UIs for developers, it tends to fall apart when writing end-user UIs (which includes content creation tools) +however it is continuing to keep improving in this regard, especially with the docking branch. +With enough work, it can get pretty enough too, which you check out in the screenshot thread. + +However remember that it is meant for debugging UI's, so including images, icons, among other smaller +UI elements are more of an afterthought. For many developers though, that's an okay trade-off for how +easy it is to develop for. + +### WPF or Win32 + +Win32 or WPF-based tooling is still popular for many developers, either due to the fact that most of your +content creators are using Windows, or because you run legacy software. See the Creation SDK, or Breath of the Wild: + + + + +However, there is some issues, for one using WPF in C++ is agony. It is easier if you write your tools in C#, +but then you're split between two languages (assuming you were using C/C++ in the first place). Win32 is a better +solution if you use C++, but it's extremely limited and Microsoft is no longer meaningfully maintaining it. \ No newline at end of file diff --git a/content/blog/deepvulkan8/index.md b/content/blog/deepvulkan8/index.md new file mode 100644 index 0000000..eb81078 --- /dev/null +++ b/content/blog/deepvulkan8/index.md @@ -0,0 +1,35 @@ +--- +title: "Vulkan Deep Dive: Bokeh Depth of Field" +date: 2022-10-05 +draft: true +tags: +- Vulkan +- C++ +- Deep Dive +--- + +Before we get into the technique we'll implement later, let's take a look down memory lane with depth of field in games: + +## Theory + +As far as I know, there is no proper whitepaper for this technique (please contact me if there is!) and this instead an +amalgamation of different techniques made by different people. They are all credited below, and referenced accordingly. + +The technique is unique where it sounds extremely simple in theory, but it is increasingly +complex once you start to implement, as you are breaking common graphics conventions. + +First we want to choose a proper bokeh shape, as always hexagons are a common choice: + + + +Now, we want to _flood_ the screen with these. Seriously, I'm not joking - but _flood_. If you +have any intermediate experience in graphics development, you're probably screaming at you screen at the moment. +"The overdraw!! There's going to be so much overdraw!!", "The alpha blending is going to kill your frames!". + +To solve this issue, smarter people have figured out the way to avoid these issues is two-fold: + +* Limit the size of the bokeh to a balanace between reasonably looking, and performant. +* Split the bokeh fields into near and far fields. + + +## Implementation \ No newline at end of file diff --git a/content/blog/deepvulkan9/index.md b/content/blog/deepvulkan9/index.md new file mode 100644 index 0000000..6998392 --- /dev/null +++ b/content/blog/deepvulkan9/index.md @@ -0,0 +1,48 @@ +--- +title: "Vulkan Deep Dive: Parallax-corrected Reflections and Real-time Image-based Lighting" +date: 2022-10-05 +draft: true +tags: +- Vulkan +- C++ +- Deep Dive +--- + +You might be wondering why I'm covering reflections and IBLs in this series, as there +is tutorials covering these already online. However, there is two important distinctions in what I'm +teaching here: + +_Parallax-corrected_ reflections and _Real-time_ Image-based Lighting. In most tutorials, they might cover +simple screen space reflections, or if you're lucky - cubemap-based solutions. Most image-based lighting tutorials also +limit themselves to existing HDRIs for simplicty, but doing it in real-time or offline when building levels +is arguably the more popular option in games, so it's harder to figure out that yourself. As always, let's dig in! + +Although I'm covering two topics this time, I did it because they're tangentially related, but I won't be compromising on +the details. + +## Parallax-corrected Reflections + +This is something I've been wanting to cover for a long time, because despite still being popular in games, there is very +little information out there on how to accomplish this. For clarity, this is an extension of cubemap-based realtime (or offline) reflections +using some kind of probe globally or locally. The main difference is for regular reflections, there is no concept of it occupying +any real physical space, it's free-standing and doesn't look correct. For really rough or small reflections, it works okay but +look at how it looks in a watery environment: + +And now with parallax-corrected: + +As you can see, it's very convincing at most angles, and doesn't suffer from the downsides of screen-space reflections. + +### Theory + +### Implementation + +## Real-time Image-based Lighting + +This tutorial assumes that you _already_ implemented image-based lighting in your engine, as I won't dig into that here +(There is much smarter people to tell you how to do that!) This will just cover how to move from existing HDRI to one that is scene-based. + +### Theory + +_Note:_ This isn't really anything to do with any mathematical theory, but it's still a good subsection title ;-) + +### Implementation \ No newline at end of file diff --git a/content/blog/figure3/index.md b/content/blog/figure3/index.md index 6bbfe0e..82e2ca6 100644 --- a/content/blog/figure3/index.md +++ b/content/blog/figure3/index.md @@ -7,4 +7,32 @@ tags: - Review --- -Let's begin another figure review, this time with a Ryza figure. +It's time for another figure review, this time with Ryza! Funnily enough, this figure +actually convinced me to try Atelier Ryza (1) again, which runs great on the Steam Deck thanks +to the atelier-sync-fix created by doujistin, who created DXVK. Games aside, here's the figure. + +The figure is around average price ($150) and was already released by the time I got around to purchasing it. +After ordering it from AmiAmi, the figure arrived in about 3-4 days via DHL, so pretty nice shopping experience +from AmiAmi. + +The packaging is clear, and plastic - sort of reminds me of Pop-up Parades. Not that it's cheap, +it has a nice photo on the back, and a nice background. After taking her out, you have to do.. assemble it: + + + +Yes, for some reason all of the bits on the bottom are seperated. I'm no figure maker, but it's +really strange that they were removable in the first place. I guess if someone doesn't want them on the figure sure, but +they're small enough in the first place and _they still have imprints on the sand_, I'm just really +confused why they weren't just glued on. Anyway, the figure itself: + +The figure is really well sculpted, and I like the material _a lot_. It's of those weirdly +textured PVCs they like to use in more expensive figures, that feels like it's _soft_ almost but it's clearly just plastic. +Just like other figures of this caliber, they take some liberties in the proportions of Ryza, namely making her more "human sized" and +making her head less large, which makes her body look bigger. I kind of wish Ryza looked like this in the second game, which is _supposed_ to be 3 +years after the first one, yet it just looks like she's in a different out. Curiously Ryza 3 fixes this issue, but that's only +a timegap of a year. Yeah, that's confusing. + + + +I almost forgot to mention the base, which looks really nice and the additions on the bottom +are much appreciated to break up the beach texture. \ No newline at end of file diff --git a/content/blog/linux-graphics-stack.md b/content/blog/linux-graphics-stack.md new file mode 100644 index 0000000..148dc34 --- /dev/null +++ b/content/blog/linux-graphics-stack.md @@ -0,0 +1,227 @@ +--- +title: "Deep Dive: Linux Graphics Stack" +date: 2022-10-05 +draft: true +tags: +- Vulkan +- Linux +- Deep Dive +--- + +The Linux graphics stack is a complex mechanism of many, many projects that function together to +deliver images to your screen. Since other operating systems hide this away from you, we fortunately have Linux to look +at for how a desktop system might deliver graphics. + +You probably noticed this already, but this is a very long article. The idea is to give you a true "vertical slice" of a +typical Vulkan application. Other articles and videos may cover one or two subjects, but I go into excruciating detail +and want to give you the nitty gitty, especially for those who don't know what a "Mesa" is. + +## Calling Vulkan + +Let's begin by making a very simple Vulkan program, say - drawing a triangle. This is good because it covers a lot of +interesting stuff, picking a device to draw with, creating pipelines, render passes and issuing a draw call. I specifically +picked Vulkan for this article _because_ it makes swapchain managment explicit which will be covered later. + +So the first thing that should be noted is that under most circumstances, the first system in line when you call a Vulkan function +is actually not related to Linux graphics at all. It is called the Vulkan Loader. + +_Note:_ This is true in most cases, but in some systems (say, a Nintendo Switch) you might link directly to the driver's Vulkan library. + +The Vulkan Loader has a few jobs, but the one that is relevant to us is that handles ICDs. An ICD is an "Installable Client Driver", +because Vulkan is not specific to one vendor - and one system may have devices from multiple vendors (say, a integrated Intel +chipset and a dedicated AMD or Nvidia GPU), the Vulkan loader must send the right function calls to the right ICDs. The loader +also has a couple of more important jobs such as handling layers, but that is Vulkan-specific and will not be covered. + +The Vulkan loader is usually called `libvulkan.so`, but let's dig a little deeper and find out what the ICD says. You may +find your installed ICDs in `/usr/share/vulkan/icd.d`. For example, here is the `radeon_icd.x86_64.json` found on the Steam Deck: +```json +{ + "ICD": { + "api_version": "1.3.211", + "library_path": "/usr/lib/libvulkan_radeon.so" + }, + "file_format_version": "1.0.0" +} +``` + +_Note:_ It should be noted that 32-bit Vulkan drivers do exist, and the Vulkan loader handles switching to those as well. For example, +the Steam Deck also ships with an `radeon_icd.i686.json` file as well. + +As you can see, the ICD format is incredibly simple, but the library we're interested in now is `/usr/lib/libvulkan_radeon.so`. + +If we run `objdump` on this, we can get a complete list of functions from this: + +```shell +$ objdump -T --demangle /usr/lib/libvulkan_radeon.so +... +``` + +As you can see, there is a **lot** of functions here - many more than I expected personally. If you notice, there is an interesting +pattern with the function signatures, they follow this: + +* `wsi` - window screen interface +* `radv` - radeon, device-specific functions (for Vulkan device extensions) +* `vk_common` - for common vulkan functions that are not device specific, like `vkFlushMemoryRanges` +* Some game-specific names, such as `metro_exodus`. We'll come back to those. + +Seeing this makes you think, "wait a second, what are some instance-level functions doing there?" Let's explain: + +1. You call a Vulkan function, say - `vkCreateInstance`. +2. The Vulkan loader is actually what gets called, and it sends your call down the chain (through any layers, if needed) and eventually to the ICD. +3. The ICD then does what it needs to do (See: https://github.com/Mesa3D/mesa/blob/main/src/vulkan/runtime/vk_instance.c#L44) +4. Whatever that is returned is passed up + +But, I chose `vkCreateInstance` for a very good reason. This happens _way_ before you even get to creating devices, so how +does it choose which driver to go with if it could be any of them? Well this is what is called the "loader terminator", and it +specifically deals with this issue. TODO WRITE + +Now let's move out of Vulkan and into Mesa, arguably the most important piece of the Linux graphics puzzle. + +## Mesa + +Mesa is the piece of software that is probably doing most of the heavy lifting here, and is an important piece of the +Linux graphics stack. Taken from their website: + +``` +Open source implementations of OpenGL, OpenGL ES, Vulkan, OpenCL, and more! +``` + +Oh... alright - well that doesn't really explain much. Let's look at their documentation which explains it better: + +```shell +The Mesa project began as an open-source implementation of the OpenGL specification - a system for rendering interactive 3D graphics. + +Over the years the project has grown to implement more graphics APIs, including OpenGL ES, OpenCL, OpenMAX, VDPAU, VA-API, XvMC, Vulkan and EGL. + +A variety of device drivers allows the Mesa libraries to be used in many different environments ranging from software emulation to complete hardware acceleration for modern GPUs. + +Mesa ties into several other open-source projects: the Direct Rendering Infrastructure, X.org, and Wayland to provide OpenGL support on Linux, FreeBSD, and other operating systems. +``` + +What's important to note that Mesa runs in _userspace_. As it says in the last line, it interfaces with kernel APIs such as +DRM (not to be confused with _digital rights management_, and don't worry the acronyms get more confusing). + +Modern graphics drivers (and this applies to most desktop operating systems such as Windows and macOS) are split into two parts: +the kernel-space driver that interfaces directly with the hardware, and there is typically one per vendor, and then the userspace driver +that sits on top of the aforementioned layer and interacts with applications. The AMDGPU kernel driver is what we'll be covering today, +and the Mesa stack is our userspace driver. Specifically, we care about RADV. + +### Mesa Drivers + +Mesa comes with many drivers, which confusingly can be mix and matched because they handle supporting different APIs. + +* RADV is their AMD driver _for_ Vulkan. It has nothing to do with OpenGL support for this hardware (unless you use Zink ;-)) +* ANV is their Intel driver _for_ Vulkan. As usual, it has nothing to do with OpenGL support for Intel chipsets. +* And so on... + +Mesa not interfaces with the kernel DRI, which in turn calls a bunch of AMD-specific stuff +(in the case of RADV) in order to accomplish its goals. Again, this happens in _userspace_ as the kernel +driver only exists to facilitate I/O, load firmware and other really low level things. + +So _what_ is DRI? Well, it's actually comprised of DRM and DRM KMS. So wait, how does this fit into OpenGL again? Well there's +another term, "Gallium". + +### Gallium + +This is so incredibly fascinating because for the longest time - whenever I think of "Gallium", I think +of "Gallium Nine" which is the way to run DX9 almost "natively" on your system via Mesa. But this has nothing to do with +that really, and is just another frontend to this system. + +It's just a framework to reduce the amount of driver code needed to write OpenGL-compliant drivers. Unlike the +Vulkan device drivers, these drivers are located under `src/gallium/drivers`. + +* radeonsi - RADV equivalent for OpenGL +* iris - Intel OpenGL support +* etc + +### Overview + +So after going through all of that, I think we should take a step back and see what we have learned here. + +1. You call `vkCreateInstance` +2. The Vulkan loader terminator eventually calls `vk_common_create_instance`, which exists in the Radeon ICD, +or the `/usr/lib/libvulkan_radeon.so` shared object. This object contains instance-level functions as well as device-specific +functions. +3. If the function is device-specific, the Mesa Vulkan driver knows how to interface with the driver +using the kernel APIs (DRI, which includes DRM KMS for modesetting and DRM for device interfacing). +4. The I/O leaves userspace (Mesa) and heads into the kernel (AMDGPU). +5. ... +6. The I/O returns to Mesa, and goes back up to the Vulkan Loader. +7. If needed (for example, enumerating all physical devices) the Vulkan loader terminator will combine the sources from multiple ICDs, otherwise +just leaves it alone. + +Wow, that is a lot! And we learned with our brief OpenGL tangent that the process is very similar with Gallium. Now +of course this is nice and all, but what about WSI, swapchains and how those interact with Wayland? + +Before we get into that, I would like to revisit the DRM subsystem in the kernel before we head back up to the userspace +portion. + +## DRM + +To remind us, DRM stands for the "Device Rendering Manager" and handles device-specific graphics guff. For us, the part +that's important is that it exposes device-specific APIs to control them from userspace. To find it in the kernel tree, see +`drivers/gpu/drm`. + +Since we are just worrying about AMD gpus, you can see the AMD-specific bits in `drivers/gpu/drm/amd/amdgpu`. Inside +you'll see a metric ton of source and header files related to the amdgpu drm interface. Sweet! + +Now I'm curious about how does Mesa then interface with these device-specific stuff? Does it just include the relevant kernel header? Let's find out... + +If we take a look at the Mesa RADV `meson.build` we get our answer: + +`dependencies : [ ..., dep_libdrm_amdgpu, ... ]` + +Jackpot! This `dep_libdrm_amdgpu` bit is specifically referring to the libdrm project under the Mesa umbrella (https://gitlab.freedesktop.org/mesa/drm) +To get more confusing, this is _not_ DRM (the Linux subsystem) but rather a library sitting on top of it. Yes, really. To get even more +confusing, freedesktop.org also calls this library DRI for some reason. I told you the acronyms were going to get really bad. + +So, the Mesa RADV driver doesn't interface with the kernel directly, but rather access all of the AMD-related +gubbins _through_ libdrm. + +I won't be covering how the AMDGPU kernel driver actually works, partially because I don't know but the main idea is that +it basically operates on top of some propietary firmware that's loaded on startup for the GPU. The DRM kernel driver is basically +responsible for creating a suitable interface, initializing firmware, memory and doing I/O between that and the userspace. + +## Windowing + +This is a huge topic just by itself, but it's incredibly interesting as everything we said before is assuming that you're +either running your Vulkan program without any graphics output (_specifically_ presentation, as you can totally run the graphics +part of Vulkan headless). + +For _brevity_ sake, and because there's already a lot of X11 information out there - I want to cover what specifically +happens on **Wayland**. This is especially troubling because there is a lot of misinformation on the web, especially around Wayland. + +### On the Vulkan Side + +Let's momentarily mention what exactly you need to do on Vulkan to get presentation working. We'll keep this short but +it's important to get our bearings straight. + +This may be a surprise to some, but Vulkan has nothing to do with presentation. This is pretty on par with Khronos APIs +actually, as OpenGL also did not concern itself with presentation (GLX, EGL, and other similiar stuff is _not_ related). + +In Vulkan, to get presentation you must enable a device extension, specifically `VK_KHR_swapchain`. This is not the only +piece of the puzzle, as you also need a surface to render to. There is a lot of options, but we are only concerning ourselves with two: + +* `VK_KHR_surface` - this is the base surface extension +* `VK_KHR_wayland_surface` - needed to interface with the wayland client +* `VK_KHR_directfb_surface` - we will get into this later, as it provides a way to display vulkan directly to the framebuffer. + +### Wayland + +We'll be exclusively talking about how Vulkan applications under Wayland function, specifically under two scenarios. +One will be through a typical desktop environment - in this case - KDE Plasma as well as a more barebones example, bare KMS. +For both cases I will be using SDL2 instead of interacting with the Wayland layer itself, +which will be the case for many games. This does not change much though, because unlike OpenGL - SDL2 does not really interact +or intecept Vulkan functionality, apart from creating a Vulkan surface. + +I chose these two situations because one is your more typical desktop environment, where the compositor has to juggle many +windows vying for presentation. The bare KMS example is more relevant to something like a game system, and we can see if +anything is different here in terms how the swapchain and presentation is handled. + +### GBM + +GBM stands for _"generic buffer management"_ and is also part of the Mesa project. It's mostly used to initialize EGL on DRM KMS. + +You may have heard how Nvidia fought against Mesa with it's GBM, with EGLStreams before eventually conceding and +implementing GBM - this is why. Since Wayland compositors need to initialize graphics through DRM KMS, it eventually needs to allocate +memory. It does this via calling the Mesa GBM, which then in turn calls the GPU specific stuff. \ No newline at end of file diff --git a/content/blog/steam-offline-achivements.md b/content/blog/steam-offline-achivements.md new file mode 100644 index 0000000..37786af --- /dev/null +++ b/content/blog/steam-offline-achivements.md @@ -0,0 +1,49 @@ +--- +title: "Steam Offline Achivements and Why They Sometimes Don't work" +date: 2022-10-05 +draft: true +tags: +- Steam +--- + +If you've been paying attention these last couple of months, the Steam Deck is now in people's hands +and peple are starting to discover how bad Steam Offline mode is. Thankfully Valve is improving the situation +in every update but there is one thing they cant fix: Why do some games require you to be online for achievements +and some don't? + +## What works + +In the best case, the game in question is actually using the Steam API properly, as Steamworks +already keeps a local cache of achievements and stats that will eventually get uploaded back to Steam +once you enter Online Mode again. This is fine, and it's been working this way for years. + +## What doesn't work + +However in some cases, it doesn't always end up this way. Let's take for example, Nier Automata +as this is what tipped me off to this issue. I sometimes take my Steam Deck willingly offline because +I share my library with my girlfriend, but remember that this will happen sooner or later on a handheld. I haven't +gotten too far on the PC version of Automata, so I'm still in the beginning part of the game. I enter the Amusement Park and +beat the boss there, and much to my surprise.... nothing? Now I normally don't get too worked up about achivements but it's +kind of insane that I earned one, and now I have no easy way to get it back without cheating or restarting the game again. Eugh. Let's figure out what's going on. + +## Theory + +I have a sneaking suspicion what's going on here, but until I take apart the Nier binary I have no idea yet. + +I suspect that before the game records the achievement with Steamworks, it checks whether it's online, +whether through it's through its own game servers (since automata has online services) or through Steamworks. Right now +I'm going to assume it's checking the Steamworks API, so we'll be looking for those API calls. Once it detects +you're not online, it decides to not even record Steam achievements. I have no idea why developers might do this, +since _Steamworks_ already maintains a local cache. This is also made clear in the Steamworks documentation: + +INSERT IMAGE + +## Reverse Engineering + +First, we need to break the Steam DRM. The DRM is quite simple, and it's been broken before. Much more knowledgable people +have written up about this format, but in short the actual executable is wrapped within a Steam stub that checks whether +or not it's launched through Steam and then decrypts the binary. + +I tried Steamless to decrpyt the game, but that didn't work, so I opted to dump the binary when the game was running. Then, +I threw the execuable into Ghidra: +