1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-21 21:17:49 +02:00

Offloaded xbrz upscaling to background threads

This commit is contained in:
Ivan Savenko 2025-01-21 21:14:49 +00:00
parent 391986e0ba
commit c3fb76b56f
4 changed files with 142 additions and 21 deletions

View File

@ -116,8 +116,13 @@ public:
virtual void exportBitmap(const boost::filesystem::path & path, SDL_Palette * palette) const = 0; virtual void exportBitmap(const boost::filesystem::path & path, SDL_Palette * palette) const = 0;
virtual bool isTransparent(const Point & coords) const = 0; virtual bool isTransparent(const Point & coords) const = 0;
virtual Rect contentRect() const = 0; virtual Rect contentRect() const = 0;
virtual void scaledDraw(SDL_Surface * where, SDL_Palette * palette, const Point & scaling, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const = 0;
virtual void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const = 0; virtual void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const = 0;
/// Returns true if this image is still loading and can't be used
virtual bool isLoading() const = 0;
virtual ~ISharedImage() = default; virtual ~ISharedImage() = default;
virtual const SDL_Palette * getPalette() const = 0; virtual const SDL_Palette * getPalette() const = 0;

View File

@ -21,8 +21,11 @@
#include "../render/IScreenHandler.h" #include "../render/IScreenHandler.h"
#include <tbb/parallel_for.h> #include <tbb/parallel_for.h>
#include <SDL_surface.h> #include <tbb/task_arena.h>
#include <SDL_image.h> #include <SDL_image.h>
#include <SDL_surface.h>
#include <SDL_version.h>
class SDLImageLoader; class SDLImageLoader;
@ -88,8 +91,70 @@ SDLImageShared::SDLImageShared(const ImagePath & filename)
} }
} }
void SDLImageShared::scaledDraw(SDL_Surface * where, SDL_Palette * palette, const Point & scaleTo, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const
{
assert(upscalingInProgress == false);
if (!surf)
return;
Rect sourceRect(0, 0, surf->w, surf->h);
Point destShift(0, 0);
Point destScale = Point(surf->w, surf->h) * scaleTo / dimensions();
Point marginsScaled = margins * scaleTo / dimensions();
if(src)
{
Rect srcUnscaled(Point(src->topLeft() * dimensions() / scaleTo), Point(src->dimensions() * dimensions() / scaleTo));
if(srcUnscaled.x < margins.x)
destShift.x += marginsScaled.x - src->x;
if(srcUnscaled.y < margins.y)
destShift.y += marginsScaled.y - src->y;
sourceRect = Rect(srcUnscaled).intersect(Rect(margins.x, margins.y, surf->w, surf->h));
destScale.x = std::min(destScale.x, sourceRect.w * scaleTo.x / dimensions().x);
destScale.y = std::min(destScale.y, sourceRect.h * scaleTo.y / dimensions().y);
sourceRect -= margins;
}
else
destShift = marginsScaled;
destShift += dest;
SDL_SetSurfaceColorMod(surf, colorMultiplier.r, colorMultiplier.g, colorMultiplier.b);
SDL_SetSurfaceAlphaMod(surf, alpha);
if (alpha != SDL_ALPHA_OPAQUE || (mode != EImageBlitMode::OPAQUE && surf->format->Amask != 0))
SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_BLEND);
else
SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_NONE);
if (palette && surf->format->palette)
SDL_SetSurfacePalette(surf, palette);
SDL_Rect srcRect = CSDL_Ext::toSDL(sourceRect);
SDL_Rect dstRect = CSDL_Ext::toSDL(Rect(destShift, destScale));
if (sourceRect.dimensions() * scaleTo / dimensions() != destScale)
logGlobal->info("???");
SDL_Surface * tempSurface = SDL_ConvertSurface(surf, where->format, 0);
int result = SDL_BlitScaled(tempSurface, &srcRect, where, &dstRect);
SDL_FreeSurface(tempSurface);
if (result != 0)
logGlobal->error("SDL_BlitScaled failed! %s", SDL_GetError());
if (surf->format->palette)
SDL_SetSurfacePalette(surf, originalPalette);
}
void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const
{ {
assert(upscalingInProgress == false);
if (!surf) if (!surf)
return; return;
@ -140,6 +205,7 @@ void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Poin
void SDLImageShared::optimizeSurface() void SDLImageShared::optimizeSurface()
{ {
assert(upscalingInProgress == false);
if (!surf) if (!surf)
return; return;
@ -155,6 +221,7 @@ void SDLImageShared::optimizeSurface()
std::shared_ptr<const ISharedImage> SDLImageShared::scaleInteger(int factor, SDL_Palette * palette, EImageBlitMode mode) const std::shared_ptr<const ISharedImage> SDLImageShared::scaleInteger(int factor, SDL_Palette * palette, EImageBlitMode mode) const
{ {
assert(upscalingInProgress == false);
if (factor <= 0) if (factor <= 0)
throw std::runtime_error("Unable to scale by integer value of " + std::to_string(factor)); throw std::runtime_error("Unable to scale by integer value of " + std::to_string(factor));
@ -164,32 +231,50 @@ std::shared_ptr<const ISharedImage> SDLImageShared::scaleInteger(int factor, SDL
if (palette && surf->format->palette) if (palette && surf->format->palette)
SDL_SetSurfacePalette(surf, palette); SDL_SetSurfacePalette(surf, palette);
SDLImageScaler scaler(surf, Rect(margins, fullSize)); // simple heuristics to differentiate tileable UI elements from map object / combat assets
EScalingAlgorithm algorithm;
// dump heuristics to differentiate tileable UI elements from map object / combat assets
if (mode == EImageBlitMode::OPAQUE || mode == EImageBlitMode::COLORKEY || mode == EImageBlitMode::SIMPLE) if (mode == EImageBlitMode::OPAQUE || mode == EImageBlitMode::COLORKEY || mode == EImageBlitMode::SIMPLE)
scaler.scaleSurfaceIntegerFactor(GH.screenHandler().getScalingFactor(), EScalingAlgorithm::XBRZ_OPAQUE); algorithm = EScalingAlgorithm::XBRZ_OPAQUE;
else else
scaler.scaleSurfaceIntegerFactor(GH.screenHandler().getScalingFactor(), EScalingAlgorithm::XBRZ_ALPHA); algorithm = EScalingAlgorithm::XBRZ_ALPHA;
SDL_Surface * scaled = scaler.acquireResultSurface(); auto result = std::make_shared<SDLImageShared>(this, factor, algorithm);
auto ret = std::make_shared<SDLImageShared>(scaled);
ret->fullSize = scaler.getResultDimensions().dimensions();
ret->margins = scaler.getResultDimensions().topLeft();
// erase our own reference
SDL_FreeSurface(scaled);
if (surf->format->palette) if (surf->format->palette)
SDL_SetSurfacePalette(surf, originalPalette); SDL_SetSurfacePalette(surf, originalPalette);
return ret; return result;
}
SDLImageShared::SDLImageShared(const SDLImageShared * from, int integerScaleFactor, EScalingAlgorithm algorithm)
{
static tbb::task_arena upscalingArena;
upscalingInProgress = true;
auto scaler = std::make_shared<SDLImageScaler>(from->surf, Rect(from->margins, from->fullSize));
const auto & scalingTask = [this, algorithm, scaler]()
{
scaler->scaleSurfaceIntegerFactor(GH.screenHandler().getScalingFactor(), algorithm);
surf = scaler->acquireResultSurface();
fullSize = scaler->getResultDimensions().dimensions();
margins = scaler->getResultDimensions().topLeft();
upscalingInProgress = false;
};
upscalingArena.enqueue(scalingTask);
}
bool SDLImageShared::isLoading() const
{
return upscalingInProgress;
} }
std::shared_ptr<const ISharedImage> SDLImageShared::scaleTo(const Point & size, SDL_Palette * palette) const std::shared_ptr<const ISharedImage> SDLImageShared::scaleTo(const Point & size, SDL_Palette * palette) const
{ {
assert(upscalingInProgress == false);
if (palette && surf->format->palette) if (palette && surf->format->palette)
SDL_SetSurfacePalette(surf, palette); SDL_SetSurfacePalette(surf, palette);
@ -221,6 +306,7 @@ std::shared_ptr<const ISharedImage> SDLImageShared::scaleTo(const Point & size,
void SDLImageShared::exportBitmap(const boost::filesystem::path& path, SDL_Palette * palette) const void SDLImageShared::exportBitmap(const boost::filesystem::path& path, SDL_Palette * palette) const
{ {
assert(upscalingInProgress == false);
if (!surf) if (!surf)
return; return;
@ -233,6 +319,7 @@ void SDLImageShared::exportBitmap(const boost::filesystem::path& path, SDL_Palet
bool SDLImageShared::isTransparent(const Point & coords) const bool SDLImageShared::isTransparent(const Point & coords) const
{ {
assert(upscalingInProgress == false);
if (surf) if (surf)
return CSDL_Ext::isTransparent(surf, coords.x - margins.x, coords.y - margins.y); return CSDL_Ext::isTransparent(surf, coords.x - margins.x, coords.y - margins.y);
else else
@ -241,6 +328,7 @@ bool SDLImageShared::isTransparent(const Point & coords) const
Rect SDLImageShared::contentRect() const Rect SDLImageShared::contentRect() const
{ {
assert(upscalingInProgress == false);
auto tmpMargins = margins; auto tmpMargins = margins;
auto tmpSize = Point(surf->w, surf->h); auto tmpSize = Point(surf->w, surf->h);
return Rect(tmpMargins, tmpSize); return Rect(tmpMargins, tmpSize);
@ -248,6 +336,7 @@ Rect SDLImageShared::contentRect() const
const SDL_Palette * SDLImageShared::getPalette() const const SDL_Palette * SDLImageShared::getPalette() const
{ {
assert(upscalingInProgress == false);
if (!surf) if (!surf)
return nullptr; return nullptr;
return surf->format->palette; return surf->format->palette;
@ -255,11 +344,13 @@ const SDL_Palette * SDLImageShared::getPalette() const
Point SDLImageShared::dimensions() const Point SDLImageShared::dimensions() const
{ {
assert(upscalingInProgress == false);
return fullSize; return fullSize;
} }
std::shared_ptr<const ISharedImage> SDLImageShared::horizontalFlip() const std::shared_ptr<const ISharedImage> SDLImageShared::horizontalFlip() const
{ {
assert(upscalingInProgress == false);
if (!surf) if (!surf)
return shared_from_this(); return shared_from_this();
@ -275,6 +366,7 @@ std::shared_ptr<const ISharedImage> SDLImageShared::horizontalFlip() const
std::shared_ptr<const ISharedImage> SDLImageShared::verticalFlip() const std::shared_ptr<const ISharedImage> SDLImageShared::verticalFlip() const
{ {
assert(upscalingInProgress == false);
if (!surf) if (!surf)
return shared_from_this(); return shared_from_this();
@ -291,6 +383,7 @@ std::shared_ptr<const ISharedImage> SDLImageShared::verticalFlip() const
// Keep the original palette, in order to do color switching operation // Keep the original palette, in order to do color switching operation
void SDLImageShared::savePalette() void SDLImageShared::savePalette()
{ {
assert(upscalingInProgress == false);
// For some images that don't have palette, skip this // For some images that don't have palette, skip this
if(surf->format->palette == nullptr) if(surf->format->palette == nullptr)
return; return;

View File

@ -27,14 +27,16 @@ struct SDL_Palette;
class SDLImageShared final : public ISharedImage, public std::enable_shared_from_this<SDLImageShared>, boost::noncopyable class SDLImageShared final : public ISharedImage, public std::enable_shared_from_this<SDLImageShared>, boost::noncopyable
{ {
//Surface without empty borders //Surface without empty borders
SDL_Surface * surf; SDL_Surface * surf = nullptr;
SDL_Palette * originalPalette; SDL_Palette * originalPalette = nullptr;
//size of left and top borders //size of left and top borders
Point margins; Point margins;
//total size including borders //total size including borders
Point fullSize; Point fullSize;
std::atomic_bool upscalingInProgress = false;
// Keep the original palette, in order to do color switching operation // Keep the original palette, in order to do color switching operation
void savePalette(); void savePalette();
@ -47,8 +49,11 @@ public:
SDLImageShared(const ImagePath & filename); SDLImageShared(const ImagePath & filename);
//Create using existing surface, extraRef will increase refcount on SDL_Surface //Create using existing surface, extraRef will increase refcount on SDL_Surface
SDLImageShared(SDL_Surface * from); SDLImageShared(SDL_Surface * from);
/// Creates image at specified scaling factor from source image
SDLImageShared(const SDLImageShared * from, int integerScaleFactor, EScalingAlgorithm algorithm);
~SDLImageShared(); ~SDLImageShared();
void scaledDraw(SDL_Surface * where, SDL_Palette * palette, const Point & scaling, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const override;
void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const override; void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const override;
void exportBitmap(const boost::filesystem::path & path, SDL_Palette * palette) const override; void exportBitmap(const boost::filesystem::path & path, SDL_Palette * palette) const override;
@ -56,6 +61,8 @@ public:
bool isTransparent(const Point & coords) const override; bool isTransparent(const Point & coords) const override;
Rect contentRect() const override; Rect contentRect() const override;
bool isLoading() const override;
const SDL_Palette * getPalette() const override; const SDL_Palette * getPalette() const override;
[[nodiscard]] std::shared_ptr<const ISharedImage> horizontalFlip() const override; [[nodiscard]] std::shared_ptr<const ISharedImage> horizontalFlip() const override;

View File

@ -230,7 +230,7 @@ Rect ScalableImageShared::contentRect() const
void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Rect * src, const ScalableImageParameters & parameters, int scalingFactor) void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Rect * src, const ScalableImageParameters & parameters, int scalingFactor)
{ {
const auto & flipAndDraw = [&](FlippedImages & images, const ColorRGBA & colorMultiplier, uint8_t alphaValue){ const auto & getFlippedImage = [&](FlippedImages & images){
int index = 0; int index = 0;
if (parameters.flipVertical) if (parameters.flipVertical)
{ {
@ -248,7 +248,12 @@ void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Re
index |= 2; index |= 2;
} }
images[index]->draw(where, parameters.palette, dest, src, colorMultiplier, alphaValue, locator.layer); return images[index];
};
const auto & flipAndDraw = [&](FlippedImages & images, const ColorRGBA & colorMultiplier, uint8_t alphaValue){
getFlippedImage(images)->draw(where, parameters.palette, dest, src, colorMultiplier, alphaValue, locator.layer);
}; };
if (scalingFactor == 1) if (scalingFactor == 1)
@ -257,12 +262,23 @@ void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Re
} }
else else
{ {
bool shadowLoading = scaled.at(scalingFactor).shadow.at(0) && scaled.at(scalingFactor).shadow.at(0)->isLoading();
bool bodyLoading = scaled.at(scalingFactor).body.at(0) && scaled.at(scalingFactor).body.at(0)->isLoading();
bool overlayLoading = scaled.at(scalingFactor).overlay.at(0) && scaled.at(scalingFactor).overlay.at(0)->isLoading();
bool playerLoading = parameters.player.isValidPlayer() && scaled.at(scalingFactor).playerColored.at(parameters.player.getNum()) && scaled.at(scalingFactor).playerColored.at(parameters.player.getNum())->isLoading();
if (shadowLoading || bodyLoading || overlayLoading || playerLoading)
{
getFlippedImage(base)->scaledDraw(where, parameters.palette, dimensions() * scalingFactor, dest, src, parameters.colorMultiplier, parameters.alphaValue, locator.layer);
return;
}
if (scaled.at(scalingFactor).shadow.at(0)) if (scaled.at(scalingFactor).shadow.at(0))
flipAndDraw(scaled.at(scalingFactor).shadow, Colors::WHITE_TRUE, parameters.alphaValue); flipAndDraw(scaled.at(scalingFactor).shadow, Colors::WHITE_TRUE, parameters.alphaValue);
if (parameters.player.isValidPlayer()) if (parameters.player.isValidPlayer())
{ {
scaled.at(scalingFactor).playerColored[parameters.player.getNum()]->draw(where, parameters.palette, dest, src, Colors::WHITE_TRUE, parameters.alphaValue, locator.layer); scaled.at(scalingFactor).playerColored.at(parameters.player.getNum())->draw(where, parameters.palette, dest, src, Colors::WHITE_TRUE, parameters.alphaValue, locator.layer);
} }
else else
{ {