Finding a games' resolution

Resolution is an important thing when it comes to visual quality and fidelity. On the PS Vita, many game developers choose to run their game at a sub-native resolution in order to either increase performance, or save battery life. Unfortunately, they almost never tell you what they are actually using. Today we will look at some of the methods of finding a resolution at which a PS Vita game is rendered at.

1. Pixel-counting

Pixel-counting is a very simple method which requires nothing more but an in-game screenshot and MS Paint. It can however be inaccurate, especially when proper screenshot can’t be easily taken, or when the game uses good anti-aliasing.

DigitalFoundry made an excellent video explaining how it’s done, so I won’t be covering it here. The same process (as with other consoles) applies to the Vita too.

2. Checking the game code

Each Vita game uses the GXM graphics library/API that handles rendering, shading and taking care of the GPU.

Firstly, we need to differentiate between a framebuffer and internal (off-screen) buffers.

Framebuffer, a memory buffer with bitmap data, contains the final image that is being drawn on the screen, which means it has everything, including the UI/HUD and 2D/3D world of the game. Parameters of the buffer are set using sceDisplaySetFrameBuf() function (provided by the Vitas’ display API).

int sceDisplaySetFrameBuf(const SceDisplayFrameBuf *pParam, SceDisplaySetBufSync sync)

A game calls this function on each frame draw and passes a pointer to the SceDisplayFrameBuf struct as a first parameter. That’s where width, height, pitch/stride and pointer to the buffer data are stored. This is universal between each and every game. The resolution of the framebuffer is limited to only selected values:

  • 960x544 (native screen res.)
  • 720x408
  • 640x368

Internal (off-screen) buffers are handled by the game engine itself. During the GXM initialization stage, a game can create a “render target” (similar to FBO in OpenGL) that allows a 3D scene to be rendered to an intermediate memory buffer (texture or color & depth surface). This is often done to render the 3D world in a lower resolution while keeping the UI native.

How are these render targets created? During the initialization, a game calls sceGxmCreateRenderTarget() function and passes a pointer to SceGxmRenderTargetParams struct as a first parameter.

int sceGxmCreateRenderTarget(const SceGxmRenderTargetParams *params, SceGxmRenderTarget **renderTarget)

typedef struct SceGxmRenderTargetParams {
uint32_t flags;
uint16_t width;
uint16_t height;
uint16_t scenesPerFrame;
uint16_t multisampleMode;
uint32_t multisampleLocations;
SceUID driverMemBlock;
} SceGxmRenderTargetParams;

As we can see, the struct contains target width and height.

Going through the disassembled game code manually is a tedious task, so I’ve created a simple taiHEN plugin that does this for you. By hooking those functions mentioned before, we can easily extract the desired parameters.

How can I use the plugin then?

Prerequisites
  • Hacked PS Vita (obviously) running henkaku on any firmware
  • Game of your choice

1. Download the plugin
Go to the plugins' release section on GitHub, download the latest VGi.suprx
Copy it over to your Vita over FTP/USB (unless you downloaded it directly of course).

2. Install the plugin
Simply copy the .suprx to ur0:tai/ directory (or ux0:tai/ if it exists), open taiHEN config.txt and add a new section for your game, like this:

*PCSB00498
ur0:tai/VGi.suprx

3. Start your game
In this case I will be using a NoNpDrm backup of MotoGP 14 [EU], which is clearly running at < 960x544 by default.

4. Press SELECT + LEFT TRIGGER
You will be greeted by the VGi plugin menu where some basic information about the game is shown.

5. Press L/R TRIGGER to move between sections
Let's take a look at the "Graphics" section first.
Here we can see that the game uses native 960x544 framebuffer.

Great! Continuing to the "Render Targets" section:

Aaaaaand there it is. As shown, the game creates multiple RTs at 704x448 (some with 2x MSAA) indicating that this unusual resolution might be the one used to render the 3D scenery.

This might not always be true, e.g. with games like Ninja Gaiden Sigma 2+ or Wipeout 2048. These use (sub-native) dynamic resolution scaling, but only create RTs for 960x544. In that case, manual eboot.bin disassembly would most likely be necessary in order to figure out how does the dyn. scaling work.

However, more often than not, this method works and is very useful, esp. in cases where the game uses weird/unusual res. values (not even maintaining the 16:9 aspect ratio). In this case, inaccurate pixel-counting might be slightly off (and give you something like 720x408 or 720x480).

Leave a comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.