1
0
mirror of https://github.com/djhackersdev/bemanitools.git synced 2024-12-02 18:17:16 +01:00
bemanitools/doc/dev/iidx-gfx-rendering-loops.md

4.1 KiB

Notes outlining some aspects of the DirectX calls which were required to know to implement a up-/downscaling feature

As the title says, this outlines some aspects I needed to figure out in order to implement a up-/downscaling feature within the d3d9 hook module that works on all currently available IIDX versions (9 to 26).

To analyze the rendering loops, I have used a tool called apitrace which traces the calls of many graphic APIs: https://github.com/apitrace/apitrace

Defintely recommended to quickly figure out what is going on regarding rendering. It also allows you to let you render parts of a scene after the application exited because it records all API calls and data passed to them.

Anyway, considering the various iterations in (GPU) hardware the game had to undergo combined with weird quirks and "fixes" Bemanitools is undoing, I wouldn't have guessed that their rendering engine was nearly the same until IIDX 20. That's when they introduced SD/HD mode.

In this case, that's great news because I had to craft a solution that allows up-/downscaling the final frame to different resolutions (see the iidxhook-util/d3d9 module for more details about the feature).

But first, we need a breakdown of the render loop's most relevant parts for this:

IIDX pre 20

Using apitrace, we can see the following outline of a frame (not counting the first one that does a lot of setup in the beginning):

BeginScene
Clear
...
// Here are the main draw calls for the scene rendering to the back buffer
EndScene
Present

No render target switching, simply render everything to the back buffer...plain and simple.

Note: The viewport size is determined by the size returned by GetClientRect, wtf. Welp, no official Konmai seal of approval without that. ¯_(ツ)_/¯

IIDX 20+

Using apitrace, we can see the following outline of a frame (not counting the first one that does a lot of setup in the beginning):

BeginScene
// tex1 is a render target texture with size 1280x720 (also in SD mode)
// The game will render to this intermediate target and not directly to the fram ebuffer
SetRenderTarget(0, tex1)
...
Clear
BeginScene -> ErrInvalidCall return code
Clear
...
// Here are the main draw calls for the scene rendering to tex1
...
// Sets the render target to the original frame buffer. The size of the framebuffer is set to the output mode
// resolution, e.g. 1280x720 for HD mode and 640x480 for SD mode
SetRenderTarget(0, frame_buffer)
...
Clear
EndScene
...
// Render, texture and sampler state updates as well as a draw call that draws tex1 to a quad which fills the
// screen space.
...
EndScene -> ErrInvalidCall return code
Present

This is quite a different flow to implement HD and SD mode but the solution to solve that particular problem is straight forward and easy to understand. This means that the game will always render in HD mode and only downscale the final frame to SD resolution for 640x480 output.

Also, why the fuck do they call BeginScene and EndScene twice? Looks like they wanted to do this in two separate scenes for some reason. Checking the return values would have revealed to them that something's not right...lucky them that this code works nevertheless. Another Konmai seal of approval, a job well done. ¯_(ツ)_/¯

iidxhook's up-/downscaling solution

The initial solution simply hooked into BeginScene and EndScene and let the game render to an intermediate render target texture. The texture was scaled according to the actual target frame buffer size before getting presented. This solution worked fine for pre IIDX 20 games but created a black screen on IIDX 20+.

In order to avoid two different scaling flows, the final solution that works for both does the following:

  • Create a render target texture with native resolution and let the game render to it
  • Set the render target to that intermediate render target texture on BeginScene
  • Before Present
    • Scale the intermediate render target texture to the back buffer
    • Set the back buffer as the render target
  • Present frame

Just an outline which follows the actual implementation that you can find in the iidxhook-util/d3d9.