Skip to content

Improve native rendering parity and enable 57 validation scenes - #1913

Merged
bkaradzic-microsoft merged 16 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:shotgun-rendering-parity
Oct 7, 2026
Merged

bkaradzic-microsoft merged 16 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:shotgun-rendering-parity

Conversation

@bkaradzic-microsoft

@bkaradzic-microsoft bkaradzic-microsoft commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Summary

Improve native rendering parity with Babylon.js 9.29.0 / protocol 9:

  • Fix instancing, shader coordinates/varyings, texture sampling/readback, MSAA, and framebuffer handling.
  • Fix Canvas paths/clipping, image lifetime/I/O, text metrics, alpha representations, and mipmaps.
  • Improve validation runtime support and enable 57 additional scenes: 314 -> 371, with 349 excluded out of 720.

No upstream-enabled scenes are disabled. Reference images, comparison thresholds, and existing backend exclusions are unchanged.

16 commits: 14 code commits, one generated JavaScript bundle update, and one catalog update. Compute shaders, storage/dispatch changes, GPU instance repacking/topology expansion, protocol 10, and paired Babylon.js changes remain excluded.

Current status

  • Review: latest integer-coordinate, explicit-gradient, and interface-block concerns addressed. Maintainer approval is recorded.
  • CI: 33/33 jobs passed on e5ec2268. The snippet-service HTTP 403 failure cleared on retry without code changes.
  • Focused validation: 30/30 cases pass with WEBMIN disabled; 27 pass with default WEBMIN, with three interface-block skips; 2/2 DXIL compilation cases pass.
  • Earlier full validation: uninstrumented Hermes passed 371/371 scenes at the unchanged 512 MiB heap limit; full Hermes and Chakra/ASan native suites each passed 123/123.
  • Dependency: upstream JsRuntimeHost 2f8b9be3 includes the merged Hermes WeakRef fix from Release Hermes WeakRef targets after draining jobs JsRuntimeHost#262. No temporary runtime fork remains; all other dependency pins are unchanged.

Merged into master as 3428526f143f4eec86c4cf62d5bc493c1afca159.

Copilot AI balanced review requested due to automatic review settings October 6, 2026 03:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Framebuffer clearing, varying locations, volume padding, MSAA defaults, and native branding contain correctness issues.

Review effort: Balanced
Findings: 2 High severity · 3 Medium severity

Open (5)
What changed in this PR

Improves rendering parity across Canvas, NativeEngine, shader compilation, textures, MSAA, and validation infrastructure, enabling 91 additional validation scenes.

Changes:

  • Adds Canvas clipping, image conversion, text metrics, and safer native wrappers.
  • Improves shader interfaces, texture formats, framebuffer handling, and instancing.
  • Expands validation coverage, assets, browser shims, fonts, and Havok packaging.
File Description
Polyfills/​Canvas/​Source/​StbTrueType.cpp Adds script-aware kerning.
Polyfills/​Canvas/​Source/​Shaders/​glsl/​fs_canvas_unpremultiply.h Adds generated GLSL conversion shader.
Polyfills/​Canvas/​Source/​Shaders/​fs_nanovg_fill.sc Applies rasterized clip masks.
Polyfills/​Canvas/​Source/​Shaders/​fs_canvas_unpremultiply.sc Adds alpha-conversion shader source.
Polyfills/​Canvas/​Source/​Shaders/​essl/​fs_canvas_unpremultiply.h Adds generated ESSL shader.
Polyfills/​Canvas/​Source/​Shaders/​dxbc/​fs_canvas_unpremultiply.h Adds generated DXBC shader.
Polyfills/​Canvas/​Source/​Path2D.h Adds safe unwrapping and cleanup.
Polyfills/​Canvas/​Source/​NativeInstanceRegistry.h Introduces native-instance validation.
Polyfills/​Canvas/​Source/​nanovg/​nanovg.h Defines clip-mask and path APIs.
Polyfills/​Canvas/​Source/​nanovg/​nanovg.cpp Implements clip rasterization and path preservation.
Polyfills/​Canvas/​Source/​nanovg/​nanovg_filterstack.h Supports separate filter compositing.
Polyfills/​Canvas/​Source/​nanovg/​nanovg_filterstack.cpp Defers final filtered composition.
Polyfills/​Canvas/​Source/​nanovg/​nanovg_babylon.cpp Integrates GPU clip masks.
Polyfills/​Canvas/​Source/​nanovg/​fontstash.h Refines metrics and kerning.
Polyfills/​Canvas/​Source/​MeasureText.cpp Returns integral line metrics.
Polyfills/​Canvas/​Source/​Image.h Adds safe image unwrapping.
Polyfills/​Canvas/​Source/​Image.cpp Registers images and adjusts errors.
Polyfills/​Canvas/​Source/​Gradient.h Adds safe gradient unwrapping.
Polyfills/​Canvas/​Source/​Gradient.cpp Uses native-instance validation.
Polyfills/​Canvas/​Source/​FrameBufferPool.cpp Marks pooled targets single-sampled.
Polyfills/​Canvas/​Source/​Context.h Tracks persistent clip masks.
Polyfills/​Canvas/​Source/​Canvas.h Adds converted texture representations.
Polyfills/​Canvas/​Source/​Canvas.cpp Implements alpha and mip conversion.
Polyfills/​Canvas/​shaderc.cmake Targets GLSL 330.
Polyfills/​Canvas/​Readme.md Documents text metrics and fonts.
Polyfills/​Canvas/​CMakeLists.txt Adds new Canvas sources and linkage.
Plugins/​TestUtils/​Source/​TestUtils.h Exposes MSAA control.
Plugins/​TestUtils/​Source/​TestUtils.cpp Validates MSAA and normalizes images.
Plugins/​ShaderCompiler/​Source/​ShaderCompilerVulkan.cpp Assigns Vulkan interfaces and built-ins.
Plugins/​ShaderCompiler/​Source/​ShaderCompilerTraversers.h Declares varying-location traversal.
Plugins/​ShaderCompiler/​Source/​ShaderCompilerTraversers.cpp Extends coordinate and varying transforms.
Plugins/​ShaderCompiler/​Source/​ShaderCompilerMetal.cpp Assigns Metal varying locations.
Plugins/​ShaderCompiler/​Source/​ShaderCompilerDXIL.cpp Assigns DXIL varying locations.
Plugins/​ShaderCompiler/​Source/​ShaderCompilerDXBC.cpp Assigns flattened DXBC varying locations.
Plugins/​NativeXr/​Source/​NativeXrImpl.cpp Uses floating-point framebuffer clears.
Plugins/​NativeEngine/​Source/​VertexArray.cpp Supports bufferless instancing.
Plugins/​NativeEngine/​Source/​NativeEngine.h Expands texture and framebuffer APIs.
Plugins/​NativeEngine/​CMakeLists.txt Links image-format support.
Documentation/​Playground.md Documents validation runtime behavior.
Core/​Graphics/​Source/​Texture.cpp Updates volume, ownership, and LOD handling.
Core/​Graphics/​Source/​FrameBuffer.cpp Adds float clears and MSAA tracking.
Core/​Graphics/​Source/​DeviceImpl.h Declares MSAA querying.
Core/​Graphics/​Source/​DeviceImpl.cpp Implements MSAA querying.
Core/​Graphics/​Source/​DeviceContext.cpp Forwards MSAA operations.
Core/​Graphics/​InternalInclude/​Babylon/​Graphics/​Texture.h Extends texture ownership and LOD API.
Core/​Graphics/​InternalInclude/​Babylon/​Graphics/​FrameBuffer.h Extends framebuffer clear and MSAA API.
Core/​Graphics/​InternalInclude/​Babylon/​Graphics/​DeviceContext.h Exposes MSAA operations.
Apps/​UnitTests/​Source/​Tests.ShaderCompilation.FragCoord.cpp Clarifies coordinate tests.
Apps/​UnitTests/​Source/​Tests.ShaderCompilation.cpp Adds Vulkan interface tests.
Apps/​UnitTests/​Source/​Tests.NativeEngine.TextureFormats.cpp Expands texture validation tests.
Apps/​UnitTests/​Source/​Tests.NativeEngine.Msaa.cpp Updates MSAA framebuffer tests.
Apps/​UnitTests/​Source/​Tests.NativeEngine.Mipmaps.cpp Tests linear and sRGB mipmaps.
Apps/​UnitTests/​Source/​Tests.NativeEngine.ImageFormats.cpp Tests PNG transparency formats.
Apps/​UnitTests/​Source/​Tests.NativeEngine.DepthTextures.cpp Tests shared depth ownership.
Apps/​UnitTests/​Source/​Tests.NativeEngine.CubeRenderTargets.cpp Updates cube clear tests.
Apps/​UnitTests/​Source/​Tests.JavaScript.cpp Configures added runtime capabilities.
Apps/​UnitTests/​Source/​Tests.Device.SwapChain.cpp Updates float-clear tests.
Apps/​UnitTests/​Source/​Tests.Device.FrameEncoder.cpp Tests framebuffer MSAA state.
Apps/​UnitTests/​Source/​Tests.Device.D3D11.ExternalBackBuffer.cpp Updates external-buffer clears.
Apps/​UnitTests/​Source/​Tests.Device.cpp Updates recovery clear tests.
Apps/​UnitTests/​Source/​Tests.Canvas.Readback.cpp Tests Canvas image and texture conversion.
Apps/​UnitTests/​JavaScript/​src/​tests.nativeEngine.png.ts Expands PNG decoding coverage.
Apps/​UnitTests/​JavaScript/​src/​tests.nativeEngine.canvasImage.ts Tests Canvas-backed image drawing.
Apps/​UnitTests/​JavaScript/​src/​tests.nativeEngine.attributeLessInstancing.ts Tests bufferless instancing.
Apps/​UnitTests/​CMakeLists.txt Packages assets and new tests.
Apps/​Playground/​Win32/​App.cpp Disables debugger when headless.
Apps/​Playground/​Shared/​PlaygroundScripts.cpp Loads shims and procedural textures.
Apps/​Playground/​Shared/​CommandLine.cpp Documents headless debugger behavior.
Apps/​Playground/​Scripts/​native_env.js Supplies browser-like globals.
Apps/​Playground/​CMakeLists.txt Packages Havok, fonts, and shims.
Apps/​package.json Adds Havok dependency.
Apps/​package-lock.json Locks Havok dependencies.
Apps/​Dependencies/​DroidSans-PROVENANCE.txt Records DroidSans provenance.
Apps/​Dependencies/​DroidSans-APACHE-2.0.txt Adds DroidSans license.
Apps/​Dependencies/​Arimo-PROVENANCE.txt Records Arimo provenance.
Apps/​Dependencies/​Arimo-OFL.txt Adds Arimo license.
Files not reviewed (1)
  • Apps/package-lock.json: Generated file

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp
Comment thread Polyfills/Canvas/Source/NativeInstanceRegistry.h
Comment thread Core/Graphics/InternalInclude/Babylon/Graphics/FrameBuffer.h Outdated
Comment thread Core/Graphics/Source/FrameBuffer.cpp Outdated
Comment thread Core/Graphics/Source/Texture.cpp Outdated
Submit the requested instance count when a draw derives its per-instance
data from gl_InstanceID instead of an instance buffer. Map
vertex/instance built-ins to Vulkan's index built-ins and assign
matching shader interface locations, including explicitly reserved
locations. Add compiler and rendering regressions for these paths.

Run the indexed and unindexed rendering regression on every real
renderer, including D3D12 and installed-SDK consumers. Skip it only
when the test build uses a no-op Metal device.

Review-Group: E1
Source: PR BabylonJS#1888
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Split-From: a79dba316c9e38f9597fa4bf16f652551a20a864
Split-Part: code
Reject invalid buffer offsets, lengths, face/layer indices, and
uninitialized textures before readback work begins. Check full source
and destination storage sizes before bgfx's uint32 conversion. Link bimg
independently of image decoding so these checks also build with image
loading disabled.

Review-Group: E2
Source: PR BabylonJS#1892
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Split-From: ac3f4081ed11b26f509693c60f97b296790451e2
Split-Part: code
@bkaradzic-microsoft
bkaradzic-microsoft force-pushed the shotgun-rendering-parity branch 4 times, most recently from 2613de1 to 251032c Compare October 6, 2026 16:22
bkaradzic-microsoft and others added 3 commits October 6, 2026 11:01
Track genuine native Canvas, Image, Gradient, and Path2D instances in an
environment-local registry before unwrapping them. Reject prototype-
spoofed inputs without depending on mutable JavaScript instanceof
behavior, while preserving valid overloads. Add regressions for forged
objects and legitimate Canvas operations.

Review-Group: E7
Source: PR BabylonJS#1844
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Split-From: 5333ecc57aebebb51753797a042de064d3391d6b
Split-Part: code
Begin a fresh NanoVG path before constructing a strokeRect rectangle so
cached geometry from an earlier operation is not stroked again. Add
pixel regressions covering repeated rectangles and interaction with
preceding drawing operations.

Review-Group: N1
Source: Review branch cf56bbd
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Split-From: b22c311cdca10e3528688fbd68611ffc1dc2b46e
Split-Part: code
Derive raster multisampling from the currently bound framebuffer rather
than the window's sample count. Carry sample metadata through default,
offscreen, Canvas, and filter targets so switching targets does not leak
MSAA state. Expose validation sample control and add a framebuffer-state
regression.

Review-Group: N2
Source-Shotgun: d2727b4
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Split-From: c7a5b655d78734a143b17dad5afc76a7c6b5b87f
Split-Part: code
@bkaradzic-microsoft bkaradzic-microsoft changed the title Improve native rendering parity and enable 91 validation scenes Improve native rendering parity and enable 60 validation scenes Oct 6, 2026
@bkaradzic-microsoft bkaradzic-microsoft changed the title Improve native rendering parity and enable 60 validation scenes Improve native rendering parity and enable 57 validation scenes Oct 6, 2026
Comment thread Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp Outdated
Comment thread Polyfills/Canvas/Source/Canvas.cpp Outdated
Comment thread Plugins/NativeEngine/Source/NativeEngine.cpp Outdated
Comment thread Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp Outdated
bkaradzic-microsoft and others added 4 commits October 6, 2026 15:11
Link Canvas directly to bimg for pixel conversion and PNG writing,
including builds with image loading disabled, instead of bimg_encode.
Use the upstream imageParse path for SVG and bitmap images without a
second Canvas-specific parser or size cap. Cover RGBA output, fractional
dimensions, the 4096-pixel bound, and invalid dimensions, and retain
image onerror callback behavior.

Review-Group: N3
Source-Shotgun: d2727b4
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Split-From: 09a32051c5f9c523dcb9f012aaecc8d4a9cffe57
Split-Part: code
Make fragment coordinates, texture sampling/fetches, cube orientation,
and raw uploads agree on WebGL's logical row convention. Keep Canvas
readback GPU-authoritative without an unused CPU pixel mirror, and
include the required path-state helpers. Preserve upstream coordinate
regressions; defer shadow and IBL integration cases needing Babylon.js changes.

Review-Group: N4
Source-Shotgun: d2727b4
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Split-From: fb7d9aeb185c435291f78058dc2aabd39574d64d
Split-Part: code
Adjust fontstash glyph placement and script-aware GPOS kerning to better
match browser text rendering. Report an integral CSS line box separately
from precise ink bounds so GUI layout and measureText agree. Add text
regressions and font-loading hooks, and package licensed Arimo and
DroidSans assets with their provenance.

Review-Group: N5
Source-Shotgun: d2727b4
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Split-From: 2ef0aedee35ae5a7cd2aa6c6265161c45c35ab6a
Split-Part: code
Preserve the caller's current path across immediate drawing operations
and save/restore clip state correctly. Normalize signed rectangular
clips and rasterize curved or compound paths into antialiased masks
consumed by NanoVG draws. Update shader sources and compiled backends
together, with nested-clip and path-preservation regressions.

Review-Group: N6
Source-Shotgun: d2727b4
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Split-From: 6181f3efcacb1fae23b375a973b62c6c1560ced9
Split-Part: code
bkaradzic-microsoft added a commit to BabylonJS/JsRuntimeHost that referenced this pull request Oct 6, 2026
## Fix

Call `Runtime::clearKeptObjects()` after draining Hermes microtasks,
matching Hermes's own JSI and ABI embeddings. Without this, targets
protected by `WeakRef` remain rooted across JavaScript turns, even after
full GC.

This was identified while investigating retained disposed scenes and
heap exhaustion in BabylonJS/BabylonNative#1913. It does not increase
the heap limit.

## Regression

The new Hermes-only native test creates 256 one-MiB weak targets across
job drains. The original embedding retains **268,437,568 external
bytes** and fails the **64 MiB** bound; the fixed embedding passes.

The regression is selected by CMake only for Hermes, alongside the
existing engine-specific Node-API sources. Shared tests remain
unchanged; other engines do not compile a reference to the Hermes-only
`DrainJobs` API.

Windows Hermes native Node-API/runtime/scheduling coverage: **10 passed,
one existing engine-specific skip**. BabylonNative production validation
with this runtime fix passes **123/123 native tests** and **371/371
rendering scenes**, with snapshots enabled, memory instrumentation
disabled, stock Babylon.js 9.29.0, and the unchanged **512 MiB** heap
limit. Follow-up CI is still required.

## CI follow-up

Non-Hermes compilation is repaired by selecting the regression at build
time, not with a runtime skip. JSI builds and passes its applicable
runtime tests.

The next run exposed an existing x86 Chakra scheduling assertion using
`Date` (29 ms observed against a 30 ms bound). Timer tests now use the
high-resolution `performance.now()` clock, with **unchanged
minimum-delay assertions** and no scheduling implementation changes. The
complete x86 Chakra suite passes **26/26**, including **251 JavaScript
cases**, in each of **three consecutive iterations**. Fresh CI remains
required.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae

@bghgary bghgary left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Reviewed by Copilot on behalf of @bghgary]

Concerns inline.

Comment thread Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp Outdated
Comment thread Plugins/NativeEngine/Source/NativeEngine.cpp
Comment thread Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp Outdated
bkaradzic-microsoft and others added 3 commits October 6, 2026 17:24
Keep no-mip LOD limits separate from spatial filters and pass image
color space into mip generation. Match vertex/fragment varying
locations, placing shared inputs before vertex-only outputs. Add native
mip/filter and PNG upload regressions; defer paired Babylon.js integration tests.

Defer the FXC nested-loop workaround until its SPIRV-Cross dependency lands.

Review-Group: H1
Source-Shotgun: d2727b4
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Split-From: 872c2f1291162601894984f29b3d6fb062838a7a
Split-Part: code
Add multiple-render-target attachment routing, shared sampleable depth,
layer/mip selection, and explicit texture-handle ownership. Use
floating-point clear colors and additional blend equations for HDR
rendering, preserving float readbacks and manually
authored mip levels. Migrate clear callers and retain upstream MSAA
initialization with the correct framebuffer sample metadata.

Keep protocol-9 clear and stencil payloads; defer the new mask fields.

Review-Group: H2a
Source-Shotgun: d2727b4
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Split-From: 8007d43a58afbc67d136b061c8cc2ccea3a78499
Split-Part: code
Extend render-target allocation to texture arrays and 3D volumes, map
depth comparison functions, and use compatible backing storage for
additional formats. Validate dimensions, layers, and comparison enums
before mutation or integer narrowing, while preserving fractional-size
truncation and default-layer behavior. Add allocation and invalid-input
regressions.

Review-Group: H2b
Source-Shotgun: d2727b4
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Split-From: 9429dd4eb0b6d0c790971dd5c2b1abcf48facaad
Split-Part: code
bkaradzic-microsoft and others added 4 commits October 6, 2026 17:24
Keep Canvas render targets premultiplied internally, but produce a
separate straight-alpha GPU copy when a dynamic texture requests it.
Preserve the legacy premultiplied behavior when the argument is omitted
and regenerate mipmaps for updated dynamic textures. Add conversion
shaders for each backend and pixel-backed Canvas regressions; this
contract requires matching Babylon.js uploads.

Review-Group: H4
Source-Shotgun: d2727b4
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Split-From: 29b5f90601b9055e21f50f979697ddc2b8a5d87a
Split-Part: code
Supply the globals, image/font setup, synthetic pointer handling, and
engine options needed to run browser Playground scenes in Native. Match
browser capture composition and sample settings, reset per-test state,
and wait for GUI/utility-scene convergence before capture. Wire
bootstrap loading and headless behavior without changing reference
images or catalog budgets.

Review-Group: H5a
Source-Shotgun: d2727b4
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Split-From: 56f09c7f26caf65db33d22e845f6b08d0f7c58e8
Split-Part: code
Build tests.javaScript.all.js once for the retained protocol-9 regressions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Enable the independent rendering cases supported by the reduced native
stack. Defer newly enabled cases still failing stock CI, restoring their
upstream catalog entries without disabling any upstream-enabled tests.

Keep the original pixel budgets, references, scene IDs, render counts,
and explicit backend exclusions. The catalog enables 371 of 720 cases.
Babylon.js and native dependency pins remain unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants