From 771e80c02bc954961f97a029d74944b993413e20 Mon Sep 17 00:00:00 2001 From: jcant0n Date: Tue, 11 Aug 2026 15:45:54 +0200 Subject: [PATCH] Adopt the Evergine.Bindings toolbox CI and CD moved from evergine-standards@v2 to the Evergine.Bindings@v1 reusable workflows, which is where the fleet standard now lives; Meshoptimizer and the other tracked bindings are already there. Adds binding.yml, the manifest both the deterministic and the agentic workflows read. It validates against binding.schema.json. Its NOTE comments record the two hazards specific to this repository: rtcore_config.h is generated by Embree's CMake rather than fetched, and the headers cannot move without a native rebuild. CD becomes three jobs, matching Meshoptimizer. binding-resolve-upstream settles on one release, embree-cmake builds the natives at that release, and binding-tracked-cd regenerates and publishes. One place decides the revision: if the headers and the binaries each worked it out for themselves, a release published midway through a run would leave them on different versions, and that mismatch is invisible until it throws in a consumer's application. embree-cmake.yml gains workflow_call so the CD can drive it, and its artifacts now carry their final repository paths so unpacking them over the tree is the whole mapping. rtcore_config.h comes from the linux-x64 leg alone: every leg generates an identical file but the Windows one is CRLF, so letting them all contribute would flip the committed header's line endings depending on unpack order. Unlike most of the fleet, generation stays on ubuntu-latest. MeshOptimizer, RenderDoc and KTX all generate on Windows because the libclang in the CppAst package cannot resolve system headers on Linux; this generator parses against the stubs in EmbreeGen/Headers/stubs with a pinned x86_64 target, so it does not have that problem and its output is identical on any host. --- .github/workflows/CD.yml | 58 ++++++++++++++++++++++++++-- .github/workflows/CI.yml | 16 +++++--- .github/workflows/embree-cmake.yml | 40 +++++++++++++++----- README.md | 8 ++++ binding.yml | 61 ++++++++++++++++++++++++++++++ 5 files changed, 164 insertions(+), 19 deletions(-) create mode 100644 binding.yml diff --git a/.github/workflows/CD.yml b/.github/workflows/CD.yml index a2be4e2..e852b34 100644 --- a/.github/workflows/CD.yml +++ b/.github/workflows/CD.yml @@ -1,8 +1,22 @@ -# Binding Simple CD - Template +# Keeps this binding on the latest stable Embree release, headers and native libraries +# together. +# +# Three jobs rather than one, because the two halves cannot share a runner: the native +# libraries need a matrix across Linux, Windows and macOS, and the generator and packaging +# need a single machine holding the whole tree. +# +# `resolve` runs first and alone so exactly one place decides which revision this run is +# about. If the headers and the binaries each worked it out for themselves, a release +# published midway through would leave them on different versions -- and that particular +# mismatch is invisible: P/Invoke binds late, so the package compiles, passes CI, publishes, +# and throws in the consumer's application. name: CD on: + schedule: + # Monthly, in step with the rest of the fleet. + - cron: '0 2 1 * *' workflow_dispatch: inputs: skip-assets-publishing: @@ -10,11 +24,38 @@ on: required: false type: boolean default: false + force-natives: + description: 'Rebuild the native libraries even when the release has not moved' + required: false + type: boolean + default: false + force-publish: + description: 'Publish even when the generated code is unchanged' + required: false + type: boolean + default: false jobs: - cd: + resolve: if: github.event_name != 'schedule' || github.ref == 'refs/heads/main' - uses: EvergineTeam/evergine-standards/.github/workflows/binding-simple-cd.yml@v2 + uses: EvergineTeam/Evergine.Bindings/.github/workflows/binding-resolve-upstream.yml@v1 + + natives: + needs: resolve + # Four platform builds is not something to spend on a maybe, hence resolving first and + # asking. The x64 legs alone take about an hour. + if: needs.resolve.outputs.ref_moved == 'true' || inputs.force-natives + uses: ./.github/workflows/embree-cmake.yml + with: + ref: ${{ needs.resolve.outputs.resolved_ref }} + + cd: + needs: [resolve, natives] + # `natives` is skipped whenever the release has not moved, and a skipped dependency + # would otherwise take this job with it. The run still has to reach the CD so it can + # report the no-op. + if: always() && needs.resolve.result == 'success' && needs.natives.result != 'failure' && !cancelled() + uses: EvergineTeam/Evergine.Bindings/.github/workflows/binding-tracked-cd.yml@v1 with: generator-project: "EmbreeGen/EmbreeGen.csproj" generator-name: "EmbreeGen" @@ -22,9 +63,20 @@ jobs: target-framework: "net10.0" dotnet-version: "10.x" nuget-version: "6.x" + # See CI.yml: this generator parses against its own stub headers, so unlike most of + # the fleet it does not need a Windows runner. + runner-os: ubuntu-latest runtime-identifier: "linux-x64" build-configuration: "Release" revision: ${{ github.run_number }} + # Every artifact already carries its final repository path, so unpacking them over the + # tree is the whole mapping. + # + # Asked for only when the natives job actually ran. It is skipped whenever the release + # has not moved -- most runs -- and asking for artifacts that were never produced fails + # the download and takes the publish with it. + natives-artifact-pattern: ${{ needs.natives.result == 'success' && 'natives-*' || '' }} + force-publish: ${{ inputs.force-publish || false }} publish-enabled: ${{ !inputs.skip-assets-publishing }} enable-email-notifications: true secrets: diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e1bf463..420b24d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,5 +1,3 @@ -# Binding CI - Simple Template - name: CI on: @@ -17,13 +15,19 @@ on: jobs: ci: - uses: EvergineTeam/evergine-standards/.github/workflows/binding-common-ci.yml@v2 + uses: EvergineTeam/Evergine.Bindings/.github/workflows/binding-common-ci.yml@v1 with: - generator-project: "EmbreeGen/EmbreeGen.csproj" # Path to the generator .csproj - generator-name: "EmbreeGen" # Generator executable name - binding-project: "Evergine.Bindings.Embree/Evergine.Bindings.Embree.csproj" # Path to the binding .csproj + generator-project: "EmbreeGen/EmbreeGen.csproj" + generator-name: "EmbreeGen" + binding-project: "Evergine.Bindings.Embree/Evergine.Bindings.Embree.csproj" target-framework: "net10.0" dotnet-version: "10.x" + # Generation runs on Linux, which most of the fleet cannot do: the libclang in the + # CppAst package carries no system include paths and none of its own builtin headers, + # so and fail to resolve there. This generator sidesteps that + # by parsing against the stubs in EmbreeGen/Headers/stubs with a pinned x86_64 target, + # which also makes its output identical on any host. + runner-os: ubuntu-latest runtime-identifier: "linux-x64" build-configuration: "Release" nuget-artifacts: ${{ inputs.publish-artifacts || false }} diff --git a/.github/workflows/embree-cmake.yml b/.github/workflows/embree-cmake.yml index b6cc2b2..c812c02 100644 --- a/.github/workflows/embree-cmake.yml +++ b/.github/workflows/embree-cmake.yml @@ -12,15 +12,24 @@ name: Build Embree Libraries on: + # Called by CD.yml with the release that `binding-resolve-upstream` settled on, so the + # binaries and the headers committed in the same run come from one revision of Embree. + workflow_call: + inputs: + ref: + description: 'Embree tag to build' + required: false + type: string + default: 'v4.4.1' workflow_dispatch: inputs: - embree-ref: + ref: description: 'Embree tag to build' required: false type: string default: 'v4.4.1' only-rid: - description: 'Build a single RID (e.g. win-arm64). Empty builds all five.' + description: 'Build a single RID (e.g. linux-arm64). Empty builds all four.' required: false type: string default: '' @@ -79,7 +88,7 @@ jobs: uses: actions/checkout@v4 with: repository: RenderKit/embree - ref: ${{ inputs.embree-ref || 'v4.4.1' }} + ref: ${{ inputs.ref || 'v4.4.1' }} - name: Install build dependencies (Linux) if: (inputs.only-rid == '' || inputs.only-rid == matrix.rid) && startsWith(matrix.os, 'ubuntu') @@ -121,6 +130,9 @@ jobs: if: inputs.only-rid == '' || inputs.only-rid == matrix.rid run: cmake --build build --config Release --parallel ${{ matrix.parallel }} + # Each artifact is laid out with the paths it will occupy in the repository, so the CD + # unpacks it over the tree and that is the whole mapping. + # # On Linux and macOS CMake emits versioned names behind symlinks # (libembree4.so -> libembree4.so.4 -> libembree4.so.4.4.1). Symlinks do not survive # NuGet packaging, so the real file is copied out under the plain name that @@ -130,26 +142,34 @@ jobs: shell: bash run: | set -euo pipefail - mkdir -p out + native="out/Evergine.Bindings.Embree/runtimes/${{ matrix.rid }}/native" + mkdir -p "$native" case "${{ matrix.rid }}" in win-*) - cp "$(find build -name 'embree4.dll' -print -quit)" out/embree4.dll + cp "$(find build -name 'embree4.dll' -print -quit)" "$native/embree4.dll" ;; linux-*) - cp "$(readlink -f "$(find build -name 'libembree4.so*' -print -quit)")" out/libembree4.so + cp "$(readlink -f "$(find build -name 'libembree4.so*' -print -quit)")" "$native/libembree4.so" ;; osx-*) - cp "$(readlink -f "$(find build -name 'libembree4*.dylib' -print -quit)")" out/libembree4.dylib + cp "$(readlink -f "$(find build -name 'libembree4*.dylib' -print -quit)")" "$native/libembree4.dylib" ;; esac - cp include/embree4/rtcore_config.h out/ - ls -l out + # rtcore_config.h comes from one leg only. Every leg generates a byte-identical + # file, but the Windows one is written with CRLF, so letting them all contribute + # would make the committed header flip line endings depending on unpack order. + if [ "${{ matrix.rid }}" = "linux-x64" ]; then + mkdir -p out/EmbreeGen/Headers/embree4 + cp include/embree4/rtcore_config.h out/EmbreeGen/Headers/embree4/rtcore_config.h + fi + + find out -type f - name: Upload if: inputs.only-rid == '' || inputs.only-rid == matrix.rid uses: actions/upload-artifact@v4 with: - name: embree-${{ matrix.rid }} + name: natives-${{ matrix.rid }} path: out/ diff --git a/README.md b/README.md index b3a4a96..f42a2a2 100644 --- a/README.md +++ b/README.md @@ -70,11 +70,19 @@ the 1-byte C `_Bool`. ## Repository layout ``` +binding.yml Manifest read by the Evergine.Bindings toolbox EmbreeGen/ Generator console app (CppAst); vendored headers in Headers/ Evergine.Bindings.Embree/ The NuGet package: Generated/ bindings + runtimes/ natives HelloEmbree/ Sample: CPU ray tracer drawn with the Evergine low-level API ``` +CI and CD are the shared workflows from +[EvergineTeam/Evergine.Bindings](https://github.com/EvergineTeam/Evergine.Bindings), and +[`binding.yml`](binding.yml) is what tells them where the upstream headers come from, which +release is tracked, and which paths are generated output. Read its `NOTE` comments before +changing how the headers or the native binaries are refreshed — they record why the two cannot +move independently. + [HelloEmbree](HelloEmbree/README.md) traces a small scene on the CPU with `rtcIntersect1`/`rtcOccluded1`, uploads the result to a texture every frame and blits it to a DX11 swapchain hosted in a Windows Forms window. It also has a `--bench` mode that reports the diff --git a/binding.yml b/binding.yml new file mode 100644 index 0000000..17f0209 --- /dev/null +++ b/binding.yml @@ -0,0 +1,61 @@ +# Binding manifest — see https://github.com/EvergineTeam/Evergine.Bindings +# Schema: https://github.com/EvergineTeam/Evergine.Bindings/blob/main/binding.schema.json +toolbox: 1.29.0 + +package: + id: Evergine.Bindings.Embree + project: Evergine.Bindings.Embree/Evergine.Bindings.Embree.csproj + target-framework: net10.0 + # linux-x64, unlike most bindings in the fleet. The CppAst generator parses against the + # stub system headers in EmbreeGen/Headers/stubs against a pinned x86_64 target, so it + # does not depend on the libclang include paths that force MeshOptimizer, RenderDoc and + # KTX to generate on Windows. Do not "fix" this by moving it to windows-latest. + runtime-identifier: linux-x64 + +upstream: + kind: git-tree + language: cpp + project: https://github.com/RenderKit/embree + version-from: git-release + + release: + # Tagged releases, not main. This binding ships native binaries, and the headers and + # those binaries have to come from one revision of Embree; a branch would move the + # headers underneath libraries built from something else. `current` is rewritten by + # binding-fetch-upstream when it brings in a newer release, which makes this the one + # place the version lives. + track: stable + current: v4.4.1 + + sources: + - repo: RenderKit/embree + remote-path: include/embree4 + path: EmbreeGen/Headers/embree4 + format: c-header + +generator: + project: EmbreeGen/EmbreeGen.csproj + name: EmbreeGen + output: + - Evergine.Bindings.Embree/Generated + +# NOTE — rtcore_config.h is not part of the upstream sources above. Embree's CMake generates +# it, and it decides the layout of several public structs: RTC_MAX_INSTANCE_LEVEL_COUNT sizes +# the instID arrays in RTCHit and RTCRayQueryContext, RTC_GEOMETRY_INSTANCE_ARRAY adds the +# instPrimID ones, and RTC_MIN_WIDTH adds a field to RTCIntersectArguments. The copy vendored +# in EmbreeGen/Headers/embree4/ therefore has to come from the same build as the binaries, and +# it does: embree-cmake.yml emits it alongside them. Refreshing the header tree leaves it +# alone, because the git-tree adapter only writes the files it fetched. +# +# NOTE — this package ships native binaries under Evergine.Bindings.Embree/runtimes. Headers +# and binaries must come from the same upstream revision, so a header bump is only valid +# together with a native rebuild. That rebuild is not optional and CI would not catch it being +# skipped: P/Invoke binds late, so a mismatched package compiles, passes CI, publishes, and +# then throws in the consumer's application. The CD keeps the two in step — embree-cmake.yml +# rebuilds every platform at the resolved release before the headers are committed. Do not +# bump the header by hand. +# +# NOTE — there is no win-arm64. Embree 4.4.1 does not build for Windows on ARM with either +# toolset available on the runner; see the tracking issue. Do not add the RID back without a +# green build, and do not ship a binary produced by forcing flags past Embree's CMake: a wrong +# SIMD path in a ray tracing kernel returns wrong intersections rather than crashing.