feat(webgl): Replace legacy GL with modern OpenGL ES 3.0
Major refactoring to remove ALL legacy OpenGL immediate mode calls from WEBGL_GAL and replace them with WebGL 2.0/OpenGL ES 3.0 compatible code. Key changes: - Add MVP matrix uniform support (replaces glMatrixMode/glOrtho/glLoadMatrix) - Add matrix math helpers (computeOrthoMatrix, multiplyMatrix4x4, etc.) - Replace glBegin/glEnd/glVertex with vertex manager pattern - Rewrite DrawCursor to use vertex manager and DrawLine() calls - Rewrite DrawBitmap to use vertex manager with SHADER_FONT mode - Replace glEnableClientState with glVertexAttribPointer - Replace glDrawBuffer with glDrawBuffers (WebGL 2.0 API) - Add fullscreen_quad.cpp/h for compositor Present() - Update SHADER class with mat4 uniform support - Add MultiplyMatrix() to vertex_manager for Transform() - Remove all legacy GL stubs from wasm_stubs.cpp Build now completes with FULL_ES3 mode (no LEGACY_GL_EMULATION). Remaining issue: wxWidgets GL library calls glColor3f internally. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
6c2cf85177
commit
d2f1a496f5
27 changed files with 840 additions and 349 deletions
|
|
@ -11,61 +11,187 @@ import sys
|
|||
|
||||
import re
|
||||
|
||||
def convert_glsl120_to_es100(shader_source, is_fragment_shader):
|
||||
def convert_glsl120_to_es300(shader_source, is_fragment_shader):
|
||||
"""
|
||||
Convert GLSL 1.20 (OpenGL 2.1) shader to GLSL ES 1.00 (WebGL 1.0).
|
||||
Convert GLSL 1.20 (OpenGL 2.1) shader to GLSL ES 3.00 (WebGL 2.0).
|
||||
|
||||
GLSL ES 1.00 is similar to GLSL 1.20 but:
|
||||
- No #version directive needed (defaults to 100)
|
||||
- Needs precision qualifiers
|
||||
- attribute/varying are the correct keywords (not in/out)
|
||||
- gl_FragColor is correct (not custom output)
|
||||
- texture2D is correct (not texture)
|
||||
- STRICT: int * float not allowed - need explicit float literals
|
||||
|
||||
Main changes needed:
|
||||
- Remove #version 120 (ES 1.00 is default)
|
||||
Key changes:
|
||||
- #version 120 -> #version 300 es (must be ABSOLUTE first line!)
|
||||
- Move any comments before #version to after declarations
|
||||
- Add precision qualifiers
|
||||
- attribute -> in
|
||||
- varying -> out (vertex) / in (fragment)
|
||||
- gl_FragColor -> custom output variable (fragment)
|
||||
- texture2D -> texture
|
||||
- Fix int * float type issues (2 * x -> 2.0 * x)
|
||||
- Fix int / float type issues (x / 4 -> x / 4.0)
|
||||
|
||||
Legacy GL built-in conversions (for KiCad shaders):
|
||||
- gl_ModelViewProjectionMatrix -> uniform u_modelViewProjectionMatrix
|
||||
- gl_Vertex -> attribute a_vertex
|
||||
- gl_Color -> attribute a_color (vertex) / varying v_color (fragment)
|
||||
- gl_FrontColor -> varying v_color (vertex output)
|
||||
- gl_TexCoord[0] -> varying v_texCoord
|
||||
- ftransform() -> u_modelViewProjectionMatrix * a_vertex
|
||||
"""
|
||||
lines = shader_source.split('\n')
|
||||
result = []
|
||||
added_precision = False
|
||||
version_replaced = False
|
||||
|
||||
# Pattern to find integer literals multiplied by variables
|
||||
# Matches patterns like "2 * var" or "2* var" etc
|
||||
# Track what legacy built-ins are used so we can add declarations
|
||||
uses_mvp_matrix = 'gl_ModelViewProjectionMatrix' in shader_source or 'ftransform()' in shader_source
|
||||
uses_gl_vertex = 'gl_Vertex' in shader_source or 'ftransform()' in shader_source
|
||||
uses_gl_color = 'gl_Color' in shader_source
|
||||
uses_gl_front_color = 'gl_FrontColor' in shader_source
|
||||
uses_gl_texcoord = 'gl_TexCoord' in shader_source
|
||||
uses_gl_multitexcoord0 = 'gl_MultiTexCoord0' in shader_source
|
||||
|
||||
# GLSL ES 3.00 requires #version to be the ABSOLUTE first line
|
||||
# Collect any comments before #version to add after declarations
|
||||
pre_version_comments = []
|
||||
in_multiline_comment = False
|
||||
|
||||
# Patterns to fix type issues
|
||||
int_mult_pattern = re.compile(r'\b(\d+)\s*\*\s*([a-zA-Z_])')
|
||||
div_int_pattern = re.compile(r'([a-zA-Z_)\]]+)\s*/\s*(\d+)(?!\.)')
|
||||
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
|
||||
# Skip the #version directive - ES 1.00 doesn't use it
|
||||
# Before #version is found, collect comments and empty lines
|
||||
if not version_replaced:
|
||||
# Track multiline comment state
|
||||
if '/*' in stripped and '*/' not in stripped:
|
||||
in_multiline_comment = True
|
||||
pre_version_comments.append(line)
|
||||
continue
|
||||
elif in_multiline_comment:
|
||||
pre_version_comments.append(line)
|
||||
if '*/' in stripped:
|
||||
in_multiline_comment = False
|
||||
continue
|
||||
elif stripped.startswith('//') or stripped == '' or ('/*' in stripped and '*/' in stripped):
|
||||
pre_version_comments.append(line)
|
||||
continue
|
||||
|
||||
# Replace #version 120 with #version 300 es + precision + legacy built-in replacements
|
||||
if stripped.startswith('#version'):
|
||||
# Add precision qualifiers instead
|
||||
if not added_precision:
|
||||
result.append('// GLSL ES 1.00 (WebGL 1.0) - converted from GLSL 1.20')
|
||||
result.append('precision highp float;')
|
||||
result.append('precision highp int;')
|
||||
result.append('')
|
||||
added_precision = True
|
||||
result.append('#version 300 es')
|
||||
result.append('precision highp float;')
|
||||
result.append('precision highp int;')
|
||||
|
||||
if is_fragment_shader:
|
||||
result.append('out vec4 fragColor;')
|
||||
# Fragment shader receives varyings from vertex shader
|
||||
if uses_gl_color or uses_gl_front_color:
|
||||
result.append('in vec4 v_color;')
|
||||
if uses_gl_texcoord:
|
||||
result.append('in vec2 v_texCoord;')
|
||||
else:
|
||||
# Vertex shader - add uniforms and attributes for legacy built-ins
|
||||
if uses_mvp_matrix:
|
||||
result.append('uniform mat4 u_modelViewProjectionMatrix;')
|
||||
if uses_gl_vertex:
|
||||
result.append('in vec4 a_vertex;')
|
||||
if uses_gl_color:
|
||||
result.append('in vec4 a_color;')
|
||||
if uses_gl_multitexcoord0:
|
||||
result.append('in vec4 a_texCoord0;')
|
||||
# Vertex shader outputs varyings
|
||||
if uses_gl_front_color or uses_gl_color:
|
||||
result.append('out vec4 v_color;')
|
||||
if uses_gl_texcoord:
|
||||
result.append('out vec2 v_texCoord;')
|
||||
|
||||
# Add back any pre-version comments after the declarations
|
||||
if pre_version_comments:
|
||||
result.append('') # Blank line before comments
|
||||
result.extend(pre_version_comments)
|
||||
|
||||
version_replaced = True
|
||||
continue
|
||||
|
||||
# Convert attribute to in (vertex shaders only)
|
||||
if not is_fragment_shader and stripped.startswith('attribute '):
|
||||
line = line.replace('attribute ', 'in ', 1)
|
||||
|
||||
# Convert varying to out (vertex) or in (fragment)
|
||||
if stripped.startswith('varying '):
|
||||
if is_fragment_shader:
|
||||
line = line.replace('varying ', 'in ', 1)
|
||||
else:
|
||||
line = line.replace('varying ', 'out ', 1)
|
||||
|
||||
# Convert gl_FragColor to fragColor (fragment shaders)
|
||||
if is_fragment_shader and 'gl_FragColor' in line:
|
||||
line = line.replace('gl_FragColor', 'fragColor')
|
||||
|
||||
# Convert texture2D to texture
|
||||
if 'texture2D' in line:
|
||||
line = line.replace('texture2D', 'texture')
|
||||
|
||||
# Convert legacy GL built-ins
|
||||
# ftransform() -> u_modelViewProjectionMatrix * a_vertex (must be done before other replacements)
|
||||
if 'ftransform()' in line:
|
||||
line = line.replace('ftransform()', 'u_modelViewProjectionMatrix * a_vertex')
|
||||
|
||||
# gl_ModelViewProjectionMatrix -> u_modelViewProjectionMatrix
|
||||
if 'gl_ModelViewProjectionMatrix' in line:
|
||||
line = line.replace('gl_ModelViewProjectionMatrix', 'u_modelViewProjectionMatrix')
|
||||
|
||||
# gl_Vertex -> a_vertex
|
||||
if 'gl_Vertex' in line:
|
||||
line = line.replace('gl_Vertex', 'a_vertex')
|
||||
|
||||
# gl_FrontColor -> v_color (vertex shader output)
|
||||
if 'gl_FrontColor' in line:
|
||||
line = line.replace('gl_FrontColor', 'v_color')
|
||||
|
||||
# gl_Color -> a_color (vertex) or v_color (fragment)
|
||||
if 'gl_Color' in line:
|
||||
if is_fragment_shader:
|
||||
line = line.replace('gl_Color', 'v_color')
|
||||
else:
|
||||
line = line.replace('gl_Color', 'a_color')
|
||||
|
||||
# gl_TexCoord[0].st or gl_TexCoord[0].xy -> v_texCoord
|
||||
if 'gl_TexCoord' in line:
|
||||
# Handle gl_TexCoord[0].st and gl_TexCoord[0].xy
|
||||
line = re.sub(r'gl_TexCoord\[0\]\.st', 'v_texCoord', line)
|
||||
line = re.sub(r'gl_TexCoord\[0\]\.xy', 'v_texCoord', line)
|
||||
# Handle bare gl_TexCoord[0] (less common)
|
||||
line = re.sub(r'gl_TexCoord\[0\]', 'vec4(v_texCoord, 0.0, 0.0)', line)
|
||||
|
||||
# gl_MultiTexCoord0 -> a_texCoord0 (for SMAA shaders)
|
||||
if 'gl_MultiTexCoord0' in line:
|
||||
line = re.sub(r'gl_MultiTexCoord0\.st', 'a_texCoord0.st', line)
|
||||
line = re.sub(r'gl_MultiTexCoord0\.xy', 'a_texCoord0.xy', line)
|
||||
line = line.replace('gl_MultiTexCoord0', 'a_texCoord0')
|
||||
|
||||
# Fix uniform int -> uniform float (GLSL ES 3.00 doesn't allow implicit int/float conversion)
|
||||
# This specifically handles u_fontTextureWidth which is multiplied with floats
|
||||
if 'uniform int ' in line:
|
||||
line = line.replace('uniform int ', 'uniform float ')
|
||||
|
||||
# Fix int * float type issues: "2 * x" -> "2.0 * x"
|
||||
# Only convert integers that don't already have a decimal point
|
||||
def fix_int_mult(match):
|
||||
int_val = match.group(1)
|
||||
var_start = match.group(2)
|
||||
# Add .0 to make it a float literal
|
||||
return f'{int_val}.0 * {var_start}'
|
||||
|
||||
line = int_mult_pattern.sub(fix_int_mult, line)
|
||||
|
||||
# Fix float / int type issues: "x / 4" -> "x / 4.0"
|
||||
def fix_div_int(match):
|
||||
var_part = match.group(1)
|
||||
int_val = match.group(2)
|
||||
return f'{var_part} / {int_val}.0'
|
||||
line = div_int_pattern.sub(fix_div_int, line)
|
||||
|
||||
result.append(line)
|
||||
|
||||
# If no version was found but we haven't added precision yet, add it at the start
|
||||
if not added_precision:
|
||||
prefix = ['// GLSL ES 1.00 (WebGL 1.0)', 'precision highp float;', 'precision highp int;', '']
|
||||
result = prefix + result
|
||||
# If no #version was found, DON'T add one - the shader will get its version
|
||||
# from a runtime preamble (e.g. SMAA shaders). Just do basic conversions.
|
||||
# NOTE: If the shader has no #version, we still need to convert legacy GL built-ins
|
||||
|
||||
return '\n'.join(result)
|
||||
|
||||
|
|
@ -75,11 +201,10 @@ def convert_shader_to_cpp(source_path, var_name):
|
|||
with open(source_path, 'rb') as f:
|
||||
data = f.read()
|
||||
|
||||
# Convert GLSL 1.20 to GLSL ES 1.00 for WebGL 1.0 compatibility
|
||||
# (ES 1.00 is closer to GLSL 1.20 and doesn't conflict with Emscripten prepends)
|
||||
# Convert GLSL 1.20 to GLSL ES 3.00 for WebGL 2.0
|
||||
shader_source = data.decode('utf-8')
|
||||
is_fragment = '_frag' in var_name or 'frag' in source_path.lower()
|
||||
shader_source = convert_glsl120_to_es100(shader_source, is_fragment)
|
||||
shader_source = convert_glsl120_to_es300(shader_source, is_fragment)
|
||||
data = shader_source.encode('utf-8')
|
||||
|
||||
# Convert to hex array
|
||||
|
|
|
|||
Loading…
Reference in a new issue