feat(webgl): Fix GL initialization and add GLSL ES shader conversion

Progress on WEBGL_GAL rendering:

- Fix GLImmediateShim initialization by adding GL function dependencies
  (glBegin, glEnd, glVertex2f, etc.) to $GLImmediateShim__deps
- Add COLOR4D(EDA_COLOR_T) constructor stub to wasm_stubs.cpp
- Fix GAL context locking - add LockContext/UnlockContext calls
- Add exception catching to build (-sNO_DISABLE_EXCEPTION_CATCHING)
- Create GLSL ES 1.00 shader converter in generate_shaders.py:
  - Remove #version 120 directive (ES 1.00 default)
  - Add precision highp float/int qualifiers
  - Convert int * float to float literals (2 * x -> 2.0 * x)
- Update build script to clean output files for linker flag changes
- Add console capture to test spec for debugging

Current status:
- WASM builds and loads successfully
- GLImmediateShim initializes correctly
- WEBGL_GAL creates successfully
- Shader compilation fails due to Emscripten LEGACY_GL_EMULATION
  prepending code that conflicts with custom shader precision

Next: Investigate shader compilation with LEGACY_GL_EMULATION or
consider alternative approaches for GL compatibility.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Viktor Vaczi 2026-01-07 21:59:21 +01:00
commit 6c2cf85177
17 changed files with 182 additions and 24 deletions

View file

@ -9,11 +9,79 @@ KiCad's BUILTIN_SHADERS namespace.
import os
import sys
import re
def convert_glsl120_to_es100(shader_source, is_fragment_shader):
"""
Convert GLSL 1.20 (OpenGL 2.1) shader to GLSL ES 1.00 (WebGL 1.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)
- Add precision qualifiers
- Fix int * float type issues (2 * x -> 2.0 * x)
"""
lines = shader_source.split('\n')
result = []
added_precision = False
# Pattern to find integer literals multiplied by variables
# Matches patterns like "2 * var" or "2* var" etc
int_mult_pattern = re.compile(r'\b(\d+)\s*\*\s*([a-zA-Z_])')
for line in lines:
stripped = line.strip()
# Skip the #version directive - ES 1.00 doesn't use it
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
continue
# 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)
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
return '\n'.join(result)
def convert_shader_to_cpp(source_path, var_name):
"""Convert a shader file to C++ header content."""
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)
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)
data = shader_source.encode('utf-8')
# Convert to hex array
hex_values = ', '.join(f'0x{b:02x}' for b in data)
hex_values += ', 0x00' # Null terminate