Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions .github/workflows/CD.yml
Original file line number Diff line number Diff line change
@@ -1,30 +1,82 @@
# 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:
description: 'Skip assets publishing'
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"
binding-project: "Evergine.Bindings.Embree/Evergine.Bindings.Embree.csproj"
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:
Expand Down
16 changes: 10 additions & 6 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# Binding CI - Simple Template

name: CI

on:
Expand All @@ -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 <stddef.h> and <stdbool.h> 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 }}
Expand Down
40 changes: 30 additions & 10 deletions .github/workflows/embree-cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: ''
Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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
Expand All @@ -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/
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
61 changes: 61 additions & 0 deletions binding.yml
Original file line number Diff line number Diff line change
@@ -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.