Skip to content
eyalamirmusicPublic

About

C++ Cross platform graphics, networking, windowing and other utilities

Resources

Stars

23 stars

Watchers

0 watching

Forks

Repository files navigation

eacp

A cross-platform C++20 framework that abstracts native OS primitives behind a single, modern API. eacp lets you write desktop and mobile applications once and have them target the platform's first-class primitives directly. The heavy lifting stays with the OS: there is no bundled renderer, no bundled widget toolkit and no VM. That reaches the GPU too: a shader is a C++ struct, emitted as Metal, HLSL and GLSL from one source, and the pipeline around it is one API over all three backends.

What it abstracts

eacp wraps the platform's native building blocks rather than reimplementing them, so apps inherit the look, feel, and performance of the host OS:

  • Application lifecycle — a templated Apps::run<T>() entry point that wires up the platform's main event loop.
  • Command-line tools — Apps::runConsole runs a tool's body inside the loop and turns an escaping exception into an exit code, Args::Parser binds options straight to your variables and writes the help text, and Console::print / printError write plain lines to stdout and stderr.
  • Event loops & threading — EventLoop, Timer, DisplayLink, and callAsync on top of CFRunLoop / NSTimer / CADisplayLink, their Windows equivalents, and on Linux one epoll descriptor a plugin host's own loop can pump (getEventLoopFd, pumpEventLoop).
  • Files — FilePath carries a path as UTF-8 text with / separators on every platform (nativeString and fromNative convert to and from the platform's own form, \ on Windows), Files::readFile / writeFile / writeFileAtomically move whole files, File and MemoryMappedFile read big ones in pieces, and Zip reads and writes archives, extracting one entry or all of them straight to disk a chunk at a time, with a progress callback that can cancel, so a multi-gigabyte archive unpacks in bounded memory. A directory is walked with Files::forEachEntry (a visitor that can prune a subtree or stop early, with unreadable entries reported to a callback) or collected with listDirectory / listFiles, recursively or not, hidden entries and symlinks by choice, filtered by a wildcard such as "*.wav; *.flac". copyFile, copyDirectory, moveFile, removeFile, appendFile and writeFileAt change files and report failure as false; FileWriter streams into one, TemporaryFile deletes its path when it goes out of scope, and legalFileName / nonexistentSibling name new ones. Files::openWithSystem hands a file to its default application or opens a folder in Finder, Explorer or the desktop's file manager, and revealInFileManager shows an item there selected (Linux opens the folder holding it); both answer false on iOS and Android.
  • Graphics — Window, View, Path, Font, and a Context drawing abstraction backed by Core Graphics / CoreText on Apple platforms and the native Windows graphics stack. primaryDisplay() reports the screen's frame and work area in points, so an app can pick a first window size that fits; View::getWindow() lets a view reach the window it is in rather than be handed it. setMouseCursor takes one of the named shapes or a MouseCursor made from an Image, a hotspot and how many of its pixels make a point, so a 32-pixel brush can be an 8-point pointer that stays sharp on a Retina screen (macOS and Windows; the arrow elsewhere). Files dragged in from Finder, Explorer or a DAW's browser reach a view that calls setAcceptsFileDrops() as fileDragEnter / fileDragMove / fileDragExit / filesDropped with UTF-8 paths, and a UI::ComponentHost hands them to the deepest UI::FileDropTarget component under the pointer that wants them (macOS and Windows). Going the other way, startFileDrag(paths, options) from a view's or a component's mouse handler drags files out to Finder, Explorer or a DAW, optionally letting the receiver move them and reporting Cancelled, Copied or Moved once it ends (macOS and Windows). A trackpad pinch reaches the view under the pointer as mouseMagnify, each step a magnifyScale to multiply the zoom by (1.25 is a quarter larger) with the gesture's Began / Changed / Ended in scrollPhase, and a UI::ComponentHost hands it to the component under the pointer and up the tree until a Component::mouseMagnify returns true, as it does the wheel (macOS, and touch-screen zoom gestures on Windows). On a touch screen a view that calls setHandlesTouchEvents() gets every finger as its own TouchEvent (touchBegan / touchMoved / touchEnded, one id per finger, held by the view it came down on, with its contact radius in points), and any other view gets the first finger as the mouse, so widgets written for a mouse work unchanged. View::getSafeAreaInsets() is what the status bar, a notch or the home indicator covers, with safeAreaInsetsChanged() when it moves; both stay zero and silent on desktop windows. Apps/UI/TouchDemo draws both. A UI::ComponentHost lays its root inside the safe area by default (setRespectsSafeArea(false) gives it the whole view), and ScrollPanel and ListBox scroll by finger, coasting on when flung, while a mouse drag over them means what it always did.
  • Game input — GameInput is keyboard and mouse state for a game loop, polled once a frame beside the View callbacks that UI and text entry keep using: snapshot() returns a frame that answers isDown, wasPressed, wasReleased and mouseDelta, with every event timestamped on the clock FrameTime uses. On Apple platforms the events come from the GameController framework off the main thread, so they are captured while the app is still drawing; elsewhere they come from the window's own events. Input counts only while the window has key focus, and losing it releases every key. frame.gamepads() lists the connected controllers — buttons named by position, sticks and triggers as raw axes — from GameController on Apple and XInput on Windows, so an Xbox controller on USB or Bluetooth works on both and a Switch Pro Controller on macOS. Apps/GPU/Maze is a first-person maze driven by it, and Apps/UI/GamepadDemo is every button and axis of a controller doing something on screen and announcing itself.
  • GPU — GPUView, frames, passes, buffers, textures and pipelines over Metal and D3D12, plus compute — and a shader EDSL that makes a shader a C++ struct rather than a string literal per backend. The same compute kernel also runs on the CPU, on the calling thread and without allocating, where no device came up or an audio callback needs it now. See Lib/eacp/GPU/README.md.
  • ML — tensor-level compute as a Graph of whole-tensor ops (linear, matmul, softmax, layerNorm, conv, gather, attention with a causal or run-time mask, argmax, and apply for anything elementwise, written over the shader EDSL's own Float), written out as an .mlpackage that Core ML compiles and runs on the CPU, the GPU or the Apple Neural Engine. Model caches the compile, predicts blocking or as a Threads::Async, reports where each op was placed, and its MultiArray copies to and from a GPU::Buffer. The graph builder and writers build everywhere; only the runner is Apple. See Lib/eacp/ML/README.md.
  • Widgets & menus — native text inputs, menus, and embedded views.
  • WebView — embed a system web view (WKWebView on Apple, WebView2 on Windows) with support for popups and new-window requests.
  • Networking — an HTTP::Request / HTTP::Response API plus an HTTPServer, a WebSocket::Connection client and WebSocket::Server, TCP sockets, IPC channels and an RPC layer over both — Apps/Network/WebSocketDemo runs both WebSocket ends in one process. Backed by NSURLSession and Network.framework on Apple platforms, WinHTTP on Windows, libcurl on Linux and Java's own HttpURLConnection and sockets on Android. OnlineResource fetches a file an app needs into its own Application Support folder once, revalidates it against the server's ETag on later runs, and unpacks a zip; UI::OnlineResourceMonitor shows every such fetch as a list with progress bars. Apps/Console/OnlineResource and Apps/UI/ResourceMonitor are the two examples.
  • SVG — SVG documents parsed (SVG::parseXML) and drawn by SVG::SVGComponent, a UI component, or rendered off-screen to an image (SVG::renderToImage), the same on every platform: linear and radial gradients, clip paths, element and group opacity, <use>/<symbol>, nested <svg>, preserveAspectRatio, dashes, the style attribute, text, and <image> from a base64 PNG or JPEG data URI, drawn directly or as a <pattern> fill. Not <mask>, <style> selectors or filters.
  • Processes & plugins — launch a child process with args, env and working directory, feed its stdin and capture its output, separately or with stderr merged into stdout, and optionally watch it arrive through a callback (eacp::Processes). A whole command line can be given instead of executable and arguments: Windows hands it to CreateProcessW verbatim (so cmd /c "a.bat && b.exe" works), POSIX splits it into argv the way JUCE's StringArray::fromTokens does and runs no shell. Load and unload shared libraries at runtime (DynamicLibrary). Both directions of plugin UI are covered: EmbeddedView puts a view tree of ours inside a window a host owns (an NSView, a UIView such as an AUv3's view controller hands over, an HWND or an X11 window), and NativeChildSurface is the inverse, a View in one of our layouts that hands a foreign toolkit an NSView* or a child HWND to parent its own editor into — what a plugin host passes to IPlugView::attached(). EmbedderKeyForwarder and KeyGrab carry the keys a hosted editor does not want back to the host's main window, so a DAW's shortcuts still work.
  • Text & sprites — font metrics, glyph rasterization and a GPU glyph atlas, alongside a batched textured-quad renderer for everything that draws in bulk.
  • UI — a lightweight component tier: a whole widget tree in one GPUView, drawn through the sprite and glyph batchers. UI::GPUComponent puts custom GPU rendering inside that tree: a GPUView-style render(Frame&) and update, drawn in the window's own frame and then clipped, scrolled, overlapped, faded and transformed like any other component, so a 3D viewport or a shader preview sits among the widgets rather than on top of them. Apps/UI/GPUComponents puts up to 64 of them in one window. See Lib/eacp/UI/README.md.
  • SIMD — the image hot loops run through ESIMD, a portable SIMD library with runtime backend dispatch that is also usable on its own.
  • Maths — Vec2 / Vec3 / Vec4 and a column-major Mat4 with the transform and projection builders, packed exactly as the shader types they register as, so the same value does the CPU-side geometry and crosses to the GPU as a vertex field or uniform without repacking.
  • Camera & video — capture devices and frames with a CameraView to show them, screen capture, video encoding, and decoded playback through the GPU display stack.
  • Interop helpers — RAII wrappers (Ptr<T>, CFRef<T>, AutoReleasePool) for safe Objective-C / Core Foundation interop, plus generic utilities (Pimpl, Singleton, vector helpers).

Supported platforms

The dividing line is drawing. Everything that never touches a screen — the app and threading core, processes, plugins, files, the HTTP client and server, IPC and RPC, the CPU compute interpreter, the ML graph builder — builds on Linux too, which is what makes eacp usable for a headless service as well as for a GUI. The graphics stack builds on all four platforms, because it wraps each one's own compositor instead of shipping one: Cocoa and Metal, Win32 and D3D12, UIKit, and Wayland or X11 with Vulkan.

Module macOS Windows iOS Linux
Core — lifecycle, event loops, timers, processes, plugins, files ✅ ✅ ✅ ✅
Network — HTTP client and server, WebSocket client, TCP, IPC, RPC ✅ ✅ ✅ ✅
Graphics — windows, views, widgets, menus, drawing ✅ ✅ ✅ ✅ †
GameInput — polled keyboard, mouse and gamepads for a game loop ✅ ‡ ✅ ‡ ✅ ‡ ✅
GPU / GPUWidgets — Metal, D3D12, Vulkan and the shader EDSL ✅ ✅ ✅ ✅
CpuCompute — the same compute kernels run on the CPU, no device needed ✅ ✅ ✅ ✅
Text / Sprites — glyph rasterization, atlas, batched quads ✅ ✅ ✅ ✅
UI / SVG — component tier and SVG rendering ✅ ✅ ✅ ✅ †
WebView — WKWebView and WebView2 ✅ ✅ ✅ —
Camera / CameraView — capture devices and frames ✅ ✅ ✅ —
Video / VideoView — screen capture, encode, playback ✅ ✅ — —
ML — tensor graphs compiled and run through Core ML ✅ — ✅ —

† Linux has no platform 2D tier: no Graphics::Context, Font, TextInput, image codecs, menus or tray. Everything drawn through a GPUView — the UI component tier, its text, its images and SVGComponent — works there in full, and a Window is a real Wayland or X11 window with input, clipboard, fractional scaling and mouse lock. Audio-plugin hosting has what it needs (EmbeddedView into a host's X11 window, and an event loop a host's own loop can pump), and EACP_HEADLESS=1 runs every window and every GPU test with no display server at all. Docs/Linux.md is the full account.

‡ Keys and mouse are fed by the GameController framework on Apple platforms, which times events off the main thread, and by the window's own events elsewhere; gamepads come from GameController on Apple and XInput on Windows.

CI builds and tests macOS (universal), Windows x64 and ARM64 (MSVC and clang-cl) and Linux (GCC, Clang, and a lane that runs the graphics stack on Mesa's software Vulkan under a headless Weston and then an Xvfb), and builds iOS for the simulator and Android (Vulkan 1.1; the HelloGPU and HelloNetwork examples for arm64-v8a with a pinned NDK; the second checks the HTTP client and server, downloads, OnlineResource and both WebSocket ends when run on a device). macOS is the most exercised of them. On Android, Camera and CameraView work as well, with the camera permission asked for at run time (eacp::Android::requestPermission); Video and VideoView are not available there yet. Android builds through an Android Studio project the configure writes; Apps/Android/README.md is the guide.

A taste of the API

A minimal console app with a recurring timer:

#include <eacp/Core/Core.h>

using namespace eacp;

struct App
{
    void update()
    {
        LOG(numTimes);

        numTimes++;

        if (numTimes == 4)
            Apps::quit();
    }

    int numTimes = 0;
    Threads::Timer timer {[&] { update(); }, 1};
};

int main()
{
    return Apps::run<App>();
}

A GUI app embedding a web view:

#include <eacp/WebView/WebView.h>

using namespace eacp;
using namespace Graphics;

WindowOptions windowOptions()
{
    auto options = WindowOptions {};
    options.title = "Browser";
    options.width = 1100;
    options.height = 760;
    return options;
}

// A Window built with its view adopts it as its content, so the pair is two
// members and the constructor body is left for what the app actually does.
struct MyApp
{
    MyApp() { webView.loadURL("https://example.com"); }

    WebView webView;
    Window window {webView, windowOptions()};
};

int main()
{
    return eacp::Apps::run<MyApp>();
}

An app that is one view in one window and nothing else needs no struct at all: Graphics::runWindowedApp<MyView>(options, viewArgs...) runs a ViewWindow<MyView> — the view built from viewArgs, then the window showing it — as the app.

#include <eacp/Graphics/Graphics.h>

using namespace eacp;

struct HelloView final : Graphics::View
{
    void paint(Graphics::Context& g) override
    {
        g.setColor(Graphics::Color::white());
        g.fillRect(getLocalBounds());
    }
};

int main()
{
    auto options = Graphics::WindowOptions {};
    options.title = "Hello";

    return Graphics::runWindowedApp<HelloView>(options);
}

An HTTP request:

#include <eacp/Network/HTTP/Http.h>

auto req = eacp::HTTP::Request::post("https://api.example.com/posts", body);
req.headers["Content-Type"] = "application/json";
auto res = req.perform();

Redirects are followed unless req.followRedirects = false, which hands back the 3xx itself and sends nothing to the new address; req.maxRedirects caps how many are followed before the request fails. req.maxResponseSize caps the body: a response that grows past it is cut off as it arrives and comes back as an error.

req.timeout bounds the whole request. req.connectTimeout bounds getting connected (on Apple platforms, which report no connect event, it runs until the response headers arrive), and req.idleTimeout fails a transfer that goes quiet for that long without capping how long a steady one may run - the way to catch a stalled download of any size. No backend answers from a local cache unless req.cachePolicy = HTTP::CachePolicy::UseLocalCache.

A request sends the platform's own User-Agent - CFNetwork's on Apple platforms, HttpURLConnection's on Android - and eacp/<version> (HTTP::eacpUserAgent()) on Windows and Linux, which have none. HTTP::setDefaultUserAgent("MyApp/1.2") replaces that for every request on every platform, and an empty string puts it back; a User-Agent header on the request beats both:

eacp::HTTP::setDefaultUserAgent("MyApp/1.2");

auto req = eacp::HTTP::Request("https://api.example.com/");
req.headers["User-Agent"] = "MyApp/1.2 (sync)"; // this request only

req.resumeDownload makes downloadTo continue a partial file: it asks for the bytes it lacks with a Range header, appends a 206 and counts the bytes already there in its progress, and starts over when the server sends the whole file instead.

HTTP::URL builds an address from a base and an ordered list of query parameters, encoding each once:

auto url = eacp::HTTP::URL("https://api.example.com/search");
url.addParameter("q", "rock & roll").addParameter("page", "2");
auto res = eacp::HTTP::Request(url.toString()).perform();

Saving a file under a name the user typed, without overwriting anything:

using namespace eacp;

auto folder = FilePath::documentsDirectory() / "Presets";
auto name = Files::legalFileName("Kick: 808 / Hard"); // "Kick 808  Hard"
auto path = Files::nonexistentSibling((folder / name).withExtension("preset"));

Files::writeFileAtomically(path, bytes);

auto options = Files::DirectoryOptions {};
options.pattern = "*.wav; *.flac";
auto samples = Files::listFiles(FilePath::musicDirectory(), options);

More examples live under Apps/, grouped by the module they exercise: Console, Network, Graphics, GPU, UI, SVG, WebView, Camera, Video, Plugins and Mixed.

Shaders in C++

There is no shader string anywhere in an eacp app. A shader is a struct that derives from ShaderProgram; define() records a graph of typed value handles, and the emitters turn that one graph into Metal Shading Language for macOS and iOS, into HLSL for Direct3D 12 on Windows and into GLSL for Vulkan on Linux, where glslang compiles it to SPIR-V inside the binary. Vertex inputs are pulled straight out of the CPU vertex struct, so that struct is the vertex layout; uniforms and textures are typed members assigned by name, and Maths::Vec2 crosses to the GPU packed exactly as the float2 it registers as.

#include <eacp/GPU/GPU.h>

using namespace eacp;
using namespace GPU;

struct Vertex
{
    Maths::Vec2 position;
    Maths::Vec2 uv;
};

struct Waves final : ShaderProgram
{
    Waves() { compile(); }

    void define() override
    {
        auto position = vertexInput(&Vertex::position);
        auto uv = varying(vertexInput(&Vertex::uv));

        auto ripple = 0.5f + 0.5f * sin(uv.x() * 8.f + time);

        setPosition(float4(position, 0.f, 1.f));
        auto texel = sample(image, uv);
        setFragment(texel * float4(ripple, uv.y(), 1.f, 1.f));
    }

    Uniform<Float> time;
    Uniform<Texture2D> image;

    EACP_SHADER(time, image)
};

The view that draws it is as portable as the shader. setVertices uploads the typed vertex array, prepare takes a RenderPipelineDescriptor — sample count, depth test and write, blend equation, cull mode, winding — and builds the pipeline state from it, and pass.draw(shader) binds the pipeline, the vertices, the uniform block and every assigned texture:

struct WavesView final : GPUView
{
    WavesView()
        : image(loadTexture())
    {
        shader.setVertices(quad);
        shader.prepare({.sampleCount = sampleCount(),
                        .blendMode = BlendMode::AlphaBlend});
        shader.image = image;
        setContinuous(true);
    }

    void update(Threads::FrameTime time) override
    {
        elapsed += static_cast<float>(time.delta);
    }

    void render(Frame& frame) override
    {
        shader.time = elapsed;

        auto pass = frame.beginPass({});
        pass.draw(shader);
    }

    Texture image;
    Waves shader;
    float elapsed = 0.f;
};

Render targets, depth and multisampling, and compute passes all sit on the same Frame, ordered for you, with no fences to write.

The EDSL covers the Float, Int, UInt and Bool families and the matrices, every swizzle, the intrinsic set spelled the way MSL and HLSL spell it, var / select / ifThen / loop, 2D, cube and depth textures, storage buffers readable from either stage, instancing, and compute — ComputeProgram is the same struct shape, with atomics, threadgroup memory, barriers, SIMD-group matrix fragments, packed fp16, bf16, int8 and int4 weight reads and a dispatch the GPU sized. What the backends cannot pack the same way — a Bool or a Float3x3 uniform — is a static_assert rather than a footnote. Lib/eacp/GPU/README.md is the full account, and Apps/GPU has a worked example of every piece.

The graph a kernel records is also what runs it where there is no device. The CPU compute interpreter runs a ComputeProgram on the calling thread over plain arrays, allocation-free and realtime-safe, so an audio callback or a driverless Linux box runs the very same kernel the GPU does, and every emitter test has a numeric half that needs no driver — Apps/GPU/CpuCompute times the two against each other. One tier up, Lib/eacp/ML takes tensor-level compute written against the same buffers and compiles it to a Core ML program, so a net that runs through the compute kernels on every platform also runs on the Apple Neural Engine where there is one (see Lib/eacp/ML/README.md).

Building

eacp uses CMake (3.31+) and a C++20 toolchain. Dependencies are fetched via CPM automatically at configure time — among them ESIMD, the SIMD kernels the image operations run through — except miniz, which is carried in ThirdParty/ and wrapped by eacp::Zip, and fmt, carried beside it and used internally.

cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build

Build a specific example:

cmake --build build --target GUI       # build/Apps/GUI/GUI.app
cmake --build build --target Console   # build/Apps/Console/Console

On Linux the window systems, the text stack and the HTTP client are found by pkg-config against the machine's own libraries, and the Vulkan loader is opened at runtime, so a driver is all the GPU half needs:

sudo apt install pkg-config libcurl4-openssl-dev \
    libwayland-dev wayland-protocols libwayland-bin libxkbcommon-dev libdecor-0-dev \
    libxcb1-dev libxcb-xkb-dev libxkbcommon-x11-dev libxcb-randr0-dev \
    libxcb-xfixes0-dev libxcb-cursor-dev libxcb-icccm4-dev libxcb-xinput-dev \
    libfreetype-dev libharfbuzz-dev libfontconfig-dev mesa-vulkan-drivers

The options most builds reach for:

  • EACP_UNITY_BUILD (default OFF) — unity builds, markedly faster for a cold full build, off by default because they collapse the per-file entries in compile_commands.json that language servers read.
  • EACP_BUILD_GRAPHICS (default ON) — turn it off to build only the portable modules, on any platform.
  • EACP_BUILD_SPIRV (default ON on Linux, OFF elsewhere) — the GLSL compiler the Vulkan backend ships. Passing ON on a Mac or a Windows machine turns the GLSL compile checks in the GPU test suites on.

Every option, the CPM and pkg-config dependencies, the capability variables that decide which modules a platform gets, and how CI runs are in Docs/Build.md.

Repository layout

Each subdirectory of Lib/eacp is a self-contained area you can include on its own — take Network without pulling in GPU.

Lib/eacp/
  Core/       App lifecycle, threading, processes, plugins, files, vector maths,
              ObjC/CF interop
  Network/    HTTP client and server, WebSocket client, TCP, IPC, RPC
  Graphics/   Windows, views, widgets, menus, drawing primitives
  GPU/        Metal / D3D12 / Vulkan: device, buffers, textures, pipelines,
              passes, the shader EDSL and its CPU interpreter — see GPU/README.md
  GPUWidgets/ Views drawn on the GPU (gradients, paths)
  ML/         Tensor graphs compiled and run through Core ML — see ML/README.md
  Text/       Font metrics, glyph rasterization and a GPU glyph atlas
  Sprites/    Batched textured-quad renderer
  UI/         A whole widget tree in one GPUView, GPU components included —
              see UI/README.md
  SVG/        SVG parsing and rendering
  WebView/    System web view embedding
  Camera/     Capture devices and frames, plus CameraView/ to show them
  Video/      Screen capture and encoding, plus VideoView/ for playback
Apps/         Example applications
Tests/        Unit tests
Docs/         In-depth documentation: the build system and the Linux backend
ThirdParty/   Vendored libraries (miniz, behind eacp::Zip; fmt)
CMake/        Build helpers (TargetSetup, CPM)

Documentation

  • Docs/Build.md — every build option, the dependencies, the capability variables and the CI matrix.
  • Docs/Linux.md — the Linux backend: Wayland, X11, Vulkan and FreeType, what is and is not there, and the environment variables that drive it.
  • Lib/eacp/GPU/README.md — the GPU module and the shader EDSL end to end: pipelines, render targets, compute, packed weights, the SIMD-group matrix, running a kernel on the CPU, and the Vulkan backend. SAMPLERS.md beside it is the sampler model.
  • Lib/eacp/UI/README.md — the component tier: how a frame is built, and GPUComponent, custom GPU rendering inside a component tree, with what it costs on and off screen.
  • Lib/eacp/ML/README.md — the Core ML tier: building a tensor graph, running a model, the compile cache and what was measured.
  • Apps/GPU/VariableFont/README.md — the variable-font example.

Built with eacp

Four projects lean on different parts of the framework, and between them are what keeps it honest — each one is a demand the API has to meet, and the gaps they surface are what gets fixed next.

  • PureDOOM — DOOM on eacp's application, GPU and input stack. The level is drawn as real hardware 3D at the window's resolution rather than at 320×200, but the shading is DOOM's own: the texture yields a palette index, the COLORMAP row picked by sector light and distance remaps it, and the palette resolves the colour. Three attract-mode demos, 11,410 tics, replay as a test that hashes the world after every tic.

  • ShaderToyEACP — Shadertoy's GLSL turned into eacp GPU programs, shaders authored as C++ structs and compiled to Metal and HLSL from one source. A corpus of two hundred real fragment shaders turns "what is the shader EDSL missing?" into a measurement: every shader that fails to convert names a gap, and the number blocked on each gap decides which one to close next.

  • imgui-eacp — Dear ImGui as an ordinary eacp View, drawn by the GPU module, with ImGui's shader written once in the EDSL and emitted as both MSL and HLSL. Because it is a real view it composes: Apps/MixedViews puts it beside a WebView in one window, wired in both directions.

  • WhisperEACP — Whisper on eacp's compute stack: the encoder and decoder as EDSL compute kernels over packed fp16 weights, running on Metal, D3D12 and Vulkan from one source, and the encoder again through Lib/eacp/ML on Core ML. It is what drove the SIMD-group matrix, the packed weight reads and the Core ML backend, and its tests are what say the two ways of running the same net agree.

  • CowTerm — a GPU-accelerated terminal emulator and session manager by Jamie Pond. Every visible pixel is composited on the GPU from a CoreText glyph atlas; sessions, a fuzzy palette and persistent pane layouts replace the tmux-sessionizer workflow.

Contributing

eacp is developed in the open. Issues, patches and experiments are all welcome; the examples under Apps/ are the fastest way in.

License

MIT — see LICENSE.

About

C++ Cross platform graphics, networking, windowing and other utilities

Resources

Stars

23 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages