Some video refactoring, but render-to-texture still not working.

This commit is contained in:
Stephen Anthony 2025-05-27 18:01:13 -02:30
parent d2436a2637
commit 6c815186a6
16 changed files with 79 additions and 155 deletions

View File

@ -234,9 +234,7 @@ void Bezel::apply()
// Note: Variable bezel window positions are handled in VideoModeHandler::Mode
// Enable blending to allow overlaying the bezel over the TIA output
mySurface->attributes().blending = true;
mySurface->attributes().blendalpha = 100;
mySurface->applyAttributes();
mySurface->setBlendLevel(100);
mySurface->setVisible(true);
}
else

View File

@ -47,7 +47,7 @@ FBBackendSDL::FBBackendSDL(OSystem& osystem)
// It's done this way (vs directly accessing a FBSurfaceSDL object)
// since the structure may be needed before any FBSurface's have
// been created
myPixelFormat = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_ARGB8888);
myPixelFormat = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_RGBA32);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -582,7 +582,7 @@ void FBBackendSDL::setWindowIcon()
ASSERT_MAIN_THREAD;
SDL_Surface* surface =
SDL_CreateSurfaceFrom(32, 32, SDL_PIXELFORMAT_ARGB8888, stella_icon, 32 * 4);
SDL_CreateSurfaceFrom(32, 32, SDL_PIXELFORMAT_RGBA32, stella_icon, 32 * 4);
SDL_SetWindowIcon(myWindow, surface);
SDL_DestroySurface(surface);
#endif
@ -596,8 +596,11 @@ unique_ptr<FBSurface> FBBackendSDL::createSurface(
const uInt32* data
) const
{
return make_unique<FBSurfaceSDL>
(const_cast<FBBackendSDL&>(*this), w, h, inter, data);
unique_ptr<FBSurface> s = make_unique<FBSurfaceSDL>
(const_cast<FBBackendSDL&>(*this), w, h, inter, data);
s->setBlendLevel(100); // by default, disable shading (use full alpha)
return s;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -232,7 +232,6 @@ void FBSurfaceSDL::createSurface(uInt32 width, uInt32 height, const uInt32* data
// Create a surface in the same format as the parent GL class
const SDL_PixelFormatDetails& pf = myBackend.pixelFormat();
mySurface = SDL_CreateSurface(width, height, pf.format);
//SDL_SetSurfaceBlendMode(mySurface, SDL_BLENDMODE_ADD); // default: SDL_BLENDMODE_BLEND
// We start out with the src and dst rectangles containing the same
// dimensions, indicating no scaling or re-positioning
@ -258,28 +257,30 @@ void FBSurfaceSDL::createSurface(uInt32 width, uInt32 height, const uInt32* data
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::reinitializeBlitter(bool force)
{
if (force)
if(force)
myBlitter.reset();
if (!myBlitter && myBackend.isInitialized())
if(!myBlitter && myBackend.isInitialized())
myBlitter = BlitterFactory::createBlitter(
myBackend, scalingAlgorithm(myInterpolationMode));
if (myBlitter)
myBlitter->reinitialize(mySrcR, myDstR, myAttributes,
if(myBlitter)
myBlitter->reinitialize(mySrcR, myDstR, myBlendLevel,
myIsStatic ? mySurface : nullptr);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::applyAttributes()
void FBSurfaceSDL::setBlendLevel(uInt32 percent)
{
myBlendLevel = BSPF::clamp(percent, 0U, 100U);
reinitializeBlitter();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::setScalingInterpolation(ScalingInterpolation interpolation)
{
if (interpolation == ScalingInterpolation::sharp &&
if(interpolation == ScalingInterpolation::sharp &&
(
static_cast<int>(mySrcGUIR.h()) >= myBackend.scaleY(myDstGUIR.h()) ||
static_cast<int>(mySrcGUIR.w()) >= myBackend.scaleX(myDstGUIR.w())
@ -287,7 +288,8 @@ void FBSurfaceSDL::setScalingInterpolation(ScalingInterpolation interpolation)
)
interpolation = ScalingInterpolation::blur;
if (interpolation == myInterpolationMode) return;
if(interpolation == myInterpolationMode)
return;
myInterpolationMode = interpolation;
reload();

View File

@ -63,11 +63,9 @@ class FBSurfaceSDL : public FBSurface
void reload() override;
void resize(uInt32 width, uInt32 height) override;
void setBlendLevel(uInt32 percent) override;
void setScalingInterpolation(ScalingInterpolation) override;
protected:
void applyAttributes() override;
private:
bool setSrcPosInternal(uInt32 x, uInt32 y) {
if(std::cmp_not_equal(x, mySrcR.x) || std::cmp_not_equal(y, mySrcR.y))

View File

@ -34,10 +34,8 @@ BilinearBlitter::~BilinearBlitter()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void BilinearBlitter::reinitialize(
SDL_Rect srcRect,
SDL_Rect destRect,
FBSurface::Attributes attributes,
SDL_Surface* staticData
SDL_Rect srcRect, SDL_Rect destRect,
uInt8 blendLevel, SDL_Surface* staticData
)
{
myRecreateTextures = myRecreateTextures || !(
@ -45,11 +43,11 @@ void BilinearBlitter::reinitialize(
mySrcRect.h == srcRect.h &&
myDstRect.w == myFB.scaleX(destRect.w) &&
myDstRect.h == myFB.scaleY(destRect.h) &&
attributes == myAttributes &&
blendLevel == myBlendLevel &&
myStaticData == staticData
);
myAttributes = attributes;
myBlendLevel = blendLevel;
myStaticData = staticData;
mySrcRect = srcRect;
@ -135,15 +133,12 @@ void BilinearBlitter::recreateTexturesIfNecessary()
SDL_UpdateTexture(myTexture, nullptr, myStaticData->pixels, myStaticData->pitch);
}
if (myAttributes.blending) {
const std::array<SDL_Texture*, 2> textures = { myTexture, mySecondaryTexture };
for (SDL_Texture* texture: textures) {
if (!texture) continue;
const std::array<SDL_Texture*, 2> textures = { myTexture, mySecondaryTexture };
for (SDL_Texture* texture: textures) {
if (!texture) continue;
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
const auto blendAlpha = static_cast<uInt8>(myAttributes.blendalpha * 2.55);
SDL_SetTextureAlphaMod(texture, blendAlpha);
}
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(texture, myBlendLevel * 2.55);
}
myRecreateTextures = false;

View File

@ -32,10 +32,8 @@ class BilinearBlitter : public Blitter {
~BilinearBlitter() override;
void reinitialize(
SDL_Rect srcRect,
SDL_Rect destRect,
FBSurface::Attributes attributes,
SDL_Surface* staticData = nullptr
SDL_Rect srcRect, SDL_Rect destRect,
uInt8 blendLevel, SDL_Surface* staticData = nullptr
) override;
void blit(SDL_Surface& surface) override;
@ -43,12 +41,11 @@ class BilinearBlitter : public Blitter {
private:
FBBackendSDL& myFB;
SDL_Texture* myTexture{nullptr};
SDL_Texture* mySecondaryTexture{nullptr};
SDL_Texture *myTexture{nullptr}, *mySecondaryTexture{nullptr};
SDL_Rect mySrcRect{0, 0, 0, 0}, myDstRect{0, 0, 0, 0};
SDL_FRect mySrcFRect{0.F, 0.F, 0.F, 0.F}, myDstFRect{0.F, 0.F, 0.F, 0.F};
FBSurface::Attributes myAttributes;
uInt8 myBlendLevel{100};
bool myInterpolate{false};
bool myTexturesAreAllocated{false};
bool myRecreateTextures{false};

View File

@ -29,10 +29,8 @@ class Blitter {
virtual ~Blitter() = default;
virtual void reinitialize(
SDL_Rect srcRect,
SDL_Rect destRect,
FBSurface::Attributes attributes,
SDL_Surface* staticData = nullptr
SDL_Rect srcRect, SDL_Rect destRect,
uInt8 blendLevel, SDL_Surface* staticData = nullptr
) = 0;
virtual void blit(SDL_Surface& surface) = 0;

View File

@ -19,19 +19,6 @@
#include "ThreadDebugging.hxx"
#include "QisBlitter.hxx"
static void P(const char* str, const SDL_Rect r, const SDL_FRect fr)
{
cerr << str << ": "
<< r.x << "," << r.y << " " << r.w << "x" << r.h << " => "
<< fr.x << "," << fr.y << " " << fr.w << "x" << fr.h << '\n';
}
static void PF(const char* str, const SDL_FRect r, const SDL_FRect fr)
{
cerr << str << ": "
<< r.x << "," << r.y << " " << r.w << "x" << r.h << " => "
<< fr.x << "," << fr.y << " " << fr.w << "x" << fr.h << '\n';
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QisBlitter::QisBlitter(FBBackendSDL& fb)
: myFB{fb}
@ -54,10 +41,8 @@ bool QisBlitter::isSupported(const FBBackendSDL& fb)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void QisBlitter::reinitialize(
SDL_Rect srcRect,
SDL_Rect destRect,
FBSurface::Attributes attributes,
SDL_Surface* staticData
SDL_Rect srcRect, SDL_Rect destRect,
uInt8 blendLevel, SDL_Surface* staticData
)
{
myRecreateTextures = myRecreateTextures || !(
@ -65,11 +50,11 @@ void QisBlitter::reinitialize(
mySrcRect.h == srcRect.h &&
myDstRect.w == myFB.scaleX(destRect.w) &&
myDstRect.h == myFB.scaleY(destRect.h) &&
attributes == myAttributes &&
blendLevel == myBlendLevel &&
myStaticData == staticData
);
myAttributes = attributes;
myBlendLevel = blendLevel;
myStaticData = staticData;
mySrcRect = srcRect;
@ -120,6 +105,7 @@ void QisBlitter::blit(SDL_Surface& surface)
myIntermediateTexture = mySecondaryIntermediateTexture;
mySecondaryIntermediateTexture = intermediateTexture;
// std::swap(mySrcTexture, mySecondarySrcTexture);
SDL_Texture* temporary = mySrcTexture;
mySrcTexture = mySecondarySrcTexture;
mySecondarySrcTexture = temporary;
@ -200,18 +186,14 @@ void QisBlitter::recreateTexturesIfNecessary()
blitToIntermediate();
}
if (myAttributes.blending) {
const auto blendAlpha = static_cast<uInt8>(myAttributes.blendalpha * 2.55);
const std::array<SDL_Texture*, 2> textures = {
myIntermediateTexture, mySecondaryIntermediateTexture
};
for (SDL_Texture* texture: textures) {
if (!texture) continue;
const std::array<SDL_Texture*, 2> textures = {
myIntermediateTexture, mySecondaryIntermediateTexture
};
for (SDL_Texture* texture: textures) {
if (!texture) continue;
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(texture, blendAlpha);
}
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(texture, myBlendLevel * 2.55);
}
myRecreateTextures = false;

View File

@ -34,10 +34,8 @@ class QisBlitter : public Blitter {
~QisBlitter() override;
void reinitialize(
SDL_Rect srcRect,
SDL_Rect destRect,
FBSurface::Attributes attributes,
SDL_Surface* staticData
SDL_Rect srcRect, SDL_Rect destRect,
uInt8 blendLevel, SDL_Surface* staticData
) override;
void blit(SDL_Surface& surface) override;
@ -57,8 +55,8 @@ class QisBlitter : public Blitter {
SDL_FRect mySrcFRect{0.F, 0.F, 0.F, 0.F},
myIntermediateFRect{0.F, 0.F, 0.F, 0.F},
myDstFRect{0.F, 0.F, 0.F, 0.F};
FBSurface::Attributes myAttributes;
uInt32 myBlendLevel{100};
bool myTexturesAreAllocated{false};
bool myRecreateTextures{false};

View File

@ -277,23 +277,10 @@ class FBSurface
string& left, string& right);
/**
The rendering attributes that can be modified for this texture.
These probably can only be implemented in child FBSurfaces where
the specific functionality actually exists.
Get the currently applied alpha percentage for this surface.
Note that this level is 0 - 100%, and is not scaled.
*/
struct Attributes {
bool blending{false}; // Blending is enabled
uInt32 blendalpha{100}; // Alpha to use in blending mode (0-100%)
bool operator==(const Attributes& other) const {
return blendalpha == other.blendalpha && blending == other.blending;
}
};
/**
Get the currently applied attributes.
*/
Attributes& attributes() { return myAttributes; }
uInt32 blendLevel() const { return myBlendLevel; }
//////////////////////////////////////////////////////////////////////////
// Note: The following methods are FBSurface-specific, and must be
@ -379,16 +366,19 @@ class FBSurface
*/
virtual void resize(uInt32 width, uInt32 height) = 0;
/**
Set alpha level used in blending.
@param percent Internally, uses formula 'srcA = srcA * (alpha / 255)'
Note that this level is 0 - 100%, and is not scaled;
the scaling is done inside this method call
*/
virtual void setBlendLevel(uInt32 percent) = 0;
/**
Configure scaling interpolation.
*/
virtual void setScalingInterpolation(ScalingInterpolation) = 0;
/**
The child class chooses which (if any) of the actual attributes
can be applied.
*/
virtual void applyAttributes() = 0;
//////////////////////////////////////////////////////////////////////////
static void setPalette(const FullPaletteArray& palette) { myPalette = palette; }
@ -417,8 +407,7 @@ class FBSurface
protected:
uInt32* myPixels{nullptr}; // NOTE: MUST be set in child classes
uInt32 myPitch{0}; // NOTE: MUST be set in child classes
Attributes myAttributes;
uInt32 myBlendLevel{100}; // NOTE: MUST be set in child classes
static FullPaletteArray myPalette;

View File

@ -300,9 +300,7 @@ FBInitStatus FrameBuffer::createDisplay(string_view title, BufferType type,
if(!myStatsMsg.surface)
{
myStatsMsg.surface = allocateSurface(myStatsMsg.w, myStatsMsg.h);
myStatsMsg.surface->attributes().blending = true;
myStatsMsg.surface->attributes().blendalpha = 92; //aligned with TimeMachineDialog
myStatsMsg.surface->applyAttributes();
myStatsMsg.surface->setBlendLevel(92); //aligned with TimeMachineDialog
}
if(!myMsg.surface)

View File

@ -65,14 +65,8 @@ TIASurface::TIASurface(OSystem& system)
// Create shading surface
static constexpr uInt32 data = 0xff000000;
myShadeSurface = myFB.allocateSurface(1, 1, ScalingInterpolation::sharp, &data);
FBSurface::Attributes& attr = myShadeSurface->attributes();
attr.blending = true;
attr.blendalpha = 35; // darken stopped emulation by 35%
myShadeSurface->applyAttributes();
myShadeSurface->setBlendLevel(35); // darken stopped emulation by 35%
myRGBFramebuffer.fill(0);
@ -231,13 +225,9 @@ void TIASurface::changeCurrentNTSCAdjustable(int direction)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIASurface::changeScanlineIntensity(int direction)
{
FBSurface::Attributes& attr = mySLineSurface->attributes();
attr.blendalpha += direction * 2;
attr.blendalpha = BSPF::clamp(static_cast<Int32>(attr.blendalpha), 0, 100);
mySLineSurface->applyAttributes();
const uInt32 intensity = attr.blendalpha;
const int intensity =
BSPF::clamp<int>(mySLineSurface->blendLevel() + direction * 2, 0, 100);
mySLineSurface->setBlendLevel(intensity);
myOSystem.settings().setValue("tv.scanlines", intensity);
enableNTSC(ntscEnabled());
@ -453,11 +443,9 @@ void TIASurface::enableNTSC(bool enable)
// Generate a scanline surface from current scanline pattern
// Apply current blend to scan line surface
myScanlinesEnabled = myOSystem.settings().getInt("tv.scanlines") > 0;
FBSurface::Attributes& sl_attr = mySLineSurface->attributes();
sl_attr.blending = myScanlinesEnabled;
sl_attr.blendalpha = myOSystem.settings().getInt("tv.scanlines");
mySLineSurface->applyAttributes();
const int scanlines = myOSystem.settings().getInt("tv.scanlines");
myScanlinesEnabled = scanlines > 0;
mySLineSurface->setBlendLevel(scanlines);
myRGBFramebuffer.fill(0);
}
@ -465,9 +453,7 @@ void TIASurface::enableNTSC(bool enable)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string TIASurface::effectsInfo() const
{
const FBSurface::Attributes& attr = mySLineSurface->attributes();
ostringstream buf;
switch(myFilter)
{
case Filter::Normal:
@ -485,8 +471,8 @@ string TIASurface::effectsInfo() const
default:
break; // Not supposed to get here
}
if(attr.blendalpha)
buf << ", scanlines=" << attr.blendalpha
if(mySLineSurface->blendLevel() > 0)
buf << ", scanlines=" << mySLineSurface->blendLevel()
<< "/" << myOSystem.settings().getString("tv.scanmask");
buf << ", inter=" << (myOSystem.settings().getBool("tia.inter") ? "enabled" : "disabled");
buf << ", aspect correction=" << (correctAspect() ? "enabled" : "disabled");

View File

@ -363,12 +363,7 @@ void Dialog::render()
_shadeSurface = instance().frameBuffer().allocateSurface(
1, 1, ScalingInterpolation::sharp, &data);
FBSurface::Attributes& attr = _shadeSurface->attributes();
attr.blending = true;
attr.blendalpha = 25; // darken background dialogs by 25%
_shadeSurface->applyAttributes();
_shadeSurface->setBlendLevel(25); // darken background dialogs by 25%
}
_shadeSurface->setDstRect(_surface->dstRect());
_shadeSurface->render();

View File

@ -220,12 +220,8 @@ HighScoresDialog::~HighScoresDialog() // NOLINT (we need an empty d'tor)
void HighScoresDialog::loadConfig()
{
// Enable blending (only once is necessary)
if (myMode == AppMode::emulator && !surface().attributes().blending)
{
surface().attributes().blending = true;
surface().attributes().blendalpha = 90;
surface().applyAttributes();
}
if (myMode == AppMode::emulator/* && surface().blendLevel() ... */)
surface().setBlendLevel(90);
VariantList items;

View File

@ -104,12 +104,7 @@ void RomImageWidget::parseProperties(const FSNode& node, bool full)
myNavSurface = fb.allocateSurface(_w, myImageHeight);
myNavSurface->setDstSize(_w * scale, myImageHeight * scale);
FBSurface::Attributes& attr = myNavSurface->attributes();
attr.blending = true;
attr.blendalpha = 60;
myNavSurface->applyAttributes();
myNavSurface->setBlendLevel(60);
}
// Check if a surface has ever been created; if so, we use it
@ -118,9 +113,9 @@ void RomImageWidget::parseProperties(const FSNode& node, bool full)
if(mySurface == nullptr)
{
mySurface = fb.allocateSurface(_w, myImageHeight, ScalingInterpolation::blur);
mySurface->applyAttributes();
// FIXME mySurface->applyAttributes();
myFrameSurface = fb.allocateSurface(1, 1, ScalingInterpolation::sharp);
myFrameSurface->applyAttributes();
// FIXME myFrameSurface->applyAttributes();
myFrameSurface->setVisible(true);
dialog().addRenderCallback([this]() {

View File

@ -312,13 +312,7 @@ void TimeMachineDialog::setPosition()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TimeMachineDialog::loadConfig()
{
// Enable blending (only once is necessary)
if(!surface().attributes().blending)
{
surface().attributes().blending = true;
surface().attributes().blendalpha = 92;
surface().applyAttributes();
}
surface().setBlendLevel(92);
initBar();
}