This update fixes (hopefully) the last of the texture upload lags that were affecting some games, in particular Daytona 2 and Spikeout.

To achieve this UploadTextures no longer clears the model cache when called.  Instead the cache is kept in-tact (which should help improve cache hits) and all textures referenced by models being rendered are (re-)decoded with every frame.
To help with tracking all the unique texture references contained in a model a new class TextureRefs has been added.
This commit is contained in:
Nik Henson 2012-02-23 23:20:21 +00:00
parent aba801ebb4
commit 3b41239cfb
11 changed files with 556 additions and 46 deletions

View file

@ -90,7 +90,7 @@ endif
HEADERS = Src/Supermodel.h Src/Games.h Src/OSD/SDL/Types.h HEADERS = Src/Supermodel.h Src/Games.h Src/OSD/SDL/Types.h
OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/Config.o $(OBJ_DIR)/INIFile.o $(OBJ_DIR)/BlockFile.o $(OBJ_DIR)/93C46.o \ OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/Config.o $(OBJ_DIR)/INIFile.o $(OBJ_DIR)/BlockFile.o $(OBJ_DIR)/93C46.o \
$(OBJ_DIR)/ROMLoad.o $(OBJ_DIR)/unzip.o $(OBJ_DIR)/ioapi.o $(OBJ_DIR)/Error.o $(OBJ_DIR)/glew.o $(OBJ_DIR)/Shader.o \ $(OBJ_DIR)/ROMLoad.o $(OBJ_DIR)/unzip.o $(OBJ_DIR)/ioapi.o $(OBJ_DIR)/Error.o $(OBJ_DIR)/glew.o $(OBJ_DIR)/Shader.o \
$(OBJ_DIR)/Real3D.o $(OBJ_DIR)/Render3D.o $(OBJ_DIR)/Models.o $(OBJ_DIR)/Render2D.o $(OBJ_DIR)/TileGen.o \ $(OBJ_DIR)/Real3D.o $(OBJ_DIR)/Render3D.o $(OBJ_DIR)/Models.o $(OBJ_DIR)/TextureRefs.o $(OBJ_DIR)/Render2D.o $(OBJ_DIR)/TileGen.o \
$(OBJ_DIR)/Model3.o $(OBJ_DIR)/ppc.o $(OBJ_DIR)/Main.o $(OBJ_DIR)/Audio.o $(OBJ_DIR)/Thread.o $(OBJ_DIR)/SoundBoard.o \ $(OBJ_DIR)/Model3.o $(OBJ_DIR)/ppc.o $(OBJ_DIR)/Main.o $(OBJ_DIR)/Audio.o $(OBJ_DIR)/Thread.o $(OBJ_DIR)/SoundBoard.o \
$(OBJ_DIR)/SCSP.o $(OBJ_DIR)/SCSPDSP.o $(OBJ_DIR)/68K.o $(OBJ_DIR)/m68kcpu.o $(OBJ_DIR)/m68kopnz.o $(OBJ_DIR)/m68kopdm.o \ $(OBJ_DIR)/SCSP.o $(OBJ_DIR)/SCSPDSP.o $(OBJ_DIR)/68K.o $(OBJ_DIR)/m68kcpu.o $(OBJ_DIR)/m68kopnz.o $(OBJ_DIR)/m68kopdm.o \
$(OBJ_DIR)/m68kopac.o $(OBJ_DIR)/m68kops.o $(OBJ_DIR)/DSB.o $(OBJ_DIR)/Z80.o \ $(OBJ_DIR)/m68kopac.o $(OBJ_DIR)/m68kops.o $(OBJ_DIR)/DSB.o $(OBJ_DIR)/Z80.o \
@ -216,4 +216,4 @@ $(OBJ_DIR)/amp_%.o: Src/Sound/MPEG/%.cpp Src/Sound/MPEG/%.h
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o $(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o
$(OBJ_DIR)/amp_%.o: Src/Sound/MPEG/%.cpp $(OBJ_DIR)/amp_%.o: Src/Sound/MPEG/%.cpp
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o $(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o

View file

@ -92,7 +92,7 @@ endif
HEADERS = Src/Supermodel.h Src/Games.h Src/OSD/SDL/Types.h HEADERS = Src/Supermodel.h Src/Games.h Src/OSD/SDL/Types.h
OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/Config.o $(OBJ_DIR)/INIFile.o $(OBJ_DIR)/BlockFile.o $(OBJ_DIR)/93C46.o \ OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/Config.o $(OBJ_DIR)/INIFile.o $(OBJ_DIR)/BlockFile.o $(OBJ_DIR)/93C46.o \
$(OBJ_DIR)/ROMLoad.o $(OBJ_DIR)/unzip.o $(OBJ_DIR)/ioapi.o $(OBJ_DIR)/Error.o $(OBJ_DIR)/glew.o $(OBJ_DIR)/Shader.o \ $(OBJ_DIR)/ROMLoad.o $(OBJ_DIR)/unzip.o $(OBJ_DIR)/ioapi.o $(OBJ_DIR)/Error.o $(OBJ_DIR)/glew.o $(OBJ_DIR)/Shader.o \
$(OBJ_DIR)/Real3D.o $(OBJ_DIR)/Render3D.o $(OBJ_DIR)/Models.o $(OBJ_DIR)/Render2D.o $(OBJ_DIR)/TileGen.o \ $(OBJ_DIR)/Real3D.o $(OBJ_DIR)/Render3D.o $(OBJ_DIR)/Models.o $(OBJ_DIR)/TextureRefs.o $(OBJ_DIR)/Render2D.o $(OBJ_DIR)/TileGen.o \
$(OBJ_DIR)/Model3.o $(OBJ_DIR)/ppc.o $(OBJ_DIR)/Main.o $(OBJ_DIR)/Audio.o $(OBJ_DIR)/Thread.o $(OBJ_DIR)/SoundBoard.o \ $(OBJ_DIR)/Model3.o $(OBJ_DIR)/ppc.o $(OBJ_DIR)/Main.o $(OBJ_DIR)/Audio.o $(OBJ_DIR)/Thread.o $(OBJ_DIR)/SoundBoard.o \
$(OBJ_DIR)/SCSP.o $(OBJ_DIR)/SCSPDSP.o $(OBJ_DIR)/68K.o $(OBJ_DIR)/m68kcpu.o $(OBJ_DIR)/m68kopnz.o $(OBJ_DIR)/m68kopdm.o \ $(OBJ_DIR)/SCSP.o $(OBJ_DIR)/SCSPDSP.o $(OBJ_DIR)/68K.o $(OBJ_DIR)/m68kcpu.o $(OBJ_DIR)/m68kopnz.o $(OBJ_DIR)/m68kopdm.o \
$(OBJ_DIR)/m68kopac.o $(OBJ_DIR)/m68kops.o $(OBJ_DIR)/DSB.o $(OBJ_DIR)/Z80.o \ $(OBJ_DIR)/m68kopac.o $(OBJ_DIR)/m68kops.o $(OBJ_DIR)/DSB.o $(OBJ_DIR)/Z80.o \

View file

@ -115,7 +115,7 @@ endif
# #
OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/Config.o $(OBJ_DIR)/INIFile.o $(OBJ_DIR)/BlockFile.o $(OBJ_DIR)/93C46.o \ OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/Config.o $(OBJ_DIR)/INIFile.o $(OBJ_DIR)/BlockFile.o $(OBJ_DIR)/93C46.o \
$(OBJ_DIR)/ROMLoad.o $(OBJ_DIR)/unzip.o $(OBJ_DIR)/ioapi.o $(OBJ_DIR)/Error.o $(OBJ_DIR)/glew.o $(OBJ_DIR)/Shader.o \ $(OBJ_DIR)/ROMLoad.o $(OBJ_DIR)/unzip.o $(OBJ_DIR)/ioapi.o $(OBJ_DIR)/Error.o $(OBJ_DIR)/glew.o $(OBJ_DIR)/Shader.o \
$(OBJ_DIR)/Real3D.o $(OBJ_DIR)/Render3D.o $(OBJ_DIR)/Models.o $(OBJ_DIR)/Render2D.o $(OBJ_DIR)/TileGen.o \ $(OBJ_DIR)/Real3D.o $(OBJ_DIR)/Render3D.o $(OBJ_DIR)/Models.o $(OBJ_DIR)/TextureRefs.o $(OBJ_DIR)/Render2D.o $(OBJ_DIR)/TileGen.o \
$(OBJ_DIR)/Model3.o $(OBJ_DIR)/ppc.o $(OBJ_DIR)/Main.o $(OBJ_DIR)/Audio.o $(OBJ_DIR)/Thread.o $(OBJ_DIR)/SoundBoard.o \ $(OBJ_DIR)/Model3.o $(OBJ_DIR)/ppc.o $(OBJ_DIR)/Main.o $(OBJ_DIR)/Audio.o $(OBJ_DIR)/Thread.o $(OBJ_DIR)/SoundBoard.o \
$(OBJ_DIR)/SCSP.o $(OBJ_DIR)/SCSPDSP.o $(OBJ_DIR)/68K.o $(OBJ_DIR)/m68kcpu.o $(OBJ_DIR)/m68kopnz.o $(OBJ_DIR)/m68kopdm.o \ $(OBJ_DIR)/SCSP.o $(OBJ_DIR)/SCSPDSP.o $(OBJ_DIR)/68K.o $(OBJ_DIR)/m68kcpu.o $(OBJ_DIR)/m68kopnz.o $(OBJ_DIR)/m68kopdm.o \
$(OBJ_DIR)/m68kopac.o $(OBJ_DIR)/m68kops.o $(OBJ_DIR)/DSB.o $(OBJ_DIR)/Z80.o \ $(OBJ_DIR)/m68kopac.o $(OBJ_DIR)/m68kops.o $(OBJ_DIR)/DSB.o $(OBJ_DIR)/Z80.o \
@ -233,4 +233,4 @@ $(OBJ_DIR)/%.o: Src/Pkgs/%.c
# To eliminate name conflicts, object files have the prefix "amp_" attached. # To eliminate name conflicts, object files have the prefix "amp_" attached.
# #
$(OBJ_DIR)/amp_%.o: Src/Sound/MPEG/%.cpp $(OBJ_DIR)/amp_%.o: Src/Sound/MPEG/%.cpp
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o $(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o

View file

@ -146,7 +146,7 @@ endif
HEADERS = Src/Supermodel.h Src/OSD/SDL/Types.h HEADERS = Src/Supermodel.h Src/OSD/SDL/Types.h
OBJ = $(OBJ_DIR)/PPCDisasm.obj $(OBJ_DIR)/Games.obj $(OBJ_DIR)/Config.obj $(OBJ_DIR)/INIFile.obj $(OBJ_DIR)/BlockFile.obj $(OBJ_DIR)/93C46.obj \ OBJ = $(OBJ_DIR)/PPCDisasm.obj $(OBJ_DIR)/Games.obj $(OBJ_DIR)/Config.obj $(OBJ_DIR)/INIFile.obj $(OBJ_DIR)/BlockFile.obj $(OBJ_DIR)/93C46.obj \
$(OBJ_DIR)/ROMLoad.obj $(OBJ_DIR)/unzip.obj $(OBJ_DIR)/ioapi.obj $(OBJ_DIR)/Error.obj $(OBJ_DIR)/glew.obj $(OBJ_DIR)/Shader.obj \ $(OBJ_DIR)/ROMLoad.obj $(OBJ_DIR)/unzip.obj $(OBJ_DIR)/ioapi.obj $(OBJ_DIR)/Error.obj $(OBJ_DIR)/glew.obj $(OBJ_DIR)/Shader.obj \
$(OBJ_DIR)/Real3D.obj $(OBJ_DIR)/Render3D.obj $(OBJ_DIR)/Models.obj $(OBJ_DIR)/Render2D.obj $(OBJ_DIR)/TileGen.obj \ $(OBJ_DIR)/Real3D.obj $(OBJ_DIR)/Render3D.obj $(OBJ_DIR)/Models.obj $(OBJ_DIR)/TextureRefs.obj $(OBJ_DIR)/Render2D.obj $(OBJ_DIR)/TileGen.obj \
$(OBJ_DIR)/Model3.obj $(OBJ_DIR)/ppc.obj $(OBJ_DIR)/Main.obj $(OBJ_DIR)/Audio.obj $(OBJ_DIR)/Thread.obj $(OBJ_DIR)/SoundBoard.obj \ $(OBJ_DIR)/Model3.obj $(OBJ_DIR)/ppc.obj $(OBJ_DIR)/Main.obj $(OBJ_DIR)/Audio.obj $(OBJ_DIR)/Thread.obj $(OBJ_DIR)/SoundBoard.obj \
$(OBJ_DIR)/SCSP.obj $(OBJ_DIR)/SCSPDSP.obj $(OBJ_DIR)/68K.obj $(OBJ_DIR)/m68kcpu.obj $(OBJ_DIR)/m68kopnz.obj $(OBJ_DIR)/m68kopdm.obj \ $(OBJ_DIR)/SCSP.obj $(OBJ_DIR)/SCSPDSP.obj $(OBJ_DIR)/68K.obj $(OBJ_DIR)/m68kcpu.obj $(OBJ_DIR)/m68kopnz.obj $(OBJ_DIR)/m68kopdm.obj \
$(OBJ_DIR)/m68kopac.obj $(OBJ_DIR)/m68kops.obj $(OBJ_DIR)/DSB.obj $(OBJ_DIR)/Z80.obj \ $(OBJ_DIR)/m68kopac.obj $(OBJ_DIR)/m68kops.obj $(OBJ_DIR)/DSB.obj $(OBJ_DIR)/Z80.obj \
@ -287,4 +287,4 @@ $(OBJ_DIR)/amp_%.obj: Src/Sound/MPEG/%.cpp Src/Sound/MPEG/%.h
$(CC) $< $(CPPFLAGS) /Fo$(OBJ_DIR)/amp_$(*F).obj $(CC) $< $(CPPFLAGS) /Fo$(OBJ_DIR)/amp_$(*F).obj
$(OBJ_DIR)/amp_%.obj: Src/Sound/MPEG/%.cpp $(OBJ_DIR)/amp_%.obj: Src/Sound/MPEG/%.cpp
$(CC) $< $(CPPFLAGS) /Fo$(OBJ_DIR)/amp_$(*F).obj $(CC) $< $(CPPFLAGS) /Fo$(OBJ_DIR)/amp_$(*F).obj

View file

@ -611,39 +611,40 @@ bool CRender3D::InsertPolygon(ModelCache *Cache, const Poly *P)
} }
// Begins caching a new model by resetting to the start of the local vertex buffer // Begins caching a new model by resetting to the start of the local vertex buffer
bool CRender3D::BeginModel(ModelCache *Cache) struct VBORef *CRender3D::BeginModel(ModelCache *Cache)
{ {
int m; struct VBORef *Model;
unsigned m = Cache->numModels;
// Determine whether we've exceeded the model cache limits (caller will have to recache) // Determine whether we've exceeded the model cache limits (caller will have to recache)
if (Cache->numModels >= Cache->maxModels) if (m >= Cache->maxModels)
return FAIL; // too many models {
//return ErrorLog("Too many %s models.", Cache->dynamic?"dynamic":"static"); //ErrorLog("Too many %s models.", Cache->dynamic?"dynamic":"static");
return NULL;
}
m = Cache->numModels; Model = &(Cache->Models[m]);
// Reset to the beginning of the local vertex buffer // Reset to the beginning of the local vertex buffer
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
Cache->curVertIdx[i] = 0; Cache->curVertIdx[i] = 0;
// Clear the VBO reference to 0 // Clear the VBO reference to 0 and clear texture references
memset(&(Cache->Models[m]), 0, sizeof(VBORef)); memset(Model, 0, sizeof(VBORef) - sizeof(CTextureRefs));
Model->texRefs.Clear();
// Record starting index of first opaque polygon in VBO (alpha poly index will be re-set in EndModel()) // Record starting index of first opaque polygon in VBO (alpha poly index will be re-set in EndModel())
Cache->Models[m].index[POLY_STATE_NORMAL] = Cache->vboCurOffset/(VBO_VERTEX_SIZE*sizeof(GLfloat)); Model->index[POLY_STATE_NORMAL] = Cache->vboCurOffset/(VBO_VERTEX_SIZE*sizeof(GLfloat));
Cache->Models[m].index[POLY_STATE_ALPHA] = Cache->Models[m].index[POLY_STATE_NORMAL]; Model->index[POLY_STATE_ALPHA] = Model->index[POLY_STATE_NORMAL];
return OKAY; return Model;
} }
// Uploads all vertices from the local vertex buffer to the VBO, sets up the VBO reference, updates the LUT // Uploads all vertices from the local vertex buffer to the VBO, sets up the VBO reference, updates the LUT
struct VBORef *CRender3D::EndModel(ModelCache *Cache, int lutIdx, UINT16 texOffset) void CRender3D::EndModel(ModelCache *Cache, struct VBORef *Model, int lutIdx, UINT16 texOffset)
{ {
struct VBORef *Model; int m = Cache->numModels++;
int m;
m = Cache->numModels++;
Model = &(Cache->Models[m]);
// Record the number of vertices, completing the VBORef // Record the number of vertices, completing the VBORef
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
@ -669,9 +670,6 @@ struct VBORef *CRender3D::EndModel(ModelCache *Cache, int lutIdx, UINT16 texOffs
if (Cache->lut[lutIdx] >= 0) // another texture offset state already cached if (Cache->lut[lutIdx] >= 0) // another texture offset state already cached
Model->nextTexOffset = &(Cache->Models[Cache->lut[lutIdx]]); Model->nextTexOffset = &(Cache->Models[Cache->lut[lutIdx]]);
Cache->lut[lutIdx] = m; Cache->lut[lutIdx] = m;
// Return a pointer to the cached model's VBO reference
return Model;
} }
/* /*
@ -699,7 +697,8 @@ struct VBORef *CRender3D::CacheModel(ModelCache *Cache, int lutIdx, UINT16 texOf
return NULL; return NULL;
// Start constructing a new model // Start constructing a new model
if (FAIL == BeginModel(Cache)) struct VBORef *Model = BeginModel(Cache);
if (NULL == Model)
return NULL; // too many models! return NULL; // too many models!
// Cache all polygons // Cache all polygons
@ -758,7 +757,13 @@ struct VBORef *CRender3D::CacheModel(ModelCache *Cache, int lutIdx, UINT16 texOf
// Decode the texture // Decode the texture
if (texEnable) if (texEnable)
DecodeTexture(texFormat, texBaseX, texBaseY, texWidth, texHeight); {
// If model cache is static, record texture reference in model cache entry for later decoding.
// If cache is dynamic, or if it's not possible to record the texture reference (due to lack of
// memory) then decode the texture now.
if (Cache->dynamic || !Model->texRefs.AddRef(texFormat, texBaseX, texBaseY, texWidth, texHeight))
DecodeTexture(texFormat, texBaseX, texBaseY, texWidth, texHeight);
}
// Polygon normal is in upper 24 bits: sign + 1.22 fixed point // Polygon normal is in upper 24 bits: sign + 1.22 fixed point
P.n[0] = (GLfloat) (((INT32)P.header[1])>>8) * (1.0f/4194304.0f); P.n[0] = (GLfloat) (((INT32)P.header[1])>>8) * (1.0f/4194304.0f);
@ -826,7 +831,8 @@ struct VBORef *CRender3D::CacheModel(ModelCache *Cache, int lutIdx, UINT16 texOf
} }
// Finish model and enter it into the LUT // Finish model and enter it into the LUT
return EndModel(Cache,lutIdx,texOffset); EndModel(Cache,Model,lutIdx,texOffset);
return Model;
} }

View file

@ -160,7 +160,6 @@
#define ISINF(x) (std::isinf(x)) #define ISINF(x) (std::isinf(x))
#endif #endif
/****************************************************************************** /******************************************************************************
Definitions and Constants Definitions and Constants
******************************************************************************/ ******************************************************************************/
@ -427,12 +426,8 @@ void CRender3D::UploadTextures(unsigned x, unsigned y, unsigned width, unsigned
} }
} }
} }
ClearModelCache(&VROMCache);
ClearModelCache(&PolyCache);
} }
/****************************************************************************** /******************************************************************************
Real3D Address Translation Real3D Address Translation
@ -625,6 +620,11 @@ bool CRender3D::DrawModel(UINT32 modelAddr)
} }
} }
// If cache is static then decode all the texture references contained in the cached model
// before rendering (models in dynamic cache will have been decoded already in CacheModel)
if (!Cache->dynamic)
ModelRef->texRefs.DecodeAllTextures(this);
// Add to display list // Add to display list
return AppendDisplayList(Cache, false, ModelRef); return AppendDisplayList(Cache, false, ModelRef);
} }
@ -957,10 +957,6 @@ void CRender3D::RenderViewport(UINT32 addr, int pri)
stackDepth = 0; stackDepth = 0;
listDepth = 0; listDepth = 0;
// Descend down the node link: Use stack machine to traverse display list
//ClearStack();
//StackMachine(nodeAddr);
// Descend down the node link: Use recursive traversal // Descend down the node link: Use recursive traversal
DescendNodePtr(nodeAddr); DescendNodePtr(nodeAddr);
} }

View file

@ -67,13 +67,17 @@ struct Poly
* vertices: normal and alpha. Copies of the model with different texture * vertices: normal and alpha. Copies of the model with different texture
* offsets applied are searchable via the linked list of texture offset states. * offsets applied are searchable via the linked list of texture offset states.
*/ */
struct VBORef struct VBORef
{ {
unsigned index[2]; // index of model polygons in VBO unsigned index[2]; // index of model polygons in VBO
unsigned numVerts[2];// number of vertices unsigned numVerts[2]; // number of vertices
unsigned lutIdx; // LUT index associated with this model (for fast LUT clearing) unsigned lutIdx; // LUT index associated with this model (for fast LUT clearing)
struct VBORef *nextTexOffset; // linked list of models with different texture offset states struct VBORef *nextTexOffset; // linked list of models with different texture offset states
UINT16 texOffset; // texture offset data for this model UINT16 texOffset; // texture offset data for this model
CTextureRefs texRefs; // unique texture references contained in this model
}; };
// Display list items: model instances and viewport settings // Display list items: model instances and viewport settings
@ -196,6 +200,8 @@ public:
*/ */
class CRender3D class CRender3D
{ {
friend class CTextureRefs;
public: public:
/* /*
* RenderFrame(void): * RenderFrame(void):
@ -299,7 +305,7 @@ public:
*/ */
CRender3D(void); CRender3D(void);
~CRender3D(void); ~CRender3D(void);
private: private:
/* /*
* Private Members * Private Members
@ -315,8 +321,8 @@ private:
void ClearDisplayList(ModelCache *Cache); void ClearDisplayList(ModelCache *Cache);
bool InsertPolygon(ModelCache *cache, const Poly *p); bool InsertPolygon(ModelCache *cache, const Poly *p);
void InsertVertex(ModelCache *cache, const Vertex *v, const Poly *p, float normFlip); void InsertVertex(ModelCache *cache, const Vertex *v, const Poly *p, float normFlip);
bool BeginModel(ModelCache *cache); struct VBORef *BeginModel(ModelCache *cache);
struct VBORef *EndModel(ModelCache *cache, int lutIdx, UINT16 texOffset); void EndModel(ModelCache *cache, struct VBORef *Model, int lutIdx, UINT16 texOffset);
struct VBORef *CacheModel(ModelCache *cache, int lutIdx, UINT16 texOffset, const UINT32 *data); struct VBORef *CacheModel(ModelCache *cache, int lutIdx, UINT16 texOffset, const UINT32 *data);
struct VBORef *LookUpModel(ModelCache *cache, int lutIdx, UINT16 texOffset); struct VBORef *LookUpModel(ModelCache *cache, int lutIdx, UINT16 texOffset);
void ClearModelCache(ModelCache *cache); void ClearModelCache(ModelCache *cache);

329
Src/Graphics/TextureRefs.cpp Executable file
View file

@ -0,0 +1,329 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* CTextureRefs.cpp
*
* Class that tracks unique texture references, eg in a cached model.
*
* Texture references are stored internally as a 27-bit field (3 bits for format, 6 bits each for x, y, width & height) to save space.
*
* A pre-allocated array is used for storing up to TEXREFS_ARRAY_SIZE texture references. When that limit is exceeded, it switches
* to using a hashset to store the texture references, but this requires extra memory allocation.
*/
#include "Supermodel.h"
CTextureRefs::CTextureRefs() : m_size(0), m_hashCapacity(0), m_hashEntries(NULL)
{
//
}
CTextureRefs::~CTextureRefs()
{
DeleteAllHashEntries();
}
unsigned CTextureRefs::GetSize() const
{
return m_size;
}
void CTextureRefs::Clear()
{
// Delete all hash entries
DeleteAllHashEntries();
m_size = 0;
m_hashCapacity = 0;
m_hashEntries = NULL;
}
bool CTextureRefs::ContainsRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height)
{
// Pack texture reference into bitfield
unsigned texRef = (fmt&7)<<24|(x&0x7E0)<<13|(y&0x7E0)<<7|(width&0x7E0)<<1|(height&0x7E0)>>5;
// Check if using array or hashset
if (m_size <= TEXREFS_ARRAY_SIZE)
{
// See if texture reference held in array
for (unsigned i = 0; i < m_size; i++)
{
if (texRef == m_array[i])
return true;
}
return false;
}
else
// See if texture reference held in hashset
return HashContains(texRef);
}
bool CTextureRefs::AddRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height)
{
// Pack texture reference into bitfield
unsigned texRef = (fmt&7)<<24|(x&0x7E0)<<13|(y&0x7E0)<<7|(width&0x7E0)<<1|(height&0x7E0)>>5;
// Check if using array or hashset
if (m_size <= TEXREFS_ARRAY_SIZE)
{
// See if already held in array, if so nothing to do
for (unsigned i = 0; i < m_size; i++)
{
if (texRef == m_array[i])
return true;
}
// If not, check if array is full
if (m_size == TEXREFS_ARRAY_SIZE)
{
// If so, set initial hashset capacity to 47 to initialize it
UpdateHashCapacity(47);
// Copy array into hashset
for (unsigned i = 0; i < TEXREFS_ARRAY_SIZE; i++)
AddToHash(m_array[i]);
// Add texture reference to hashset
AddToHash(texRef);
}
else
{
// Add texture reference to array
m_array[m_size] = texRef;
m_size++;
}
return true;
}
else
// Add texture reference to hashset
return AddToHash(texRef);
}
bool CTextureRefs::RemoveRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height)
{
// Pack texture reference into bitfield
unsigned texRef = (fmt&7)<<24|(x&0x7E0)<<13|(y&0x7E0)<<7|(width&0x7E0)<<1|(height&0x7E0)>>5;
// Check if using array or hashset
if (m_size <= TEXREFS_ARRAY_SIZE)
{
for (unsigned i = 0; i < m_size; i++)
{
if (texRef == m_array[i])
{
for (unsigned j = i + 1; j < m_size; j++)
m_array[j - 1] = m_array[j];
m_size--;
return true;
}
}
return false;
}
else
{
// Remove texture reference from hashset
bool removed = RemoveFromHash(texRef);
// See if should switch back to array
if (m_size == TEXREFS_ARRAY_SIZE)
{
// Loop through all hash entries and copy texture references into array
unsigned j = 0;
for (unsigned i = 0; i < m_hashCapacity; i++)
{
for (HashEntry *entry = m_hashEntries[i]; entry; entry = entry->nextEntry)
m_array[j++] = entry->texRef;
}
// Delete all hash entries
DeleteAllHashEntries();
}
return removed;
}
}
void CTextureRefs::DecodeAllTextures(CRender3D *Render3D)
{
// Check if using array or hashset
if (m_size <= TEXREFS_ARRAY_SIZE)
{
// Loop through elements in array and call CRender3D::DecodeTexture
for (unsigned i = 0; i < m_size; i++)
{
// Unpack texture reference from bitfield
unsigned texRef = m_array[i];
unsigned fmt = texRef>>24;
unsigned x = (texRef>>13)&0x7E0;
unsigned y = (texRef>>7)&0x7E0;
unsigned width = (texRef>>1)&0x7E0;
unsigned height = (texRef<<5)&0x7E0;
Render3D->DecodeTexture(fmt, x, y, width, height);
}
}
else
{
// Loop through all hash entriesa and call CRender3D::DecodeTexture
for (unsigned i = 0; i < m_hashCapacity; i++)
{
for (HashEntry *entry = m_hashEntries[i]; entry; entry = entry->nextEntry)
{
// Unpack texture reference from bitfield
unsigned texRef = entry->texRef;
unsigned fmt = texRef>>24;
unsigned x = (texRef>>13)&0x7E0;
unsigned y = (texRef>>7)&0x7E0;
unsigned width = (texRef>>1)&0x7E0;
unsigned height = (texRef<<5)&0x7E0;
Render3D->DecodeTexture(fmt, x, y, width, height);
}
}
}
}
bool CTextureRefs::UpdateHashCapacity(unsigned capacity)
{
unsigned oldCapacity = m_hashCapacity;
HashEntry **oldEntries = m_hashEntries;
// Update capacity and create new empty entries array
m_hashCapacity = capacity;
m_hashEntries = new(std::nothrow) HashEntry*[capacity];
if (!m_hashEntries)
return false;
memset(m_hashEntries, NULL, capacity * sizeof(HashEntry*));
if (oldEntries)
{
// Redistribute entries into new entries array
for (unsigned i = 0; i < oldCapacity; i++)
{
HashEntry *entry = oldEntries[i];
while (entry)
{
HashEntry *nextEntry = entry->nextEntry;
unsigned hash = entry->texRef % capacity;
entry->nextEntry = m_hashEntries[hash];
m_hashEntries[hash] = entry;
entry = nextEntry;
}
}
// Delete old entries array
delete[] oldEntries;
}
return true;
}
HashEntry *CTextureRefs::CreateHashEntry(unsigned texRef, bool &hashCapacityUpdated)
{
// Update size and increase hash capacity if required
m_size++;
hashCapacityUpdated = m_size >= m_hashCapacity;
if (hashCapacityUpdated)
{
if (m_hashCapacity < 89)
UpdateHashCapacity(89); // Capacity of 89 gives good sequence of mostly prime capacities (89, 179, 359, 719, 1439, 2879 etc)
else
UpdateHashCapacity(2 * m_hashCapacity + 1);
}
return new(std::nothrow) HashEntry(texRef);
}
void CTextureRefs::DeleteHashEntry(HashEntry *entry)
{
// Update size and delete hash entry
m_size--;
delete entry;
}
void CTextureRefs::DeleteAllHashEntries()
{
if (!m_hashEntries)
return;
// Delete all hash entries and their storage
for (unsigned i = 0; i < m_hashCapacity; i++)
{
HashEntry *entry = m_hashEntries[i];
if (entry)
delete entry;
}
delete[] m_hashEntries;
}
bool CTextureRefs::AddToHash(unsigned texRef)
{
// Convert texture reference to hash value
unsigned hash = texRef % m_hashCapacity;
// Loop through linked list for hash value and see if have texture reference already
HashEntry *headEntry = m_hashEntries[hash];
HashEntry *entry = headEntry;
while (entry && texRef != entry->texRef)
entry = entry->nextEntry;
// If found, nothing to do
if (entry)
return true;
// Otherwise, create new hash entry for texture reference
bool hashCapacityUpdated;
entry = CreateHashEntry(texRef, hashCapacityUpdated);
// If couldn't create entry (ie out of memory), let caller know
if (!entry)
return false;
if (hashCapacityUpdated)
{
// If hash capacity was increased recalculate hash value
hash = texRef % m_hashCapacity;
headEntry = m_hashEntries[hash];
}
// Store hash entry in linked list for hash value
entry->nextEntry = headEntry;
m_hashEntries[hash] = entry;
return true;
}
bool CTextureRefs::RemoveFromHash(unsigned texRef)
{
// Convert texture reference to hash value
unsigned hash = texRef % m_hashCapacity;
// Loop through linked list for hash value and see if have texture reference
HashEntry *entry = m_hashEntries[hash];
HashEntry *prevEntry = NULL;
while (entry && texRef != entry->texRef)
{
prevEntry = entry;
entry = entry->nextEntry;
}
// If not found, nothing to do
if (!entry)
return false;
// Otherwise, remove entry from linked list for hash value
if (prevEntry)
prevEntry->nextEntry = entry->nextEntry;
else
m_hashEntries[hash] = entry->nextEntry;
// Delete hash entry storage
DeleteHashEntry(entry);
return true;
}
bool CTextureRefs::HashContains(unsigned texRef) const
{
// Convert texture reference to hash value
unsigned hash = texRef % m_hashCapacity;
// Loop through linked list for hash value and see if have texture reference
HashEntry *entry = m_hashEntries[hash];
while (entry && texRef != entry->texRef)
entry = entry->nextEntry;
return !!entry;
}

164
Src/Graphics/TextureRefs.h Executable file
View file

@ -0,0 +1,164 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* CTextureRefs.h
*
* Class that tracks unique texture references, eg in a cached model.
*/
#ifndef INCLUDED_TEXTUREREFS_H
#define INCLUDED_TEXTUREREFS_H
#define TEXREFS_ARRAY_SIZE 12
// Hash entry that holds a texture reference in the hashset
struct HashEntry
{
const unsigned texRef; // Texture reference as a bitfield
HashEntry *nextEntry; // Next entry with the same hash
HashEntry(unsigned theTexRef) : texRef(theTexRef), nextEntry(NULL) { }
};
class CRender3D;
class CTextureRefs
{
public:
/*
* CTextureRefs():
*
* Constructor.
*/
CTextureRefs();
/*
* ~CTextureRefs():
*
* Destructor.
*/
~CTextureRefs();
/*
* GetSize():
*
* Returns number of unique texture references held.
*/
unsigned GetSize() const;
/*
* Clear():
*
* Removes all texture references.
*/
void Clear();
/*
* ContainsRef(fmt, x, y, width, height):
*
* Returns true if holds the given texture reference.
*/
bool ContainsRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height);
/*
* AddRef(fmt, x, y, width, height):
*
* Adds the given texture reference. Returns false if it was not possible to add the reference (ie out of memory).
*/
bool AddRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height);
/*
* RemoveRef(fmt, x, y, width, height):
*
* Removes the given texture reference. Return true if the reference was found.
*/
bool RemoveRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height);
/*
* RemoveRef(fmt, x, y, width, height):
*
* Decodes all texture references held, calling CRender3D::DecodeTexture for each one.
*/
void DecodeAllTextures(CRender3D *Render3D);
private:
// Number of texture references held.
unsigned m_size;
// Pre-allocated array used to hold first TEXREFS_ARRAY_SIZE texture references.
unsigned m_array[TEXREFS_ARRAY_SIZE];
// Dynamically allocated hashset used to hold texture references when there are more than TEXREFS_ARRAY_SIZE.
unsigned m_hashCapacity;
HashEntry **m_hashEntries;
/*
* UpdateHashCapacity(hashCapacity)
*
* Increases capacity of the hashset to given size.
*/
bool UpdateHashCapacity(unsigned hashCapacity);
/*
* CreateHashEntry(texRef, hashCapacityUpdated)
*
* Creates and returns a new hash entry, updating the capacity if required (hashCapacityUpdated is set to true).
*/
HashEntry *CreateHashEntry(unsigned texRef, bool &hashCapacityUpdated);
/*
* DeleteHashEntry(entry)
*
* Deletes the given hash entry and its storage.
*/
void DeleteHashEntry(HashEntry *entry);
/*
* DeleteAllHashEntries()
*
* Deletes all hash entries and their storage.
*/
void DeleteAllHashEntries();
/*
* AddToHash(texRef)
*
* Adds the given texture reference (as a bitfield) to the hashset.
*/
bool AddToHash(unsigned texRef);
/*
* RemoveFromHash(texRef)
*
* Removes the given texture reference (as a bitfield) from the hashset.
*/
bool RemoveFromHash(unsigned texRef);
/*
* HashContains(texRef)
*
* Returns true if given texture reference (as a bitfield) is held in the hashset.
*/
bool HashContains(unsigned texRef) const;
};
#endif // INCLUDED_TEXTUREREFS_H

View file

@ -118,6 +118,7 @@
#include "INIFile.h" #include "INIFile.h"
#include "BlockFile.h" #include "BlockFile.h"
#include "Graphics/Render2D.h" #include "Graphics/Render2D.h"
#include "Graphics/TextureRefs.h"
#include "Graphics/Render3D.h" #include "Graphics/Render3D.h"
#include "Graphics/Shader.h" #include "Graphics/Shader.h"
#ifdef SUPERMODEL_DEBUGGER #ifdef SUPERMODEL_DEBUGGER

View file

@ -690,6 +690,10 @@
RelativePath="..\Src\Graphics\Shader.cpp" RelativePath="..\Src\Graphics\Shader.cpp"
> >
</File> </File>
<File
RelativePath="..\Src\Graphics\TextureRefs.cpp"
>
</File>
<Filter <Filter
Name="Shaders" Name="Shaders"
> >
@ -1824,6 +1828,10 @@
RelativePath="..\Src\Graphics\Shaders3D.h" RelativePath="..\Src\Graphics\Shaders3D.h"
> >
</File> </File>
<File
RelativePath="..\Src\Graphics\TextureRefs.h"
>
</File>
</Filter> </Filter>
<Filter <Filter
Name="Model3" Name="Model3"