From 6a1768dd807d115c65acfce30772d63d5d378063 Mon Sep 17 00:00:00 2001 From: Jordan Christiansen Date: Mon, 28 Nov 2022 12:51:26 -0600 Subject: [PATCH] Render to a small texture, then scale up to the screen size To be honest, I thought this is what SDL_RenderSetLogicalSize() did, but actually it scales up each object that you draw with SDL_RenderCopy(). This is necessary because on some GPUs, scaling up can cause a sprite to bleed into an adjacent frame in the spritesheet texture. This shouldn't happen with nearest neighbor scaling, but it nevertheless does happen on some GPUs such as my laptop's embedded Intel GPU. On this GPU I saw a one pixel wide strip on the top and left of every sprite that showed the neighboring sprite frame. The strip was one pixel wide in my screen's pixels, not the logical resolution's pixels. Rendering everything to a 320x240 texture and then scaling up the whole texture at once makes strange bugs like this impossible. --- src/endgame/island.cpp | 2 +- src/game.cpp | 4 ++-- src/graphics/Renderer.cpp | 49 ++++++++++++++++++++++++++++++--------- src/graphics/Renderer.h | 13 ++++++----- src/intro/intro.cpp | 2 +- src/intro/title.cpp | 6 ++--- src/map.cpp | 14 +++++------ src/pause/mods.cpp | 2 +- src/pause/options.cpp | 2 +- src/screeneffect.cpp | 4 ++-- 10 files changed, 63 insertions(+), 35 deletions(-) diff --git a/src/endgame/island.cpp b/src/endgame/island.cpp index 12a45db3..7eb59346 100644 --- a/src/endgame/island.cpp +++ b/src/endgame/island.cpp @@ -80,7 +80,7 @@ void island_draw() { // draw the scene - Renderer::getInstance()->fillScreen(BLACK); + Renderer::getInstance()->clearScreen(BLACK); Renderer::getInstance()->setClip(island.scene_x, island.scene_y, Renderer::getInstance()->sprites.sprites[SPR_ISLAND_SCENE].w, Renderer::getInstance()->sprites.sprites[SPR_ISLAND_SCENE].h); diff --git a/src/game.cpp b/src/game.cpp index 52d8f37b..8c85c63d 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -220,7 +220,7 @@ bool Game::pause(int pausemode, int param) void Game::tick(void) { - Renderer::getInstance()->clearScreen(); + Renderer::getInstance()->clearScreen(BLACK); debug_clear(); if (game.paused) @@ -353,7 +353,7 @@ void DrawScene(void) { int scr_x, scr_y; extern int flipacceltime; - Renderer::getInstance()->fillScreen(BLACK); + Renderer::getInstance()->clearScreen(BLACK); // draw background map tiles if (!flipacceltime) diff --git a/src/graphics/Renderer.cpp b/src/graphics/Renderer.cpp index 701299f8..292a9034 100644 --- a/src/graphics/Renderer.cpp +++ b/src/graphics/Renderer.cpp @@ -69,6 +69,7 @@ bool Renderer::initVideo(int scale) { uint32_t window_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI; + //TODO: sync this with setResolution if (widescreen) { screenWidth = 432; screenHeight = 243; @@ -138,6 +139,9 @@ bool Renderer::initVideo(int scale) return false; } + if (!createRenderTarget(screenWidth, screenHeight)) + return false; + std::string spotpath = ResourceManager::getInstance()->getPath("spot.png"); SDL_Surface *image; @@ -148,6 +152,24 @@ bool Renderer::initVideo(int scale) return true; } +bool Renderer::createRenderTarget(int width, int height) +{ + SDL_RendererInfo info; + SDL_GetRendererInfo(_renderer, &info); + + _texture = SDL_CreateTexture(_renderer, + info.texture_formats[0], + SDL_TEXTUREACCESS_TARGET, + width, height); + if (SDL_SetRenderTarget(_renderer, _texture)) { + LOG_ERROR("Renderer::createRenderTarget: SDL_SetRenderTarget failed: {}", SDL_GetError()); + return false; + } + SDL_RenderClear(_renderer); + + return true; +} + bool Renderer::flushAll() { LOG_DEBUG("Renderer::flushAll()"); @@ -200,18 +222,24 @@ bool Renderer::setResolution(int scale, bool newWidescreen) LOG_INFO("Renderer logical resolution: {}x{}", newWidth, newHeight); SDL_SetWindowSize(_window, newWidth * scale, newHeight * scale); + + SDL_SetRenderTarget(_renderer, NULL); + SDL_DestroyTexture(_texture); if (SDL_RenderSetLogicalSize(_renderer, newWidth, newHeight)) { LOG_ERROR("Renderer::setResolution: SDL_RenderSetLogicalSize failed: {}", SDL_GetError()); return false; } + if (!flushAll()) + return false; + + if (!createRenderTarget(newWidth, newHeight)) + return false; + screenWidth = newWidth; screenHeight = newHeight; widescreen = newWidescreen; - if (!flushAll()) - return false; - recalc_map_offsets(); textbox.RecalculateOffsets(); @@ -237,7 +265,7 @@ void Renderer::showLoadingScreen() int x = (screenWidth / 2) - (loading.width() / 2); int y = (screenHeight / 2) - loading.height(); - fillScreen(BLACK); + clearScreen(BLACK); drawSurface(&loading, x, y); flip(); } @@ -252,16 +280,15 @@ SDL_Window* Renderer::window() return _window; } -void Renderer::clearScreen() -{ - SDL_SetRenderDrawColor(_renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); - SDL_RenderClear(_renderer); -} - void Renderer::flip() { // LOG_INFO("===FLIPPING===\n"); + SDL_SetRenderTarget(_renderer, NULL); + SDL_SetRenderDrawColor(_renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_RenderClear(_renderer); + SDL_RenderCopy(_renderer, _texture, NULL, NULL); SDL_RenderPresent(_renderer); + SDL_SetRenderTarget(_renderer, _texture); // LOG_INFO("===FLIPPED===\n"); } @@ -390,7 +417,7 @@ void Renderer::drawPixel(int x, int y, uint8_t r, uint8_t g, uint8_t b) fillRect(x, y, x, y, r, g, b); } -void Renderer::fillScreen(uint8_t r, uint8_t g, uint8_t b) +void Renderer::clearScreen(uint8_t r, uint8_t g, uint8_t b) { SDL_SetRenderDrawColor(_renderer, r, g, b, SDL_ALPHA_OPAQUE); SDL_RenderFillRect(_renderer, NULL); diff --git a/src/graphics/Renderer.h b/src/graphics/Renderer.h index 59939012..088d254b 100644 --- a/src/graphics/Renderer.h +++ b/src/graphics/Renderer.h @@ -48,16 +48,14 @@ class Renderer void showLoadingScreen(); - void clearScreen(); - void drawSurface(Surface *src, int x, int y); void drawSurface(Surface *src, int dstx, int dsty, int srcx, int srcy, int wd, int ht); void drawSurfaceMirrored(Surface *src, int dstx, int dsty, int srcx, int srcy, int wd, int ht); void blitPatternAcross(Surface *sfc, int x_dst, int y_dst, int y_src, int height); - void fillScreen(NXColor color); - void fillScreen(uint8_t r, uint8_t g, uint8_t b); + void clearScreen(NXColor color); + void clearScreen(uint8_t r, uint8_t g, uint8_t b); void drawLine(int x1, int y1, int x2, int y2, NXColor color); @@ -96,12 +94,15 @@ class Renderer private: SDL_Window *_window = nullptr; SDL_Renderer *_renderer = nullptr; + SDL_Texture *_texture = nullptr; int _current_res = -1; bool _need_clip = false; SDL_Rect _clip_rect; SDL_Texture* _spot_light; bool _fullscreen = false; + bool createRenderTarget(int width, int height); + protected: friend class Singleton; @@ -151,9 +152,9 @@ void inline Renderer::drawPixel(int x, int y, NXColor color) drawPixel(x, y, color.r, color.g, color.b); } -void inline Renderer::fillScreen(NXColor color) +void inline Renderer::clearScreen(NXColor color) { - fillScreen(color.r, color.g, color.b); + clearScreen(color.r, color.g, color.b); } void inline Renderer::setClip(NXRect *rect) diff --git a/src/intro/intro.cpp b/src/intro/intro.cpp index 85b8d907..f591d860 100644 --- a/src/intro/intro.cpp +++ b/src/intro/intro.cpp @@ -43,7 +43,7 @@ void intro_tick() } if (blanktimer > 0) { - Renderer::getInstance()->fillScreen(BLACK); + Renderer::getInstance()->clearScreen(BLACK); if (--blanktimer == 0) game.setmode(GM_TITLE); diff --git a/src/intro/title.cpp b/src/intro/title.cpp index 6229aa3c..6cea97cf 100644 --- a/src/intro/title.cpp +++ b/src/intro/title.cpp @@ -62,7 +62,7 @@ std::vector _menuitems; static void draw_title() { // background is dk grey, not pure black - Renderer::getInstance()->fillScreen(0x20, 0x20, 0x20); + Renderer::getInstance()->clearScreen(0x20, 0x20, 0x20); map_draw_backdrop(); // DrawFastLeftLayered(); @@ -343,7 +343,7 @@ void title_tick() { if (title.seldelay > 0) { - Renderer::getInstance()->fillScreen(BLACK); + Renderer::getInstance()->clearScreen(BLACK); title.seldelay--; if (!title.seldelay) @@ -357,7 +357,7 @@ void title_tick() } else { - Renderer::getInstance()->fillScreen(BLACK); + Renderer::getInstance()->clearScreen(BLACK); if (!textbox.SaveSelect.IsVisible()) { // selection was made, and settings.last_save_slot is now set appropriately diff --git a/src/map.cpp b/src/map.cpp index dfe4dd72..94ec6376 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -588,9 +588,9 @@ void map_draw_backdrop(void) case BK_HIDE3: { if (game.curmap == STAGE_KINGS) // intro cutscene - Renderer::getInstance()->fillScreen(BLACK); + Renderer::getInstance()->clearScreen(BLACK); else - Renderer::getInstance()->fillScreen(DK_BLUE); + Renderer::getInstance()->clearScreen(DK_BLUE); } return; @@ -644,15 +644,15 @@ void DrawFastLeftLayered(void) y1 = x = 0; // fix for extra height if (map.backdrop == 9) - Renderer::getInstance()->fillScreen(111, 156, 214); + Renderer::getInstance()->clearScreen(111, 156, 214); else if (map.backdrop == 10 && game.curmap != 64) - Renderer::getInstance()->fillScreen(107, 105, 82); + Renderer::getInstance()->clearScreen(107, 105, 82); else if (map.backdrop == 12) - Renderer::getInstance()->fillScreen(179, 190, 210); + Renderer::getInstance()->clearScreen(179, 190, 210); else if (map.backdrop == 13) - Renderer::getInstance()->fillScreen(170, 101, 0); + Renderer::getInstance()->clearScreen(170, 101, 0); else if (map.backdrop == 14) - Renderer::getInstance()->fillScreen(202, 97, 97); + Renderer::getInstance()->clearScreen(202, 97, 97); for (i = 0; i < nlayers; i++) { diff --git a/src/pause/mods.cpp b/src/pause/mods.cpp index 9c3f83c5..4e3ac62b 100644 --- a/src/pause/mods.cpp +++ b/src/pause/mods.cpp @@ -72,7 +72,7 @@ bool mods_init(int param) void mods_tick() { if (moddlg != NULL) { - Renderer::getInstance()->fillScreen(BLACK); + Renderer::getInstance()->clearScreen(BLACK); moddlg->Draw(); moddlg->RunInput(); } diff --git a/src/pause/options.cpp b/src/pause/options.cpp index 0aa50887..e1507fcf 100644 --- a/src/pause/options.cpp +++ b/src/pause/options.cpp @@ -113,7 +113,7 @@ void options_tick() unsigned int i; FocusHolder *fh; - Renderer::getInstance()->fillScreen(BLACK); + Renderer::getInstance()->clearScreen(BLACK); Options::run_and_draw_objects(); fh = (FocusHolder *)optionstack.at(optionstack.size() - 1); diff --git a/src/screeneffect.cpp b/src/screeneffect.cpp index a091ccad..b73a69a8 100644 --- a/src/screeneffect.cpp +++ b/src/screeneffect.cpp @@ -41,7 +41,7 @@ void SE_FlashScreen::Draw(void) } if (flashstate) - Renderer::getInstance()->fillScreen(0xff, 0xff, 0xff); + Renderer::getInstance()->clearScreen(0xff, 0xff, 0xff); } /* @@ -170,7 +170,7 @@ void SE_Fade::Draw(void) } else if (state == FS_FADED_OUT) { - Renderer::getInstance()->fillScreen(DK_BLUE); + Renderer::getInstance()->clearScreen(DK_BLUE); return; }