Skip to content

Elemental is a portable low-level game platform library that targets only next-gen features. It is exposed as a simple C header and other language bindings also exist.

License

double-buffer/elemental

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

17 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Elemental License GitHub Repo stars GitHub Release Downloads GitHub Actions Workflow Status

Elemental is a portable low-level game platform library that targets only next-gen features. It is exposed as a simple C header and other language bindings also exist.

It is a great choice if you want to:

  • Create your own game engine.
  • Learn game programming.
  • Do graphics research projects with the latest available tech.

Warning

The project is currently in heavy development. API may have a lot of breaking changes until the first release.

πŸ“– Purpose

Elemental aims to simplify and standardize game development across different platforms by offering a unified API.

Back in the days of DirectX11 and OpenGL, it was very easy to start working on its own game or custom engine that took full advantage of the GPU. Newer apis give more control to the developpers but they are hard to use. They also provide many ways of doing things because they must maintain backward compatibility.

Elemental bridges this gap by focusing on powerful, next-gen features without the overhead of backward compatibility. This approach not only facilitates learning and using graphics programming as in earlier times but also gears developers up for future advancements without compromising on performance.

It doesn't mean that it will simplify or abstract graphics development. It is a challenging topic but developers can now focus on learning graphics programming like in the old days by focusing on graphics algorithms and not on the complicated platform specific implementation details.

The library is still in heavy development stage. The goal is not to be compatible with the majority of graphics cards now but to be ready for the future.

In order to simplify the API, the library makes the following assumptions:

  • Use of Mesh shaders for the geometry pipeline: so no vertex layout setup, no geometry shaders, no vertex shaders.
  • No copy queue exposed: you need to use IO queue to copy data from disk to GPU memory. (Internally the library try to use DirectStorage, MetalIO, etc.) For dynamic gpu data that is short lived, we use GPU upload heap (rebar memory).
  • Use of bindless resources
  • No indirect commands: The future will be about work graphs.

πŸ“‹ Features

Implemented features for current version 1.0.0-DEV3:

  • Application:
    • Application Lifetime with the same code for all platforms.
    • Windows.
  • Graphics:
    • Graphics Device.
    • CommandQueues and CommandLists.
    • SwapChain.
    • Mesh shaders.
  • Tools:
    • Shader Compiler.

It currently runs on:

Platform Graphics API Tools Supported
Linux DirectX12, Vulkan βœ…
Linux Vulkan/Wayland βœ…
MacOS Metal3 βœ…
iOS Metal3

The library is exposed as a C library but other bindings will be available:

Binding Status Download
.NET WIP Not available

For shader development, the shader compiler supports the following languages and platforms:

Shader Language Target API Compilation Platforms
HLSL DirectX12 Linux Linux
HLSL Vulkan Linux Linux MacOS
HLSL Metal3 Linux MacOS

πŸš€ Getting Started

Here is a minimal application that displays a colored triangle using a mesh shader.

Minimal Triangle Application

#include "Elemental.h"
#include "ElementalTools.h"

const char* shaderSource = 
    "struct Vertex { float3 Position; float4 Color; };"
    "struct VertexOutput { float4 Position: SV_Position; float4 Color: TEXCOORD0; };"

    "static Vertex triangleVertices[] ="
    "{"
    "    { float3(-0.5, 0.5, 0.0), float4(1.0, 0.0, 0.0, 1.0) },"
    "    { float3(0.5, 0.5, 0.0), float4(0.0, 1.0, 0.0, 1.0) },"
    "    { float3(-0.5, -0.5, 0.0), float4(0.0, 0.0, 1.0, 1.0) }"
    "};"

    "[shader(\"mesh\")]"
    "[OutputTopology(\"triangle\")]"
    "[NumThreads(32, 1, 1)]"
    "void MeshMain(in uint groupThreadId : SV_GroupThreadID, out vertices VertexOutput vertices[3], out indices uint3 indices[1])"
    "{"
    "    const uint meshVertexCount = 3;"
    "    SetMeshOutputCounts(meshVertexCount, 1);"

    "    if (groupThreadId < meshVertexCount)"
    "    {"
    "        vertices[groupThreadId].Position = float4(triangleVertices[groupThreadId].Position, 1);"
    "        vertices[groupThreadId].Color = triangleVertices[groupThreadId].Color;"
    "    }"

    "    if (groupThreadId == 0)"
    "    {"
    "        indices[groupThreadId] = uint3(0, 1, 2);"
    "    }"
    "}"

    "[shader(\"pixel\")]"
    "float4 PixelMain(const VertexOutput input) : SV_Target0"
    "{"
    "    return input.Color;"
    "}";

typedef struct
{
    ElemWindow Window;
    ElemGraphicsDevice GraphicsDevice;
    ElemCommandQueue CommandQueue;
    ElemSwapChain SwapChain;
    ElemPipelineState GraphicsPipeline;
} ApplicationPayload;

void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload);

void InitSample(void* payload)
{
    ApplicationPayload* applicationPayload = (ApplicationPayload*)payload;

    applicationPayload->Window = ElemCreateWindow(NULL);

    applicationPayload->GraphicsDevice = ElemCreateGraphicsDevice(NULL);
    applicationPayload->CommandQueue= ElemCreateCommandQueue(applicationPayload->GraphicsDevice, ElemCommandQueueType_Graphics, NULL);
    applicationPayload->SwapChain= ElemCreateSwapChain(applicationPayload->CommandQueue, applicationPayload->Window, UpdateSwapChain, &(ElemSwapChainOptions) { .UpdatePayload = payload });

    ElemSwapChainInfo swapChainInfo = ElemGetSwapChainInfo(applicationPayload->SwapChain);
    ElemSystemInfo systemInfo = ElemGetSystemInfo();
    ElemGraphicsDeviceInfo graphicsDeviceInfo = ElemGetGraphicsDeviceInfo(applicationPayload->GraphicsDevice);

    ElemShaderSourceData shaderSourceData = { .ShaderLanguage = ElemShaderLanguage_Hlsl, .Data = { .Items = (uint8_t*)shaderSource, .Length = strlen(shaderSource) } };
    ElemShaderCompilationResult compilationResult = ElemCompileShaderLibrary((ElemToolsGraphicsApi)graphicsDeviceInfo.GraphicsApi, (ElemToolsPlatform)systemInfo.Platform, &shaderSourceData, NULL);

    ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, (ElemDataSpan) { .Items = compilationResult.Data.Items, .Length = compilationResult.Data.Length });

    applicationPayload->GraphicsPipeline = ElemCompileGraphicsPipelineState(applicationPayload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) {
        .ShaderLibrary = shaderLibrary,
        .MeshShaderFunction = "MeshMain",
        .PixelShaderFunction = "PixelMain",
        .TextureFormats = { .Items = (ElemTextureFormat[]) { swapChainInfo.Format }, .Length = 1 }
    });
    
    ElemFreeShaderLibrary(shaderLibrary);
}

void FreeSample(void* payload)
{
    ApplicationPayload* applicationPayload = (ApplicationPayload*)payload;

    ElemFreePipelineState(applicationPayload->GraphicsPipeline);
    ElemFreeSwapChain(applicationPayload->SwapChain);
    ElemFreeCommandQueue(applicationPayload->CommandQueue);
    ElemFreeGraphicsDevice(applicationPayload->GraphicsDevice);
}

void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload)
{
    ApplicationPayload* applicationPayload = (ApplicationPayload*)payload;

    ElemCommandList commandList = ElemGetCommandList(applicationPayload->CommandQueue, NULL); 

    ElemBeginRenderPass(commandList, &(ElemBeginRenderPassParameters) {
        .RenderTargets = 
        {
            .Items = (ElemRenderPassRenderTarget[]) {{ 
                .RenderTarget = updateParameters->BackBufferTexture, 
                .LoadAction = ElemRenderPassLoadAction_Clear 
            }},
            .Length = 1
        }
    });

    ElemBindPipelineState(commandList, applicationPayload->GraphicsPipeline);
    ElemDispatchMesh(commandList, 1, 1, 1);
    ElemEndRenderPass(commandList);

    ElemCommitCommandList(commandList);
    ElemExecuteCommandList(applicationPayload->CommandQueue, commandList, NULL);

    ElemPresentSwapChain(applicationPayload->SwapChain);
}

int main(int argc, const char* argv[]) 
{
    ElemRunApplication(&(ElemRunApplicationParameters) {
        .InitHandler = InitSample,
        .FreeHandler = FreeSample,
        .Payload = &(ApplicationPayload){}
    });
}

Tip

Please note that this code uses both Elemental and ElementalTools. In a real world app, you should compile your shaders offline.

Warning

This code will only runs on platforms that support shader compilation. (Windows, MacOS, Linux) If you want to run code on other platforms you should compile your shaders offline.

You will find detailled examples in the samples folder.

About

Elemental is a portable low-level game platform library that targets only next-gen features. It is exposed as a simple C header and other language bindings also exist.

Topics

Resources

License

Stars

Watchers

Forks