feat(webgl): Migrate to pure WebGL 2.0 and fix GL canvas layering
Update submodules and build config for pure WebGL 2.0 (drop -sFULL_ES3): - kicad: VBO-based drawing, compositor FBO rewrite, shader conversion - wxwidgets: Fix z-index layering so GL canvas renders above 2D UI canvas - Build: Remove FULL_ES3 from linker flags - GAL tests: White background, opaque alpha, shared shader converter, updated baseline screenshots Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2
kicad
|
|
@ -1 +1 @@
|
|||
Subproject commit d8a9ac4304f5747be43e88deca7bfa0ac87c0213
|
||||
Subproject commit 4ddb9b47f5f9dc3f30ed6d59778e7bc407fdc3cd
|
||||
|
|
@ -243,7 +243,7 @@ emcmake cmake "${KICAD_DIR}" \
|
|||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
-DCMAKE_CXX_FLAGS="${EXTRA_FLAGS} -pthread -sUSE_ZLIB=1 -DKICAD_USE_PLATFORM_WASM=1 -I${SYSROOT}/include -I${STUBS_DIR}" \
|
||||
-DCMAKE_C_FLAGS="${EXTRA_FLAGS} -pthread -sUSE_ZLIB=1 -I${SYSROOT}/include -I${STUBS_DIR}" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="${LINKER_DEBUG_FLAGS} -pthread -sUSE_ZLIB=1 -sASYNCIFY=1 -sDYNCALLS=1 -sASYNCIFY_STACK_SIZE=65536 -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE='navigator.hardwareConcurrency' -sPTHREAD_POOL_SIZE_STRICT=0 -sALLOW_MEMORY_GROWTH=1 -sINITIAL_MEMORY=256MB -sMAXIMUM_MEMORY=4GB -sMAX_WEBGL_VERSION=2 -sFULL_ES3=1 -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString','stringToUTF8','lengthBytesUTF8','dynCall'] -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['\$dynCall'] --bind -L${SYSROOT}/lib ${STUBS_BUILD}/libgit2_stub.a ${STUBS_BUILD}/libcurl_stub.a ${STUBS_BUILD}/libpcbnew_scripting_stub.a ${STUBS_BUILD}/libnng_stub.a ${STUBS_BUILD}/pcbnew_embind.o" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="${LINKER_DEBUG_FLAGS} -pthread -sUSE_ZLIB=1 -sASYNCIFY=1 -sDYNCALLS=1 -sASYNCIFY_STACK_SIZE=65536 -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE='navigator.hardwareConcurrency' -sPTHREAD_POOL_SIZE_STRICT=0 -sALLOW_MEMORY_GROWTH=1 -sINITIAL_MEMORY=256MB -sMAXIMUM_MEMORY=4GB -sMAX_WEBGL_VERSION=2 -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString','stringToUTF8','lengthBytesUTF8','dynCall'] -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['\$dynCall'] --bind -L${SYSROOT}/lib ${STUBS_BUILD}/libgit2_stub.a ${STUBS_BUILD}/libcurl_stub.a ${STUBS_BUILD}/libpcbnew_scripting_stub.a ${STUBS_BUILD}/libnng_stub.a ${STUBS_BUILD}/pcbnew_embind.o" \
|
||||
-DCMAKE_PREFIX_PATH="${SYSROOT};${WX_BUILD}" \
|
||||
-DwxWidgets_CONFIG_EXECUTABLE="${WX_BUILD}/wx-config" \
|
||||
\
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 491 KiB After Width: | Height: | Size: 491 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
|
@ -97,6 +97,15 @@ bool SaveScreenshot(const std::string& path, KIGFX::OPENGL_GAL* gal, int width,
|
|||
if (hasContent) std::cout << " (" << nonBlackCount << " non-black pixels)";
|
||||
std::cout << std::endl;
|
||||
|
||||
// Force all alpha values to 255 (opaque) to match the composited canvas output.
|
||||
// Native reads from the FBO where alpha < 255 for semi-transparent regions.
|
||||
// WebGL captures the composited canvas where alpha is always 1.0 (composited on
|
||||
// black with GL_ONE blend). By making native output opaque, both tests produce
|
||||
// equivalent "user-visible" screenshots that can be compared directly.
|
||||
for (size_t i = 3; i < pixels.size(); i += 4) {
|
||||
pixels[i] = 255;
|
||||
}
|
||||
|
||||
// Flip vertically (OpenGL has origin at bottom-left)
|
||||
stbi_flip_vertically_on_write(1);
|
||||
|
||||
|
|
@ -173,7 +182,11 @@ public:
|
|||
|
||||
// Set up the viewport - use ResizeScreen to properly initialize compositor
|
||||
m_gal->ResizeScreen(g_width, g_height);
|
||||
m_gal->SetClearColor(KIGFX::COLOR4D(0.1, 0.1, 0.15, 1.0));
|
||||
// Use white background to match WebGL test (which captures the composited canvas).
|
||||
// Native reads from the FBO (pre-compositing), so we set alpha to 255 in SaveScreenshot
|
||||
// to simulate the composited result. White background ensures both tests see the same
|
||||
// blended colors for semi-transparent shapes.
|
||||
m_gal->SetClearColor(KIGFX::COLOR4D(1.0, 1.0, 1.0, 1.0));
|
||||
|
||||
// CRITICAL: Set worldUnitLength for 1:1 world-to-screen coordinate mapping
|
||||
// GAL default worldUnitLength is for PCB nanometers (3.937e-8), which would
|
||||
|
|
|
|||
|
|
@ -69,11 +69,11 @@ BASE_LDFLAGS = -sALLOW_MEMORY_GROWTH=1 \
|
|||
-sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','HEAPU8'] \
|
||||
-sMODULARIZE=1 \
|
||||
-sEXPORT_NAME='createGALTest' \
|
||||
-sENVIRONMENT=web
|
||||
-sENVIRONMENT=web,worker
|
||||
|
||||
# GL-specific flags - WebGL 2.0 with full ES3 support
|
||||
# GL-specific flags - pure WebGL 2.0 (matching KiCad PCBnew build, no FULL_ES3 emulation)
|
||||
# No LEGACY_GL_EMULATION - WEBGL_GAL uses VBOs and custom shaders
|
||||
EM_GL_FLAGS = -sFULL_ES3=1 -sMAX_WEBGL_VERSION=2
|
||||
EM_GL_FLAGS = -sMAX_WEBGL_VERSION=2
|
||||
|
||||
# Legacy GL stubs for wxWidgets compatibility
|
||||
# wxWidgets GL library may reference legacy functions at runtime, but actual
|
||||
|
|
|
|||
|
|
@ -9,191 +9,14 @@ KiCad's BUILTIN_SHADERS namespace.
|
|||
import os
|
||||
import sys
|
||||
|
||||
import re
|
||||
|
||||
def convert_glsl120_to_es300(shader_source, is_fragment_shader):
|
||||
"""
|
||||
Convert GLSL 1.20 (OpenGL 2.1) shader to GLSL ES 3.00 (WebGL 2.0).
|
||||
|
||||
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 = []
|
||||
version_replaced = False
|
||||
|
||||
# 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()
|
||||
|
||||
# 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'):
|
||||
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"
|
||||
def fix_int_mult(match):
|
||||
int_val = match.group(1)
|
||||
var_start = match.group(2)
|
||||
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, 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)
|
||||
# Import the canonical GLSL converter from the KiCad shaders directory.
|
||||
# This ensures the test uses the exact same conversion as the KiCad WASM build.
|
||||
_kicad_shaders_dir = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
'..', '..', '..', 'kicad', 'common', 'gal', 'shaders'
|
||||
)
|
||||
sys.path.insert(0, os.path.abspath(_kicad_shaders_dir))
|
||||
from convert_glsl_es3 import convert_glsl120_to_es300
|
||||
|
||||
|
||||
def convert_shader_to_cpp(source_path, var_name):
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit c5aaa19ca9cffe739d9ccbc99003bc712eda031e
|
||||
Subproject commit 915a2ac4d87afc7dbd8005dab3238cb9bbd0a52c
|
||||