Friday, March 1, 2019

Fixes in S2DEX microcode.


S2DEX microcode was developed to simplify development of 2D games. It provides macro commands to create sprites and backgrounds. S2DEX2 modification of S2DEX can self-load with F3DEX2 microcode and thus can be used to create 2D sprites and backgrounds in 3D games. Many N64 games use S2DEX, so all modern HLE graphics plugins implement it.

S2DEX implementation in GLideN64 was too high level. Actual microcode commands are very complex. The microcode is documented, but the documentation does not cover some tiny details in internal mechanics. Thus, S2DEX implementation not always worked right. olivieryuyu decided to not rely on documentation and decode the microcode itself. The goal of that new decoding project was to implement commands in HLE as close as possible to LLE and obtain the result, which is as good as with LLE or better.

It was very long and hard project, which took circa six months. We started form relatively simple sprite commands: OBJ_RECTANGLE, OBJ_RECTANGLE_R and OBJ_SPRITE. We fixed calculation of screen and texture coordinates of sprites. There are several revisions of the microcode, and each revision does the calculations in slightly different way. The fix eliminated several minor glitches and a major one, in Parlor! Pro 64:

Then we started decoding and implementation of remaining commands: BG_1CYC and BG_COPY, which are used to draw large 2D objects such as backgrounds. It was very hard task. These functions are very large and tangled. After several months of hard work both background commands were implemented and now they produce exactly the same result as in LLE mode on all games we tested.
One of results of our work - correct background in Neon Genesis Evangelion:

From one side, it is great result because the commands are very large and complicated. From the other side, LLE by itself not always gives perfect result. You may run previous releases of the plugin in LLE mode and find numerous graphics glitches in 2D games. Some of glitches caused by errors in texture loading code. Backgrounds commands do texture loading in quite weird manner, which "normal" game code never uses. I fixed several hard issues in plugin's texturing subsystem during development of that project. However, many glitches remained after these fixes. These glitches can be called "the curse of hardware rendering". There are two main groups of such glitches:

  • gaps between polygons. Background commands can not draw background by one piece because of limited texture memory. Background image becomes split on narrow strips. Each strip is loaded in texture memory and rendered by separate rectangle. There are no gaps between rectangles, but gaps between images in them may appear when rendering resolution is higher than game's native resolution. It usually happens when size of the image is not exactly the same as size of area where it will be rendered. Texture scaling in higher resolution may cause fetching texels out of texture image bounds, thus cause artifacts, which look as gaps between polygons.
  • garbage between polygons. Looks like a line of colored garbage between two polygons. Is is seldom kind of glitches, but it does not fixed by switching to native resolution. I did not find yet, how it can be cured.

So, new implementation has issues, but most of them appears only in non-native resolutions. GLideN64 already has tool to eliminate issues with 2D graphics in high resolutions. You may enable "Render 2D elements in native resolution" option to activate it. I adopted that tool to work with new background commands. Result is quite good in general. For example, StarCraft backgrounds have glitches in high-res, but "Render 2D elements in native resolution" eliminates them:
However, the tool is not bug-free by itself and sometimes it is slow. Taking all these issues into account we decided to keep old implementation of background commands and make an option to switch between old "one-piece background" implementation and new "stripped background" one. We recommend to use the new implementation as default one.