Skip to content

Commit e832805

Browse files
authored
Add cube sample for Web using Feature Level 0 (#9699)
Introduce a new cube sample that utilizes the feature level 0 (FL0) on the web. The `Engine.create` method accepts `config` as an argument, allowing users to explicitly request a GLES 2.0 context. In addition, this change helps a future implementation of asynchronous support for web, allowing the asynchronous mode to be turned on via the configuration setting. BUGS=[476134614]
1 parent 2ce71d6 commit e832805

File tree

4 files changed

+269
-8
lines changed

4 files changed

+269
-8
lines changed

web/filament-js/extensions.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ Filament.loadClassExtensions = function() {
7979
/// canvas ::argument:: the canvas DOM element
8080
/// options ::argument:: optional WebGL 2.0 context configuration
8181
/// ::retval:: an instance of [Engine]
82-
Filament.Engine.create = function(canvas, options) {
82+
Filament.Engine.create = function (canvas, options, config) {
8383
const defaults = {
8484
majorVersion: 2,
8585
minorVersion: 0,
@@ -103,7 +103,9 @@ Filament.loadClassExtensions = function() {
103103
window.filament_glContext = ctx;
104104

105105
// Register the GL context with emscripten and create the Engine.
106-
const engine = Filament.Engine._create();
106+
const defaultConfig = Filament.Engine.createDefaultConfig();
107+
const finalConfig = Object.assign(defaultConfig, config);
108+
const engine = Filament.Engine._create(finalConfig);
107109

108110
// Annotate the engine with the GL context to support multiple canvases.
109111
engine.context = window.filament_glContext;

web/filament-js/jsbindings.cpp

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,28 @@ value_object<filament::Aabb>("Aabb")
312312
.field("min", &filament::Aabb::min)
313313
.field("max", &filament::Aabb::max);
314314

315+
value_object<filament::Engine::Config>("Engine$Config")
316+
.field("commandBufferSizeMB", &filament::Engine::Config::commandBufferSizeMB)
317+
.field("perRenderPassArenaSizeMB", &filament::Engine::Config::perRenderPassArenaSizeMB)
318+
.field("driverHandleArenaSizeMB", &filament::Engine::Config::driverHandleArenaSizeMB)
319+
.field("minCommandBufferSizeMB", &filament::Engine::Config::minCommandBufferSizeMB)
320+
.field("perFrameCommandsSizeMB", &filament::Engine::Config::perFrameCommandsSizeMB)
321+
.field("jobSystemThreadCount", &filament::Engine::Config::jobSystemThreadCount)
322+
.field("metalUploadBufferSizeBytes", &filament::Engine::Config::metalUploadBufferSizeBytes)
323+
.field("metalDisablePanicOnDrawableFailure", &filament::Engine::Config::metalDisablePanicOnDrawableFailure)
324+
.field("disableParallelShaderCompile", &filament::Engine::Config::disableParallelShaderCompile)
325+
.field("stereoscopicType", &filament::Engine::Config::stereoscopicType)
326+
.field("stereoscopicEyeCount", &filament::Engine::Config::stereoscopicEyeCount)
327+
.field("resourceAllocatorCacheSizeMB", &filament::Engine::Config::resourceAllocatorCacheSizeMB)
328+
.field("resourceAllocatorCacheMaxAge", &filament::Engine::Config::resourceAllocatorCacheMaxAge)
329+
.field("disableHandleUseAfterFreeCheck", &filament::Engine::Config::disableHandleUseAfterFreeCheck)
330+
.field("preferredShaderLanguage", &filament::Engine::Config::preferredShaderLanguage)
331+
.field("forceGLES2Context", &filament::Engine::Config::forceGLES2Context)
332+
.field("assertNativeWindowIsValid", &filament::Engine::Config::assertNativeWindowIsValid)
333+
.field("gpuContextPriority", &filament::Engine::Config::gpuContextPriority)
334+
.field("sharedUboInitialSizeInBytes", &filament::Engine::Config::sharedUboInitialSizeInBytes)
335+
.field("asynchronousMode", &filament::Engine::Config::asynchronousMode);
336+
315337
value_object<filament::Renderer::ClearOptions>("Renderer$ClearOptions")
316338
.field("clearColor", &filament::Renderer::ClearOptions::clearColor)
317339
.field("clear", &filament::Renderer::ClearOptions::clear)
@@ -376,19 +398,48 @@ register_vector<allow_raw_pointer<MaterialInstance*>>("MaterialInstanceVector");
376398
// CORE FILAMENT CLASSES
377399
// ---------------------
378400

401+
enum_<Engine::StereoscopicType>("StereoscopicType")
402+
.value("NONE", Engine::StereoscopicType::NONE)
403+
.value("INSTANCED", Engine::StereoscopicType::INSTANCED)
404+
.value("MULTIVIEW", Engine::StereoscopicType::MULTIVIEW);
405+
406+
enum_<Engine::GpuContextPriority>("GpuContextPriority")
407+
.value("DEFAULT", Engine::GpuContextPriority::DEFAULT)
408+
.value("LOW", Engine::GpuContextPriority::LOW)
409+
.value("MEDIUM", Engine::GpuContextPriority::MEDIUM)
410+
.value("HIGH", Engine::GpuContextPriority::HIGH)
411+
.value("REALTIME", Engine::GpuContextPriority::REALTIME);
412+
413+
enum_<Engine::AsynchronousMode>("AsynchronousMode")
414+
.value("NONE", Engine::AsynchronousMode::NONE)
415+
.value("THREAD_PREFERRED", Engine::AsynchronousMode::THREAD_PREFERRED)
416+
.value("AMORTIZATION", Engine::AsynchronousMode::AMORTIZATION);
417+
418+
enum_<Engine::Config::ShaderLanguage>("ShaderLanguage")
419+
.value("DEFAULT", Engine::Config::ShaderLanguage::DEFAULT)
420+
.value("MSL", Engine::Config::ShaderLanguage::MSL)
421+
.value("METAL_LIBRARY", Engine::Config::ShaderLanguage::METAL_LIBRARY);
422+
379423
/// Engine ::core class:: Central manager and resource owner.
380424
class_<Engine>("Engine")
381-
.class_function("_create", (Engine* (*)()) [] {
425+
.class_function("_create", (Engine* (*)(Engine::Config)) [] (Engine::Config config) {
382426
EM_ASM_INT({
383427
const options = window.filament_glOptions;
384428
const context = window.filament_glContext;
385429
const handle = GL.registerContext(context, options);
386430
window.filament_contextHandle = handle;
387431
GL.makeContextCurrent(handle);
388432
});
389-
return Engine::create();
433+
return Engine::create(Engine::Backend::DEFAULT, nullptr, nullptr, &config);
390434
}, allow_raw_pointers())
391435

436+
// Create a default Engine configuration. This is for internal use to ensure that engine
437+
// creation logic in 'extensions.js' does not have to populate the default configuration
438+
// variables manually.
439+
.class_function("createDefaultConfig", (Engine::Config (*)()) [] {
440+
return Engine::Config();
441+
})
442+
392443
.class_function("getSteadyClockTimeNano", &Engine::getSteadyClockTimeNano)
393444

394445
.function("unprotected", &Engine::unprotected)
@@ -409,6 +460,8 @@ class_<Engine>("Engine")
409460

410461
.class_function("getMaxStereoscopicEyes", &Engine::getMaxStereoscopicEyes)
411462

463+
.function("getConfig", &Engine::getConfig)
464+
412465
.function("_execute", EMBIND_LAMBDA(void, (Engine* engine), {
413466
EM_ASM_INT({
414467
const handle = window.filament_contextHandle;

web/samples/CMakeLists.txt

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ set(MATERIAL_NAMES
2222
textured
2323
nonlit)
2424

25+
set(MATERIAL_FL0_NAMES
26+
nonlit
27+
)
28+
2529
set(MATC_FLAGS -a opengl -p mobile)
2630
if (NOT CMAKE_BUILD_TYPE MATCHES Release)
2731
set(MATC_FLAGS -g ${MATC_FLAGS})
@@ -32,14 +36,28 @@ foreach (NAME ${MATERIAL_NAMES})
3236
set(mat_src "materials/${NAME}.mat")
3337
get_filename_component(localname "${mat_src}" NAME_WE)
3438
get_filename_component(fullname "${mat_src}" ABSOLUTE)
35-
set(output_bin "${SERVER_DIR}/${localname}.filamat")
39+
set(output_path "${SERVER_DIR}/${localname}.filamat")
3640
add_custom_command(
37-
OUTPUT ${output_bin}
38-
COMMAND matc ${MATC_FLAGS} -o ${output_bin} ${fullname}
41+
OUTPUT ${output_path}
42+
COMMAND matc ${MATC_FLAGS} -o ${output_path} ${fullname}
3943
MAIN_DEPENDENCY ${mat_src}
4044
DEPENDS matc
4145
COMMENT "Compiling material ${mat_src} to ${output_path}")
42-
list(APPEND MATERIAL_BINS ${output_bin})
46+
list(APPEND MATERIAL_BINS ${output_path})
47+
48+
# --- Feature Level 0 variant ---
49+
list(FIND MATERIAL_FL0_NAMES ${NAME} index)
50+
if (${index} GREATER -1 AND FILAMENT_ENABLE_FEATURE_LEVEL_0)
51+
string(REGEX REPLACE "[.]filamat$" "_fl0.filamat" output_path_fl0 ${output_path})
52+
add_custom_command(
53+
OUTPUT ${output_path_fl0}
54+
COMMAND matc ${MATC_FLAGS} -PfeatureLevel=0 -o ${output_path_fl0} ${fullname}
55+
MAIN_DEPENDENCY ${fullname}
56+
DEPENDS matc
57+
COMMENT "Compiling material ${fullname} (FL0)"
58+
)
59+
list(APPEND MATERIAL_BINS ${output_path_fl0})
60+
endif()
4361
endforeach()
4462

4563
add_custom_target(sample_materials DEPENDS ${MATERIAL_BINS})
@@ -196,6 +214,7 @@ endif()
196214

197215
set(HTML_FILES
198216
animation.html
217+
cube_fl0.html
199218
helmet.html
200219
morphing.html
201220
parquet.html

web/samples/cube_fl0.html

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<title>Filament Cube</title>
6+
<meta charset="utf-8">
7+
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
8+
<link href="favicon.png" rel="icon" type="image/x-icon" />
9+
<style>
10+
body {
11+
margin: 0;
12+
overflow: hidden;
13+
}
14+
15+
canvas {
16+
touch-action: none;
17+
width: 100%;
18+
height: 100%;
19+
}
20+
</style>
21+
</head>
22+
23+
<body>
24+
<canvas></canvas>
25+
<script src="filament.js"></script>
26+
<script src="gl-matrix-min.js"></script>
27+
<script>
28+
29+
Filament.init(['nonlit_fl0.filamat'], () => {
30+
window.VertexAttribute = Filament.VertexAttribute;
31+
window.AttributeType = Filament.VertexBuffer$AttributeType;
32+
window.Projection = Filament.Camera$Projection;
33+
window.Fov = Filament.Camera$Fov;
34+
window.app = new App(document.getElementsByTagName('canvas')[0]);
35+
});
36+
37+
class App {
38+
constructor(canvas) {
39+
this.canvas = canvas;
40+
const engine = this.engine = Filament.Engine.create(this.canvas, {}, { forceGLES2Context: true });
41+
console.log("Engine Config forceGLES2Context:", engine.getConfig().forceGLES2Context);
42+
this.scene = engine.createScene();
43+
this.cube = Filament.EntityManager.get().create();
44+
this.scene.addEntity(this.cube);
45+
46+
const CUBE_POSITIONS = new Float32Array([
47+
// Front face
48+
-1.0, -1.0, 1.0,
49+
1.0, -1.0, 1.0,
50+
1.0, 1.0, 1.0,
51+
-1.0, 1.0, 1.0,
52+
53+
// Back face
54+
-1.0, -1.0, -1.0,
55+
-1.0, 1.0, -1.0,
56+
1.0, 1.0, -1.0,
57+
1.0, -1.0, -1.0,
58+
59+
// Top face
60+
-1.0, 1.0, -1.0,
61+
-1.0, 1.0, 1.0,
62+
1.0, 1.0, 1.0,
63+
1.0, 1.0, -1.0,
64+
65+
// Bottom face
66+
-1.0, -1.0, -1.0,
67+
1.0, -1.0, -1.0,
68+
1.0, -1.0, 1.0,
69+
-1.0, -1.0, 1.0,
70+
71+
// Right face
72+
1.0, -1.0, -1.0,
73+
1.0, 1.0, -1.0,
74+
1.0, 1.0, 1.0,
75+
1.0, -1.0, 1.0,
76+
77+
// Left face
78+
-1.0, -1.0, -1.0,
79+
-1.0, -1.0, 1.0,
80+
-1.0, 1.0, 1.0,
81+
-1.0, 1.0, -1.0,
82+
]);
83+
84+
const CUBE_COLORS = new Uint32Array([
85+
// Front face (red)
86+
0xff0000ff, 0xff0000ff, 0xff0000ff, 0xff0000ff,
87+
// Back face (green)
88+
0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00,
89+
// Top face (blue)
90+
0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000,
91+
// Bottom face (yellow)
92+
0xff00ffff, 0xff00ffff, 0xff00ffff, 0xff00ffff,
93+
// Right face (magenta)
94+
0xffff00ff, 0xffff00ff, 0xffff00ff, 0xffff00ff,
95+
// Left face (cyan)
96+
0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00,
97+
]);
98+
99+
const CUBE_INDICES = new Uint16Array([
100+
0, 1, 2, 0, 2, 3, // front
101+
4, 5, 6, 4, 6, 7, // back
102+
8, 9, 10, 8, 10, 11, // top
103+
12, 13, 14, 12, 14, 15, // bottom
104+
16, 17, 18, 16, 18, 19, // right
105+
20, 21, 22, 20, 22, 23, // left
106+
]);
107+
108+
this.vb = Filament.VertexBuffer.Builder()
109+
.vertexCount(24)
110+
.bufferCount(2)
111+
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, 12)
112+
.attribute(VertexAttribute.COLOR, 1, AttributeType.UBYTE4, 0, 4)
113+
.normalized(VertexAttribute.COLOR)
114+
.build(engine);
115+
116+
this.vb.setBufferAt(engine, 0, CUBE_POSITIONS);
117+
this.vb.setBufferAt(engine, 1, CUBE_COLORS);
118+
119+
this.ib = Filament.IndexBuffer.Builder()
120+
.indexCount(36)
121+
.bufferType(Filament.IndexBuffer$IndexType.USHORT)
122+
.build(engine);
123+
124+
this.ib.setBuffer(engine, CUBE_INDICES);
125+
126+
const mat = engine.createMaterial('nonlit_fl0.filamat');
127+
const matinst = mat.getDefaultInstance();
128+
Filament.RenderableManager.Builder(1)
129+
.boundingBox({ center: [-1, -1, -1], halfExtent: [1, 1, 1] })
130+
.material(0, matinst)
131+
.geometry(0, Filament.RenderableManager$PrimitiveType.TRIANGLES, this.vb, this.ib)
132+
.build(engine, this.cube);
133+
134+
this.swapChain = engine.createSwapChain();
135+
this.renderer = engine.createRenderer();
136+
this.camera = engine.createCamera(Filament.EntityManager.get().create());
137+
138+
this.view = engine.createView();
139+
this.view.setSampleCount(4);
140+
this.view.setCamera(this.camera);
141+
this.view.setScene(this.scene);
142+
this.view.setPostProcessingEnabled(false);
143+
144+
this.renderer.setClearOptions({ clearColor: [0.0, 0.1, 0.2, 1.0], clear: true });
145+
146+
this.resize();
147+
this.render = this.render.bind(this);
148+
this.resize = this.resize.bind(this);
149+
window.addEventListener('resize', this.resize);
150+
window.requestAnimationFrame(this.render);
151+
}
152+
153+
render() {
154+
const radians = Date.now() / 1000;
155+
156+
// Combine rotations around Y and X axes for a spinning effect
157+
const transformY = mat4.fromRotation(mat4.create(), radians, [0, 1, 0]);
158+
const transformX = mat4.fromRotation(mat4.create(), radians * 0.5, [1, 0, 0]);
159+
const transform = mat4.multiply(mat4.create(), transformY, transformX);
160+
161+
const tcm = this.engine.getTransformManager();
162+
const inst = tcm.getInstance(this.cube);
163+
tcm.setTransform(inst, transform);
164+
inst.delete();
165+
166+
this.renderer.render(this.swapChain, this.view);
167+
window.requestAnimationFrame(this.render);
168+
}
169+
170+
resize() {
171+
const dpr = window.devicePixelRatio;
172+
const width = this.canvas.width = window.innerWidth * dpr;
173+
const height = this.canvas.height = window.innerHeight * dpr;
174+
this.view.setViewport([0, 0, width, height]);
175+
176+
const eye = [0, 0, 5], center = [0, 0, 0], up = [0, 1, 0];
177+
this.camera.lookAt(eye, center, up);
178+
179+
const aspect = width / height;
180+
const fov = aspect < 1 ? Fov.HORIZONTAL : Fov.VERTICAL;
181+
this.camera.setProjectionFov(90, aspect, 1.0, 10.0, fov);
182+
}
183+
}
184+
</script>
185+
</body>
186+
187+
</html>

0 commit comments

Comments
 (0)