https://github.com/rusoleal/campello_gpu
C++ low level multiplatform graphics library.
https://github.com/rusoleal/campello_gpu
android directx12 graphics-rendering ios linux macos metal vulkan windows
Last synced: 2 months ago
JSON representation
C++ low level multiplatform graphics library.
- Host: GitHub
- URL: https://github.com/rusoleal/campello_gpu
- Owner: rusoleal
- License: mit
- Created: 2025-06-13T19:53:35.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2026-04-19T07:57:13.000Z (2 months ago)
- Last Synced: 2026-04-19T09:30:05.410Z (2 months ago)
- Topics: android, directx12, graphics-rendering, ios, linux, macos, metal, vulkan, windows
- Language: C++
- Homepage:
- Size: 852 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# campello_gpu
A cross-platform GPU abstraction library (C++20) with a WebGPU-inspired API. Provides a unified interface over Metal, Vulkan, and DirectX 12 for rendering, compute and raytracing workloads.
## 🚀 Part of the Campello Engine
This project is a module within the **Campello** ecosystem.
👉 Main repository: https://github.com/rusoleal/campello
Campello is a modular, composable game engine built as a collection of independent libraries.
Each module is designed to work standalone, but integrates seamlessly into the engine runtime.
## Platforms
| OS | Engine | Status |
|----|--------|--------|
| android | Vulkan 1.x | Production ready |
| macos/ios | Metal | Production ready |
| windows | DirectX 12 | Production ready |
| windows | DirectX 11 | Frozen |
| windows | OpenGL | Frozen |
| windows | Vulkan 1.x | Not started |
| macos/ios | OpenGL | Frozen |
| macos/ios | Vulkan 1.x | Not started |
| android | OpenGL | Frozen |
| linux | Vulkan 1.x | Production ready (headless) |
| linux | OpenGL | Frozen |
## Requirements
- CMake 3.22.1+
- C++20 compiler
- Platform SDK: Metal (macOS/iOS), Vulkan (Android), DirectX 12 (Windows)
## Build
```bash
cmake -B build
make -C build
```
CMake automatically selects the backend based on the target platform (`android.cmake`, `macos.cmake`, `windows.cmake`, etc.).
## Integration
### find_package (installed library)
```bash
cmake -B build -DCMAKE_INSTALL_PREFIX=/usr/local
cmake --install build
```
```cmake
find_package(campello_gpu REQUIRED)
target_link_libraries(your_target PRIVATE campello_gpu::campello_gpu)
```
### FetchContent
```cmake
include(FetchContent)
FetchContent_Declare(
campello_gpu
GIT_REPOSITORY https://github.com/rusoleal/campello_gpu.git
GIT_TAG main
)
FetchContent_MakeAvailable(campello_gpu)
target_link_libraries(your_target PRIVATE campello_gpu::campello_gpu)
```
## Usage
All types are in the `systems::leal::campello_gpu` namespace.
```cpp
#include
using namespace systems::leal::campello_gpu;
// Create a device (pd is the platform window/surface handle)
auto device = Device::createDefaultDevice(pd);
// Or enumerate adapters and pick one
auto adapters = Device::getAdapters();
auto device = Device::createDevice(adapters[0], pd);
// Query device info
std::string name = device->getName();
std::set features = device->getFeatures();
std::string version = Device::getEngineVersion();
```
### Resource Creation
```cpp
// Buffers
auto buffer = device->createBuffer(size, BufferUsage::vertex | BufferUsage::copySrc);
auto buffer = device->createBuffer(size, BufferUsage::uniform, data);
// Readback buffer (GPU → CPU)
auto readbackBuffer = device->createBuffer(size, BufferUsage::copyDst | BufferUsage::mapRead);
// Textures
auto texture = device->createTexture(TextureType::_2D, PixelFormat::RGBA8Unorm,
width, height, 1, mipLevels, 1,
TextureUsage::textureBinding | TextureUsage::renderTarget);
// Shaders (compiled bytecode/binary)
auto shader = device->createShaderModule(buffer, size);
```
### Pipelines & Binding
```cpp
// Layout describes shader resource binding points
auto bindGroupLayout = device->createBindGroupLayout(bindGroupLayoutDescriptor);
auto pipelineLayout = device->createPipelineLayout(pipelineLayoutDescriptor);
// Render pipeline
auto pipeline = device->createRenderPipeline(renderPipelineDescriptor);
// Compute pipeline
auto pipeline = device->createComputePipeline(computePipelineDescriptor);
// Bind concrete resources to a layout
auto bindGroup = device->createBindGroup(bindGroupDescriptor);
```
### Command Recording & Submission
```cpp
auto encoder = device->createCommandEncoder();
// Render pass
auto renderPass = encoder->beginRenderPass(beginRenderPassDescriptor);
renderPass->setPipeline(pipeline);
renderPass->setVertexBuffer(0, vertexBuffer, 0);
renderPass->setBindGroup(0, bindGroup, {});
renderPass->draw(vertexCount, instanceCount, 0, 0);
renderPass->end();
// Compute pass
auto computePass = encoder->beginComputePass();
computePass->setPipeline(computePipeline);
computePass->setBindGroup(0, bindGroup, {});
computePass->dispatchWorkgroups(x, y, z);
computePass->end();
auto commandBuffer = encoder->finish();
device->submit(commandBuffer);
```
### Ray Tracing
Hardware ray tracing is available when `Feature::raytracing` is reported by the device (Vulkan KHR on Android, Metal on macOS/iOS, DXR on Windows).
```cpp
#include
#include
#include
#include
#include
#include
#include
#include
// Feature check
auto features = device->getFeatures();
if (!features.count(Feature::raytracing)) return; // not supported
// 1. Vertex buffer for triangle geometry
auto vertexBuffer = device->createBuffer(sizeof(vertices),
BufferUsage::accelerationStructureInput, vertices);
// 2. Bottom-level acceleration structure (BLAS)
AccelerationStructureGeometryDescriptor geoDesc{};
geoDesc.type = AccelerationStructureGeometryType::triangles;
geoDesc.opaque = true;
geoDesc.vertexBuffer = vertexBuffer;
geoDesc.vertexStride = sizeof(float) * 3;
geoDesc.vertexCount = 3;
BottomLevelAccelerationStructureDescriptor blasDesc{};
blasDesc.geometries = { geoDesc };
blasDesc.buildFlags = AccelerationStructureBuildFlag::preferFastTrace;
auto blas = device->createBottomLevelAccelerationStructure(blasDesc);
auto scratch = device->createBuffer(blas->getBuildScratchSize(), BufferUsage::storage);
auto encoder = device->createCommandEncoder();
encoder->buildAccelerationStructure(blas, blasDesc, scratch);
device->submit(encoder->finish());
// 3. Top-level acceleration structure (TLAS)
AccelerationStructureInstance instance{};
instance.blas = blas;
instance.mask = 0xFF;
TopLevelAccelerationStructureDescriptor tlasDesc{};
tlasDesc.instances = { instance };
tlasDesc.buildFlags = AccelerationStructureBuildFlag::preferFastTrace;
auto tlas = device->createTopLevelAccelerationStructure(tlasDesc);
auto scratch2 = device->createBuffer(tlas->getBuildScratchSize(), BufferUsage::storage);
encoder = device->createCommandEncoder();
encoder->buildAccelerationStructure(tlas, tlasDesc, scratch2);
device->submit(encoder->finish());
// 4. Ray tracing pipeline
RayTracingPipelineDescriptor rtDesc{};
rtDesc.rayGeneration = { shaderModule, "rayGenMain" };
rtDesc.layout = pipelineLayout;
rtDesc.maxRecursionDepth = 1;
auto pipeline = device->createRayTracingPipeline(rtDesc);
// 5. Dispatch rays each frame
auto rtPass = encoder->beginRayTracingPass();
rtPass->setPipeline(pipeline);
rtPass->setBindGroup(0, bindGroup, {}, 0, 0);
rtPass->traceRays(width, height, 1);
rtPass->end();
device->submit(encoder->finish());
```
### GPU → CPU Readback
Copy texture data to a buffer and read it on the CPU:
```cpp
// Create a readback buffer
auto readbackBuffer = device->createBuffer(
textureSize,
BufferUsage::copyDst | BufferUsage::mapRead);
// Copy texture to buffer
auto encoder = device->createCommandEncoder();
encoder->copyTextureToBuffer(texture, mipLevel, arrayLayer,
readbackBuffer, offset, bytesPerRow);
device->submit(encoder->finish());
// Read data on CPU
std::vector data(textureSize);
readbackBuffer->download(0, textureSize, data.data());
```
Or use the convenience method for synchronous texture readback:
```cpp
// One-liner: creates temp buffer, submits command, waits, copies data
std::vector pixels(width * height * 4);
texture->download(mipLevel, arrayLayer, pixels.data(), pixels.size());
```
### Observability & Metrics
Comprehensive profiling and memory monitoring across all backends:
```cpp
#include
// --- Resource counters (Phase 1) ---
ResourceCounters counters = device->getResourceCounters();
std::cout << "Buffers: " << counters.bufferCount << "\n";
std::cout << "Textures: " << counters.textureCount << "\n";
std::cout << "Render pipelines: " << counters.renderPipelineCount << "\n";
CommandStats stats = device->getCommandStats();
std::cout << "Draw calls: " << stats.drawCalls << "\n";
std::cout << "Compute dispatches: " << stats.dispatchCalls << "\n";
// Complete snapshot
Metrics m = device->getMetrics();
// --- Memory tracking (Phase 2) ---
ResourceMemoryStats mem = device->getResourceMemoryStats();
std::cout << "GPU memory used: " << mem.totalTrackedBytes / (1024*1024) << " MB\n";
std::cout << " Buffers: " << mem.bufferBytes / (1024*1024) << " MB\n";
std::cout << " Textures: " << mem.textureBytes / (1024*1024) << " MB\n";
std::cout << "Peak memory: " << mem.peakTotalBytes / (1024*1024) << " MB\n";
// Reset peak tracking for a new measurement period
device->resetPeakMemoryStats();
// --- GPU timing (Phase 3) ---
auto encoder = device->createCommandEncoder();
// ... record commands ...
auto cmdBuffer = encoder->finish();
device->submit(cmdBuffer);
// Get actual GPU execution time (nanoseconds)
uint64_t gpuTimeNs = cmdBuffer->getGPUExecutionTime();
std::cout << "GPU time: " << (gpuTimeNs / 1e6) << " ms\n";
// Accumulated pass performance stats
PassPerformanceStats perf = device->getPassPerformanceStats();
std::cout << "Render pass GPU time: " << perf.renderPassTimeNs / 1e6 << " ms\n";
// --- Memory pressure management ---
// Configure budget thresholds
MemoryBudget budget;
budget.warningThresholdPercent = 75; // 75% of available memory
budget.criticalThresholdPercent = 90; // 90% of available memory
device->setMemoryBudget(budget);
// Register callback for pressure changes
device->setMemoryPressureCallback([](MemoryPressureLevel level, const ResourceMemoryStats& stats) {
switch (level) {
case MemoryPressureLevel::Warning:
std::cerr << "Memory warning: " << stats.totalTrackedBytes / (1024*1024) << " MB\n";
break;
case MemoryPressureLevel::Critical:
std::cerr << "Memory critical! Consider freeing resources\n";
break;
default:
break;
}
});
// Check current pressure level
MemoryPressureLevel level = device->checkMemoryPressure();
```