mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-24 14:45:40 +00:00
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:
parent
aba801ebb4
commit
3b41239cfb
|
@ -90,7 +90,7 @@ endif
|
|||
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_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)/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 \
|
||||
|
@ -216,4 +216,4 @@ $(OBJ_DIR)/amp_%.o: Src/Sound/MPEG/%.cpp Src/Sound/MPEG/%.h
|
|||
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o
|
||||
|
||||
$(OBJ_DIR)/amp_%.o: Src/Sound/MPEG/%.cpp
|
||||
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o
|
||||
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o
|
||||
|
|
|
@ -92,7 +92,7 @@ endif
|
|||
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_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)/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 \
|
||||
|
|
|
@ -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_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)/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 \
|
||||
|
@ -233,4 +233,4 @@ $(OBJ_DIR)/%.o: Src/Pkgs/%.c
|
|||
# To eliminate name conflicts, object files have the prefix "amp_" attached.
|
||||
#
|
||||
$(OBJ_DIR)/amp_%.o: Src/Sound/MPEG/%.cpp
|
||||
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o
|
||||
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o
|
||||
|
|
|
@ -146,7 +146,7 @@ endif
|
|||
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_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)/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 \
|
||||
|
@ -287,4 +287,4 @@ $(OBJ_DIR)/amp_%.obj: Src/Sound/MPEG/%.cpp Src/Sound/MPEG/%.h
|
|||
$(CC) $< $(CPPFLAGS) /Fo$(OBJ_DIR)/amp_$(*F).obj
|
||||
|
||||
$(OBJ_DIR)/amp_%.obj: Src/Sound/MPEG/%.cpp
|
||||
$(CC) $< $(CPPFLAGS) /Fo$(OBJ_DIR)/amp_$(*F).obj
|
||||
$(CC) $< $(CPPFLAGS) /Fo$(OBJ_DIR)/amp_$(*F).obj
|
||||
|
|
|
@ -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
|
||||
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)
|
||||
if (Cache->numModels >= Cache->maxModels)
|
||||
return FAIL; // too many models
|
||||
//return ErrorLog("Too many %s models.", Cache->dynamic?"dynamic":"static");
|
||||
if (m >= Cache->maxModels)
|
||||
{
|
||||
//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
|
||||
for (int i = 0; i < 2; i++)
|
||||
Cache->curVertIdx[i] = 0;
|
||||
|
||||
// Clear the VBO reference to 0
|
||||
memset(&(Cache->Models[m]), 0, sizeof(VBORef));
|
||||
// Clear the VBO reference to 0 and clear texture references
|
||||
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())
|
||||
Cache->Models[m].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_NORMAL] = Cache->vboCurOffset/(VBO_VERTEX_SIZE*sizeof(GLfloat));
|
||||
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
|
||||
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;
|
||||
|
||||
m = Cache->numModels++;
|
||||
Model = &(Cache->Models[m]);
|
||||
int m = Cache->numModels++;
|
||||
|
||||
// Record the number of vertices, completing the VBORef
|
||||
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
|
||||
Model->nextTexOffset = &(Cache->Models[Cache->lut[lutIdx]]);
|
||||
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;
|
||||
|
||||
// Start constructing a new model
|
||||
if (FAIL == BeginModel(Cache))
|
||||
struct VBORef *Model = BeginModel(Cache);
|
||||
if (NULL == Model)
|
||||
return NULL; // too many models!
|
||||
|
||||
// Cache all polygons
|
||||
|
@ -758,7 +757,13 @@ struct VBORef *CRender3D::CacheModel(ModelCache *Cache, int lutIdx, UINT16 texOf
|
|||
|
||||
// Decode the texture
|
||||
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
|
||||
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
|
||||
return EndModel(Cache,lutIdx,texOffset);
|
||||
EndModel(Cache,Model,lutIdx,texOffset);
|
||||
return Model;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -160,7 +160,6 @@
|
|||
#define ISINF(x) (std::isinf(x))
|
||||
#endif
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Definitions and Constants
|
||||
******************************************************************************/
|
||||
|
@ -427,12 +426,8 @@ void CRender3D::UploadTextures(unsigned x, unsigned y, unsigned width, unsigned
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClearModelCache(&VROMCache);
|
||||
ClearModelCache(&PolyCache);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
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
|
||||
return AppendDisplayList(Cache, false, ModelRef);
|
||||
}
|
||||
|
@ -957,10 +957,6 @@ void CRender3D::RenderViewport(UINT32 addr, int pri)
|
|||
stackDepth = 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
|
||||
DescendNodePtr(nodeAddr);
|
||||
}
|
||||
|
|
|
@ -67,13 +67,17 @@ struct Poly
|
|||
* vertices: normal and alpha. Copies of the model with different texture
|
||||
* offsets applied are searchable via the linked list of texture offset states.
|
||||
*/
|
||||
|
||||
struct VBORef
|
||||
{
|
||||
unsigned index[2]; // index of model polygons in VBO
|
||||
unsigned numVerts[2];// number of vertices
|
||||
unsigned lutIdx; // LUT index associated with this model (for fast LUT clearing)
|
||||
unsigned index[2]; // index of model polygons in VBO
|
||||
unsigned numVerts[2]; // number of vertices
|
||||
unsigned lutIdx; // LUT index associated with this model (for fast LUT clearing)
|
||||
|
||||
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
|
||||
|
@ -196,6 +200,8 @@ public:
|
|||
*/
|
||||
class CRender3D
|
||||
{
|
||||
friend class CTextureRefs;
|
||||
|
||||
public:
|
||||
/*
|
||||
* RenderFrame(void):
|
||||
|
@ -299,7 +305,7 @@ public:
|
|||
*/
|
||||
CRender3D(void);
|
||||
~CRender3D(void);
|
||||
|
||||
|
||||
private:
|
||||
/*
|
||||
* Private Members
|
||||
|
@ -315,8 +321,8 @@ private:
|
|||
void ClearDisplayList(ModelCache *Cache);
|
||||
bool InsertPolygon(ModelCache *cache, const Poly *p);
|
||||
void InsertVertex(ModelCache *cache, const Vertex *v, const Poly *p, float normFlip);
|
||||
bool BeginModel(ModelCache *cache);
|
||||
struct VBORef *EndModel(ModelCache *cache, int lutIdx, UINT16 texOffset);
|
||||
struct VBORef *BeginModel(ModelCache *cache);
|
||||
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 *LookUpModel(ModelCache *cache, int lutIdx, UINT16 texOffset);
|
||||
void ClearModelCache(ModelCache *cache);
|
||||
|
|
329
Src/Graphics/TextureRefs.cpp
Executable file
329
Src/Graphics/TextureRefs.cpp
Executable 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
164
Src/Graphics/TextureRefs.h
Executable 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
|
|
@ -118,6 +118,7 @@
|
|||
#include "INIFile.h"
|
||||
#include "BlockFile.h"
|
||||
#include "Graphics/Render2D.h"
|
||||
#include "Graphics/TextureRefs.h"
|
||||
#include "Graphics/Render3D.h"
|
||||
#include "Graphics/Shader.h"
|
||||
#ifdef SUPERMODEL_DEBUGGER
|
||||
|
|
|
@ -690,6 +690,10 @@
|
|||
RelativePath="..\Src\Graphics\Shader.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\Src\Graphics\TextureRefs.cpp"
|
||||
>
|
||||
</File>
|
||||
<Filter
|
||||
Name="Shaders"
|
||||
>
|
||||
|
@ -1824,6 +1828,10 @@
|
|||
RelativePath="..\Src\Graphics\Shaders3D.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\Src\Graphics\TextureRefs.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Model3"
|
||||
|
|
Loading…
Reference in a new issue