Skip to content

Commit

Permalink
More rendering refactoring (#1838)
Browse files Browse the repository at this point in the history
* Rename update to resize and move into SoftwareDrawingEngine

* Separate rendering from presenting the final buffer
  • Loading branch information
ZehMatt committed Feb 12, 2023
1 parent d701072 commit 6955eb8
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 116 deletions.
117 changes: 117 additions & 0 deletions src/OpenLoco/src/Drawing/SoftwareDrawingEngine.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "SoftwareDrawingEngine.h"
#include "Config.h"
#include "Ui.h"
#include "Ui/WindowManager.h"
#include <OpenLoco/Console/Console.h>
#include <OpenLoco/Interop/Interop.hpp>
#include <SDL2/SDL.h>
#include <algorithm>
Expand Down Expand Up @@ -32,6 +34,12 @@ namespace OpenLoco::Drawing
}
}

void SoftwareDrawingEngine::initialize(SDL_Window* window)
{
_window = window;
createPalette();
}

// T[m][n]
template<typename T>
class Grid
Expand Down Expand Up @@ -73,6 +81,62 @@ namespace OpenLoco::Drawing
}
};

void SoftwareDrawingEngine::resize(int32_t width, int32_t height)
{
// Scale the width and height by configured scale factor
auto scaleFactor = Config::get().scaleFactor;
width = (int32_t)(width / scaleFactor);
height = (int32_t)(height / scaleFactor);

int32_t widthShift = 6;
int16_t blockWidth = 1 << widthShift;
int32_t heightShift = 3;
int16_t blockHeight = 1 << heightShift;

if (_screenSurface != nullptr)
{
SDL_FreeSurface(_screenSurface);
}
if (_screenRGBASurface != nullptr)
{
SDL_FreeSurface(_screenRGBASurface);
}

_screenSurface = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0);

_screenRGBASurface = SDL_CreateRGBSurface(0, width, height, 32, 0, 0, 0, 0);
SDL_SetSurfaceBlendMode(_screenRGBASurface, SDL_BLENDMODE_NONE);

SDL_SetSurfacePalette(_screenSurface, Gfx::getDrawingEngine().getPalette());

int32_t pitch = _screenSurface->pitch;

auto& rt = Gfx::getScreenRT();
if (rt.bits != nullptr)
{
delete[] rt.bits;
}
rt.bits = new uint8_t[_screenSurface->pitch * height];
rt.width = width;
rt.height = height;
rt.pitch = pitch - width;

_screenInfo->width = width;
_screenInfo->height = height;
_screenInfo->width_2 = width;
_screenInfo->height_2 = height;
_screenInfo->width_3 = width;
_screenInfo->height_3 = height;

_screenInvalidation->blockWidth = blockWidth;
_screenInvalidation->blockHeight = blockHeight;
_screenInvalidation->columnCount = (width / blockWidth) + 1;
_screenInvalidation->rowCount = (height / blockHeight) + 1;
_screenInvalidation->columnShift = widthShift;
_screenInvalidation->rowShift = heightShift;
_screenInvalidation->initialised = 1;
}

/**
* 0x004C5C69
*
Expand Down Expand Up @@ -220,6 +284,59 @@ namespace OpenLoco::Drawing
Ui::WindowManager::render(rt, rect);
}

void SoftwareDrawingEngine::present()
{
// Lock the surface before setting its pixels
if (SDL_MUSTLOCK(_screenSurface))
{
if (SDL_LockSurface(_screenSurface) < 0)
{
return;
}
}

// Copy pixels from the virtual screen buffer to the surface
auto& rt = Gfx::getScreenRT();
if (rt.bits != nullptr)
{
std::memcpy(_screenSurface->pixels, rt.bits, _screenSurface->pitch * _screenSurface->h);
}

// Unlock the surface
if (SDL_MUSTLOCK(_screenSurface))
{
SDL_UnlockSurface(_screenSurface);
}

auto scaleFactor = Config::get().scaleFactor;
if (scaleFactor == 1 || scaleFactor <= 0)
{
if (SDL_BlitSurface(_screenSurface, nullptr, SDL_GetWindowSurface(_window), nullptr))
{
Console::error("SDL_BlitSurface %s", SDL_GetError());
exit(1);
}
}
else
{
// first blit to rgba surface to change the pixel format
if (SDL_BlitSurface(_screenSurface, nullptr, _screenRGBASurface, nullptr))
{
Console::error("SDL_BlitSurface %s", SDL_GetError());
exit(1);
}
// then scale to window size. Without changing to RGBA first, SDL complains
// about blit configurations being incompatible.
if (SDL_BlitScaled(_screenRGBASurface, nullptr, SDL_GetWindowSurface(_window), nullptr))
{
Console::error("SDL_BlitScaled %s", SDL_GetError());
exit(1);
}
}

SDL_UpdateWindowSurface(_window);
}

SoftwareDrawingContext& SoftwareDrawingEngine::getDrawingContext()
{
return _ctx;
Expand Down
14 changes: 13 additions & 1 deletion src/OpenLoco/src/Drawing/SoftwareDrawingEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <cstddef>

struct SDL_Palette;
struct SDL_Surface;
struct SDL_Window;

namespace OpenLoco::Drawing
{
Expand All @@ -25,12 +27,19 @@ namespace OpenLoco::Drawing
public:
~SoftwareDrawingEngine();

void initialize(SDL_Window* window);

void resize(int32_t width, int32_t height);

// Renders all invalidated regions.
void render();

// Renders a specific region.
void render(const Ui::Rect& rect);

// Presents the final image to the screen.
void present();

// Invalidates a region, this forces it to be rendered next frame.
void invalidateRegion(int32_t left, int32_t top, int32_t right, int32_t bottom);

Expand All @@ -43,7 +52,10 @@ namespace OpenLoco::Drawing
private:
void render(size_t x, size_t y, size_t dx, size_t dy);

SDL_Palette* _palette;
SDL_Window* _window{};
SDL_Palette* _palette{};
SDL_Surface* _screenSurface{};
SDL_Surface* _screenRGBASurface{};
SoftwareDrawingContext _ctx;
};
}
126 changes: 11 additions & 115 deletions src/OpenLoco/src/Ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,9 @@ namespace OpenLoco::Ui
std::vector<Resolution> _fsResolutions;

static SDL_Window* window;
static SDL_Surface* surface;
static SDL_Surface* RGBASurface;
static std::map<CursorId, SDL_Cursor*> _cursors;

static void setWindowIcon();
static void update(int32_t width, int32_t height);
static void resize(int32_t width, int32_t height);
static Config::Resolution getDisplayResolutionByMode(Config::ScreenMode mode);

Expand Down Expand Up @@ -176,9 +173,9 @@ namespace OpenLoco::Ui
setWindowIcon();

// Create a palette for the window
Gfx::getDrawingEngine().createPalette();

update(desc.width, desc.height);
auto& drawingEngine = Gfx::getDrawingEngine();
drawingEngine.initialize(window);
drawingEngine.resize(desc.width, desc.height);
}

static void setWindowIcon()
Expand Down Expand Up @@ -345,63 +342,6 @@ namespace OpenLoco::Ui
{
}

// TODO: Move this into rendering engine, this is just resize and not really an update.
void update(int32_t width, int32_t height)
{
// Scale the width and height by configured scale factor
auto scaleFactor = Config::get().scaleFactor;
width = (int32_t)(width / scaleFactor);
height = (int32_t)(height / scaleFactor);

int32_t widthShift = 6;
int16_t blockWidth = 1 << widthShift;
int32_t heightShift = 3;
int16_t blockHeight = 1 << heightShift;

if (surface != nullptr)
{
SDL_FreeSurface(surface);
}
if (RGBASurface != nullptr)
{
SDL_FreeSurface(RGBASurface);
}

surface = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0);

RGBASurface = SDL_CreateRGBSurface(0, width, height, 32, 0, 0, 0, 0);
SDL_SetSurfaceBlendMode(RGBASurface, SDL_BLENDMODE_NONE);

SDL_SetSurfacePalette(surface, Gfx::getDrawingEngine().getPalette());

int32_t pitch = surface->pitch;

auto& rt = Gfx::getScreenRT();
if (rt.bits != nullptr)
{
delete[] rt.bits;
}
rt.bits = new uint8_t[surface->pitch * height];
rt.width = width;
rt.height = height;
rt.pitch = pitch - width;

_screenInfo->width = width;
_screenInfo->height = height;
_screenInfo->width_2 = width;
_screenInfo->height_2 = height;
_screenInfo->width_3 = width;
_screenInfo->height_3 = height;

_screenInvalidation->blockWidth = blockWidth;
_screenInvalidation->blockHeight = blockHeight;
_screenInvalidation->columnCount = (width / blockWidth) + 1;
_screenInvalidation->rowCount = (height / blockHeight) + 1;
_screenInvalidation->columnShift = widthShift;
_screenInvalidation->rowShift = heightShift;
_screenInvalidation->initialised = 1;
}

static void positionChanged(int32_t x, int32_t y)
{
auto displayIndex = SDL_GetWindowDisplayIndex(window);
Expand All @@ -416,7 +356,9 @@ namespace OpenLoco::Ui

void resize(int32_t width, int32_t height)
{
update(width, height);
auto& drawingEngine = Gfx::getDrawingEngine();
drawingEngine.resize(width, height);

Gui::resize();
Gfx::invalidateScreen();

Expand All @@ -443,9 +385,11 @@ namespace OpenLoco::Ui

void render()
{
if (window == nullptr || surface == nullptr)
if (window == nullptr)
return;

auto& drawingEngine = Gfx::getDrawingEngine();

if (!Ui::dirtyBlocksInitialised())
{
return;
Expand All @@ -455,16 +399,7 @@ namespace OpenLoco::Ui

if (!Intro::isActive())
{
Gfx::render();
}

// Lock the surface before setting its pixels
if (SDL_MUSTLOCK(surface))
{
if (SDL_LockSurface(surface) < 0)
{
return;
}
drawingEngine.render();
}

// Draw FPS counter?
Expand All @@ -473,46 +408,7 @@ namespace OpenLoco::Ui
Drawing::drawFPS();
}

// Copy pixels from the virtual screen buffer to the surface
auto& rt = Gfx::getScreenRT();
if (rt.bits != nullptr)
{
std::memcpy(surface->pixels, rt.bits, surface->pitch * surface->h);
}

// Unlock the surface
if (SDL_MUSTLOCK(surface))
{
SDL_UnlockSurface(surface);
}

auto scaleFactor = Config::get().scaleFactor;
if (scaleFactor == 1 || scaleFactor <= 0)
{
if (SDL_BlitSurface(surface, nullptr, SDL_GetWindowSurface(window), nullptr))
{
Console::error("SDL_BlitSurface %s", SDL_GetError());
exit(1);
}
}
else
{
// first blit to rgba surface to change the pixel format
if (SDL_BlitSurface(surface, nullptr, RGBASurface, nullptr))
{
Console::error("SDL_BlitSurface %s", SDL_GetError());
exit(1);
}
// then scale to window size. Without changing to RGBA first, SDL complains
// about blit configurations being incompatible.
if (SDL_BlitScaled(RGBASurface, nullptr, SDL_GetWindowSurface(window), nullptr))
{
Console::error("SDL_BlitScaled %s", SDL_GetError());
exit(1);
}
}

SDL_UpdateWindowSurface(window);
drawingEngine.present();
}

// 0x00406FBA
Expand Down

0 comments on commit 6955eb8

Please sign in to comment.