OxC3 (0xC3, Oxsomi core 3) is a cross-platform C11 framework for applications, tools and games. It is the successor to O(x)somi core v2/v1, merging ostlc (standard template library), owc (window core) and ogc (graphics core) into one coherent, layered codebase. It is written in C so it stays fast to build, easy to parse for reflection/codegen, and straightforward to wrap from other languages (bindings or a future VM), with a C++20 convenience layer on top of that C API rather than in place of it (23 headers under include/, most of them generated by generate_hpp.py from the C headers they wrap).
For per-module maturity, see STATUS.md. For how the modules fit together (and the error-handling idiom used everywhere), see docs/ARCHITECTURE.md.
The matrix above builds each platform with its usual toolchain (MSVC on Windows, AppleClang on OS X, GCC on Linux, NDK clang on Android). A second toolchain is covered where it's a genuinely different compiler on the same platform code, since that's what catches portability problems rather than OS differences:
| Platform | Default | Alternate |
|---|---|---|
| Windows | MSVC | clang-cl |
| Linux | GCC | clang |
| OS X | AppleClang | N/A, gcc there is a clang symlink and would retest the same compiler |
| Android | NDK clang | N/A |
The alternate toolchain is built across the same axes as the default one rather than in a single configuration, since a compiler difference tends to show up in one corner (arm64 intrinsics, a dynamic link's symbol visibility) rather than everywhere at once:
| Alternate | x64 -> Vulkan | x64 -> Native API | x64 dynamic (Vk + Native) | ARM -> Vulkan | ARM -> Native API | ARM dynamic (Vk + Native) |
|---|---|---|---|---|---|---|
| Windows (clang-cl) | D3D12: |
D3D12: |
||||
| Linux (clang) | N/A | N/A |
MinGW GCC isn't supported on Windows: it's a different CRT and ABI, so it would be a new target rather than a new compiler, and the prebuilt dependencies (DXC among them) are MSVC.
These run on a nightly schedule rather than per push, and instrument OxC3, the spirv_reflect fork and DXC. A green badge means that configuration built and ran the suite clean under the sanitizers.
| Nightly (clang, ASan + UBSan) | x64 | ARM64 |
|---|---|---|
| Windows (clang-cl) | N/A, the runner's LLVM ships no aarch64 sanitizer runtime | |
| Linux (clang) | ||
| Mac OS X (clang) |
Windows is x64 only. DXC's UBSan has enum excluded everywhere: its reflection uses an out-of-range
_D3D_SHADER_VARIABLE_TYPE sentinel that -fsanitize=enum flags, which is DXC's design rather than our
bug; the rest of UBSan stays on.
- OxC3_types, the foundation, split in three:
- base: fixed-width types,
Error+ stacktraces,Allocatorinterface,Bufferviews with const/ref safety,CharStringbasics, atomics,SpinLock,Thread,Time(ISO-8601, cycle counters), fixed point, endianness,ETypeIdreflection ids. Allocation-free. - math: SIMD vectors (
F32x4,I32x4,F32x2,I32x2) with SSE/NEON/scalar backends, quaternions, arbitrary IEEE-754 float format casts (F16/BF16/TF19/PXR24/FP24/…), checked numeric casts, bit packing, PRNG helpers. - container:
TList(T)typed lists over oneGenericListcore, owning strings + full UTF-8/16/32 interop,Archive,BigInt/U128,AllocationBuffer(GPU-style suballocator),RefPtr, streams,JobQueue,Log,Bufferhashing (SHA256, CRC32C, MD5), AES256/128-GCM encryption (AES-NI/VAES/AVX512 and ARM crypto paths) and CSPRNG. - Docs: docs/types.md.
- base: fixed-width types,
- OxC3_formats_*, file format read/write, all input-validated:
- Standard: BMP (BGRA8), DDS (modern DXGI subset), WAV.
- Oxsomi (oiXX family, sharing encryption/endianness conventions): oiCA archives (zip-like, AES-GCM capable, streamable), oiDL data lists, oiSH shader containers (DXIL+SPIR-V + reflection), oiSB shader buffer layouts, oiBC bytecode (spec draft; not implemented yet).
- Docs: docs/formats.md.
- OxC3_platforms, everything OS-dependent: default/tracked allocators (leak reports with per-allocation stacktraces in debug), sandboxed file IO (app/working dir only) +
FileStream, virtual file system for assets embedded in the exe/apk (oiCA-backed), windows (physical + virtual), monitors, keyboard/mouse multi-device input, dynamic library loading. Backends: Windows, Linux (Wayland), OS X (partial), Android.- Docs: docs/platforms.md.
- OxC3_graphics, modern-only GPU abstraction over Vulkan and Direct3D12 (Metal/WebGPU reserved): refcounted objects, virtual command lists with automatic resource transitions, descriptor heap/layout/table model, bindless, compute/graphics/raytracing pipelines, BLAS/TLAS, mesh shaders, VRS, swapchains. Vulkan and D3D12 can be loaded side-by-side via dynamic linking and selected at runtime.
- Docs: docs/graphics_api.md, minimum spec: docs/graphics_spec.md.
- OxC3_shader_compiler, statically linked DXC wrapper: HLSL → DXIL and/or SPIR-V on all target platforms, multithreaded batch compilation,
[[oxc::...]]annotations (stage/model/vendor/extensions/uniforms/binary masks), reflection + include tracking for incremental builds and hot reload, output to oiSH. - OxC3(CLI), the
OxC3command line tool: oiCA/oiDL ↔ raw conversion, encrypt/decrypt, file inspection, hashing (sha256/crc32c/md5), random generation, profiling (float casts, CSPRNG, hashes, AES), multithreaded shader compilation, graphics device enumeration, and the asset packager used by the CMake integration.- Docs: docs/OxC3_tool.md.
- Python 3.8.10+ and Conan 2.7.1+ (avoids huge build times for DXC/LLVM/SPIRV deps).
- CMake 3.13+.
- A C11/C++ compiler (MSVC, clang, gcc); see the toolchain table above for what's covered per platform, and
-compilerunder build.py syntax to pick one. The library itself is C; C++ is used to interface with C++ deps such as DXC, and for the.hppconvenience layer that samples, tools and most of the test suites are written against. - Windows on ARM64: ARMASM64 (install the ARM64 build tools via the VS installer) when using MSVC.
- OS X: If using the Vulkan SDK with bindless, export
MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=1and setVULKAN_SDK(e.g. in~/.bash_profile). - Linux: Wayland is the window backend,
sudo apt install libwayland-dev libxkbcommon-dev -y(plus wayland-scanner). For audio deps:sudo apt install libasound2-dev libpipewire-0.3-dev -y. For the windowed functional tests:sudo apt install xdotool -y. X11-only sessions are currently unsupported for windowing. - Android: NDK installed with
ANDROID_NDKset (plusANDROID_SDK+ a JDK when building an apk); Android 10 (API 29)+ on device (Vulkan 1.1+). Cross compiles from Windows, Linux and macOS. Ninja or make can drive the build (Ninja required for Debug builds due to the Vulkan validation layers). Since the packaging tool is built for the host too, the host's own prerequisites (above) apply as well. See Android SDK setup.
git clone --recurse-submodules -j8 https://github.com/Oxsomi/core3
cd core3
python build.py -mode Release -tests Truebuild.py syntax:
python build.py -mode [Release/Debug/MinSizeRel/RelWithDebInfo/(empty; Windows multi config)] -simd [True/False] -tests [True/False] -dynamic_linking [True/False]simd: use SIMD (vectors, AES, SHA, CRC). Keep on (default); off exists for porting/fallback validation.tests: build + run the unit tests. Each suite runs right after it is built, and only when something it was built from changed, so a rebuild that changed nothing runs nothing. What a suite counts as an input includes the data it reads (goldens, HLSL corpora, packaged shaders), not just the sources it links.ctest: run the whole suite through ctest at the end of the build instead, which is what CI does so a failure lands as one report. Turning it on turns the per-suite runs off, so nothing runs twice.generator:ninjabuilds with Ninja instead of the platform default (Visual Studio on Windows). Ninja is what emitscompile_commands.json, so it is the one to use for clangd or VS Code IntelliSense. It gets its own build tree, since a CMake cache belongs to the generator that made it as much as to the compiler. On Windows it additionally needs the MSVC 14.4x toolset installed, which the Visual Studio generator does not: only Ninja runsvcvars, and conan asks it for the toolset the profiles pin throughcompiler.runtime_version. Building without it is a hard error naming both versions rather than avcvarsfailure that says nothing about profiles.dynamic_linking: desktop-only; allows multiple graphics APIs in one process.compiler: toolchain to build with, defaulting to the platform's usual one (msvcon Windows,clangon OS X,gccon Linux).-compiler clangon Windows means clang-cl, which keeps MSVC's ABI and CRT. A non-default toolchain gets its own build tree, since a CMake cache belongs to the compiler that configured it, and its own dependencies, since conan derives package ids from the compiler; expect the first build with one to rebuild DXC.asan/ubsan: build with AddressSanitizer / UndefinedBehaviorSanitizer. Diagnostic only, and supported on clang and gcc alone; asking for either under MSVC is a hard configure error rather than a silent no-op, since a sanitizer that quietly does nothing reads as proof that nothing is wrong. Use-mode RelWithDebInfo: they want optimized code with frame pointers and symbols, not a Debug build. On Windows (clang-cl) UBSan traps into OxC3's crash handler instead of relying on its own reporting runtime, which is the least dependable part of UBSan there.
- Extra flags via
-o flag=Bool:forceVulkan: prefer Vulkan over the native API (e.g. over D3D12 on Windows). Off by default.enableOxC3CLI: build the OxC3 CLI. On by default.forceFloatFallback: software half↔float casts. Off by default.enableShaderCompiler: include the shader compiler (longer build). On by default.dynamicLinkingShaderCompiler: build the shader compiler as a shared library. On by default on Windows/Linux/OS X; coerced off on Android and whenenableShaderCompileris off. Independent ofdynamicLinkingGraphics, which exists for a different reason (runtime Vulkan/D3D12 selection). DXC is statically linked, so every executable touching the shader compiler otherwise carries ~28 MB of it; shared, that bulk exists once and links once, so builds are faster too, and it can be shipped or omitted separately. It also puts the compiler behind a module boundary, so a different backend could be swapped in without relinking consumers as long as it keeps the ABI. Callers must callCompiler_setPlatform(Platform_instance)once afterPlatform_create(an inline no-op in static builds), since the module has its ownPlatform_instance.debugShaderCompiler: build/consume DXC and SPIRV-Reflect in the current mode instead of Release. Off by default, so a Debug build doesn't pay for a Debug DXC; those two dominate a from-scratch build and are rarely what you're stepping into. Also available as-debug_shader_compiler True.cliGraphics: allow CLI operations that need OxC3_graphics; turn off for headless or to avoid shipping graphics dlls.
Cross compiled from Windows, Linux or macOS; the host half goes through the same code as build.py
(see build_common.py), so the same conan profiles apply.
python3 build_android.py -mode DebugAn android build can't run its own packager: the shader compiler is off there, and the resulting
binaries wouldn't be runnable on the build machine anyway. So it tool_requires a host OxC3 that has
OxC3_package in it, which add_virtual_files() then finds via find_program. build_android.py
builds and exports that host package from your working tree before cross compiling. The option set it's
built with lives in HOST_TOOL_OPTIONS in conanfile.py and is deliberately fixed, so one host package
serves every android configuration.
-api 31(default): target API level;-archdefaults to arm64 and x64;-simd Trueby default;-generatordefaults to "MinGW Makefiles" on Windows and "Unix Makefiles" elsewhere (Ninja is a good choice on every host, and is required for Debug builds because of the Vulkan validation layers).--host_package_onlybuilds just the hostOxC3_packageand stops;--skip_host_packageassumes it's already in the conan cache. CI uses the pair so the three android configurations share one host build.--apk -package net.osomi.test -version 0.1.0 -lib myLibName -name "My test app"builds an APK (same arch/mode/api/simd/generator settings must match prebuilt binaries when combined with--skip_build).-packages <dir>(repeatable) adds another folder of oiCA archives to the apk, for when the app that's being packaged has virtual files of its own next to OxC3's.--signsigns the APK: provide-keystore(and optionally-keystore_password), or haveJAVA_HOMEset to create a temporary keystore.--runinstalls and runs on a connected device in developer mode (requires-packageand-libif no apk step).-ip 192.168.2.93runs over the network instead of usb (port defaults to 5555, givehost:portin full for android's Wireless debugging, which picks its own). Enable it first withadb tcpip 5555over usb. Every adb call then goes through-s, which is needed anyway once a usb and a wireless transport are both attached, since adb otherwise refuses with "more than one device/emulator".-category game(default) sets the app category;--installexports the android package so a dependent project canrequires()it;--skip_buildreuses prebuilt binaries.
Only ANDROID_NDK is needed to build the libraries; ANDROID_SDK is additionally required for --apk
and --run (aapt/d8/zipalign/adb).
CMake presets are checked in, so cmake --list-presets shows every configuration this repo builds and VS
Code's CMake Tools picks them up on its own. Windows, Linux, macOS and Android are all covered, host
conditions hiding the ones that cannot apply (Android has none, since it is a cross build that runs from
any host).
They point at the same trees the build scripts produce rather than a separate one, so the two stay interchangeable; run the script once for a configuration and the matching preset reuses what it made. Until then configuring one is an error naming the exact command for that tree, since the dependencies come from conan and do not exist yet.
Only the Windows presets name a generator. Everywhere else the tree's own cache decides, because conan picks that from the profile and pinning a different one here would be a generator mismatch rather than a second tree.
python build.py -mode Debug -compiler clang -generator ninja -tests True # makes the tree
cmake --build --preset windows-clang-ninja-debug # and reuses itFor code intelligence, build once with -generator ninja. That writes compile_commands.json into the build
tree and copies it beside the sources, which is where clangd and the C/C++ extension look. The Visual Studio
generator does not emit one at all; it feeds IntelliSense from the .vcxproj files instead, so a VS-generated
tree needs no extra setup and gets no compile_commands.json.
.vscode/settings.json wires both up already. Nothing has to be configured by hand.
A preset builds into build/ide/<preset> rather than into the tree the build script made, and reads only the
toolchain out of it. An IDE takes ownership of the build folder it configures and clears what it does not
recognise, which includes the generators conan wrote, so sharing one directory meant "Open with Visual Studio"
could leave the next build.py with no toolchain. Owning separate directories is what makes both flows safe
at once; the cost is that a preset compiles its own copy rather than reusing the script's objects.
OxC3IdeTree keeps that copy's binaries inside the preset's tree too. Both trees describe the same
configuration, so otherwise they would write the same paths under build/<config>/<platform>/<arch>/.
Packages are shared on purpose: those come from the source root and writing one is idempotent.
Setting a configuration up from inside the editor is Tasks: Run Task -> OxC3: set up a build tree, which lists every configuration the presets cover and runs the matching script. The entries are spelled exactly as the configure error prints them, so the error and the fix name the same command.
The build target is separate from the preset in VS Code: the status bar shows it ([all] by default) and
CMake: Set Build Target changes it, while CMake: Select Configure Preset changes the configuration.
cmake.defaultBuildTarget pins one, or a build preset can name a targets array.
Android has no exec, so the per-suite executables that run on the host don't exist there. -tests True
builds OxC3_atest instead: one .so with every suite, loaded by a NativeActivity
(see src/test/android). One command builds, packages, installs, launches and reports:
python build_android.py -mode Release -arch arm64 -generator Ninja -tests True --apk --sign --run \
-keystore_password <pw> -package net.osomi.oxc3test -version 0.1.0 -lib OxC3_atest -name "OxC3 tests"--run streams the device log and exits non-zero if any suite fails; it waits for the OXC3_TEST_END
line the runner emits, since am start gives back no exit code (-test_timeout, default 600s, bounds
the wait). Test apks are marked android:exported in every mode, because a non-exported activity can't
be launched by am start at all.
Add --interactive to also run the functional suites (window/input/audio); they need a human watching
the device, so they're skipped otherwise. That sets debug.oxc3.interactive, which you can also flip by
hand with adb shell setprop. Interactive runs aren't timed out.
The functional suites want a keyboard and mouse, which a phone doesn't have. Pairing them over Bluetooth is the least painful route: it needs no USB OTG, no powered hub, and leaves the port free. Otherwise run over the network so the usb port is available for a hub, and note android blocks new usb peripherals while the screen is locked:
adb tcpip 5555 # over usb, once; doesn't survive a reboot
python build_android.py -mode Release -arch arm64 -generator Ninja -tests True --apk --sign --run --interactive \
-ip 192.168.2.93 -keystore_password <pw> \
-package net.osomi.oxc3test -version 0.1.0 -lib OxC3_atest -name "OxC3 tests"Every suite is bundled except shader_compiler, since DXC isn't built for android.
Android Studio is not needed. Four standalone downloads, ~400 MB total:
- JDK 11+ (Temurin).
sdkmanageris itself a Java program, so this comes first. SetJAVA_HOME;--signcallskeytoolthrough it. - Command line tools only. Unzip so
you end up with
<sdk>/cmdline-tools/latest/bin/sdkmanager. The zip's own top folder is namedcmdline-tools, so the inner one has to be moved intolatest/, sdkmanager refuses to run with "Could not determine SDK root" if it isn't in alatest/version subdirectory. - SDK packages.
<sdk>is the parent directory, and becomesANDROID_SDK:Add platform-tools (adb) if you wantsdkmanager --licenses sdkmanager "platforms;android-31" "build-tools;30.0.3"
--run; it unzips straight into<sdk>/platform-tools. - NDK, a plain zip, unpack anywhere and point
ANDROID_NDKat it. r27 is what CI uses.
Two things that will bite you otherwise:
- build-tools version matters. The apk step uses
aapt(v1, not aapt2), which newer build-tools no longer ship, andbuildToolsDir()always picks the newest installed. Keep anaapt-bearing version (30.0.3) as the newest one you have. Itsd8warns that API 31 isn't supported and dexes anyway; that's cosmetic, it only affects desugaring, notminSdkVersion. - The legacy
toolspackage is not required, despite build-tools'd8wrapper looking forfind_javainside it.build_android.pyrunsd8.jardirectly to avoid that (the wrapper otherwise exits 0 having produced noclasses.dex, and the build fails much later in aapt).
Expected layout:
<sdk>/cmdline-tools/latest/bin/sdkmanager
<sdk>/build-tools/30.0.3/{aapt,d8,zipalign}
<sdk>/platforms/android-31/android.jar
<sdk>/platform-tools/adb # only for --run/--install
Graphics apps must embed the OxC3_graphics oiCA file(s) (built-in shaders, fonts, future LUTs):
# Optional: configure_icon(OxC3 "${CMAKE_CURRENT_SOURCE_DIR}/res/logo.ico")
add_virtual_dependencies_external(TARGET Target DEPENDENCIES OxC3)
apply_dependencies(Target)| Platform | Status |
|---|---|
| Windows | Full |
| Linux | Full (Wayland sessions) |
| SteamOS | Full (a few gamescope bugs left) |
| OS X | Partial, no window support or input yet |
| Android | Okay, close to full; missing render passes and bindful |
| Xbox UWP / iOS | Planned |
| Web | Not for a while |
| GDK Xbox / Playstation / Switch | Not planned |
| ISA | Status |
|---|---|
| x64 | Decent, full on Windows; SSE transcendentals (sin/exp/…) not yet efficient elsewhere |
| arm64 | Okay, same transcendental caveat |
| scalar fallback | Full, used to bring up new platforms before their SIMD backend exists |
| risc-v / wasm | None / not yet |
64-bit CPUs only. The SIMD build requires SSE4.2/AES/PCLMULQDQ/BMI1+2/F16C/AVX/FMA (Intel Haswell 2013 / AMD Zen and up), and on arm64 the ARMv8 crypto (AES, PMULL) and CRC extensions; Intel Gen 11+ / AMD Zen are recommended for hardware SHA256. Platform_checkCPUSupport enforces both baselines at startup, including that the OS actually enabled AVX state, so an unsupported CPU is told why instead of faulting somewhere arbitrary. The SIMD-less build exists for porting, emulation and debugging, it is markedly slower (no AES/SHA/CRC/SIMD intrinsics).
A full build typically ships:
D3D12:
D3D12/*.dll, D3D12/*.pdb
(debug only) d3d10warp.dll
(optional) OxC3.exe
yourExecutable.exe
Vulkan:
(optional) OxC3 / OxC3.exe
yourExecutable(.exe/.apk/.ipa/…)
Dynamic linking additionally:
OxC3_graphics_d3d12.dll (Windows) and/or OxC3_graphics_vk(.dll/.so)
The shader compiler needs no extra binaries (DXC is statically linked). d3d10warp.dll is testing-only. The OxC3 CLI is optional at runtime but handy (shader compilation, OxC3 graphics devices capability checks, and more). Dynamic linking enables per-API dlls: runtime API switching, side-by-side Vulkan+D3D12 (some extensions are only reachable on one), and drop-in dll updates; static linking gives easier distribution and lower call overhead.
- OxC3_types: none (OS only).
- OxC3_formats / OxC3_platforms: OxC3_types only. (Note: platforms depends on formats for its oiCA-backed virtual file system.)
- OxC3_graphics: Vulkan and/or D3D12 (+ NVAPI, AMD_AGS, AgilitySDK; optional WARP).
- OxC3_shader_compiler: DXC (LLVM/clang, DirectX-Headers, SPIRV-Headers/Tools), SPIRV-Reflect; SPIRV-Cross planned for MSL.
- OxC3(CLI): platforms + formats (+ optional shader_compiler, graphics).
See FOR_CONTRIBUTORS.md (code style: docs/code_style.md). External PRs require signing a CLA before merge.
This repository is dual-licensed:
- GPL3 open source license (see LICENSE). Note that GPL3 is a strong copyleft license: distributing a product that links OxC3 (statically or dynamically) requires that product to be GPL3 as well, with source available.
- Commercial license — for closed-source use, contact us at contact@osomi.net.