First pass at fixing the 'white texture' issue with OpenGL mode in OSX.

Basically, all textures/surfaces need to be reloaded each time the GL
context changes (which seems to be more often under OSX than on other
systems).  This new code takes care of that.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1560 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2008-12-12 15:51:07 +00:00
parent f599e1611d
commit 203cdaf323
7 changed files with 239 additions and 87 deletions

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferGL.cxx,v 1.112 2008-12-10 18:11:21 stephena Exp $
// $Id: FrameBufferGL.cxx,v 1.113 2008-12-12 15:51:06 stephena Exp $
//============================================================================
#ifdef DISPLAY_OPENGL
@ -356,6 +356,10 @@ cerr << "dimensions: " << endl
myBaseSurface = new FBSurfaceGL(*this, baseWidth, baseHeight,
mode.image_w, mode.image_h);
// Old textures currently in use by various UI items need to be
// refreshed as well (only seems to be required for OSX)
reloadSurfaces();
// Make sure any old parts of the screen are erased
p_glClear(GL_COLOR_BUFFER_BIT);
SDL_GL_SwapBuffers();
@ -528,6 +532,8 @@ FBSurfaceGL::FBSurfaceGL(FrameBufferGL& buffer,
uInt32 baseWidth, uInt32 baseHeight,
uInt32 scaleWidth, uInt32 scaleHeight)
: myFB(buffer),
myTexture(NULL),
myTexID(0),
myXOrig(0),
myYOrig(0),
myWidth(scaleWidth),
@ -558,9 +564,6 @@ cerr << " FBSurfaceGL::FBSurfaceGL: w = " << baseWidth << ", h = " << baseHeigh
// Based on experimentation, the following is the fastest 16-bit
// format for OpenGL (on all platforms)
// TODO - make sure this is endian-clean
GLenum tex_intformat;
tex_intformat = GL_RGB5;
myTexFormat = GL_BGRA;
myTexType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
myTexture = SDL_CreateRGBSurface(SDL_SWSURFACE,
@ -582,41 +585,15 @@ cerr << " FBSurfaceGL::FBSurfaceGL: w = " << baseWidth << ", h = " << baseHeigh
break;
}
/*
// Create an OpenGL texture from the SDL texture
const string& filter = myOSystem->settings().getString("gl_filter");
if(filter == "linear")
{
myBuffer.filter = GL_LINEAR;
myFilterParamName = "GL_LINEAR";
}
else if(filter == "nearest")
{
myBuffer.filter = GL_NEAREST;
myFilterParamName = "GL_NEAREST";
}
*/
p_glGenTextures(1, &myTexID);
p_glBindTexture(myTexTarget, myTexID);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, myBuffer.filter);
// p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, myBuffer.filter);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Finally, create the texture in the most optimal format
p_glTexImage2D(myTexTarget, 0, tex_intformat,
myTexWidth, myTexHeight, 0,
myTexFormat, myTexType, myTexture->pixels);
p_glEnable(myTexTarget);
// Associate the SDL surface with a GL texture object
reload();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceGL::~FBSurfaceGL()
{
cerr << " FBSurfaceGL::~FBSurfaceGL(): " << this << endl;
if(myTexture)
SDL_FreeSurface(myTexture);
@ -816,6 +793,53 @@ void FBSurfaceGL::update()
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::reload()
{
// This does a 'soft' reset of the surface
// It seems that on some system (notably, OSX), creating a new SDL window
// destroys the GL context, requiring a reload of all textures
// However, destroying the entire FBSurfaceGL object is wasteful, since
// it will also regenerate SDL software surfaces (which are not required
// to be regenerated)
// Basically, all that needs to be done is to re-call glTexImage2D with a
// new texture ID, so that's what we do here
/*
// Create an OpenGL texture from the SDL texture
const string& filter = myOSystem->settings().getString("gl_filter");
if(filter == "linear")
{
myBuffer.filter = GL_LINEAR;
myFilterParamName = "GL_LINEAR";
}
else if(filter == "nearest")
{
myBuffer.filter = GL_NEAREST;
myFilterParamName = "GL_NEAREST";
}
*/
p_glDeleteTextures(1, &myTexID);
p_glGenTextures(1, &myTexID);
p_glBindTexture(myTexTarget, myTexID);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, myBuffer.filter);
// p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, myBuffer.filter);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Finally, create the texture in the most optimal format
p_glTexImage2D(myTexTarget, 0, GL_RGB5,
myTexWidth, myTexHeight, 0,
myTexFormat, myTexType, myTexture->pixels);
p_glEnable(myTexTarget);
cerr << " ==> FBSurfaceGL::reload(): myTexID = " << myTexID << endl;
}
#if 0
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GUI::Surface* FrameBufferGL::createSurface(int width, int height) const

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferGL.hxx,v 1.60 2008-12-10 18:11:21 stephena Exp $
// $Id: FrameBufferGL.hxx,v 1.61 2008-12-12 15:51:06 stephena Exp $
//============================================================================
#ifndef FRAMEBUFFER_GL_HXX
@ -35,7 +35,7 @@ class FBSurfaceGL;
This class implements an SDL OpenGL framebuffer.
@author Stephen Anthony
@version $Id: FrameBufferGL.hxx,v 1.60 2008-12-10 18:11:21 stephena Exp $
@version $Id: FrameBufferGL.hxx,v 1.61 2008-12-12 15:51:06 stephena Exp $
*/
class FrameBufferGL : public FrameBuffer
{
@ -83,16 +83,6 @@ class FrameBufferGL : public FrameBuffer
*/
BufferType type() const { return kGLBuffer; }
/**
This method is called to create a surface compatible with the one
currently in use, but having the given dimensions.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param useBase Use the base surface instead of creating a new one
*/
FBSurface* createSurface(int w, int h, bool useBase = false) const;
/**
This method is called to get the specified scanline data.
@ -125,6 +115,16 @@ class FrameBufferGL : public FrameBuffer
*/
bool setVidMode(VideoMode& mode);
/**
This method is called to create a surface compatible with the one
currently in use, but having the given dimensions.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param useBase Use the base surface instead of creating a new one
*/
FBSurface* createSurface(int w, int h, bool useBase = false) const;
/**
Switches between the two filtering options in OpenGL.
Currently, these are GL_NEAREST and GL_LINEAR.
@ -201,7 +201,7 @@ class FrameBufferGL : public FrameBuffer
A surface suitable for OpenGL rendering mode.
@author Stephen Anthony
@version $Id: FrameBufferGL.hxx,v 1.60 2008-12-10 18:11:21 stephena Exp $
@version $Id: FrameBufferGL.hxx,v 1.61 2008-12-12 15:51:06 stephena Exp $
*/
class FBSurfaceGL : public FBSurface
{
@ -227,6 +227,7 @@ class FBSurfaceGL : public FBSurface
void setHeight(uInt32 h);
void translateCoords(Int32& x, Int32& y) const;
void update();
void reload();
private:
inline void* pixels() const { return myTexture->pixels; }

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferSoft.hxx,v 1.55 2008-11-02 16:46:05 stephena Exp $
// $Id: FrameBufferSoft.hxx,v 1.56 2008-12-12 15:51:06 stephena Exp $
//============================================================================
#ifndef FRAMEBUFFER_SOFT_HXX
@ -32,7 +32,7 @@ class RectList;
This class implements an SDL software framebuffer.
@author Stephen Anthony
@version $Id: FrameBufferSoft.hxx,v 1.55 2008-11-02 16:46:05 stephena Exp $
@version $Id: FrameBufferSoft.hxx,v 1.56 2008-12-12 15:51:06 stephena Exp $
*/
class FrameBufferSoft : public FrameBuffer
{
@ -72,16 +72,6 @@ class FrameBufferSoft : public FrameBuffer
*/
BufferType type() const { return kSoftBuffer; }
/**
This method is called to create a surface compatible with the one
currently in use, but having the given dimensions.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param useBase Use the base surface instead of creating a new one
*/
FBSurface* createSurface(int w, int h, bool useBase = false) const;
/**
This method is called to get the specified scanline data.
@ -114,6 +104,16 @@ class FrameBufferSoft : public FrameBuffer
*/
bool setVidMode(VideoMode& mode);
/**
This method is called to create a surface compatible with the one
currently in use, but having the given dimensions.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param useBase Use the base surface instead of creating a new one
*/
FBSurface* createSurface(int w, int h, bool useBase = false) const;
/**
Switches between the filtering options in software mode.
Currently, none exist.
@ -172,7 +172,7 @@ class FrameBufferSoft : public FrameBuffer
A surface suitable for software rendering mode.
@author Stephen Anthony
@version $Id: FrameBufferSoft.hxx,v 1.55 2008-11-02 16:46:05 stephena Exp $
@version $Id: FrameBufferSoft.hxx,v 1.56 2008-12-12 15:51:06 stephena Exp $
*/
class FBSurfaceSoft : public FBSurface
{
@ -195,6 +195,7 @@ class FBSurfaceSoft : public FBSurface
void setHeight(uInt32 h);
void translateCoords(Int32& x, Int32& y) const;
void update();
void reload() { } // Not required for software mode
private:
void recalc();

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBuffer.cxx,v 1.142 2008-12-08 18:56:54 stephena Exp $
// $Id: FrameBuffer.cxx,v 1.143 2008-12-12 15:51:07 stephena Exp $
//============================================================================
#include <algorithm>
@ -46,17 +46,19 @@ FrameBuffer::FrameBuffer(OSystem* osystem)
myUsePhosphor(false),
myPhosphorBlend(77),
myInitializedCount(0),
myPausedCount(0)
myPausedCount(0),
mySurfaceCount(0)
{
myMsg.surface = myStatsMsg.surface = 0;
myMsg.surfaceID = myStatsMsg.surfaceID = -1;
myMsg.enabled = myStatsMsg.enabled = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBuffer::~FrameBuffer(void)
{
delete myMsg.surface;
delete myStatsMsg.surface;
freeSurface(myMsg.surfaceID);
freeSurface(myStatsMsg.surfaceID);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -126,10 +128,16 @@ cerr << " <== FrameBuffer::initialize: w = " << width << ", h = " << height << e
myStatsMsg.color = kBtnTextColor;
myStatsMsg.w = myOSystem->consoleFont().getStringWidth("000 LINES %00.00 FPS");
myStatsMsg.h = myOSystem->consoleFont().getFontHeight();
if(!myStatsMsg.surface)
myStatsMsg.surface = createSurface(myStatsMsg.w, myStatsMsg.h);
if(myStatsMsg.surfaceID < 0)
{
myStatsMsg.surfaceID = allocateSurface(myStatsMsg.w, myStatsMsg.h);
myStatsMsg.surface = surface(myStatsMsg.surfaceID);
}
if(!myMsg.surface) // TODO - change this to the font we'll really use
myMsg.surface = createSurface(320, myOSystem->consoleFont().getFontHeight()+10);
{
myMsg.surfaceID = allocateSurface(320, myOSystem->consoleFont().getFontHeight()+10);
myMsg.surface = surface(myMsg.surfaceID);
}
// Finally, show some information about the framebuffer,
// but only on the first initialization
@ -373,6 +381,51 @@ void FrameBuffer::refresh()
myRedrawEntireFrame = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int FrameBuffer::allocateSurface(int w, int h, bool useBase)
{
// Create a new surface
FBSurface* surface = createSurface(w, h, useBase);
// Add it to the list
mySurfaceList.insert(make_pair(mySurfaceCount, surface));
mySurfaceCount++;
// Return a reference to it
return mySurfaceCount - 1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int FrameBuffer::freeSurface(int id)
{
// Really delete the surface this time
// That means actually deleting the FBSurface object, and removing it
// from the list
map<int,FBSurface*>::iterator iter = mySurfaceList.find(id);
if(iter != mySurfaceList.end())
{
cerr << " delete id = " << iter->first << ", " << iter->second << endl;
delete iter->second;
mySurfaceList.erase(iter);
}
return -1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurface* FrameBuffer::surface(int id) const
{
map<int,FBSurface*>::const_iterator iter = mySurfaceList.find(id);
return iter != mySurfaceList.end() ? iter->second : NULL;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::reloadSurfaces()
{
map<int,FBSurface*>::iterator iter;
for(iter = mySurfaceList.begin(); iter != mySurfaceList.end(); ++iter)
iter->second->reload();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::setTIAPalette(const uInt32* palette)
{

View File

@ -13,12 +13,13 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBuffer.hxx,v 1.104 2008-08-04 11:56:12 stephena Exp $
// $Id: FrameBuffer.hxx,v 1.105 2008-12-12 15:51:07 stephena Exp $
//============================================================================
#ifndef FRAMEBUFFER_HXX
#define FRAMEBUFFER_HXX
#include <map>
#include <SDL.h>
class FBSurface;
@ -90,7 +91,7 @@ enum {
turn drawn here as well.
@author Stephen Anthony
@version $Id: FrameBuffer.hxx,v 1.104 2008-08-04 11:56:12 stephena Exp $
@version $Id: FrameBuffer.hxx,v 1.105 2008-12-12 15:51:07 stephena Exp $
*/
class FrameBuffer
{
@ -150,6 +151,35 @@ class FrameBuffer
*/
void enableMessages(bool enable);
/**
Allocate a new surface with a unique ID.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param useBase Use the base surface instead of creating a new one
@return A unique ID used to identify this surface
*/
int allocateSurface(int w, int h, bool useBase = false);
/**
De-allocate a previously allocated surface. Other classes should
call this method when a surface is no longer needed; it shouldn't
try to manually delete the surface object.
@param id The ID for the surface to de-allocate.
@return The ID indicating a non-existent surface (-1).
*/
int freeSurface(int id);
/**
Retrieve the surface associated with the given ID.
@param id The ID for the surface to retrieve.
@return A pointer to a valid surface object, or NULL.
*/
FBSurface* surface(int id) const;
/**
Returns the current dimensions of the framebuffer image.
Note that this will take into account the current scaling (if any)
@ -268,16 +298,6 @@ class FrameBuffer
*/
virtual BufferType type() const = 0;
/**
This method is called to create a surface compatible with the one
currently in use, but having the given dimensions.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param useBase Use the base surface instead of creating a new one
*/
virtual FBSurface* createSurface(int w, int h, bool useBase = false) const = 0;
/**
This method is called to get the specified scanline data.
@ -343,6 +363,16 @@ class FrameBuffer
*/
virtual bool setVidMode(VideoMode& mode) = 0;
/**
This method is called to create a surface compatible with the one
currently in use, but having the given dimensions.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param useBase Use the base surface instead of creating a new one
*/
virtual FBSurface* createSurface(int w, int h, bool useBase = false) const = 0;
/**
Switches between the filtering options in the video subsystem.
*/
@ -364,6 +394,12 @@ class FrameBuffer
*/
virtual string about() const = 0;
/**
Issues the 'reload' instruction to all surfaces that the
framebuffer knows about.
*/
void reloadSurfaces();
protected:
// The parent system for the framebuffer
OSystem* myOSystem;
@ -492,6 +528,7 @@ class FrameBuffer
int x, y, w, h;
uInt32 color;
FBSurface* surface;
int surfaceID;
bool enabled;
};
Message myMsg;
@ -502,6 +539,10 @@ class FrameBuffer
VideoModeList myFullscreenModeList;
VideoModeList* myCurrentModeList;
// Holds a reference to all the surfaces that have been created
map<int,FBSurface*> mySurfaceList;
int mySurfaceCount;
// Holds static strings for the remap menu (emulation and menu events)
static GraphicsMode ourGraphicsModes[GFX_NumModes];
};
@ -516,7 +557,7 @@ class FrameBuffer
FrameBuffer type.
@author Stephen Anthony
@version $Id: FrameBuffer.hxx,v 1.104 2008-08-04 11:56:12 stephena Exp $
@version $Id: FrameBuffer.hxx,v 1.105 2008-12-12 15:51:07 stephena Exp $
*/
// Text alignment modes for drawString()
enum TextAlignment {
@ -650,6 +691,11 @@ class FBSurface
*/
virtual void update() = 0;
/**
This method should be called to reload the surface data/state.
*/
virtual void reload() = 0;
/**
This method should be called to draw a rectangular box with sides
at the specified coordinates.

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Dialog.cxx,v 1.64 2008-08-01 12:16:00 stephena Exp $
// $Id: Dialog.cxx,v 1.65 2008-12-12 15:51:07 stephena Exp $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
@ -47,7 +47,8 @@ Dialog::Dialog(OSystem* instance, DialogContainer* parent,
_isBase(isBase),
_ourTab(NULL),
_surface(NULL),
_focusID(0)
_focusID(0),
_surfaceID(-1)
{
}
@ -62,7 +63,8 @@ Dialog::~Dialog()
_ourButtonGroup.clear();
delete _surface;
_surfaceID = instance().frameBuffer().freeSurface(_surfaceID);
_surface = NULL;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -77,6 +79,28 @@ void Dialog::open()
// Base surfaces are typically large, and will probably cause slow
// performance if we update the whole area each frame
// Instead, dirty rectangle updates should be performed
// However, this policy is left entirely to the framebuffer
// We suggest the hint here, but specific framebuffers are free to
// ignore it
if(_surfaceID < 0 || _surface == NULL)
{
_surfaceID = instance().frameBuffer().allocateSurface(_w, _h, _isBase);
_surface = instance().frameBuffer().surface(_surfaceID);
}
else if((uInt32)_w > _surface->getWidth() || (uInt32)_h > _surface->getHeight())
{
_surfaceID = instance().frameBuffer().freeSurface(_surfaceID);
_surfaceID = instance().frameBuffer().allocateSurface(_w, _h, _isBase);
_surface = instance().frameBuffer().surface(_surfaceID);
}
else
{
_surface->setWidth(_w);
_surface->setHeight(_h);
}
/*
if(_surface == NULL)
_surface = instance().frameBuffer().createSurface(_w, _h, _isBase);
else if((uInt32)_w > _surface->getWidth() || (uInt32)_h > _surface->getHeight())
@ -89,6 +113,8 @@ void Dialog::open()
_surface->setWidth(_w);
_surface->setHeight(_h);
}
*/
center();

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Dialog.hxx,v 1.39 2008-06-19 19:15:44 stephena Exp $
// $Id: Dialog.hxx,v 1.40 2008-12-12 15:51:07 stephena Exp $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
@ -47,7 +47,7 @@ class TabWidget;
This is the base class for all dialog boxes.
@author Stephen Anthony
@version $Id: Dialog.hxx,v 1.39 2008-06-19 19:15:44 stephena Exp $
@version $Id: Dialog.hxx,v 1.40 2008-12-12 15:51:07 stephena Exp $
*/
class Dialog : public GuiObject
{
@ -135,6 +135,7 @@ class Dialog : public GuiObject
int _result;
int _focusID;
int _surfaceID;
};
#endif