From 3b41239cfbe83d808027f1a1d5e61a6cf01fd1bb Mon Sep 17 00:00:00 2001 From: Nik Henson Date: Thu, 23 Feb 2012 23:20:21 +0000 Subject: [PATCH] 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. --- Makefiles/Makefile.SDL.OSX.GCC | 4 +- Makefiles/Makefile.SDL.UNIX.GCC | 2 +- Makefiles/Makefile.SDL.Win32.GCC | 4 +- Makefiles/Makefile.SDL.Win32.MSVC | 4 +- Src/Graphics/Models.cpp | 52 ++--- Src/Graphics/Render3D.cpp | 14 +- Src/Graphics/Render3D.h | 20 +- Src/Graphics/TextureRefs.cpp | 329 ++++++++++++++++++++++++++++++ Src/Graphics/TextureRefs.h | 164 +++++++++++++++ Src/Supermodel.h | 1 + VS2008/Supermodel.vcproj | 8 + 11 files changed, 556 insertions(+), 46 deletions(-) create mode 100755 Src/Graphics/TextureRefs.cpp create mode 100755 Src/Graphics/TextureRefs.h diff --git a/Makefiles/Makefile.SDL.OSX.GCC b/Makefiles/Makefile.SDL.OSX.GCC index 1a28daf..056ba83 100644 --- a/Makefiles/Makefile.SDL.OSX.GCC +++ b/Makefiles/Makefile.SDL.OSX.GCC @@ -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 \ No newline at end of file + $(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o diff --git a/Makefiles/Makefile.SDL.UNIX.GCC b/Makefiles/Makefile.SDL.UNIX.GCC index d4571bd..ff80255 100644 --- a/Makefiles/Makefile.SDL.UNIX.GCC +++ b/Makefiles/Makefile.SDL.UNIX.GCC @@ -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 \ diff --git a/Makefiles/Makefile.SDL.Win32.GCC b/Makefiles/Makefile.SDL.Win32.GCC index 3cf451c..d347ebb 100644 --- a/Makefiles/Makefile.SDL.Win32.GCC +++ b/Makefiles/Makefile.SDL.Win32.GCC @@ -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 \ No newline at end of file + $(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o diff --git a/Makefiles/Makefile.SDL.Win32.MSVC b/Makefiles/Makefile.SDL.Win32.MSVC index 15c4ac9..3c82a8b 100644 --- a/Makefiles/Makefile.SDL.Win32.MSVC +++ b/Makefiles/Makefile.SDL.Win32.MSVC @@ -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 \ No newline at end of file + $(CC) $< $(CPPFLAGS) /Fo$(OBJ_DIR)/amp_$(*F).obj diff --git a/Src/Graphics/Models.cpp b/Src/Graphics/Models.cpp index d4b3bb5..e490710 100644 --- a/Src/Graphics/Models.cpp +++ b/Src/Graphics/Models.cpp @@ -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; } diff --git a/Src/Graphics/Render3D.cpp b/Src/Graphics/Render3D.cpp index 02fe420..3649cad 100644 --- a/Src/Graphics/Render3D.cpp +++ b/Src/Graphics/Render3D.cpp @@ -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); } diff --git a/Src/Graphics/Render3D.h b/Src/Graphics/Render3D.h index f79160b..3ce82e5 100644 --- a/Src/Graphics/Render3D.h +++ b/Src/Graphics/Render3D.h @@ -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); diff --git a/Src/Graphics/TextureRefs.cpp b/Src/Graphics/TextureRefs.cpp new file mode 100755 index 0000000..072d594 --- /dev/null +++ b/Src/Graphics/TextureRefs.cpp @@ -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 . + **/ + +/* + * 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; +} diff --git a/Src/Graphics/TextureRefs.h b/Src/Graphics/TextureRefs.h new file mode 100755 index 0000000..9f65106 --- /dev/null +++ b/Src/Graphics/TextureRefs.h @@ -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 . + **/ + +/* + * 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 diff --git a/Src/Supermodel.h b/Src/Supermodel.h index d6a8bed..01906ff 100644 --- a/Src/Supermodel.h +++ b/Src/Supermodel.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 diff --git a/VS2008/Supermodel.vcproj b/VS2008/Supermodel.vcproj index f3bb427..1b504f8 100755 --- a/VS2008/Supermodel.vcproj +++ b/VS2008/Supermodel.vcproj @@ -690,6 +690,10 @@ RelativePath="..\Src\Graphics\Shader.cpp" > + + @@ -1824,6 +1828,10 @@ RelativePath="..\Src\Graphics\Shaders3D.h" > + +