feat(3d): gl1 shim M0-M2 — FFP-on-WebGL2 substrate + lighting; 12 scenarios truly green
wasm/gl1: the GL1.x fixed-function emulation layer replacing the gl_ffp_stub.c no-ops in the 3d-regression harness link. This batch: - symbol split: 52 FFP-only entry points implemented; 10 Emscripten-owned names intercepted via wasm-ld --wrap (sources.txt/wrapped_symbols.txt are the shared manifests for both link sites; production hookup lands in M7) - matrix stacks (MODELVIEW/PROJECTION, glGetFloatv readback), immediate mode with all 8 GL1 primitive conversions, GL1-default state mirror - full GL 1.5 Gouraud lighting uber-shader (eye-space light capture at glLightfv time, color-material, two-side, COMBINE evaluator + alpha test wired but inert until M3/M5) - draw routing: FFP traffic identified by GL_VERTEX_ARRAY client state; blit/2D-GAL draws pass through untouched - display lists: correct glGenLists/glIsList existing-empty semantics; recorder itself is M3 (recorded commands drop with a one-time warning) Parity: 20/47 under the 0.02 floor, of which 12 verified genuinely rendering (bg-gradient x2, bounding-box, half-open-cylinder, segment x2, material x3, light x3 — eyeballed against baselines); the other 8 are small-geometry scenarios whose missing GLU/display-list content sits under the floor (become real in M3/M4). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
19e850d098
commit
071eef5454
14 changed files with 2496 additions and 6 deletions
267
wasm/gl1/include/gl1_shim.h
Normal file
267
wasm/gl1/include/gl1_shim.h
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* gl1_shim — GL 1.x fixed-function pipeline emulated on WebGL2.
|
||||
*
|
||||
* Internal header shared by the wasm/gl1/src modules. The public surface
|
||||
* is the set of C entry points in gl1_entry_ffp.cpp (FFP-only names absent from
|
||||
* WebGL2, previously no-op'd by wasm/stubs/gl_ffp_stub.c) plus the __wrap_*
|
||||
* interceptors in gl1_entry_wrapped.cpp (Emscripten-owned names the emulator
|
||||
* must observe; see wrapped_symbols.txt and the -Wl,--wrap flags both link
|
||||
* sites derive from it).
|
||||
*
|
||||
* Everything here is main-thread-only (both consumers render on the main
|
||||
* browser thread) — no TLS, no atomics, no exceptions.
|
||||
*/
|
||||
|
||||
#ifndef GL1_SHIM_H
|
||||
#define GL1_SHIM_H
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h> // wasm/stubs/GL/glu.h via -I wasm/stubs
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
// One-time diagnostics: the suite requires a clean console, but unsupported
|
||||
// paths must not fail silently. Each call site warns exactly once.
|
||||
#define GL1_WARN_ONCE( fmt, ... ) \
|
||||
do \
|
||||
{ \
|
||||
static bool _gl1_warned = false; \
|
||||
if( !_gl1_warned ) \
|
||||
{ \
|
||||
_gl1_warned = true; \
|
||||
std::fprintf( stderr, "[gl1] " fmt "\n", ##__VA_ARGS__ ); \
|
||||
} \
|
||||
} while( 0 )
|
||||
|
||||
// The __real_* counterparts of every wrapped symbol (resolved by wasm-ld back
|
||||
// to the Emscripten WebGL JS-library import). Shim-internal code that needs
|
||||
// the true WebGL behavior calls these directly — never the public names.
|
||||
extern "C"
|
||||
{
|
||||
void __real_glEnable( GLenum cap );
|
||||
void __real_glDisable( GLenum cap );
|
||||
GLboolean __real_glIsEnabled( GLenum cap );
|
||||
void __real_glDrawArrays( GLenum mode, GLint first, GLsizei count );
|
||||
void __real_glDrawElements( GLenum mode, GLsizei count, GLenum type, const GLvoid* indices );
|
||||
void __real_glGetFloatv( GLenum pname, GLfloat* params );
|
||||
void __real_glBindTexture( GLenum target, GLuint texture );
|
||||
void __real_glBlendFunc( GLenum sfactor, GLenum dfactor );
|
||||
void __real_glLineWidth( GLfloat width );
|
||||
void __real_glHint( GLenum target, GLenum mode );
|
||||
}
|
||||
|
||||
namespace gl1
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// State mirror — the GL 1.x state the emulator owns. WebGL-native state
|
||||
// (blend, depth, stencil, cull, viewport, textures, buffers...) is NOT
|
||||
// mirrored; those calls pass through untouched.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct Light
|
||||
{
|
||||
// GL 1.5 defaults: LIGHT0 gets white diffuse/specular, others black
|
||||
// (applied in State::State).
|
||||
glm::vec4 ambient{ 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
glm::vec4 diffuse{ 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
glm::vec4 specular{ 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
// GL_POSITION is transformed by the modelview CURRENT AT THE glLightfv CALL
|
||||
// and stored in eye space — this is what eye-anchors KiCad's directional
|
||||
// lights (init_lights() runs under an identity modelview).
|
||||
glm::vec4 posEye{ 0.0f, 0.0f, 1.0f, 0.0f };
|
||||
};
|
||||
|
||||
struct Material
|
||||
{
|
||||
glm::vec4 ambient{ 0.2f, 0.2f, 0.2f, 1.0f };
|
||||
glm::vec4 diffuse{ 0.8f, 0.8f, 0.8f, 1.0f };
|
||||
glm::vec4 specular{ 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
glm::vec4 emission{ 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
float shininess = 0.0f;
|
||||
};
|
||||
|
||||
struct ClientArray
|
||||
{
|
||||
bool enabled = false;
|
||||
GLint size = 4;
|
||||
GLenum type = GL_FLOAT;
|
||||
GLsizei stride = 0;
|
||||
const GLvoid* pointer = nullptr;
|
||||
// GL1 semantics: gl*Pointer captures the GL_ARRAY_BUFFER binding current at
|
||||
// the call; 0 = client memory, nonzero = offset into that VBO.
|
||||
GLuint boundBuffer = 0;
|
||||
};
|
||||
|
||||
// Interleaved immediate-mode vertex (glBegin/glEnd stream and display-list
|
||||
// geometry bake share this layout). Matches the attribute setup in gl1_draw.
|
||||
struct ImmVertex
|
||||
{
|
||||
float px, py, pz;
|
||||
float nx, ny, nz;
|
||||
float r, g, b, a;
|
||||
float u, v;
|
||||
};
|
||||
|
||||
static_assert( sizeof( ImmVertex ) == 12 * sizeof( float ), "ImmVertex must stay tightly packed" );
|
||||
|
||||
enum ClientArrayIndex
|
||||
{
|
||||
CA_VERTEX = 0,
|
||||
CA_NORMAL,
|
||||
CA_COLOR,
|
||||
CA_TEXCOORD,
|
||||
CA_COUNT
|
||||
};
|
||||
|
||||
struct State
|
||||
{
|
||||
// --- matrix stacks ---
|
||||
GLenum matrixMode = GL_MODELVIEW;
|
||||
std::vector<glm::mat4> mv; // modelview stack, top = back()
|
||||
std::vector<glm::mat4> proj; // projection stack, top = back()
|
||||
|
||||
// --- immediate-mode current attributes (persist across Begin/End) ---
|
||||
glm::vec4 currentColor{ 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
glm::vec3 currentNormal{ 0.0f, 0.0f, 1.0f };
|
||||
|
||||
// --- FFP capabilities (tracked; never forwarded to WebGL) ---
|
||||
bool lighting = false;
|
||||
bool lightEnabled[8] = {};
|
||||
bool colorMaterial = false;
|
||||
bool texture2D = false;
|
||||
bool normalizeNormals = false;
|
||||
bool alphaTest = false;
|
||||
// Tracked only so glIsEnabled stays consistent; no rendering effect here
|
||||
// (forwarding them would raise INVALID_ENUM in WebGL2).
|
||||
bool lineSmooth = false;
|
||||
bool pointSmooth = false;
|
||||
bool multisample = false;
|
||||
|
||||
// --- lighting rig / materials ---
|
||||
Light lights[8];
|
||||
glm::vec4 lightModelAmbient{ 0.2f, 0.2f, 0.2f, 1.0f };
|
||||
bool twoSide = false;
|
||||
Material material; // GL_FRONT_AND_BACK only (asserted at the entry point)
|
||||
GLenum colorMaterialMode = GL_AMBIENT_AND_DIFFUSE;
|
||||
|
||||
// --- texture environment, unit 0 (GL 1.5 initial values) ---
|
||||
GLenum texEnvMode = GL_MODULATE;
|
||||
glm::vec4 texEnvColor{ 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
GLenum combineRGB = GL_MODULATE;
|
||||
GLenum combineAlpha = GL_MODULATE;
|
||||
GLenum srcRGB[3] = { GL_TEXTURE, GL_PREVIOUS, GL_CONSTANT };
|
||||
GLenum operandRGB[3] = { GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_ALPHA };
|
||||
GLenum srcAlpha[3] = { GL_TEXTURE, GL_PREVIOUS, GL_CONSTANT };
|
||||
GLenum operandAlpha[3] = { GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA };
|
||||
|
||||
// --- alpha test ---
|
||||
GLenum alphaFunc = GL_ALWAYS;
|
||||
float alphaRef = 0.0f;
|
||||
|
||||
// --- misc ---
|
||||
GLenum shadeModel = GL_SMOOTH;
|
||||
float pointSize = 1.0f;
|
||||
float lineWidth = 1.0f;
|
||||
GLuint boundTexture2D = 0; // mirror of the unit-0 GL_TEXTURE_2D binding
|
||||
|
||||
// --- client arrays ---
|
||||
ClientArray clientArrays[CA_COUNT];
|
||||
|
||||
// Nonzero while a non-shim GLSL program is bound (raytracer blit, 2D GAL):
|
||||
// routed draws pass through untouched.
|
||||
GLuint externalProgram = 0;
|
||||
|
||||
// --- dirty flags (uniform re-upload gates) ---
|
||||
bool matricesDirty = true;
|
||||
bool lightingDirty = true;
|
||||
bool texEnvDirty = true;
|
||||
bool miscDirty = true;
|
||||
|
||||
State()
|
||||
{
|
||||
mv.reserve( 64 );
|
||||
proj.reserve( 8 );
|
||||
mv.push_back( glm::mat4( 1.0f ) );
|
||||
proj.push_back( glm::mat4( 1.0f ) );
|
||||
|
||||
lights[0].diffuse = glm::vec4( 1.0f, 1.0f, 1.0f, 1.0f );
|
||||
lights[0].specular = glm::vec4( 1.0f, 1.0f, 1.0f, 1.0f );
|
||||
}
|
||||
|
||||
glm::mat4& mvTop() { return mv.back(); }
|
||||
glm::mat4& projTop() { return proj.back(); }
|
||||
glm::mat4& currentTop() { return matrixMode == GL_PROJECTION ? proj.back() : mv.back(); }
|
||||
std::vector<glm::mat4>& currentStack() { return matrixMode == GL_PROJECTION ? proj : mv; }
|
||||
};
|
||||
|
||||
State& S();
|
||||
|
||||
// Returns the tracked-flag slot for FFP-only glEnable/glDisable caps, or
|
||||
// nullptr for caps WebGL owns natively (forward those).
|
||||
bool* ffpCapSlot( GLenum cap );
|
||||
|
||||
// Marks the state blocks a cap flip invalidates.
|
||||
void onCapChanged( GLenum cap );
|
||||
|
||||
// --- matrix module (gl1_matrix.cpp) ---
|
||||
void matrixLoadIdentity();
|
||||
void matrixLoadf( const GLfloat* m );
|
||||
void matrixPush();
|
||||
void matrixPop();
|
||||
void matrixTranslate( float x, float y, float z );
|
||||
void matrixRotate( float angleDeg, float x, float y, float z );
|
||||
void matrixScale( float x, float y, float z );
|
||||
void matrixPerspective( double fovyDeg, double aspect, double zNear, double zFar );
|
||||
|
||||
// --- immediate-mode module (gl1_immediate.cpp) ---
|
||||
// Also the emission path for GLU quadrics and display-list replay, so all
|
||||
// geometry funnels through one primitive-conversion + draw pipeline.
|
||||
void immBegin( GLenum mode );
|
||||
void immVertex( float x, float y, float z );
|
||||
void immEnd();
|
||||
bool immActive();
|
||||
|
||||
// --- draw module (gl1_draw.cpp) ---
|
||||
// Uploads `count` interleaved ImmVertex records to the streaming VBO and draws
|
||||
// them with the FFP program. `mode` must already be a WebGL2-legal primitive.
|
||||
void drawImmVertices( GLenum mode, const ImmVertex* verts, GLsizei count );
|
||||
// Routed glDrawArrays/glDrawElements over FFP client-array state (M3/M5).
|
||||
void drawClientArrays( GLenum mode, GLint first, GLsizei count );
|
||||
void drawClientElements( GLenum mode, GLsizei count, GLenum type, const GLvoid* indices );
|
||||
|
||||
// --- shader module (gl1_shaders.cpp) ---
|
||||
// Binds the FFP program and re-uploads whatever state is dirty. Returns false
|
||||
// (once, with a console warning) if the program failed to build.
|
||||
bool programSync();
|
||||
GLuint programId();
|
||||
|
||||
// --- display-list module (gl1_dlist.cpp) ---
|
||||
bool dlistRecording();
|
||||
GLuint dlistGenLists( GLsizei range );
|
||||
GLboolean dlistIsList( GLuint list );
|
||||
void dlistNewList( GLuint list, GLenum mode );
|
||||
void dlistEndList();
|
||||
void dlistCallList( GLuint list );
|
||||
void dlistDeleteLists( GLuint list, GLsizei range );
|
||||
// Recording hooks (called from entry points / wrappers while recording).
|
||||
void dlistRecordEnable( GLenum cap, bool enable );
|
||||
void dlistRecordBindTexture( GLenum target, GLuint texture );
|
||||
void dlistRecordBlendFunc( GLenum sfactor, GLenum dfactor );
|
||||
void dlistRecordLineWidth( GLfloat width );
|
||||
void dlistRecordAlphaFunc( GLenum func, GLclampf ref );
|
||||
void dlistRecordNormal( float nx, float ny, float nz );
|
||||
void dlistRecordColor( float r, float g, float b, float a );
|
||||
void dlistRecordBegin( GLenum mode );
|
||||
void dlistRecordVertex( float x, float y, float z );
|
||||
void dlistRecordEnd();
|
||||
void dlistRecordDrawArrays( GLenum mode, GLint first, GLsizei count );
|
||||
|
||||
} // namespace gl1
|
||||
|
||||
#endif // GL1_SHIM_H
|
||||
Loading…
Reference in a new issue