- I couldn't work much because of company affairs.. Today, I took a vacation for housework and made a very basic renderer.
- Using the two objects IPrimitiveBuffer and ISurfaceMaterial, I wrote code that uses buffer data containing surface material information, rendering state values, and primitive (shape) information to set the camera viewpoint and pass it to DrawCommand.
- After creating a DrawCommand for rendering one shape, it is added to the render queue of the current frame.
- There is no transparency/opacity separation yet, but I have implemented a function that sorts commands based on their values.
- Opaque objects need to be drawn from front to back.. Transparent objects (like effects) need to be drawn from back to front. I've included this sorting functionality by default. (Depth value setting)
- API calls for state value changes can be quite expensive. (Blend state changes, shader changes, texture changes, etc.)
- On modern hardware, shader changes are generally the most expensive, followed by texture changes and then rendering state value changes.
- I'm putting bit values into a 64-bit unsigned integer value. If you directly insert bit values like 1 << 60, endianness won't matter, but since the code is going to be assigned later, it will need to be handled accordingly.
- The reason LINK_NODE exists is that COMMANDs are going to be pre-allocated from an Object Pool. Using STL's list or deque would result in frequent Node allocations, so we use a custom linked_list written in C to manage the Nodes.
- RenderQueue uses a template library called TArray, which I developed myself.
- Instead of pre-generating ID values for textures, shaders, and render states, they are cached in the order they are drawn and regenerated each frame. To completely avoid dynamic allocation, I use the Marker-based StackPool allocator mentioned earlier.
- In the D3D11RenderCommandExecutor code, the collected DrawCommands are finally rendered. Currently, it's just a long implementation of basic renderer execution based on Forward rendering.
- Future work will include separating transparent and opaque objects, simplifying some code, and creating common code functions. These tasks will be added or refactored as features are developed.
- After developing character rendering, effect (particle) rendering, UI rendering, etc., a Deferred renderer will be added. This will allow for virtually unlimited lighting capabilities and various advanced rendering techniques.
- To be honest, after writing all the code initially, no triangles were rendered at all.
- At first, there were errors in viewport and projection matrix calculations, and another issue was with rendering state value settings.
- Finally, I foolishly set SampleMask to 0, so the pixel shader stage wasn't executing at all, preventing rendering.
- I was able to solve all these problems using the GPU debugging feature built into Visual Studio.
- This is one of the reasons why I think Microsoft's Visual Studio is the most perfect development tool. The detailed GPU debugging capability makes debugging so much easier.
- It has significantly reduced the need for tools like Pix or RenderDoc.
- It even supports setting breakpoints in shaders and line-by-line debugging.
Conclusion
- Anyway, I had a really frustrating experience (damn it!) due to the mistake of setting SampleMask to 0. Finally, I succeeded in rendering a triangle.
- Now that triangle rendering is successful, I plan to review the entire code and do refactoring and code cleanup.
- Optimization is needed in some areas, so I'll work on that too. At this stage, the goal is to eliminate dynamic allocation during the game loop!
Thank you