Cache rom models, for better performance.

This commit is contained in:
Ian Curtis 2016-03-26 22:44:26 +00:00
parent 8488f22ac5
commit 4992e59673
8 changed files with 142 additions and 70 deletions

View file

@ -35,7 +35,10 @@ struct R3DPoly
struct Mesh struct Mesh
{ {
std::shared_ptr<Texture> texture; // texture
int format, x, y, width, height = 0;
bool mirrorU = false;
bool mirrorV = false;
// attributes // attributes
bool doubleSided = false; bool doubleSided = false;
@ -49,10 +52,6 @@ struct Mesh
float fogIntensity = 1.0f; float fogIntensity = 1.0f;
// texture
bool mirrorU = false;
bool mirrorV = false;
// opengl resources // opengl resources
int vboOffset = 0; // this will be calculated later int vboOffset = 0; // this will be calculated later
int triangleCount = 0; int triangleCount = 0;
@ -65,14 +64,18 @@ struct SortingMesh : public Mesh // This struct temporarily holds the model dat
struct Model struct Model
{ {
std::vector<Mesh> meshes; std::shared_ptr<std::vector<Mesh>> meshes; // this reason why this is a shared ptr to an array, is that multiple models might use the same meshes
//which memory are we in
bool dynamic = true;
// texture offsets for model
int textureOffsetX = 0;
int textureOffsetY = 0;
//matrices //matrices
float modelMat[16]; float modelMat[16];
float determinant; // we check if the determinant of the matrix is negative, if it is, the matrix will swap the axis order float determinant; // we check if the determinant of the matrix is negative, if it is, the matrix will swap the axis order
// misc
int lutIdx = 0;
}; };
struct Viewport struct Viewport

View file

@ -4,6 +4,9 @@
#include "Vec.h" #include "Vec.h"
#include <cmath> // needed by gcc #include <cmath> // needed by gcc
#define MAX_RAM_POLYS 100000
#define MAX_ROM_POLYS 500000
#ifndef M_PI #ifndef M_PI
#define M_PI 3.14159265359 #define M_PI 3.14159265359
#endif #endif
@ -21,7 +24,7 @@ CNew3D::CNew3D()
CNew3D::~CNew3D() CNew3D::~CNew3D()
{ {
m_vboDynamic.Destroy(); m_vbo.Destroy();
} }
void CNew3D::AttachMemory(const UINT32 *cullingRAMLoPtr, const UINT32 *cullingRAMHiPtr, const UINT32 *polyRAMPtr, const UINT32 *vromPtr, const UINT16 *textureRAMPtr) void CNew3D::AttachMemory(const UINT32 *cullingRAMLoPtr, const UINT32 *cullingRAMHiPtr, const UINT32 *polyRAMPtr, const UINT32 *vromPtr, const UINT16 *textureRAMPtr)
@ -50,8 +53,7 @@ void CNew3D::SetStep(int stepID)
m_vertexFactor = (1.0f / 128.0f); // 17.7 m_vertexFactor = (1.0f / 128.0f); // 17.7
} }
m_vboDynamic.Create(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(Poly)* 100000); // allocate space for 100k polys ~ 10meg m_vbo.Create(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(Poly) * (MAX_RAM_POLYS + MAX_ROM_POLYS));
//m_vboStatic.Create(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, 0x80000); // 64meg buffer
} }
bool CNew3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXResParam, unsigned totalYResParam) bool CNew3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXResParam, unsigned totalYResParam)
@ -99,13 +101,13 @@ void CNew3D::RenderScene(int priority, bool alpha)
bool matrixLoaded = false; bool matrixLoaded = false;
if (m.meshes.empty()) { if (m.meshes->empty()) {
continue; continue;
} }
m_r3dShader.SetModelStates(&m); m_r3dShader.SetModelStates(&m);
for (auto &mesh : m.meshes) { for (auto &mesh : *m.meshes) {
if (alpha) { if (alpha) {
if (!mesh.textureAlpha && !mesh.polyAlpha) { if (!mesh.textureAlpha && !mesh.polyAlpha) {
@ -123,9 +125,12 @@ void CNew3D::RenderScene(int priority, bool alpha)
matrixLoaded = true; // do this here to stop loading matrices we don't need. Ie when rendering non transparent etc matrixLoaded = true; // do this here to stop loading matrices we don't need. Ie when rendering non transparent etc
} }
if (mesh.texture) { if (mesh.textured) {
mesh.texture->BindTexture(); auto tex = m_texSheet.BindTexture(m_textureRAM, mesh.format, mesh.mirrorU, mesh.mirrorV, mesh.x + m.textureOffsetX, mesh.y + m.textureOffsetY, mesh.width, mesh.height);
mesh.texture->SetWrapMode(mesh.mirrorU, mesh.mirrorV); if (tex) {
tex->BindTexture();
tex->SetWrapMode(mesh.mirrorU, mesh.mirrorV);
}
} }
m_r3dShader.SetMeshUniforms(&mesh); m_r3dShader.SetMeshUniforms(&mesh);
@ -141,7 +146,7 @@ void CNew3D::RenderScene(int priority, bool alpha)
void CNew3D::RenderFrame(void) void CNew3D::RenderFrame(void)
{ {
// release any resources from last frame // release any resources from last frame
m_polyBuffer.clear(); // clear dyanmic model memory buffer m_polyBufferRam.clear(); // clear dyanmic model memory buffer
m_nodes.clear(); // memory will grow during the object life time, that's fine, no need to shrink to fit m_nodes.clear(); // memory will grow during the object life time, that's fine, no need to shrink to fit
m_modelMat.Release(); // would hope we wouldn't need this but no harm in checking m_modelMat.Release(); // would hope we wouldn't need this but no harm in checking
m_nodeAttribs.Reset(); m_nodeAttribs.Reset();
@ -156,9 +161,29 @@ void CNew3D::RenderFrame(void)
RenderViewport(0x800000, pri); // build model structure RenderViewport(0x800000, pri); // build model structure
} }
m_vboDynamic.Bind(true); m_vbo.Bind(true);
m_vboDynamic.BufferSubData(0, m_polyBuffer.size()*sizeof(Poly), m_polyBuffer.data()); // upload all the data to GPU in one go m_vbo.BufferSubData(MAX_ROM_POLYS*sizeof(Poly), m_polyBufferRam.size()*sizeof(Poly), m_polyBufferRam.data()); // upload all the dynamic data to GPU in one go
if (m_polyBufferRom.size()) {
// sync rom memory with vbo
int romBytes = (int)m_polyBufferRom.size() * sizeof(Poly);
int vboBytes = m_vbo.GetSize();
int size = romBytes - vboBytes;
if (size) {
//check we haven't blown up the memory buffers
//we will lose rom models for 1 frame is this happens, not the end of the world, as probably won't ever happen anyway
if (m_polyBufferRom.size() >= MAX_ROM_POLYS) {
m_polyBufferRom.clear();
m_vbo.Reset();
}
else {
m_vbo.AppendData(size, &m_polyBufferRom[vboBytes / sizeof(Poly)]);
}
}
}
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY);
@ -179,7 +204,7 @@ void CNew3D::RenderFrame(void)
} }
m_r3dShader.SetShader(false); // unbind shader m_r3dShader.SetShader(false); // unbind shader
m_vboDynamic.Bind(false); m_vbo.Bind(false);
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_VERTEX_ARRAY);
@ -232,7 +257,8 @@ const UINT32* CNew3D::TranslateModelAddress(UINT32 modelAddr)
bool CNew3D::DrawModel(UINT32 modelAddr) bool CNew3D::DrawModel(UINT32 modelAddr)
{ {
const UINT32 *modelAddress; const UINT32* modelAddress;
bool cached = false;
Model* m; Model* m;
modelAddress = TranslateModelAddress(modelAddr); modelAddress = TranslateModelAddress(modelAddr);
@ -240,13 +266,28 @@ bool CNew3D::DrawModel(UINT32 modelAddr)
// create a new model to push onto the vector // create a new model to push onto the vector
m_nodes.back().models.emplace_back(Model()); m_nodes.back().models.emplace_back(Model());
// get the pointer to the last element in the array // get the last model in the array
m = &m_nodes.back().models.back(); m = &m_nodes.back().models.back();
// copy lutidx - wtf is this if (IsVROMModel(modelAddr) && !IsDynamicModel((UINT32*)modelAddress)) {
m->lutIdx = modelAddr & 0xFFFFFF;
// copy model matrix // try to find meshes in the rom cache
if (m_romMap.count(modelAddr)) {
m->meshes = m_romMap[modelAddr];
cached = true;
}
else {
m->meshes = std::make_shared<std::vector<Mesh>>();
m->dynamic = false;
m_romMap[modelAddr] = m->meshes; // store meshes in our rom map here
}
}
else {
m->meshes = std::make_shared<std::vector<Mesh>>();
}
// copy current model matrix
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
m->modelMat[i] = m_modelMat.currentMatrix[i]; m->modelMat[i] = m_modelMat.currentMatrix[i];
} }
@ -254,7 +295,13 @@ bool CNew3D::DrawModel(UINT32 modelAddr)
//calculate determinant //calculate determinant
m->determinant = Determinant3x3(m_modelMat); m->determinant = Determinant3x3(m_modelMat);
CacheModel(m, modelAddress); // update texture offsets
m->textureOffsetX = m_nodeAttribs.currentTexOffsetX;
m->textureOffsetY = m_nodeAttribs.currentTexOffsetY;
if (!cached) {
CacheModel(m, modelAddress);
}
return true; return true;
} }
@ -763,7 +810,6 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data)
UINT64 lastHash = -1; UINT64 lastHash = -1;
SortingMesh* currentMesh = nullptr; SortingMesh* currentMesh = nullptr;
std::shared_ptr<Texture> tex;
std::map<UINT64, SortingMesh> sMap; std::map<UINT64, SortingMesh> sMap;
if (data == NULL) if (data == NULL)
@ -800,7 +846,7 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data)
data += 7; // data will now point to first vertex data += 7; // data will now point to first vertex
// create a hash value based on poly attributes -todo add more attributes // create a hash value based on poly attributes -todo add more attributes
auto hash = ph.Hash(m_nodeAttribs.currentTexOffsetX, m_nodeAttribs.currentTexOffsetY); auto hash = ph.Hash();
if (hash != lastHash && validPoly) { if (hash != lastHash && validPoly) {
@ -815,18 +861,12 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data)
//copy attributes //copy attributes
currentMesh->doubleSided = ph.DoubleSided(); currentMesh->doubleSided = ph.DoubleSided();
currentMesh->mirrorU = ph.TexUMirror();
currentMesh->mirrorV = ph.TexVMirror();
currentMesh->textured = ph.TexEnabled(); currentMesh->textured = ph.TexEnabled();
currentMesh->alphaTest = ph.AlphaTest(); currentMesh->alphaTest = ph.AlphaTest();
currentMesh->textureAlpha = ph.TextureAlpha(); currentMesh->textureAlpha = ph.TextureAlpha();
currentMesh->polyAlpha = ph.PolyAlpha(); currentMesh->polyAlpha = ph.PolyAlpha();
currentMesh->lighting = ph.LightEnabled(); currentMesh->lighting = ph.LightEnabled();
if (ph.header[6] & 0x10000) {
currentMesh->testBit = true;
}
if (!ph.Luminous()) { if (!ph.Luminous()) {
currentMesh->fogIntensity = 1.0f; currentMesh->fogIntensity = 1.0f;
} }
@ -835,18 +875,17 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data)
} }
if (ph.TexEnabled()) { if (ph.TexEnabled()) {
currentMesh->texture = m_texSheet.BindTexture(m_textureRAM, ph.TexFormat(), ph.TexUMirror(), ph.TexVMirror(), ph.X(m_nodeAttribs.currentTexOffsetX), ph.Y(m_nodeAttribs.currentTexOffsetY), ph.TexWidth(), ph.TexHeight()); currentMesh->format = ph.TexFormat();
currentMesh->x = ph.X();
currentMesh->y = ph.Y();
currentMesh->width = ph.TexWidth();
currentMesh->height = ph.TexHeight();
currentMesh->mirrorU = ph.TexUMirror();
currentMesh->mirrorV = ph.TexVMirror();
} }
} }
currentMesh = &sMap[hash]; currentMesh = &sMap[hash];
if (ph.TexEnabled()) {
tex = currentMesh->texture;
}
else {
tex = nullptr;
}
} }
if (validPoly) { if (validPoly) {
@ -917,8 +956,8 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data)
float texU, texV = 0; float texU, texV = 0;
// tex coords // tex coords
if (tex) { if (validPoly && currentMesh->textured) {
tex->GetCoordinates((UINT16)(it >> 16), (UINT16)(it & 0xFFFF), uvScale, texU, texV); Texture::GetCoordinates(currentMesh->width, currentMesh->height, (UINT16)(it >> 16), (UINT16)(it & 0xFFFF), uvScale, texU, texV);
} }
p.v[j].texcoords[0] = texU; p.v[j].texcoords[0] = texU;
@ -942,21 +981,31 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data)
//sorted the data, now copy to main data structures //sorted the data, now copy to main data structures
// we know how many meshes we have so reserve appropriate space // we know how many meshes we have so reserve appropriate space
m->meshes.reserve(sMap.size()); m->meshes->reserve(sMap.size());
for (auto& it : sMap) { for (auto& it : sMap) {
// calculate VBO values for current mesh if (m->dynamic) {
it.second.vboOffset = m_polyBuffer.size();
it.second.triangleCount = it.second.polys.size();
//it.second.clockWise = cw;
// copy poly data to main buffer // calculate VBO values for current mesh
m_polyBuffer.insert(m_polyBuffer.end(), it.second.polys.begin(), it.second.polys.end()); it.second.vboOffset = m_polyBufferRam.size() + MAX_ROM_POLYS;
it.second.triangleCount = it.second.polys.size();
// copy poly data to main buffer
m_polyBufferRam.insert(m_polyBufferRam.end(), it.second.polys.begin(), it.second.polys.end());
}
else {
// calculate VBO values for current mesh
it.second.vboOffset = m_polyBufferRom.size();
it.second.triangleCount = it.second.polys.size();
// copy poly data to main buffer
m_polyBufferRom.insert(m_polyBufferRom.end(), it.second.polys.begin(), it.second.polys.end());
}
//copy the temp mesh into the model structure //copy the temp mesh into the model structure
//this will lose the associated vertex data, which is now copied to the main buffer anyway //this will lose the associated vertex data, which is now copied to the main buffer anyway
m->meshes.push_back(it.second); m->meshes->push_back(it.second);
} }
} }
@ -998,4 +1047,9 @@ bool CNew3D::IsDynamicModel(UINT32 *data)
return false; return false;
} }
bool CNew3D::IsVROMModel(UINT32 modelAddr)
{
return modelAddr >= 0x100000;
}
} // New3D } // New3D

View file

@ -172,6 +172,8 @@ private:
void RenderScene(int priority, bool alpha); void RenderScene(int priority, bool alpha);
float Determinant3x3(const float m[16]); float Determinant3x3(const float m[16]);
bool IsDynamicModel(UINT32 *data); // check if the model has a colour palette bool IsDynamicModel(UINT32 *data); // check if the model has a colour palette
bool IsVROMModel(UINT32 modelAddr);
/* /*
* Data * Data
@ -199,14 +201,15 @@ private:
TextureSheet m_texSheet; TextureSheet m_texSheet;
NodeAttributes m_nodeAttribs; NodeAttributes m_nodeAttribs;
Mat4 m_modelMat; // current modelview matrix Mat4 m_modelMat; // current modelview matrix
int m_listDepth; int m_listDepth;
std::vector<Node> m_nodes; // build the scene std::vector<Node> m_nodes; // this represents the entire render frame
std::vector<Poly> m_polyBuffer; // we actually hold the vertex data here, one buffer to send to opengl, instead of 2000+ small ones. std::vector<Poly> m_polyBufferRam; // dynamic polys
std::vector<Poly> m_polyBufferRom; // rom polys
std::unordered_map<UINT32, std::shared_ptr<std::vector<Mesh>>> m_romMap; // a hash table for all the ROM models. The meshes don't have model matrices or tex offsets yet
VBO m_vboDynamic; // dynamic data from poly ram, rom polys can go in a different buffer VBO m_vbo; // large VBO to hold our poly data, start of VBO is ROM data, ram polys follow
VBO m_vboStatic; // for ROM models
R3DShader m_r3dShader; R3DShader m_r3dShader;
int m_currentVPPriority; int m_currentVPPriority;

View file

@ -190,18 +190,18 @@ int PolyHeader::Page()
// header 5 // header 5
// //
int PolyHeader::X(int textureXOffset) int PolyHeader::X()
{ {
//==== //====
int x; int x;
//==== //====
x = (32 * (((header[4] & 0x1F) << 1) | ((header[5] >> 7) & 1))) + textureXOffset; x = (32 * (((header[4] & 0x1F) << 1) | ((header[5] >> 7) & 1)));
x &= 2047; x &= 2047;
return x; return x;
} }
int PolyHeader::Y(int textureYOffset) int PolyHeader::Y()
{ {
//======= //=======
int y; int y;
@ -215,7 +215,7 @@ int PolyHeader::Y(int textureYOffset)
page = 0; page = 0;
} }
y = (32 * (header[5] & 0x1F) + page) + textureYOffset; // if we hit 2nd page add 1024 to y coordinate y = (32 * (header[5] & 0x1F) + page); // if we hit 2nd page add 1024 to y coordinate
y &= 2047; y &= 2047;
return y; return y;
@ -289,15 +289,15 @@ float PolyHeader::LightModifier()
// misc // misc
// //
UINT64 PolyHeader::Hash(int textureXOffset, int textureYOffset) UINT64 PolyHeader::Hash()
{ {
UINT64 hash = 0; UINT64 hash = 0;
hash |= (header[2] & 3); // bits 0-1 uv mirror bits hash |= (header[2] & 3); // bits 0-1 uv mirror bits
hash |= (UINT64)((header[3] >> 0) & 7) << 2; // bits 2-4 tex height hash |= (UINT64)((header[3] >> 0) & 7) << 2; // bits 2-4 tex height
hash |= (UINT64)((header[3] >> 3) & 7) << 5; // bits 5-7 tex width hash |= (UINT64)((header[3] >> 3) & 7) << 5; // bits 5-7 tex width
hash |= (UINT64)X(textureXOffset) << 8; // bits 8-17 x offset hash |= (UINT64)X() << 8; // bits 8-17 x offset
hash |= (UINT64)Y(textureYOffset) << 18; // bits 18-27 y offset hash |= (UINT64)Y() << 18; // bits 18-27 y offset
hash |= (UINT64)TexFormat() << 28; // bits 28-30 tex format hash |= (UINT64)TexFormat() << 28; // bits 28-30 tex format
hash |= (UINT64)TexEnabled() << 31; // bits 31 textures enabled hash |= (UINT64)TexEnabled() << 31; // bits 31 textures enabled
hash |= (UINT64)LightEnabled() << 32; // bits 32 light enabled hash |= (UINT64)LightEnabled() << 32; // bits 32 light enabled

View file

@ -108,8 +108,8 @@ public:
int Page(); int Page();
// header 5 // header 5
int X(int textureXOffset); int X();
int Y(int textureYOffset); int Y();
//header 6 //header 6
int TexFormat(); int TexFormat();
@ -126,7 +126,7 @@ public:
float LightModifier(); float LightModifier();
// misc // misc
UINT64 Hash(int textureXOffset, int textureYOffset); // make a unique hash for sorting by state UINT64 Hash(); // make a unique hash for sorting by state
//============= //=============

View file

@ -42,8 +42,14 @@ void Texture::BindTexture()
void Texture::GetCoordinates(UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut) void Texture::GetCoordinates(UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut)
{ {
uOut = ((uIn*uvScale)+0.0f) / m_width; uOut = (uIn*uvScale) / m_width;
vOut = ((vIn*uvScale)+0.0f) / m_height; vOut = (vIn*uvScale) / m_height;
}
void Texture::GetCoordinates(int width, int height, UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut)
{
uOut = (uIn*uvScale) / width;
vOut = (vIn*uvScale) / height;
} }
void Texture::SetWrapMode(bool mirrorU, bool mirrorV) void Texture::SetWrapMode(bool mirrorU, bool mirrorV)

View file

@ -23,6 +23,8 @@ public:
void GetDetails (int& x, int&y, int& width, int& height, int& format); void GetDetails (int& x, int&y, int& width, int& height, int& format);
void SetWrapMode (bool mirrorU, bool mirrorV); void SetWrapMode (bool mirrorU, bool mirrorV);
static void GetCoordinates(int width, int height, UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut);
private: private:
void Reset(); void Reset();

View file

@ -30,6 +30,10 @@ void VBO::BufferSubData(GLintptr offset, GLsizeiptr size, const GLvoid* data)
bool VBO::AppendData(GLsizeiptr size, const GLvoid* data) bool VBO::AppendData(GLsizeiptr size, const GLvoid* data)
{ {
if (size == 0 || !data) {
return true; // nothing to do
}
if (m_size + size >= m_capacity) { if (m_size + size >= m_capacity) {
return false; return false;
} }