1
0
Fork 0
mirror of https://github.com/k4zmu2a/SpaceCadetPinball.git synced 2024-11-14 13:58:04 +01:00

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" POPUP "&Window"
BEGIN BEGIN
MENUITEM "&Uniform Scaling", Menu1_WindowUniformScale MENUITEM "&Uniform Scaling", Menu1_WindowUniformScale
MENUITEM "&Alternative Rendering", Menu1_AlternativeRender
END END
END END
POPUP "&Help" 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.RightTableBumpKey = get_int(nullptr, "Right Table Bump key", Options.RightTableBumpKey);
Options.BottomTableBumpKey = get_int(nullptr, "Bottom Table Bump key", Options.BottomTableBumpKey); Options.BottomTableBumpKey = get_int(nullptr, "Bottom Table Bump key", Options.BottomTableBumpKey);
Options.UniformScaling = get_int(nullptr, "Uniform scaling", true); Options.UniformScaling = get_int(nullptr, "Uniform scaling", true);
Options.AlternativeRender = get_int(nullptr, "Alternative Render", false);
menu_check(Menu1_Sounds, Options.Sounds); menu_check(Menu1_Sounds, Options.Sounds);
Sound::Enable(0, 7, Options.Sounds); Sound::Enable(0, 7, Options.Sounds);
menu_check(Menu1_Music, Options.Music); 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_3Players, Options.Players == 3);
menu_check(Menu1_4Players, Options.Players == 4); menu_check(Menu1_4Players, Options.Players == 4);
menu_check(Menu1_WindowUniformScale, Options.UniformScaling); menu_check(Menu1_WindowUniformScale, Options.UniformScaling);
menu_check(Menu1_AlternativeRender, Options.AlternativeRender);
auto tmpBuf = memory::allocate(0x1F4u); auto tmpBuf = memory::allocate(0x1F4u);
if (tmpBuf) if (tmpBuf)
{ {
@ -149,6 +152,7 @@ void options::uninit()
set_int(nullptr, "Bottom Table Bump key", Options.BottomTableBumpKey); set_int(nullptr, "Bottom Table Bump key", Options.BottomTableBumpKey);
set_int(nullptr, "Screen Resolution", Options.Resolution); set_int(nullptr, "Screen Resolution", Options.Resolution);
set_int(nullptr, "Uniform scaling", Options.UniformScaling); set_int(nullptr, "Uniform scaling", Options.UniformScaling);
set_int(nullptr, "Alternative Render", Options.AlternativeRender);
} }
void options::path_init(LPCSTR regPath) void options::path_init(LPCSTR regPath)
@ -343,6 +347,12 @@ void options::toggle(UINT uIDCheckItem)
fullscrn::window_size_changed(); fullscrn::window_size_changed();
fullscrn::paint(); fullscrn::paint();
break; break;
case Menu1_AlternativeRender:
Options.AlternativeRender ^= true;
menu_check(Menu1_AlternativeRender, Options.AlternativeRender);
fullscrn::window_size_changed();
fullscrn::paint();
break;
default: default:
break; break;
} }

View file

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

View file

@ -26,7 +26,7 @@
TPinballTable* pb::MainTable = nullptr; TPinballTable* pb::MainTable = nullptr;
datFileStruct* pb::record_table = 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; float pb::time_now, pb::time_next, pb::ball_speed_limit;
high_score_struct pb::highscore_table[5]; high_score_struct pb::highscore_table[5];
bool pb::FullTiltMode = false, pb::cheat_mode = false; bool pb::FullTiltMode = false, pb::cheat_mode = false;
@ -129,9 +129,7 @@ void pb::reset_table()
void pb::firsttime_setup() void pb::firsttime_setup()
{ {
render::blit = 0; render::update(false);
render::update();
render::blit = 1;
} }
void pb::paint() void pb::paint()
@ -223,6 +221,8 @@ void pb::ballset(int x, int y)
int pb::frame(int time) int pb::frame(int time)
{ {
static int frameTime = 0;
if (time > 100) if (time > 100)
time = 100; time = 100;
float timeMul = time * 0.001f; float timeMul = time * 0.001f;
@ -244,7 +244,29 @@ int pb::frame(int time)
nudge::nudge_count = nudgeDec; nudge::nudge_count = nudgeDec;
} }
timer::check(); 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); score::update(MainTable->CurScoreStruct);
if (!MainTable->TiltLockFlag) 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; ball->Acceleration.Y = ball->Speed * ball->Acceleration.Y;
maths::vector_add(&ball->Acceleration, &vec2); maths::vector_add(&ball->Acceleration, &vec2);
ball->Speed = maths::normalize_2d(&ball->Acceleration); ball->Speed = maths::normalize_2d(&ball->Acceleration);
ball->InvAcceleration.X = ball->Acceleration.X == 0.0f ? 1000000000.0f : 1.0f / ball->Acceleration.X; ball->InvAcceleration.X = ball->Acceleration.X == 0.0f ? 1.0e9f : 1.0f / ball->Acceleration.X;
ball->InvAcceleration.Y = ball->Acceleration.Y == 0.0f ? 1000000000.0f : 1.0f / ball->Acceleration.Y; ball->InvAcceleration.Y = ball->Acceleration.Y == 0.0f ? 1.0e9f : 1.0f / ball->Acceleration.Y;
} }
auto timeDelta2 = timeDelta; auto timeDelta2 = timeDelta;

View file

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

View file

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

View file

@ -31,7 +31,6 @@ struct render_sprite_type_struct
class render class render
{ {
public: public:
static int blit;
static int many_dirty, many_sprites, many_balls; static int many_dirty, many_sprites, many_balls;
static render_sprite_type_struct **dirty_list, **sprite_list, **ball_list; static render_sprite_type_struct **dirty_list, **sprite_list, **ball_list;
static zmap_header_type* background_zmap; 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 init(gdrv_bitmap8* bmp, float zMin, float zScaler, int width, int height);
static void uninit(); static void uninit();
static void update(); static void update(bool blit);
static void paint(); static void paint();
static void sprite_modified(render_sprite_type_struct* sprite); static void sprite_modified(render_sprite_type_struct* sprite);
static render_sprite_type_struct* create_sprite(VisualType visualType, gdrv_bitmap8* bmp, 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_EditName3 603
#define DLG_HIGHSCORES_EditName4 604 #define DLG_HIGHSCORES_EditName4 604
#define DLG_HIGHSCORES_EditName5 605 #define DLG_HIGHSCORES_EditName5 605
#define Menu1_AlternativeRender 601
// Next default values for new objects // Next default values for new objects
// //

View file

@ -220,7 +220,16 @@ int winmain::WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
if (prevTime) if (prevTime)
{ {
char buf[60]; 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); SetWindowTextA(hwnd_frame, buf);
if (DispGRhistory) if (DispGRhistory)
@ -610,6 +619,7 @@ LRESULT CALLBACK winmain::message_handler(HWND hWnd, UINT Msg, WPARAM wParam, LP
case Menu1_800x600: case Menu1_800x600:
case Menu1_1024x768: case Menu1_1024x768:
case Menu1_WindowUniformScale: case Menu1_WindowUniformScale:
case Menu1_AlternativeRender:
options::toggle(wParamI); options::toggle(wParamI);
break; break;
case Menu1_Help_Topics: case Menu1_Help_Topics: