Added new render mode with reduced tearing.

Available under new option Window->Alternative Rendering.
Issue #29.
This commit is contained in:
Muzychenko Andrey 2021-10-12 16:30:20 +03:00
parent de76557325
commit 0d9610ddb6
9 changed files with 121 additions and 88 deletions

View File

@ -98,6 +98,7 @@ BEGIN
POPUP "&Window"
BEGIN
MENUITEM "&Uniform Scaling", Menu1_WindowUniformScale
MENUITEM "&Alternative Rendering", Menu1_AlternativeRender
END
END
POPUP "&Help"

View File

@ -108,6 +108,8 @@ void options::init(HMENU menuHandle)
Options.RightTableBumpKey = get_int(nullptr, "Right Table Bump key", Options.RightTableBumpKey);
Options.BottomTableBumpKey = get_int(nullptr, "Bottom Table Bump key", Options.BottomTableBumpKey);
Options.UniformScaling = get_int(nullptr, "Uniform scaling", true);
Options.AlternativeRender = get_int(nullptr, "Alternative Render", false);
menu_check(Menu1_Sounds, Options.Sounds);
Sound::Enable(0, 7, Options.Sounds);
menu_check(Menu1_Music, Options.Music);
@ -117,6 +119,7 @@ void options::init(HMENU menuHandle)
menu_check(Menu1_3Players, Options.Players == 3);
menu_check(Menu1_4Players, Options.Players == 4);
menu_check(Menu1_WindowUniformScale, Options.UniformScaling);
menu_check(Menu1_AlternativeRender, Options.AlternativeRender);
auto tmpBuf = memory::allocate(0x1F4u);
if (tmpBuf)
{
@ -149,6 +152,7 @@ void options::uninit()
set_int(nullptr, "Bottom Table Bump key", Options.BottomTableBumpKey);
set_int(nullptr, "Screen Resolution", Options.Resolution);
set_int(nullptr, "Uniform scaling", Options.UniformScaling);
set_int(nullptr, "Alternative Render", Options.AlternativeRender);
}
void options::path_init(LPCSTR regPath)
@ -343,6 +347,12 @@ void options::toggle(UINT uIDCheckItem)
fullscrn::window_size_changed();
fullscrn::paint();
break;
case Menu1_AlternativeRender:
Options.AlternativeRender ^= true;
menu_check(Menu1_AlternativeRender, Options.AlternativeRender);
fullscrn::window_size_changed();
fullscrn::paint();
break;
default:
break;
}

View File

@ -23,6 +23,7 @@ struct optionsStruct
int BottomTableBumpKeyDft;
int Resolution;
bool UniformScaling;
bool AlternativeRender;
};

View File

@ -26,7 +26,7 @@
TPinballTable* pb::MainTable = nullptr;
datFileStruct* pb::record_table = nullptr;
int pb::time_ticks = 0, pb::demo_mode = 0, pb::game_mode = 2, pb::mode_countdown_, pb::state;
int pb::time_ticks = 0, pb::demo_mode = 0, pb::game_mode = 2, pb::mode_countdown_, pb::state, pb::frameCounter = 0;
float pb::time_now, pb::time_next, pb::ball_speed_limit;
high_score_struct pb::highscore_table[5];
bool pb::FullTiltMode = false, pb::cheat_mode = false;
@ -129,9 +129,7 @@ void pb::reset_table()
void pb::firsttime_setup()
{
render::blit = 0;
render::update();
render::blit = 1;
render::update(false);
}
void pb::paint()
@ -223,6 +221,8 @@ void pb::ballset(int x, int y)
int pb::frame(int time)
{
static int frameTime = 0;
if (time > 100)
time = 100;
float timeMul = time * 0.001f;
@ -244,7 +244,29 @@ int pb::frame(int time)
nudge::nudge_count = nudgeDec;
}
timer::check();
render::update();
if (!options::Options.AlternativeRender)
{
render::update(true);
}
else
{
// Screen update at UPS > screen refresh rate cause tearing.
// Especially noticeable on fast moving ball in scaled up window.
// Retained render prevents frame skip. The next best thing - complete refresh at fixed rate.
render::update(false);
// Frame time at 60 FPS = 16.(6) ms = (16 + 17 + 17) / 3
auto targetTime = frameCounter % 3 == 0 ? 16 : 17;
frameTime += time;
if (frameTime >= targetTime)
{
frameTime = min(frameTime - targetTime, 100);
render::shift(0, 0, 0, 0, MainTable->Width, MainTable->Height);
frameCounter++;
}
}
score::update(MainTable->CurScoreStruct);
if (!MainTable->TiltLockFlag)
{
@ -288,8 +310,8 @@ void pb::timed_frame(float timeNow, float timeDelta, bool drawBalls)
ball->Acceleration.Y = ball->Speed * ball->Acceleration.Y;
maths::vector_add(&ball->Acceleration, &vec2);
ball->Speed = maths::normalize_2d(&ball->Acceleration);
ball->InvAcceleration.X = ball->Acceleration.X == 0.0f ? 1000000000.0f : 1.0f / ball->Acceleration.X;
ball->InvAcceleration.Y = ball->Acceleration.Y == 0.0f ? 1000000000.0f : 1.0f / ball->Acceleration.Y;
ball->InvAcceleration.X = ball->Acceleration.X == 0.0f ? 1.0e9f : 1.0f / ball->Acceleration.X;
ball->InvAcceleration.Y = ball->Acceleration.Y == 0.0f ? 1.0e9f : 1.0f / ball->Acceleration.Y;
}
auto timeDelta2 = timeDelta;

View File

@ -10,7 +10,7 @@ class pb
public:
static int time_ticks;
static float ball_speed_limit, time_now, time_next;
static int game_mode;
static int game_mode, frameCounter;
static bool cheat_mode;
static datFileStruct* record_table;
static TPinballTable* MainTable;

View File

@ -2,7 +2,6 @@
#include "render.h"
#include "memory.h"
int render::blit = 0;
int render::many_dirty, render::many_sprites, render::many_balls;
render_sprite_type_struct **render::dirty_list, **render::sprite_list, **render::ball_list;
zmap_header_type* render::background_zmap;
@ -60,83 +59,69 @@ void render::uninit()
many_balls = 0;
}
void render::update()
void render::update(bool blit)
{
rectangle_type overlapRect{};
auto dirtyPtr = dirty_list;
for (int index = 0; index < many_dirty; ++dirtyPtr, ++index)
{
auto curSprite = *dirtyPtr;
if ((*dirtyPtr)->VisualType != VisualType::None)
{
if ((*dirtyPtr)->VisualType == VisualType::Sprite)
{
if (curSprite->BmpRectCopy.Width > 0)
maths::enclosing_box(&curSprite->BmpRectCopy, &curSprite->BmpRect, &curSprite->DirtyRect);
if (!maths::rectangle_clip(&curSprite->DirtyRect, &vscreen_rect, &curSprite->DirtyRect))
{
curSprite->DirtyRect.Width = -1;
continue;
}
auto yPos = curSprite->DirtyRect.YPosition;
auto width = curSprite->DirtyRect.Width;
auto xPos = curSprite->DirtyRect.XPosition;
auto height = curSprite->DirtyRect.Height;
zdrv::fill(&zscreen, width, height, xPos, yPos, 0xFFFF);
if (background_bitmap)
gdrv::copy_bitmap(&vscreen, width, height, xPos, yPos, background_bitmap, xPos, yPos);
else
gdrv::fill_bitmap(&vscreen, width, height, xPos, yPos, 0);
}
}
else
{
if (!maths::rectangle_clip(&curSprite->BmpRect, &vscreen_rect, &curSprite->DirtyRect))
{
curSprite->DirtyRect.Width = -1;
continue;
}
if (!curSprite->Bmp)
{
auto yPos = curSprite->DirtyRect.YPosition;
auto width = curSprite->DirtyRect.Width;
auto xPos = curSprite->DirtyRect.XPosition;
auto height = curSprite->DirtyRect.Height;
zdrv::fill(&zscreen, width, height, xPos, yPos, 0xFFFF);
if (background_bitmap)
gdrv::copy_bitmap(&vscreen, width, height, xPos, yPos, background_bitmap, xPos, yPos);
else
gdrv::fill_bitmap(&vscreen, width, height, xPos, yPos, 0);
}
}
}
dirtyPtr = dirty_list;
for (int index = 0; index < many_dirty; ++index)
{
auto sprite = *dirtyPtr;
if ((*dirtyPtr)->DirtyRect.Width > 0 && (sprite->VisualType == VisualType::None || sprite->VisualType ==
VisualType::Sprite))
repaint(*dirtyPtr);
++dirtyPtr;
auto curSprite = dirty_list[index];
bool clearSprite = false;
switch (curSprite->VisualType)
{
case VisualType::Sprite:
if (curSprite->BmpRectCopy.Width > 0)
maths::enclosing_box(&curSprite->BmpRectCopy, &curSprite->BmpRect, &curSprite->DirtyRect);
if (maths::rectangle_clip(&curSprite->DirtyRect, &vscreen_rect, &curSprite->DirtyRect))
clearSprite = true;
else
curSprite->DirtyRect.Width = -1;
break;
case VisualType::None:
if (maths::rectangle_clip(&curSprite->BmpRect, &vscreen_rect, &curSprite->DirtyRect))
clearSprite = !curSprite->Bmp;
else
curSprite->DirtyRect.Width = -1;
break;
default: break;
}
if (clearSprite)
{
auto yPos = curSprite->DirtyRect.YPosition;
auto width = curSprite->DirtyRect.Width;
auto xPos = curSprite->DirtyRect.XPosition;
auto height = curSprite->DirtyRect.Height;
zdrv::fill(&zscreen, width, height, xPos, yPos, 0xFFFF);
if (background_bitmap)
gdrv::copy_bitmap(&vscreen, width, height, xPos, yPos, background_bitmap, xPos, yPos);
else
gdrv::fill_bitmap(&vscreen, width, height, xPos, yPos, 0);
}
}
for (int index = 0; index < many_dirty; ++index)
{
auto sprite = dirty_list[index];
if (sprite->DirtyRect.Width > 0 && (sprite->VisualType == VisualType::None || sprite->VisualType ==
VisualType::Sprite))
repaint(sprite);
}
paint_balls();
if (blit)
{
paint_balls();
gdrv::start_blit_sequence();
auto xPos = vscreen.XPosition + offset_x;
auto yPos = vscreen.YPosition + offset_y;
dirtyPtr = dirty_list;
for (int index = 0; index < many_dirty; ++dirtyPtr, ++index)
for (int index = 0; index < many_dirty; ++index)
{
auto sprite = *dirtyPtr;
auto dirtyRect = &(*dirtyPtr)->DirtyRect;
auto width2 = (*dirtyPtr)->DirtyRect.Width;
auto sprite = dirty_list[index];
auto dirtyRect = &sprite->DirtyRect;
auto width2 = sprite->DirtyRect.Width;
if (width2 > 0)
gdrv::blit_sequence(
&vscreen,
@ -147,21 +132,16 @@ void render::update()
width2,
dirtyRect->Height);
auto rect = &sprite->BmpRectCopy;
rect->XPosition = dirtyRect->XPosition;
rect->YPosition = dirtyRect->YPosition;
rect->Width = dirtyRect->Width;
rect->Height = dirtyRect->Height;
sprite->BmpRectCopy = *dirtyRect;
if (sprite->UnknownFlag != 0)
remove_sprite(sprite);
}
dirtyPtr = ball_list;
for (int index = 0; index < many_balls; ++dirtyPtr, ++index)
for (int index = 0; index < many_balls; ++index)
{
auto rectCopy = &(*dirtyPtr)->BmpRectCopy;
auto dirtyRect = &(*dirtyPtr)->DirtyRect;
auto sprite = ball_list[index];
auto rectCopy = &sprite->BmpRectCopy;
auto dirtyRect = &sprite->DirtyRect;
if (maths::overlapping_box(dirtyRect, rectCopy, &overlapRect) && dirtyRect->Width > 0)
{
if (overlapRect.Width > 0)
@ -198,13 +178,22 @@ void render::update()
}
gdrv::end_blit_sequence();
unpaint_balls();
}
else
{
for (int index = 0; index < many_dirty; ++index)
{
auto sprite = dirty_list[index];
sprite->BmpRectCopy = sprite->DirtyRect;
if (sprite->UnknownFlag != 0)
remove_sprite(sprite);
}
}
many_dirty = 0;
unpaint_balls();
}
void render::paint()
{
paint_balls();
@ -473,7 +462,7 @@ void render::paint_balls()
void render::unpaint_balls()
{
for (int index = many_balls-1; index >= 0; index--)
for (int index = many_balls - 1; index >= 0; index--)
{
auto curBall = ball_list[index];
if (curBall->DirtyRect.Width > 0)

View File

@ -31,7 +31,6 @@ struct render_sprite_type_struct
class render
{
public:
static int blit;
static int many_dirty, many_sprites, many_balls;
static render_sprite_type_struct **dirty_list, **sprite_list, **ball_list;
static zmap_header_type* background_zmap;
@ -43,7 +42,7 @@ public:
static void init(gdrv_bitmap8* bmp, float zMin, float zScaler, int width, int height);
static void uninit();
static void update();
static void update(bool blit);
static void paint();
static void sprite_modified(render_sprite_type_struct* sprite);
static render_sprite_type_struct* create_sprite(VisualType visualType, gdrv_bitmap8* bmp,

View File

@ -240,6 +240,7 @@
#define DLG_HIGHSCORES_EditName3 603
#define DLG_HIGHSCORES_EditName4 604
#define DLG_HIGHSCORES_EditName5 605
#define Menu1_AlternativeRender 601
// Next default values for new objects
//

View File

@ -220,7 +220,16 @@ int winmain::WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
if (prevTime)
{
char buf[60];
sprintf_s(buf, "Frames/sec = %02.02f", 300.0f / (static_cast<float>(curTime - prevTime) * 0.001f));
auto dt = static_cast<float>(curTime - prevTime) * 0.001f;
if (!options::Options.AlternativeRender)
sprintf_s(buf, "Frames/sec = %02.02f", 300.0f / dt);
else
{
sprintf_s(buf, "Updates/sec = %02.02f Frames/sec = %02.02f",
300.0f / dt, pb::frameCounter / dt);
pb::frameCounter = 0;
}
SetWindowTextA(hwnd_frame, buf);
if (DispGRhistory)
@ -610,6 +619,7 @@ LRESULT CALLBACK winmain::message_handler(HWND hWnd, UINT Msg, WPARAM wParam, LP
case Menu1_800x600:
case Menu1_1024x768:
case Menu1_WindowUniformScale:
case Menu1_AlternativeRender:
options::toggle(wParamI);
break;
case Menu1_Help_Topics: