Skip to content

Commit

Permalink
Added new render mode with reduced tearing.
Browse files Browse the repository at this point in the history
Available under new option Window->Alternative Rendering.
Issue #29.
  • Loading branch information
k4zmu2a committed Oct 12, 2021
1 parent de76557 commit 0d9610d
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 79 deletions.
1 change: 1 addition & 0 deletions SpaceCadetPinball/SpaceCadetPinball.rc
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ BEGIN
POPUP "&Window"
BEGIN
MENUITEM "&Uniform Scaling", Menu1_WindowUniformScale
MENUITEM "&Alternative Rendering", Menu1_AlternativeRender
END
END
POPUP "&Help"
Expand Down
10 changes: 10 additions & 0 deletions SpaceCadetPinball/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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)
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions SpaceCadetPinball/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ struct optionsStruct
int BottomTableBumpKeyDft;
int Resolution;
bool UniformScaling;
bool AlternativeRender;
};


Expand Down
36 changes: 29 additions & 7 deletions SpaceCadetPinball/pb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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;
Expand All @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion SpaceCadetPinball/pb.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
125 changes: 57 additions & 68 deletions SpaceCadetPinball/render.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
for (int index = 0; index < many_dirty; ++index)
{
auto curSprite = *dirtyPtr;
if ((*dirtyPtr)->VisualType != VisualType::None)
auto curSprite = dirty_list[index];
bool clearSprite = false;
switch (curSprite->VisualType)
{
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;
}
case VisualType::Sprite:
if (curSprite->BmpRectCopy.Width > 0)
maths::enclosing_box(&curSprite->BmpRectCopy, &curSprite->BmpRect, &curSprite->DirtyRect);

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);
}
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;
}
else

if (clearSprite)
{
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);
}
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 ==
auto sprite = dirty_list[index];
if (sprite->DirtyRect.Width > 0 && (sprite->VisualType == VisualType::None || sprite->VisualType ==
VisualType::Sprite))
repaint(*dirtyPtr);
++dirtyPtr;
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,
Expand All @@ -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)
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 1 addition & 2 deletions SpaceCadetPinball/render.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -43,7 +42,7 @@ class render

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,
Expand Down
1 change: 1 addition & 0 deletions SpaceCadetPinball/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
//
Expand Down
12 changes: 11 additions & 1 deletion SpaceCadetPinball/winmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit 0d9610d

Please sign in to comment.