GL/D3D Screen Refresh Differences

Reporter: RobertBColton  |  Status: open  |  Last Modified: January 18, 2019, 10:21:35 PM

I don't think I am planning on fixing this because it doesn't really affect very many games, but I just want to make a post about it so that people are aware of the inconsistency. Our hands, I think, are basically tied as far as fixing it goes.

There are several ways to do a screen refresh, the backbuffer can be copied to the front buffer, the back and the front buffers can be swapped, and the font buffer can simply be discarded after being presented. This is at least true for Direct3D.
https://docs.microsoft.com/en-us/windows/desktop/direct3d9/d3dswapeffect

OpenGL on the other hand, at least in Windows, provides only a single swap model, which is to just flip the front and backbuffer (note SwapBuffers which ENIGMA uses now).
https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-swapbuffers
https://docs.microsoft.com/en-us/windows/desktop/opengl/wgl-functions

As it currently stands ENIGMA uses a flip model in all of its graphics systems and bridges very consistently. Our OpenGL systems swap the front and backbuffers. The Direct3D systems use a discard swap effect.

Now the inconsistency is GM8 and GMSv1.4 use a copy swap effect where screen_refresh/present copies the backbuffer into the frontbuffer. I noticed this after testing a screen_redraw call to the room goto so that we can draw the room in its initial state before going to step events (GM8 fires the first draw before the first step). This means after screen_refresh the backbuffer is still preserved. So far, I've only found this to lead to an inconsistency in a single game, the Wild Racing game. What happens in current ENIGMA GL is we have a blackscreen with the controls. What happens in ENIGMA D3D and GM is we have the scene (including terrain) with the controls appearing as a HUD. What's notable here is that ENIGMA D3D and GM produce the same effect, even though ENIGMA uses a discard swap model which should cause it to behave like ENIGMA GL.

This all happens because the alarm event which draws the controls HUD draws into the backbuffer. In ENIGMA D3D9, the backbuffer is not being thrown out, despite being a discard swap effect. In ENIGMA GL, the backbuffer is black because it was swapped with the front buffer by screen_refresh at the end of the screen_redraw that was added to the room goto.

One thing we could do to make this compatible is by addition of application_surface where we would manually blit the global application surface to the front buffer and treat it as our main buffer. I also managed to work around it by manually copying the backbuffer to the frontbuffer in the example bellow. That said, @rpjohnst says the disadvantages of making this compatible is potentially higher power consumption.

void ScreenRefresh() {
  //SwapBuffers(enigma::window_hDC);
  glReadBuffer(GL_BACK);
  glDrawBuffer(GL_FRONT);
  // set up raster pos
  GLint dims[4] = {0};
  glGetIntegerv(GL_VIEWPORT, dims);
  GLint fbWidth = dims[2];
  GLint fbHeight = dims[3];
  glCopyPixels(0, 0, fbWidth, fbHeight, GL_COLOR);
  glDrawBuffer(GL_BACK);
}

GMSv1.4 and GM8 Swap Mode

GMSv1.4 Device Create
GM8 Device Create

ENIGMA GL1 With Draw Before Step

ENIGMA GL1 Draw Before Step Start

ENIGMA D3D9 With Draw Before Step

ENIGMA D3D9 Draw Before Step Start

GameMaker 8 Which Draws Before Step

GM8 Wild Racing Start

RobertBColton  
Thanks to @rpjohnst, I was able to confirm today that GMS2 complicates this even further. GMS2 has switched to a discard swap effect, like the current ENIGMA D3D systems use.

GMS2 Swap Effect Discard

Please sign in to post comments, or you can view this issue on GitHub.