TIA rendering and Blargg/scanline effects are now working again.

git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2891 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2014-05-14 22:04:59 +00:00
parent b7ae007093
commit d04c42580b
11 changed files with 106 additions and 133 deletions

View File

@ -20,12 +20,13 @@
#include "FBSurfaceSDL2.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceSDL2::FBSurfaceSDL2(FrameBufferSDL2& buffer, uInt32 width, uInt32 height)
FBSurfaceSDL2::FBSurfaceSDL2(FrameBufferSDL2& buffer,
uInt32 width, uInt32 height, const uInt32* data)
: myFB(buffer),
mySurface(NULL),
myTexture(NULL),
mySurfaceIsDirty(true),
myDataIsStatic(false),
myTexAccess(SDL_TEXTUREACCESS_STREAMING),
myInterpolate(false),
myBlendEnabled(false),
myBlendAlpha(255),
@ -49,6 +50,14 @@ FBSurfaceSDL2::FBSurfaceSDL2(FrameBufferSDL2& buffer, uInt32 width, uInt32 heigh
myPitch = mySurface->pitch / pf->BytesPerPixel;
////////////////////////////////////////////////////
if(data)
{
myTexAccess = SDL_TEXTUREACCESS_STATIC;
myStaticPitch = mySurface->w * 4; // we need pitch in 'bytes'
myStaticData = new uInt32[mySurface->w * mySurface->h];
SDL_memcpy(myStaticData, data, mySurface->w * mySurface->h * 4);
}
// To generate texture
reload();
}
@ -88,28 +97,6 @@ void FBSurfaceSDL2::drawSurface(const FBSurface* surface)
SDL_BlitSurface(s->mySurface, &(s->mySrcR), mySurface, &dst);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h)
{
// It's faster to just update the entire (hardware) surface
mySurfaceIsDirty = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::setStaticContents(const uInt32* pixels, uInt32 pitch)
{
myDataIsStatic = true;
myStaticPitch = pitch * 4; // we need pitch in 'bytes'
if(!myStaticData)
myStaticData = new uInt32[mySurface->w * mySurface->h];
SDL_memcpy(myStaticData, pixels, mySurface->w * mySurface->h);
// Re-create the texture with the new settings
free();
reload();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FBSurfaceSDL2::width() const
{
@ -157,7 +144,7 @@ void FBSurfaceSDL2::setDstPos(uInt32 x, uInt32 y)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::setDstSize(uInt32 w, uInt32 h)
{
myDstR.w = w; myDstR.w = w;
myDstR.w = w; myDstR.h = h;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -175,7 +162,7 @@ void FBSurfaceSDL2::render()
//cerr << "src: x=" << mySrcR.x << ", y=" << mySrcR.y << ", w=" << mySrcR.w << ", h=" << mySrcR.h << endl;
//cerr << "dst: x=" << myDstR.x << ", y=" << myDstR.y << ", w=" << myDstR.w << ", h=" << myDstR.h << endl;
if(!myDataIsStatic)
if(myTexAccess == SDL_TEXTUREACCESS_STREAMING)
SDL_UpdateTexture(myTexture, &mySrcR, mySurface->pixels, mySurface->pitch);
SDL_RenderCopy(myFB.myRenderer, myTexture, &mySrcR, &myDstR);
@ -208,11 +195,10 @@ void FBSurfaceSDL2::reload()
// Re-create texture; the underlying SDL_Surface is fine as-is
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, myInterpolate ? "1" : "0");
myTexture = SDL_CreateTexture(myFB.myRenderer, myFB.myPixelFormat->format,
myDataIsStatic ? SDL_TEXTUREACCESS_STATIC : SDL_TEXTUREACCESS_STREAMING,
mySurface->w, mySurface->h);
myTexAccess, mySurface->w, mySurface->h);
// If the data is static, we only upload it once
if(myDataIsStatic)
if(myTexAccess == SDL_TEXTUREACCESS_STATIC)
SDL_UpdateTexture(myTexture, NULL, myStaticData, myStaticPitch);
// Blending enabled?

View File

@ -32,7 +32,8 @@
class FBSurfaceSDL2 : public FBSurface
{
public:
FBSurfaceSDL2(FrameBufferSDL2& buffer, uInt32 width, uInt32 height);
FBSurfaceSDL2(FrameBufferSDL2& buffer, uInt32 width, uInt32 height,
const uInt32* data);
virtual ~FBSurfaceSDL2();
// Most of the surface drawing primitives are implemented in FBSurface;
@ -40,9 +41,9 @@ class FBSurfaceSDL2 : public FBSurface
//
void fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, uInt32 color);
void drawSurface(const FBSurface* surface);
void addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h);
void setStaticContents(const uInt32* pixels, uInt32 pitch);
// With hardware surfaces, it's faster to just update the entire surface
void addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) { mySurfaceIsDirty = true; }
void addDirtyRect() { mySurfaceIsDirty = true; }
uInt32 width() const;
uInt32 height() const;
@ -72,7 +73,7 @@ class FBSurfaceSDL2 : public FBSurface
bool mySurfaceIsDirty;
bool myDataIsStatic; // Is pixel data constant or can it change?
SDL_TextureAccess myTexAccess; // Is pixel data constant or can it change?
bool myInterpolate; // Scaling is smoothed or blocky
bool myBlendEnabled; // Blending is enabled
uInt8 myBlendAlpha; // Alpha to use in blending mode

View File

@ -223,9 +223,10 @@ void FrameBufferSDL2::postFrameUpdate()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurface* FrameBufferSDL2::createSurface(uInt32 w, uInt32 h) const
FBSurface* FrameBufferSDL2::createSurface(uInt32 w, uInt32 h,
const uInt32* data) const
{
return new FBSurfaceSDL2((FrameBufferSDL2&)*this, w, h);
return new FBSurfaceSDL2((FrameBufferSDL2&)*this, w, h, data);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -138,10 +138,11 @@ class FrameBufferSDL2 : public FrameBuffer
/**
This method is called to create a surface with the given attributes.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param data If non-null, use the given data values as a static surface
*/
FBSurface* createSurface(uInt32 w, uInt32 h) const;
FBSurface* createSurface(uInt32 w, uInt32 h, const uInt32* data) const;
/**
Grabs or ungrabs the mouse based on the given boolean value.

View File

@ -22,7 +22,7 @@
#include <cstdlib>
#define STELLA_VERSION "3.9.103_svn"
#define STELLA_VERSION "3.9.110_svn"
#define STELLA_BUILD atoi("$Rev$" + 6)
#endif

View File

@ -128,11 +128,6 @@ void FBSurface::drawPixels(uInt32* data, uInt32 tx, uInt32 ty, uInt32 numpixels)
*buffer++ = (uInt32) data[i];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurface::addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurface::box(uInt32 x, uInt32 y, uInt32 w, uInt32 h,
uInt32 colorA, uInt32 colorB)

View File

@ -57,6 +57,8 @@ class FBSurface
public:
/**
Creates a new FBSurface object
@param data If non-null, the data values to use as a static surface
*/
FBSurface();
@ -215,7 +217,8 @@ class FBSurface
@param w The width of the area
@param h The height of the area
*/
virtual void addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h);
virtual void addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) { }
virtual void addDirtyRect() { }
//////////////////////////////////////////////////////////////////////////
// Note: The following methods are FBSurface-specific, and must be
@ -249,19 +252,6 @@ class FBSurface
virtual void setDstPos(uInt32 x, uInt32 y) = 0;
virtual void setDstSize(uInt32 w, uInt32 h) = 0;
/**
This method should be called to indicate that the surface contains
static data that will never change. Some rendering toolkits can
use this information to optimize how the data is rendered.
Note that once this method is called, all other drawing primitives
will not work.
@param pixels The static data to use
@param pitch The number of pixels in a row
*/
virtual void setStaticContents(const uInt32* pixels, uInt32 pitch) = 0;
/**
This method should be called to translate the given coordinates
to the (destination) surface coordinates.

View File

@ -312,7 +312,7 @@ void FrameBuffer::update()
msg, 1, 1, myStatsMsg.w, myStatsMsg.color, kTextAlignLeft);
myStatsMsg.surface->drawString(infoFont(),
info.BankSwitch, 1, 15, myStatsMsg.w, myStatsMsg.color, kTextAlignLeft);
myStatsMsg.surface->addDirtyRect(0, 0, 0, 0); // force a full draw
myStatsMsg.surface->addDirtyRect(); // force a full draw
myStatsMsg.surface->setDstPos(myImageRect.x() + 1, myImageRect.y() + 1);
myStatsMsg.surface->render();
}
@ -514,7 +514,7 @@ inline void FrameBuffer::drawMessage()
}
else
{
myMsg.surface->addDirtyRect(0, 0, 0, 0);
myMsg.surface->addDirtyRect();
myMsg.surface->render();
}
}
@ -610,10 +610,10 @@ void FrameBuffer::refresh()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FrameBuffer::allocateSurface(int w, int h)
uInt32 FrameBuffer::allocateSurface(int w, int h, const uInt32* data)
{
// Create a new surface
FBSurface* surface = createSurface(w, h);
FBSurface* surface = createSurface(w, h, data);
// Add it to the list
mySurfaceList.insert(make_pair((uInt32)mySurfaceList.size(), surface));
@ -648,7 +648,6 @@ void FrameBuffer::resetSurfaces()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::setPalette(const uInt32* palette)
{
cerr << "FrameBuffer::setPalette\n";
// Set palette for normal fill
for(int i = 0; i < 256; ++i)
{
@ -730,6 +729,11 @@ bool FrameBuffer::changeWindowedVidMode(int direction)
myScreenSize = mode.screen;
if(setVideoMode(myScreenTitle, mode))
{
// Inform TIA surface about new mode
if(myOSystem.eventHandler().state() != EventHandler::S_LAUNCHER &&
myOSystem.eventHandler().state() != EventHandler::S_DEBUGGER)
myTIASurface->initialize(myOSystem.console(), mode);
resetSurfaces();
showMessage(mode.description);
myOSystem.settings().setValue("tia.zoom", mode.zoom);

View File

@ -202,12 +202,13 @@ class FrameBuffer
all responsibility for freeing this surface (ie, other classes must not
delete it directly).
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param data If non-null, use the given data values as a static surface
@return A unique ID used to identify this surface
*/
uInt32 allocateSurface(int w, int h);
uInt32 allocateSurface(int w, int h, const uInt32* data = 0);
/**
Retrieve the surface associated with the given ID.
@ -395,10 +396,11 @@ class FrameBuffer
/**
This method is called to create a surface with the given attributes.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param data If non-null, use the given data values as a static surface
*/
virtual FBSurface* createSurface(uInt32 w, uInt32 h) const = 0;
virtual FBSurface* createSurface(uInt32 w, uInt32 h, const uInt32* data) const = 0;
/**
Grabs or ungrabs the mouse based on the given boolean value.

View File

@ -41,9 +41,17 @@ TIASurface::TIASurface(FrameBuffer& buffer, OSystem& system)
myNTSCFilter.loadConfig(myOSystem.settings());
// Create a surface for the TIA image and scanlines; we'll need them eventually
uInt32 tiaID = myFB.allocateSurface(ATARI_NTSC_OUT_WIDTH(160), 320);
uInt32 tiaID = myFB.allocateSurface(ATARI_NTSC_OUT_WIDTH(kTIAW), kTIAH);
myTiaSurface = myFB.surface(tiaID);
uInt32 scanID = myFB.allocateSurface(1, 320);
// Generate scanline data, and a pre-defined scanline surface
uInt32 scanData[kScanH];
for(int i = 0; i < kScanH; i+=2)
{
scanData[i] = 0x00000000;
scanData[i+1] = 0xff000000;
}
uInt32 scanID = myFB.allocateSurface(1, kScanH, scanData);
mySLineSurface = myFB.surface(scanID);
}
@ -57,50 +65,33 @@ void TIASurface::initialize(const Console& console, const VideoMode& mode)
{
myTIA = &(console.tia());
myTiaSurface->setDstPos(mode.image.x(), mode.image.y());
myTiaSurface->setDstSize(mode.image.width(), mode.image.height());
mySLineSurface->setDstPos(mode.image.x(), mode.image.y());
mySLineSurface->setDstSize(mode.image.width(), mode.image.height());
bool p_enable = console.properties().get(Display_Phosphor) == "YES";
int p_blend = atoi(console.properties().get(Display_PPBlend).c_str());
enablePhosphor(p_enable, p_blend);
setNTSC((NTSCFilter::Preset)myOSystem.settings().getInt("tv.filter"), false);
// Scanline repeating is sensitive to non-integral vertical resolution,
// so rounding is performed to eliminate it
// This won't be 100% accurate, but non-integral scaling isn't 100%
// accurate anyway
mySLineSurface->setSrcSize(1, int(2 * float(mode.image.height()) /
floor(((float)mode.image.height() / myTIA->height()) + 0.5)));
#if 0
// FIX HERE ///////////////////////////////////
#if 0
bool enable = myProperties.get(Display_Phosphor) == "YES";
int blend = atoi(myProperties.get(Display_PPBlend).c_str());
myOSystem.frameBuffer().tiaSurface().enablePhosphor(enable, blend);
myOSystem.frameBuffer().tiaSurface().setNTSC(
(NTSCFilter::Preset)myOSystem.settings().getInt("tv.filter"), false);
#endif
// Grab the initial height before it's updated below
// We need it for the creating the TIA surface
uInt32 baseHeight = mode.image.height() / mode.zoom;
// The framebuffer only takes responsibility for TIA surfaces
// Other surfaces (such as the ones used for dialogs) are allocated
// in the Dialog class
if(inTIAMode)
{
// Since we have free hardware stretching, the base TIA surface is created
// only once, and its texture coordinates changed when we want to draw a
// smaller or larger image
if(!myTiaSurface)
myTiaSurface = new FBSurfaceTIA(*this);
myTiaSurface->updateCoords(baseHeight, mode.image.x(), mode.image.y(),
mode.image.width(), mode.image.height());
myTiaSurface->enableScanlines(ntscEnabled());
myTiaSurface->setTexInterpolation(myOSystem.settings().getBool("tia.inter"));
myTiaSurface->setScanIntensity(myOSystem.settings().getInt("tv.scanlines"));
myTiaSurface->setScanInterpolation(myOSystem.settings().getBool("tv.scaninter"));
myTiaSurface->setTIA(myOSystem.console().tia());
}
/////////////////////////////////////////////
cerr << "INITIALIZE:\n"
<< "TIA:\n"
<< "src: " << myTiaSurface->srcRect() << endl
<< "dst: " << myTiaSurface->dstRect() << endl
<< endl;
cerr << "SLine:\n"
<< "src: " << mySLineSurface->srcRect() << endl
<< "dst: " << mySLineSurface->dstRect() << endl
<< endl;
#endif
}
@ -109,11 +100,6 @@ void TIASurface::setPalette(const uInt32* tia_palette, const uInt32* rgb_palette
{
myPalette = tia_palette;
cerr << "TIASurface::setPalette\n";
for(int i = 0; i < 256; ++i)
cerr << myPalette[i] << " ";
cerr << endl;
// Set palette for phosphor effect
for(int i = 0; i < 256; ++i)
{
@ -211,7 +197,8 @@ uInt32 TIASurface::enableScanlines(int relative, int absolute)
intensity = BSPF_min(100u, intensity);
mySLineSurface->applyAttributes();
//FIXSDL myRedrawEntireFrame = true;
mySLineSurface->addDirtyRect();
return intensity;
}
@ -220,7 +207,7 @@ void TIASurface::enableScanlineInterpolation(bool enable)
{
mySLineSurface->myAttributes.smoothing = enable;
mySLineSurface->applyAttributes();
//FIXSDL myRedrawEntireFrame = true;
mySLineSurface->addDirtyRect();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -229,7 +216,8 @@ void TIASurface::enablePhosphor(bool enable, int blend)
myUsePhosphor = enable;
myPhosphorBlend = blend;
myFilterType = FilterType(enable ? myFilterType | 0x01 : myFilterType & 0x10);
//FIXSDL myRedrawEntireFrame = true;
myTiaSurface->addDirtyRect();
mySLineSurface->addDirtyRect();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -247,8 +235,7 @@ void TIASurface::enableNTSC(bool enable)
myFilterType = FilterType(enable ? myFilterType | 0x10 : myFilterType & 0x01);
// Normal vs NTSC mode uses different source widths
const GUI::Rect& src = myTiaSurface->srcRect();
myTiaSurface->setSrcSize(enable ? ATARI_NTSC_OUT_WIDTH(160) : 160, src.height());
myTiaSurface->setSrcSize(enable ? ATARI_NTSC_OUT_WIDTH(160) : 160, myTIA->height());
myTiaSurface->myAttributes.smoothing =
myOSystem.settings().getBool("tia.inter");
@ -257,11 +244,13 @@ void TIASurface::enableNTSC(bool enable)
myScanlinesEnabled = enable;
mySLineSurface->myAttributes.smoothing =
myOSystem.settings().getBool("tv.scaninter");
mySLineSurface->myAttributes.blending = myScanlinesEnabled;
mySLineSurface->myAttributes.blendalpha =
myOSystem.settings().getInt("tv.scanlines");
mySLineSurface->applyAttributes();
//FIXSDL myRedrawEntireFrame = true;
myTiaSurface->addDirtyRect();
mySLineSurface->addDirtyRect();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -293,11 +282,6 @@ string TIASurface::effectsInfo() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIASurface::render()
{
#if 0
cerr << "src: " << myTiaSurface->srcRect() << endl
<< "dst: " << myTiaSurface->dstRect() << endl
<< endl;
#endif
// Copy the mediasource framebuffer to the RGB texture
// In hardware rendering mode, it's faster to just assume that the screen
// is dirty and always do an update
@ -309,7 +293,6 @@ cerr << "src: " << myTiaSurface->srcRect() << endl
uInt32 *buffer, pitch;
myTiaSurface->basePtr(buffer, pitch);
//cerr << "buffer=" << buffer << ", pitch=" << pitch << endl;
// TODO - Eventually 'phosphor' won't be a separate mode, and will become
// a post-processing filter by blending several frames.
@ -351,21 +334,25 @@ cerr << "src: " << myTiaSurface->srcRect() << endl
case kBlarggNormal:
{
myNTSCFilter.blit_single(currentFrame, width, height,
buffer, pitch);
buffer, pitch << 2);
break;
}
case kBlarggPhosphor:
{
myNTSCFilter.blit_double(currentFrame, previousFrame, width, height,
buffer, pitch);
buffer, pitch << 2);
break;
}
}
// Draw TIA image
myTiaSurface->addDirtyRect();
myTiaSurface->render();
// Draw overlaying scanlines
if(myScanlinesEnabled)
{
mySLineSurface->addDirtyRect();
mySLineSurface->render();
}
}

View File

@ -152,6 +152,12 @@ class TIASurface
};
FilterType myFilterType;
enum TIAConstants {
kTIAW = 160,
kTIAH = 320,
kScanH = kTIAH*2
};
// NTSC object to use in TIA rendering mode
NTSCFilter myNTSCFilter;