Polygon header bit analysis code (need to #define DEBUG and use -gfx-state option)

This commit is contained in:
Bart Trzynadlowski 2016-04-27 04:09:50 +00:00
parent 04fba1466d
commit c0ec610e99
4 changed files with 226 additions and 34 deletions

View file

@ -162,6 +162,11 @@
#include <cmath>
#include <cstdint>
#ifdef DEBUG
extern bool g_forceFlushModels;
#endif
namespace Legacy3D {
// Microsoft doesn't provide isnan() and isinf()
@ -989,7 +994,10 @@ void CLegacy3D::RenderFrame(void)
if (fogIntensityLoc != -1) glEnableVertexAttribArray(fogIntensityLoc);
// Draw
//ClearModelCache(&VROMCache); // only enable this for debugging
#ifdef DEBUG
if (g_forceFlushModels)
ClearModelCache(&VROMCache);
#endif
ClearModelCache(&PolyCache);
for (int pri = 0; pri <= 3; pri++)
{

View file

@ -83,6 +83,12 @@
#include <cstring>
#include "Supermodel.h"
#ifdef DEBUG
extern int g_testPolyHeaderIdx;
extern uint32_t g_testPolyHeaderMask;
#endif
namespace Legacy3D {
/******************************************************************************
@ -545,32 +551,6 @@ void CLegacy3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P,
if (!(P->header[0]&0x80) || (shininess == 0)) // bit 0x80 seems to enable specular lighting
shininess = -1; // disable
#if 0
if (texFormat==5)//texFormat==6||texFormat==2)
{
//printf("%03X\n", P->header[4]>>8);
//texEnable=0;
g=b=1.0;
r=1.0f;
}
#endif
#if 0
int testWord = 0;
int testBit = 7;
//if ((P->header[testWord]&(1<<testBit)))
if (((P->header[0]>>24) & 0x3) != 0)
//if (!((P->header[0]>>26) & 0x3F) && (P->header[0]&0x80))
{
texEnable = 0;
r=b=0;
g=1.0f;
g = ((P->header[0]>>26)&0x3F) * (1.0f/64.0f);
//if (!lightEnable)
// b=1.0f;
lightEnable=0;
}
#endif
// Determine whether polygon is translucent
GLfloat translucence = (GLfloat) ((P->header[6]>>18)&0x1F) * (1.0f/31.0f);
if ((P->header[6]&0x00800000)) // if set, polygon is opaque
@ -595,6 +575,24 @@ void CLegacy3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P,
((texFormat==3) && (P->header[6]&4))) // A4L4 interleaved
contourProcessing = 1.0f;
#ifdef DEBUG
if (g_testPolyHeaderIdx >= 0)
{
if ((P->header[g_testPolyHeaderIdx] & g_testPolyHeaderMask))
{
r = 0.;
g = 1.;
b = 0.;
lightEnable = 0;
texEnable = 0;
contourProcessing = 0.;
fogIntensity = 0.;
translucence = 1.;
shininess = -1;
}
}
#endif
// Store to local vertex buffer
size_t s = P->state;
size_t baseIdx = Cache->curVertIdx[s]*VBO_VERTEX_SIZE;

View file

@ -43,6 +43,8 @@
#include <cstdio>
#include <cstring>
#include <cstdarg>
#include <memory>
#include <vector>
#include "Pkgs/glew.h"
#ifdef SUPERMODEL_OSX
#include <SDL/SDL.h>
@ -51,16 +53,13 @@
#endif
#include "Supermodel.h"
#include "Util/Format.h"
#include "SDLInputSystem.h"
#ifdef SUPERMODEL_WIN32
#include "DirectInputSystem.h"
#include "WinOutputs.h"
#endif
#ifdef DEBUG
#include "Model3/Model3GraphicsState.h"
#endif
/******************************************************************************
Error and Debug Logging
@ -414,6 +413,88 @@ static void DumpPPCRegisters(IBus *bus)
#endif
/******************************************************************************
Render State Analysis
******************************************************************************/
#ifdef DEBUG
#include "Model3/Model3GraphicsState.h"
#include "Util/BMPFile.h"
#include "OSD/SDL/PolyAnalysis.h"
static void SaveFrameBuffer(const std::string &file)
{
std::shared_ptr<uint8_t> pixels(new uint8_t[totalXRes*totalYRes*4], std::default_delete<uint8_t[]>());
glReadPixels(0, 0, totalXRes, totalYRes, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get());
Util::WriteRGBA8SurfaceToBMP(file, pixels.get(), totalXRes, totalYRes, true);
}
bool g_forceFlushModels = false;
int g_testPolyHeaderIdx = -1;
uint32_t g_testPolyHeaderMask = 0;
static std::string s_gfxStatePath;
static std::string GetFileBaseName(const std::string &file)
{
std::string base = file;
size_t pos = file.find_last_of('/');
if (pos != std::string::npos)
base = file.substr(pos + 1);
pos = file.find_last_of('\\');
if (pos != std::string::npos)
base = file.substr(pos + 1);
return base;
}
static void TestPolygonHeaderBits(IEmulator *Emu)
{
const static std::vector<uint32_t> unknownPolyBits
{
0x000003b0, // not sure about specular
0x000000a9,
0x000000fc,
0x000000c0,
0x000000a0,
0xffffff60,
0xff8200ff // not sure about contour and luminous
};
// Render separate image for each unknown bit
g_forceFlushModels = true;
for (int idx = 0; idx < 7; idx++)
{
for (int bit = 0; bit < 32; bit++)
{
uint32_t mask = 1 << bit;
g_testPolyHeaderIdx = idx;
g_testPolyHeaderMask = mask;
if ((unknownPolyBits[idx] & mask))
{
Emu->RenderFrame();
std::string file = Util::Format() << "Analysis/" << GetFileBaseName(s_gfxStatePath) << "." << idx << "_" << Util::Hex(mask) << ".bmp";
SaveFrameBuffer(file);
}
}
}
// Generate the HTML GUI
std::string file = Util::Format() << "Analysis/_" << GetFileBaseName(s_gfxStatePath) << ".html";
std::ofstream fs(file);
if (!fs.good())
ErrorLog("Unable to open '%s' for writing.", file.c_str());
else
{
std::string contents = s_polyAnalysisHTMLPrologue;
contents += " var g_file_base_name = '" + GetFileBaseName(s_gfxStatePath) + "';\n";
contents += " var g_unknown_bits = [" + std::string(Util::Format(",").Join(unknownPolyBits)) + "];\n";
contents += s_polyAnalysisHTMLEpilogue;
fs << contents;
printf("Produced %s\n", file.c_str());
}
}
#endif
/******************************************************************************
Configuration
@ -951,6 +1032,13 @@ int Supermodel(const char *zipFile, IEmulator *Model3, CInputs *Inputs, COutputs
quit = false;
paused = false;
dumpTimings = false;
#ifdef DEBUG
if (dynamic_cast<CModel3GraphicsState *>(Model3))
{
TestPolygonHeaderBits(Model3);
quit = true;
}
#endif
while (!quit)
{
// Render if paused, otherwise run a frame
@ -1491,7 +1579,6 @@ int main(int argc, char **argv)
bool cmdPrintInputs = false;
bool cmdConfigInputs = false;
bool cmdDis = false;
std::string gfxStatePath;
CINIFile CmdLine; // not associated with any files, only holds command line options
CmdLine.SetDefaultSectionName("Global"); // command line settings are global-level
for (int i = 1; i < argc; i++)
@ -1744,7 +1831,7 @@ int main(int argc, char **argv)
else if (argv[i][10] == '\0')
ErrorLog("'-gfx-state' requires a file path.");
else
gfxStatePath.assign(&argv[i][11]);
s_gfxStatePath.assign(&argv[i][11]);
}
#endif
else if (argv[i][0] == '-')
@ -1767,7 +1854,7 @@ int main(int argc, char **argv)
// Create Model 3 emulator
#ifdef DEBUG
IEmulator *Model3 = gfxStatePath.empty() ? static_cast<IEmulator *>(new CModel3()) : static_cast<IEmulator *>(new CModel3GraphicsState(gfxStatePath));
IEmulator *Model3 = s_gfxStatePath.empty() ? static_cast<IEmulator *>(new CModel3()) : static_cast<IEmulator *>(new CModel3GraphicsState(s_gfxStatePath));
#else
IEmulator *Model3 = new CModel3();
#endif

View file

@ -0,0 +1,99 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011-2016 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/>.
**/
/*
* PolyAnalysis.h
*
* HTML GUI used to analyze polygon header bits.
*/
#ifndef INCLUDED_POLYANALYSIS_H
#define INCLUDED_POLYANALYSIS_H
static const char s_polyAnalysisHTMLPrologue[] =
{
"<html>\n"
"<head>\n"
" <style>\n"
" table { table-layout: fixed; }\n"
" td { border: 1px solid black; width: 1em; }\n"
" td.nonselectable { background-color: gray; }\n"
" td.selectable { background-color: #5555ee; }\n"
" td.selected { background-color: #22ff22; }\n"
" </style>\n"
" <script src='https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js'></script>\n"
"</head>\n"
"<body>\n"
" <div id='Header'></div>\n"
" <div><img id='Image' /></div>\n"
" <script>\n"
// Insert g_file_base_name and g_unknown bits here, then append epilogue
};
static const char s_polyAnalysisHTMLEpilogue[] =
{
" function hex32(val)\n"
" {\n"
" return '0x' + ('0000000'+Number(val).toString(16)).slice(-8);\n"
" }\n"
" function getOnClickHandler(table, td, word, mask)\n"
" {\n"
" return function()\n"
" {\n"
" var file = g_file_base_name + '.' + word + '_' + hex32(mask) + '.bmp';\n"
" table.find('td').removeClass('selected'); // remove from all cells\n"
" td.addClass('selected');\n"
" $('#Image').empty();\n"
" $('#Image').attr('src', file);\n"
" };\n"
" }\n"
" $(document).ready(function()\n"
" {\n"
" // Construct table\n"
" var table = $('<table></table>');\n"
" var bits_tr = $('<tr></tr>'); // first row will be bit legend\n"
" bits_tr.append($('<td></td>')); // column for word numbers\n"
" for (var bit = 31; bit >= 0; bit--)\n"
" bits_tr.append($('<td>'+bit+'</td>'));\n"
" table.append(bits_tr);\n"
" for (var word = 0; word < 7; word++)\n"
" {\n"
" var tr = $('<tr></tr>');\n"
" tr.append($('<td>'+word+'</td>'));\n"
" for (var bit = 31; bit >= 0; bit--)\n"
" {\n"
" var mask = 1 << bit;\n"
" var td = $('<td></td>');\n"
" if (g_unknown_bits[word] & mask)\n"
" td.addClass('selectable').click(getOnClickHandler(table, td, word, mask));\n"
" else"
" td.addClass('nonselectable');\n"
" tr.append(td);\n"
" }\n"
" table.append(tr);\n"
" }\n"
" $('#Header').append(table);\n"
" });\n"
" </script>\n"
"</body>\n"
};
#endif // INCLUDED_POLYANALYSIS_H