Skip to content

Commit

Permalink
Add a threshold for skipping the loading screen
Browse files Browse the repository at this point in the history
  • Loading branch information
glebm authored and AJenbo committed Nov 10, 2024
1 parent 7b05581 commit 66b0f43
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 69 deletions.
24 changes: 12 additions & 12 deletions Source/engine/palette.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,15 +296,15 @@ int UpdateGamma(int gamma)
return 130 - *sgOptions.Graphics.gammaCorrection;
}

void SetFadeLevel(int fadeval, bool updateHardwareCursor)
void SetFadeLevel(int fadeval, bool updateHardwareCursor, const std::array<SDL_Color, 256> &srcPalette)
{
if (HeadlessMode)
return;

for (int i = 0; i < 256; i++) {
system_palette[i].r = (fadeval * logical_palette[i].r) / 256;
system_palette[i].g = (fadeval * logical_palette[i].g) / 256;
system_palette[i].b = (fadeval * logical_palette[i].b) / 256;
system_palette[i].r = (fadeval * srcPalette[i].r) / 256;
system_palette[i].g = (fadeval * srcPalette[i].g) / 256;
system_palette[i].b = (fadeval * srcPalette[i].b) / 256;
#if SDL_VERSION_ATLEAST(2, 0, 0)
system_palette[i].a = SDL_ALPHA_OPAQUE;
#endif
Expand All @@ -323,14 +323,14 @@ void BlackPalette()
SetFadeLevel(0, /*updateHardwareCursor=*/false);
}

void PaletteFadeIn(int fr)
void PaletteFadeIn(int fr, const std::array<SDL_Color, 256> &srcPalette)
{
if (HeadlessMode)
return;
if (demo::IsRunning())
fr = 0;

ApplyGamma(logical_palette, orig_palette, 256);
ApplyGamma(logical_palette, srcPalette, 256);

if (fr > 0) {
const uint32_t tc = SDL_GetTicks();
Expand All @@ -339,7 +339,7 @@ void PaletteFadeIn(int fr)
for (uint32_t i = 0; i < 256; i = fr * (SDL_GetTicks() - tc) / 50) {
if (i != prevFadeValue) {
// We can skip hardware cursor update for fade level 0 (everything is black).
SetFadeLevel(i, /*updateHardwareCursor=*/i != 0u);
SetFadeLevel(i, /*updateHardwareCursor=*/i != 0u, logical_palette);
prevFadeValue = i;
}
BltFast(nullptr, nullptr);
Expand All @@ -352,12 +352,12 @@ void PaletteFadeIn(int fr)
RenderPresent();
}

logical_palette = orig_palette;
logical_palette = srcPalette;

sgbFadedIn = true;
}

void PaletteFadeOut(int fr)
void PaletteFadeOut(int fr, const std::array<SDL_Color, 256> &srcPalette)
{
if (!sgbFadedIn || HeadlessMode)
return;
Expand All @@ -370,15 +370,15 @@ void PaletteFadeOut(int fr)
uint32_t prevFadeValue = 0;
for (uint32_t i = 0; i < 256; i = fr * (SDL_GetTicks() - tc) / 50) {
if (i != prevFadeValue) {
SetFadeLevel(256 - i);
SetFadeLevel(256 - i, /*updateHardwareCursor=*/true, srcPalette);
prevFadeValue = i;
}
BltFast(nullptr, nullptr);
RenderPresent();
}
SetFadeLevel(0);
SetFadeLevel(0, /*updateHardwareCursor=*/true, srcPalette);
} else {
SetFadeLevel(0);
SetFadeLevel(0, /*updateHardwareCursor=*/true, srcPalette);
BltFast(nullptr, nullptr);
RenderPresent();
}
Expand Down
6 changes: 3 additions & 3 deletions Source/engine/palette.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,17 @@ void ApplyGamma(std::array<SDL_Color, 256> &dst, const std::array<SDL_Color, 256
void DecreaseGamma();
int UpdateGamma(int gamma);
void BlackPalette();
void SetFadeLevel(int fadeval, bool updateHardwareCursor = true);
void SetFadeLevel(int fadeval, bool updateHardwareCursor = true, const std::array<SDL_Color, 256> &srcPalette = logical_palette);
/**
* @brief Fade screen from black
* @param fr Steps per 50ms
*/
void PaletteFadeIn(int fr);
void PaletteFadeIn(int fr, const std::array<SDL_Color, 256> &srcPalette = orig_palette);
/**
* @brief Fade screen to black
* @param fr Steps per 50ms
*/
void PaletteFadeOut(int fr);
void PaletteFadeOut(int fr, const std::array<SDL_Color, 256> &srcPalette = logical_palette);
void palette_update_caves();
void palette_update_crypt();
void palette_update_hive();
Expand Down
129 changes: 75 additions & 54 deletions Source/interfac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "loadsave.h"
#include "pfile.h"
#include "plrmsg.h"
#include "utils/log.hpp"
#include "utils/sdl_geometry.h"
#include "utils/sdl_thread.h"

Expand Down Expand Up @@ -403,59 +404,89 @@ void DoLoad(interface_mode uMsg)
}

struct {
uint32_t delayStart;
uint32_t loadStartedAt;
EventHandler prevHandler;
bool skipRendering;
bool done;
uint32_t drawnProgress;
std::array<SDL_Color, 256> palette;
} ProgressEventHandlerState;

void InitRendering()
{
// Blit the background once and then free it.
DrawCutsceneBackground();
if (RenderDirectlyToOutputSurface && PalSurface != nullptr) {
// Render into all the backbuffers if there are multiple.
const void *initialPixels = PalSurface->pixels;
if (DiabloUiSurface() == PalSurface)
BltFast(nullptr, nullptr);
RenderPresent();
while (PalSurface->pixels != initialPixels) {
DrawCutsceneBackground();
if (DiabloUiSurface() == PalSurface)
BltFast(nullptr, nullptr);
RenderPresent();
}
}
FreeCutsceneBackground();

// The loading thread sets `orig_palette`, so we make sure to use
// our own palette for the fade-in.
PaletteFadeIn(8, ProgressEventHandlerState.palette);
}

void CheckShouldSkipRendering()
{
if (!ProgressEventHandlerState.skipRendering) return;
const bool shouldSkip = ProgressEventHandlerState.loadStartedAt + *sgOptions.Gameplay.skipLoadingScreenThresholdMs > SDL_GetTicks();
if (shouldSkip) return;
ProgressEventHandlerState.skipRendering = false;
if (!HeadlessMode) InitRendering();
}

void ProgressEventHandler(const SDL_Event &event, uint16_t modState)
{
DisableInputEventHandler(event, modState);
if (!IsCustomEvent(event.type)) return;

static uint32_t drawnProgress;
drawnProgress = 0;

const interface_mode customEvent = GetCustomEvent(event.type);
switch (customEvent) {
case WM_PROGRESS:
if (!HeadlessMode && drawnProgress != sgdwProgress) {
if (!HeadlessMode && ProgressEventHandlerState.drawnProgress != sgdwProgress && !ProgressEventHandlerState.skipRendering) {
DrawCutsceneForeground();
drawnProgress = sgdwProgress;
ProgressEventHandlerState.drawnProgress = sgdwProgress;
}
break;
case WM_ERROR:
app_fatal(*static_cast<std::string *>(event.user.data1));
break;
case WM_DONE: {
// We may have loaded a new palette.
// Temporarily switch back to the load screen palette for fade out.
std::array<SDL_Color, 256> prevPalette;
if (!HeadlessMode) {
prevPalette = orig_palette;
orig_palette = ProgressEventHandlerState.palette;
ApplyGamma(logical_palette, orig_palette, 256);
}
NewCursor(CURSOR_HAND);

if (!HeadlessMode) {
assert(ghMainWnd);

if (RenderDirectlyToOutputSurface && PalSurface != nullptr) {
// Ensure that all back buffers have the full progress bar.
const void *initialPixels = PalSurface->pixels;
do {
DrawCutsceneForeground();
if (DiabloUiSurface() == PalSurface)
BltFast(nullptr, nullptr);
RenderPresent();
} while (PalSurface->pixels != initialPixels);
if (!ProgressEventHandlerState.skipRendering) {
NewCursor(CURSOR_HAND);

if (!HeadlessMode) {
assert(ghMainWnd);

if (RenderDirectlyToOutputSurface && PalSurface != nullptr) {
// The loading thread sets `orig_palette`, so we make sure to use
// our own palette for drawing the foreground.
ApplyGamma(logical_palette, ProgressEventHandlerState.palette, 256);

// Ensure that all back buffers have the full progress bar.
const void *initialPixels = PalSurface->pixels;
do {
DrawCutsceneForeground();
if (DiabloUiSurface() == PalSurface)
BltFast(nullptr, nullptr);
RenderPresent();
} while (PalSurface->pixels != initialPixels);
}

// The loading thread sets `orig_palette`, so we make sure to use
// our own palette for the fade-out.
PaletteFadeOut(8, ProgressEventHandlerState.palette);
}

PaletteFadeOut(8);
orig_palette = prevPalette;
ApplyGamma(logical_palette, orig_palette, 256);
}

[[maybe_unused]] EventHandler prevHandler = SetEventHandler(ProgressEventHandlerState.prevHandler);
Expand All @@ -465,7 +496,7 @@ void ProgressEventHandler(const SDL_Event &event, uint16_t modState)

Player &myPlayer = *MyPlayer;
NetSendCmdLocParam2(true, CMD_PLAYER_JOINLEVEL, myPlayer.position.tile, myPlayer.plrlevel, myPlayer.plrIsOnSetLevel ? 1 : 0);
DelayPlrMessages(SDL_GetTicks() - ProgressEventHandlerState.delayStart);
DelayPlrMessages(SDL_GetTicks() - ProgressEventHandlerState.loadStartedAt);

if (gbSomebodyWonGameKludge && myPlayer.isOnLevel(16)) {
PrepDoEnding();
Expand Down Expand Up @@ -552,9 +583,11 @@ void ShowProgress(interface_mode uMsg)
IsProgress = true;
gbSomebodyWonGameKludge = false;

ProgressEventHandlerState.delayStart = SDL_GetTicks();
ProgressEventHandlerState.loadStartedAt = SDL_GetTicks();
ProgressEventHandlerState.prevHandler = SetEventHandler(ProgressEventHandler);
ProgressEventHandlerState.skipRendering = true;
ProgressEventHandlerState.done = false;
ProgressEventHandlerState.drawnProgress = 0;

#ifndef USE_SDL1
DeactivateVirtualGamepad();
Expand All @@ -573,42 +606,30 @@ void ShowProgress(interface_mode uMsg)

BlackPalette();

// Blit the background once and then free it.
// Always load the background (even if we end up skipping rendering it).
// This is because the MPQ archive can only be read by a single thread at a time.
LoadCutsceneBackground(uMsg);
DrawCutsceneBackground();

// Save the palette at this point because the loading process may replace it.
ProgressEventHandlerState.palette = orig_palette;
if (RenderDirectlyToOutputSurface && PalSurface != nullptr) {
// Render into all the backbuffers if there are multiple.
const void *initialPixels = PalSurface->pixels;
if (DiabloUiSurface() == PalSurface)
BltFast(nullptr, nullptr);
RenderPresent();
while (PalSurface->pixels != initialPixels) {
DrawCutsceneBackground();
if (DiabloUiSurface() == PalSurface)
BltFast(nullptr, nullptr);
RenderPresent();
}
}
FreeCutsceneBackground();
}

// Begin loading
static interface_mode loadTarget;
loadTarget = uMsg;
SdlThread loadThread = SdlThread([]() {
const uint32_t start = SDL_GetTicks();
DoLoad(loadTarget);
LogVerbose("Load thread finished in {}ms", SDL_GetTicks() - start);
});

if (!HeadlessMode) {
PaletteFadeIn(8);
}

while (true) {
CheckShouldSkipRendering();
SDL_Event event;
// We use the real `SDL_PollEvent` here instead of `FetchEvent`
// to process real events rather than the recorded ones in demo mode.
while (SDL_PollEvent(&event)) {
CheckShouldSkipRendering();
if (event.type != SDL_QUIT) {
HandleMessage(event, SDL_GetModState());
}
Expand Down
2 changes: 2 additions & 0 deletions Source/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,7 @@ GameplayOptions::GameplayOptions()
{ FloatingNumbers::Random, N_("Random Angles") },
{ FloatingNumbers::Vertical, N_("Vertical Only") },
})
, skipLoadingScreenThresholdMs("Skip loading screen threshold, ms", OptionEntryFlags::Invisible, "", "", 0)
{
grabInput.SetValueChangedCallback(OptionGrabInputChanged);
experienceBar.SetValueChangedCallback(OptionExperienceBarChanged);
Expand Down Expand Up @@ -1012,6 +1013,7 @@ std::vector<OptionEntryBase *> GameplayOptions::GetEntries()
&adriaRefillsMana,
&grabInput,
&pauseOnFocusLoss,
&skipLoadingScreenThresholdMs,
};
}

Expand Down
7 changes: 7 additions & 0 deletions Source/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,13 @@ struct GameplayOptions : OptionCategoryBase {
OptionEntryInt<int> numFullRejuPotionPickup;
/** @brief Enable floating numbers. */
OptionEntryEnum<FloatingNumbers> enableFloatingNumbers;

/**
* @brief If loading takes less than this value, skips displaying the loading screen.
*
* Advanced option, not displayed in the UI.
*/
OptionEntryInt<int> skipLoadingScreenThresholdMs;
};

struct ControllerOptions : OptionCategoryBase {
Expand Down

0 comments on commit 66b0f43

Please sign in to comment.