VOGONS


First post, by fr500

User metadata
Rank Newbie
Rank
Newbie

I'm looking for some help to solve horrible screen tearing (I have an horizontal line running across the screen at vertical scrolling in games like jazz jackrabbit).
I didn't write this core, I just cleaned it up and rebased it on top of DOSBox-SVN so it's easier to maintain.

retroarch_F0SNsY8m1r.png

Anyway, our whole rendering code is quite simplistic, it's basically this:

#include <string.h>
#include "libretro.h"
#include "dosbox.h"
#include "video.h"

Bit8u RDOSGFXbuffer[1024*768*4];
Bitu RDOSGFXwidth, RDOSGFXheight, RDOSGFXpitch;
unsigned RDOSGFXcolorMode = RETRO_PIXEL_FORMAT_0RGB1555;
void* RDOSGFXhaveFrame;

Bitu GFX_GetBestMode(Bitu flags)
{
return GFX_CAN_32 | GFX_RGBONLY;
}

Bitu GFX_GetRGB(Bit8u red,Bit8u green,Bit8u blue)
{
return (red << 16) | (green << 8) | (blue << 0);
}

Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,GFX_CallBack_t cb)
{
memset(RDOSGFXbuffer, 0, sizeof(RDOSGFXbuffer));

RDOSGFXwidth = width;
RDOSGFXheight = height;
RDOSGFXpitch = width * 4;

if(RDOSGFXwidth > 1024 || RDOSGFXheight > 768)
return 0;

return GFX_GetBestMode(0);
}

bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch)
{
pixels = RDOSGFXbuffer;
pitch = RDOSGFXpitch;

return true;
}

void GFX_EndUpdate( const Bit16u *changedLines )
{
RDOSGFXhaveFrame = RDOSGFXbuffer;
}

// Stubs
void GFX_SetTitle(Bit32s cycles,Bits frameskip,bool paused){}
void GFX_ShowMsg(char const* format,...){}
void GFX_Events(){}
void GFX_SetPalette(Bitu start,Bitu count,GFX_PalEntry * entries){}

We're basically getting a pointer to the framebuffer and blitting that. AS IS. I see that whoever wrote this is not doing anything with changedlines, so I thought that may be the problem so that's one possibility.
The libretro core is leveraging a co-threading library (libco), so basically dosbox is running in a co-thread and we're escaping to the libretro thread on every T interval to get a finished frame. Maybe we're not doing it when the actual frame is complete so that's another possibility.

I'm not very savvy on the codebase, I tried to leave most things untouched, so I thought I'd ask for some feedback.

Reply 1 of 3, by fr500

User metadata
Rank Newbie
Rank
Newbie

Ok after messing around problem seems to be.. the scheduler. We switch contexts at 1/fps
With this change we get no tearing but bad frame pacing

$ git diff
diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp
index 4b2a4fa2..91d39ac2 100644
--- a/libretro/libretro.cpp
+++ b/libretro/libretro.cpp
@@ -1391,10 +1391,6 @@ void retro_run (void)
/* Run emulator */
co_switch(emuThread);

- /* Upload video */
- video_cb(RDOSGFXhaveFrame, RDOSGFXwidth, RDOSGFXheight, RDOSGFXpitch);
- RDOSGFXhaveFrame = 0;
-
/* Upload audio */
audio_batch_cb((int16_t*)audioData, samplesPerFrame);
}
diff --git a/libretro/libretro_gfx.cpp b/libretro/libretro_gfx.cpp
index 1baf21d9..204c86e9 100644
--- a/libretro/libretro_gfx.cpp
+++ b/libretro/libretro_gfx.cpp
@@ -7,6 +7,10 @@ Bit8u RDOSGFXbuffer[1024*768*4];
Bitu RDOSGFXwidth, RDOSGFXheight, RDOSGFXpitch;
unsigned RDOSGFXcolorMode = RETRO_PIXEL_FORMAT_0RGB1555;
void* RDOSGFXhaveFrame;
+float RDOSfps;
+
+
+extern retro_video_refresh_t video_cb;

Bitu GFX_GetBestMode(Bitu flags)
{
@@ -25,6 +29,7 @@ Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,G
RDOSGFXwidth = width;
RDOSGFXheight = height;
RDOSGFXpitch = width * 4;
+ RDOSfps = 60.0f;

if(RDOSGFXwidth > 1024 || RDOSGFXheight > 768)
return 0;
@@ -43,6 +48,8 @@ bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch)
void GFX_EndUpdate( const Bit16u *changedLines )
{
RDOSGFXhaveFrame = RDOSGFXbuffer;
+ video_cb(RDOSGFXhaveFrame, RDOSGFXwidth, RDOSGFXheight, RDOSGFXpitch);
+ RDOSGFXhaveFrame = 0;
}

// Stubs

I figure the rest of the logic may need changes to work under this new behavior of sorts.

Reply 2 of 3, by realnc

User metadata
Rank Member
Rank
Member

As mentioned in the libretro bug, with this patch applied the core seems to only present new frames when there's changes on the screen. When the image is static and nothing new is being drawn, there's no new frames being presented. This stalls the refresh rate in g-sync mode (you get 0Hz) and recovering from that produces extremely severe stutter.

I think this is a general dosbox behavior? Running dosbox on Windows and monitoring its frame presentation interval with something like RTSS shows that dosbox will stop doing frame buffer flips when no new frames are drawn and this stalls the g-sync display.

Ideally, frames should be presented at a constant pace even when nothing new was drawn on the screen. The frame presentation interval must match the emulated refresh rate to get the wanted real refresh rate (in VRR mode the application is responsible for driving the refresh rate of the monitor.)

Reply 3 of 3, by fr500

User metadata
Rank Newbie
Rank
Newbie

So realnc found a simple fix
https://github.com/libretro/dosbox-svn/issues … mment-544173355

The problem is still related to the scheduler. Must remove libco at some point. The frontend was drawing the frame while dosbox was writing to it causing the tearing.