-
Notifications
You must be signed in to change notification settings - Fork 115
414 lines (394 loc) · 17.6 KB
/
Copy pathemscripten-samples.yml
File metadata and controls
414 lines (394 loc) · 17.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
name: Emscripten WebGL 2.0 Samples
on:
push:
branches: [ "core4", "feature/*" ]
paths-ignore:
- '.devcontainer/**'
- 'snowballs2/**'
pull_request:
branches: [ "core4" ]
paths-ignore:
- '.devcontainer/**'
- 'snowballs2/**'
workflow_dispatch:
env:
EM_VERSION: 3.1.61
BUILD_TYPE: Release
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install ccache
run: sudo apt-get install -y ccache
- name: Cache ccache
uses: actions/cache@v6
with:
path: ~/.cache/ccache
key: ccache-emscripten-${{ env.EM_VERSION }}-${{ hashFiles('.github/workflows/emscripten-samples.yml') }}-${{ github.sha }}
restore-keys: ccache-emscripten-${{ env.EM_VERSION }}-${{ hashFiles('.github/workflows/emscripten-samples.yml') }}-
- name: Configure ccache
run: |
ccache --set-config=max_size=1G
ccache --set-config=compression=true
ccache -z
- name: Setup Emscripten
uses: mymindstorm/setup-emsdk@v16
with:
version: ${{ env.EM_VERSION }}
no-cache: true
- name: Cache graphics assets dump
uses: actions/cache@v6
with:
path: ${{ github.workspace }}/../ryzomcore_graphics_cache
key: ryzomcore_graphics-rev5
- name: Fetch graphics assets dump
# A `git clone` of ryzom/ryzomcore_graphics smudges LFS on checkout,
# which burns the org's LFS bandwidth on every CI run and hard-fails
# the moment the budget is exhausted. Instead pull the mirrored
# snapshot from cdn.ryzom.dev (no LFS involved) and unpack it to the
# path CMake resolves as NL_GRAPHICS_DIR. The cache above skips the
# ~2 GB download on repeat runs.
run: |
sudo apt-get install -y p7zip-full
ASSETS_URL="https://cdn.ryzom.dev/pub/assets/ryzomcore_graphics-rev5.7z"
DEST="${{ github.workspace }}/../ryzomcore_graphics"
CACHE_DIR="${{ github.workspace }}/../ryzomcore_graphics_cache"
mkdir -p "$CACHE_DIR"
if [ ! -f "$CACHE_DIR/ryzomcore_graphics-rev5.7z" ]; then
curl -L --fail --retry 3 -o "$CACHE_DIR/ryzomcore_graphics-rev5.7z" "$ASSETS_URL"
fi
mkdir -p "$DEST"
7z x -y -o"$DEST" "$CACHE_DIR/ryzomcore_graphics-rev5.7z" > /dev/null
# The archive root may or may not include a leading ryzomcore_graphics/;
# flatten if it does so NL_GRAPHICS_DIR points at the asset tree itself.
if [ -d "$DEST/ryzomcore_graphics" ] && [ ! -d "$DEST/interfaces" ]; then
mv "$DEST/ryzomcore_graphics"/* "$DEST/ryzomcore_graphics"/.[!.]* "$DEST/" 2>/dev/null || true
rmdir "$DEST/ryzomcore_graphics" 2>/dev/null || true
fi
- name: Verify Emscripten
run: emcc --version
- name: Cache nelgui_deps (wasm prefix)
id: nelgui-deps-cache
uses: actions/cache@v6
with:
path: ${{ github.workspace }}/../nelgui_deps
key: nelgui_deps-em${{ env.EM_VERSION }}-${{ hashFiles('.github/scripts/build-nelgui-deps.sh') }}
- name: Build nelgui_deps (wasm prefix)
if: steps.nelgui-deps-cache.outputs.cache-hit != 'true'
# Builds lua5.1 + libxml2 + ryzom/luabind cross-compiled to wasm plus
# boost/openssl/curl headers + nel_wasm_stubs archived as libcurl.a/
# libssl.a/libcrypto.a. Cached above so this only runs when the
# script or emsdk version changes.
run: |
bash .github/scripts/build-nelgui-deps.sh \
${{ github.workspace }}/../nelgui_deps/wasm \
${{ github.workspace }}
- name: Build host build_interface
# nl_sample_gui packs its interface atlas at build time via
# build_interface, a native tool that can't run under emscripten.
# Configure a separate host CMake tree and build just that target;
# the emscripten configure below points NL_BUILD_INTERFACE_TOOL at
# the resulting binary. Only build_interface itself is compiled,
# not the rest of nel/tools. libpng/libjpeg are top-level find_package
# requirements for nelmisc's CBitmap paths.
run: |
sudo apt-get install -y libpng-dev libjpeg-dev libxml2-dev libcurl4-openssl-dev libgif-dev
mkdir -p build-host
cd build-host
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DWITH_NEL_TOOLS=ON \
-DWITH_NEL_SAMPLES=OFF -DWITH_NEL_TESTS=OFF \
-DWITH_3D=OFF -DWITH_GUI=OFF -DWITH_SOUND=OFF \
-DWITH_LOGIC=OFF -DWITH_PACS=OFF -DWITH_GEORGES=OFF \
-DWITH_LIGO=OFF -DWITH_NET=OFF -DWITH_WEB=OFF \
-DWITH_RYZOM=OFF -DWITH_RYZOM_CLIENT=OFF \
-DWITH_RYZOM_SERVER=OFF -DWITH_RYZOM_TOOLS=OFF \
-DWITH_NELNS=OFF -DWITH_SNOWBALLS=OFF
make -j$(nproc) build_interface
- name: Configure CMake
env:
EM_FROZEN_CACHE: ""
run: |
DEPS="${{ github.workspace }}/../nelgui_deps"
mkdir -p build
cd build
emcmake cmake .. \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DWITH_PCH=OFF \
-DWITH_STATIC=ON \
-DWITH_STATIC_DRIVERS=ON \
-DWITH_STATIC_LIBXML2=ON \
-DWITH_DRIVER_OPENGL=OFF \
-DWITH_DRIVER_OPENGLES=OFF \
-DWITH_DRIVER_OPENGL3=OFF \
-DWITH_DRIVER_OPENGLES3=ON \
-DWITH_NEL_SAMPLES=ON \
-DWITH_NEL_TOOLS=OFF \
-DWITH_NEL_TESTS=OFF \
-DWITH_RYZOM=OFF \
-DWITH_RYZOM_CLIENT=OFF \
-DWITH_RYZOM_SERVER=OFF \
-DWITH_RYZOM_TOOLS=OFF \
-DWITH_NELNS=OFF \
-DWITH_SNOWBALLS=OFF \
-DWITH_GUI=ON \
-DWITH_LUA51=ON \
-DWITH_SOUND=OFF \
-DWITH_LOGIC=OFF \
-DWITH_PACS=OFF \
-DWITH_GEORGES=OFF \
-DWITH_LIGO=OFF \
-DWITH_NET=OFF \
-DWITH_WEB=OFF \
-DNL_BUILD_INTERFACE_TOOL=${{ github.workspace }}/build-host/bin/build_interface \
-DLUA_INCLUDE_DIR=$DEPS/wasm/include \
-DLUA_LIBRARIES=$DEPS/wasm/lib/liblua.a \
-DLUABIND_INCLUDE_DIR=$DEPS/wasm/include \
-DLUABIND_LIBRARY_RELEASE=$DEPS/wasm/lib/libluabind09.a \
-DLibXml2_DIR=$DEPS/wasm/lib/cmake/libxml2-2.12.10 \
-DBoost_INCLUDE_DIR=$DEPS/boost-headers \
-DCURL_INCLUDE_DIR=$DEPS/wasm/include \
-DCURL_LIBRARY=$DEPS/wasm/lib/libcurl.a \
-DOPENSSL_INCLUDE_DIR=$DEPS/openssl-headers \
-DOPENSSL_SSL_LIBRARY=$DEPS/wasm/lib/libssl.a \
-DOPENSSL_CRYPTO_LIBRARY=$DEPS/wasm/lib/libcrypto.a \
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}
- name: Build samples
env:
EM_FROZEN_CACHE: ""
run: |
cd build
emmake make -j$(nproc) \
nl_sample_clip_plane \
nl_sample_nelvp \
nl_sample_planar_reflection \
nl_sample_ppl \
nl_sample_water \
nl_sample_font \
nl_sample_gui \
nl_sample_navigation
- name: ccache stats
if: always()
run: ccache -s
- name: Prepare Pages artifact
run: |
mkdir -p pages
# Copy built sample files
for sample in clip_plane nelvp planar_reflection ppl water font gui navigation; do
cp build/bin/nl_sample_${sample}.js pages/
cp build/bin/nl_sample_${sample}.wasm pages/
# Copy any embedded data files
if [ -f "build/bin/nl_sample_${sample}.data" ]; then
cp "build/bin/nl_sample_${sample}.data" pages/
fi
done
# Generate per-sample HTML host pages
for sample in clip_plane nelvp planar_reflection ppl water font gui navigation; do
title=$(echo "$sample" | sed 's/_/ /g; s/\b\(.\)/\u\1/g')
cat > pages/nl_sample_${sample}.html << 'SAMPLEEOF'
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>NeL Sample (WebGL 2.0)</title>
<style>
body {
margin: 0; background: #1a1a2e;
display: flex; justify-content: center; align-items: center; height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
canvas { display: block; width: 800px; height: 600px; }
#loading {
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
text-align: center; z-index: 10; color: #e0e0e0; min-width: 320px;
}
#loading-title { font-size: 1.5rem; color: #e94560; margin-bottom: 1.2rem; }
.bar-outer {
width: 300px; height: 6px; background: #0f3460; border-radius: 3px;
overflow: hidden; margin: 0 auto;
}
.bar-inner {
width: 0%; height: 100%; background: #e94560; border-radius: 3px;
transition: width 0.15s ease-out;
}
#task-bar .bar-inner { background: #0f7460; }
#global-label, #task-label {
font-size: 0.75rem; color: #666; margin-top: 0.2rem; margin-bottom: 0.6rem;
}
#loading-tasks {
text-align: left; display: inline-block;
font-size: 0.85rem; line-height: 1.6;
margin-top: 0.6rem;
}
.task { color: #555; }
.task.active { color: #a0a0b0; }
.task.done { color: #4a7; }
.task-icon { display: inline-block; width: 1.2em; text-align: center; }
.task-detail { color: #666; font-size: 0.8em; }
</style>
</head>
<body>
<div id="loading">
<div id="loading-title">NeL 3D</div>
<div id="global-bar" class="bar-outer"><div class="bar-inner"></div></div>
<div id="global-label"></div>
<div id="task-bar" class="bar-outer"><div class="bar-inner"></div></div>
<div id="task-label"></div>
<div id="loading-tasks"></div>
</div>
<canvas id="canvas" width="800" height="600" oncontextmenu="event.preventDefault()"></canvas>
<script>
(function() {
var globalBarEl = document.querySelector('#global-bar .bar-inner');
var globalLabelEl = document.getElementById('global-label');
var taskBarEl = document.querySelector('#task-bar .bar-inner');
var taskLabelEl = document.getElementById('task-label');
var tasksEl = document.getElementById('loading-tasks');
var loadEl = document.getElementById('loading');
var tasks = {}, taskOrder = [];
var tasksDone = 0;
var activeTask = null;
function setGlobalBar(f) { globalBarEl.style.width = Math.min(100, Math.round(f * 100)) + '%'; }
function setTaskBar(f) { taskBarEl.style.width = Math.min(100, Math.round(f * 100)) + '%'; }
function update() {
setGlobalBar(taskOrder.length > 0 ? tasksDone / taskOrder.length : 0);
if (activeTask) {
var f = activeTask.total > 0 ? activeTask.step / activeTask.total : 0;
setTaskBar(f);
taskLabelEl.textContent = activeTask.name + ' ' + activeTask.step + ' / ' + activeTask.total;
}
globalLabelEl.textContent = tasksDone + ' / ' + taskOrder.length;
var html = '';
for (var i = 0; i < taskOrder.length; i++) {
var t = tasks[taskOrder[i]];
var done = t.step >= t.total && t.total > 0;
var cls = done ? 'done' : (t.active ? 'active' : '');
var icon = done ? '\u2713' : (t.active ? '\u25B8' : '\u00B7');
html += '<div class="task ' + cls + '"><span class="task-icon">' + icon + '</span>' + t.name;
if (t.active && !done && t.total > 0)
html += ' <span class="task-detail">' + t.step + ' / ' + t.total + '</span>';
html += '</div>';
}
tasksEl.innerHTML = html;
}
function checkTaskDone(t) {
if (t.total > 0 && t.step >= t.total && !t.counted) {
t.counted = true;
tasksDone++;
}
}
window.nlBeginTask = function(name, total) {
if (!tasks[name]) {
tasks[name] = { name: name, step: 0, total: 0, active: false, counted: false };
taskOrder.push(name);
}
tasks[name].total += total;
tasks[name].active = true;
activeTask = tasks[name];
setTaskBar(0);
update();
};
window.nlStepTask = function(name) {
if (!tasks[name]) return;
tasks[name].step++;
activeTask = tasks[name];
checkTaskDone(tasks[name]);
update();
};
window.nlLoadingComplete = function() { loadEl.style.display = 'none'; };
// Pre-register all known tasks so the full list is visible from the start
var knownTasks = ['Downloading', 'Compiling vertex shaders', 'Compiling pixel shaders', 'Linking shader programs'];
for (var i = 0; i < knownTasks.length; i++) {
tasks[knownTasks[i]] = { name: knownTasks[i], step: 0, total: 0, active: false, counted: false };
taskOrder.push(knownTasks[i]);
}
// WASM download: starts immediately
var wasmDone = false;
var wasmTask = tasks['Downloading'];
wasmTask.total = 100;
wasmTask.active = true;
activeTask = wasmTask;
update();
window.Module = {
canvas: document.getElementById('canvas'),
setStatus: function(text) {
if (wasmDone) return;
if (!text) {
wasmDone = true;
wasmTask.step = wasmTask.total;
checkTaskDone(wasmTask);
update();
return;
}
var m = text.match(/\((\d+)\/(\d+)\)/);
if (m) {
wasmTask.step = Math.round(100 * parseInt(m[1]) / parseInt(m[2]));
update();
}
},
monitorRunDependencies: function(left) {
if (left === 0 && !wasmDone) {
wasmDone = true;
wasmTask.step = wasmTask.total;
checkTaskDone(wasmTask);
update();
}
}
};
})();
</script>
SAMPLEEOF
# Append the title and sample-specific script tag (needs shell variable expansion)
sed -i "s|<title>NeL Sample (WebGL 2.0)</title>|<title>NeL Sample - ${title} (WebGL 2.0)</title>|" pages/nl_sample_${sample}.html
# Samples that run fullscreen at native devicePixelRatio: swap the fixed-size
# centered layout for a viewport-filling canvas. The driver's emscripten event
# emitter already tracks CSS size x DPR into the canvas backing store and
# re-syncs on resize, so the page only has to let the canvas fill.
#
# The viewport meta is what makes "fill" mean the real screen on a phone -
# without it 100vw resolves against a ~980px virtual viewport and the page is
# scaled down. It stays off the fixed-size samples on purpose: width=device-width
# would leave their 800px canvas overflowing a narrow screen sideways.
#
# overscroll-behavior stops pull-to-refresh and rubber-banding from stealing a
# drag that starts near an edge, which on a phone is most of them.
case "$sample" in
gui|navigation)
sed -i \
-e 's|<meta charset="utf-8">|<meta charset="utf-8">\n <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">|' \
-e 's|body {|html, body { width: 100%; height: 100%; overflow: hidden; overscroll-behavior: none;|' \
-e 's|display: flex; justify-content: center; align-items: center;||' \
-e 's|width: 800px; height: 600px;|width: 100vw; height: 100vh;|' \
-e 's|<canvas id="canvas" width="800" height="600"|<canvas id="canvas"|' \
pages/nl_sample_${sample}.html
;;
esac
echo " <script src=\"nl_sample_${sample}.js\"></script>" >> pages/nl_sample_${sample}.html
echo " </body>" >> pages/nl_sample_${sample}.html
echo " </html>" >> pages/nl_sample_${sample}.html
done
# Generate index page
cp ${{ github.workspace }}/nel/samples/3d/index.html pages/index.html
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v5
with:
path: pages
deploy:
needs: build
if: github.ref == 'refs/heads/core4' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5