Skip to main content

CrossTL: Universal Programming Language and Translator

Project description




CrossTL - Universal Shader Translator

CrossTL is a shader and compute program translator built around CrossGL — an intermediate representation (IR) language that bridges graphics APIs, GPU-compute platforms, and systems-language targets.

Supported Translation Targets

CrossTL provides bidirectional translation between CrossGL and:

  • Metal - Apple's graphics and compute API
  • DirectX (HLSL) - Microsoft's graphics API
  • OpenGL (GLSL) - Cross-platform graphics
  • Vulkan (SPIRV) - High-performance graphics and compute
  • CUDA - NVIDIA parallel computing
  • HIP - AMD GPU computing
  • Rust - GPU-oriented Rust shader code
  • Mojo - Mojo shader/compute modules
  • Slang - Real-time shading language
  • CrossGL (.cgl) - The IR/interchange format itself

Backend Readiness: DirectX / Metal / OpenGL

We maintain first-class, bidirectional support for the three cornerstone graphics APIs. Each backend is implemented as both a source (parse/import) and codegen (export) target, so you can round‑trip between native shaders and CrossGL without lossy hops.

  • DirectX / HLSL
    • Pipeline coverage: vertex, fragment/pixel, compute, geometry, hull/domain (tessellation), mesh/task, full ray‑tracing stages.
    • Resource model: cbuffers, register/space bindings, UAV/RW textures & buffers, structured buffers, Interlocked atomics, wave ops, texture/buffer dimension queries.
    • Semantics map to SV_* and user semantics, preserved through CrossGL attributes.
  • Metal
    • Stages: vertex, fragment, compute, mesh/object, and ray‑tracing qualifiers.
    • Resource/binding support: argument buffers, [[buffer]], [[texture]], [[sampler]], function constants, packed/simd types, indirect command buffers, payload/hit attributes.
    • Texture methods (sample/load/store/gather/compare) and threadgroup builtins are translated to CrossGL intrinsics.
  • OpenGL / GLSL
    • Stages: vertex, fragment, compute with version inference (defaults to #version 450 core when absent).
    • Handles interface blocks, layouts/bindings, sampler & image operations, control flow, structs/arrays, discard, builtins (gl_*) and preprocessor directives.
  • Round‑trips verified via converters in crosstl.backend.{DirectX,Metal,GLSL} and registered in translator.source_registry and translator.codegen.registry.

How we validate backend parity

  • Targeted unit suites exercise the full feature surface for these three backends (shader stages, bindings, intrinsics, control‑flow, resources, and preprocessor handling).
  • Quick check: run pytest tests/test_backend/test_directx tests/test_backend/test_metal tests/test_backend/test_GLSL -q (currently 321 passing tests).
  • End‑to‑end translation: translate native HLSL/Metal/GLSL → CrossGL → HLSL/Metal/GLSL/Vulkan to ensure attributes, layouts, and semantics survive round‑trips.

Translation Architecture

CrossTL uses a multi-stage translation pipeline:

  1. Lexical Analysis: Tokenization
  2. Syntax Analysis: AST generation
  3. Semantic Analysis: Type checking and scope resolution
  4. IR Generation: Conversion to CrossGL intermediate representation
  5. Target Generation: Backend-specific code generation

CrossGL Programming Language Examples

PBR Shader Example

// Physically-based rendering shader
struct Material {
    albedo: vec3,
    metallic: float,
    roughness: float,
    normal_map: texture2d,
    displacement: texture2d
}

struct Lighting {
    position: vec3,
    color: vec3,
    intensity: float,
    attenuation: vec3
}

shader PBRShader {
    vertex {
        input vec3 position;
        input vec3 normal;
        input vec2 texCoord;
        input vec4 tangent;

        uniform mat4 modelMatrix;
        uniform mat4 viewMatrix;
        uniform mat4 projectionMatrix;

        output vec3 worldPos;
        output vec3 worldNormal;
        output vec2 uv;
        output mat3 TBN;

        void main() {
            vec4 worldPosition = modelMatrix * vec4(position, 1.0);
            worldPos = worldPosition.xyz;

            vec3 T = normalize(vec3(modelMatrix * vec4(tangent.xyz, 0.0)));
            vec3 N = normalize(vec3(modelMatrix * vec4(normal, 0.0)));
            vec3 B = cross(N, T) * tangent.w;
            TBN = mat3(T, B, N);

            worldNormal = N;
            uv = texCoord;

            gl_Position = projectionMatrix * viewMatrix * worldPosition;
        }
    }

    fragment {
        input vec3 worldPos;
        input vec3 worldNormal;
        input vec2 uv;
        input mat3 TBN;

        uniform Material material;
        uniform Lighting lights[8];
        uniform int lightCount;
        uniform vec3 cameraPos;

        output vec4 fragColor;

        vec3 calculatePBR(vec3 albedo, float metallic, float roughness,
                         vec3 normal, vec3 viewDir, vec3 lightDir, vec3 lightColor) {
            vec3 halfVector = normalize(viewDir + lightDir);
            float NdotV = max(dot(normal, viewDir), 0.0);
            float NdotL = max(dot(normal, lightDir), 0.0);
            float NdotH = max(dot(normal, halfVector), 0.0);
            float VdotH = max(dot(viewDir, halfVector), 0.0);

            vec3 F0 = mix(vec3(0.04), albedo, metallic);
            vec3 F = F0 + (1.0 - F0) * pow(1.0 - VdotH, 5.0);

            float alpha = roughness * roughness;
            float alpha2 = alpha * alpha;
            float denom = NdotH * NdotH * (alpha2 - 1.0) + 1.0;
            float D = alpha2 / (3.14159 * denom * denom);

            float G = geometrySmith(NdotV, NdotL, roughness);

            vec3 numerator = D * G * F;
            float denominator = 4.0 * NdotV * NdotL + 0.001;
            vec3 specular = numerator / denominator;

            vec3 kS = F;
            vec3 kD = vec3(1.0) - kS;
            kD *= 1.0 - metallic;

            return (kD * albedo / 3.14159 + specular) * lightColor * NdotL;
        }

        void main() {
            vec3 normal = normalize(TBN * (texture(material.normal_map, uv).rgb * 2.0 - 1.0));
            vec3 viewDir = normalize(cameraPos - worldPos);

            vec3 color = vec3(0.0);

            for (int i = 0; i < lightCount; ++i) {
                vec3 lightDir = normalize(lights[i].position - worldPos);
                float distance = length(lights[i].position - worldPos);
                float attenuation = 1.0 / (lights[i].attenuation.x +
                                          lights[i].attenuation.y * distance +
                                          lights[i].attenuation.z * distance * distance);

                vec3 lightColor = lights[i].color * lights[i].intensity * attenuation;
                color += calculatePBR(material.albedo, material.metallic,
                                     material.roughness, normal, viewDir, lightDir, lightColor);
            }

            color += material.albedo * 0.03;

            color = color / (color + vec3(1.0));
            color = pow(color, vec3(1.0/2.2));

            fragColor = vec4(color, 1.0);
        }
    }
}

Backend Compatibility

All backends support the core CrossGL language (structs, arrays, functions, control flow, texture sampling, shader stages). Some advanced language features have partial backend coverage:

Feature Not yet supported in
Generic functions SPIR-V, CUDA, HIP, Mojo, Slang (pending monomorphization)
Geometry stage Metal, CUDA, HIP, Mojo, Rust (diagnostic fallback)
Tessellation stage OpenGL, Metal, CUDA, HIP, Mojo, Rust (diagnostic fallback)
Mesh/Task stage CUDA, HIP, Mojo, Rust, Slang (diagnostic fallback)

Translation calls targeting unsupported feature/backend combinations raise a clear error with a reason string.

Getting Started

Install CrossTL:

pip install crosstl

Basic Usage

1. Create a CrossGL shader

shader SimpleShader {
    vertex {
        input vec3 position;
        uniform mat4 modelViewProjection;
        output vec4 fragPosition;

        void main() {
            fragPosition = modelViewProjection * vec4(position, 1.0);
        }
    }

    fragment {
        input vec4 fragPosition;
        output vec4 fragColor;

        void main() {
            fragColor = vec4(1.0, 0.0, 0.0, 1.0);
        }
    }
}

2. Translate to a target

import crosstl

# Translate to any supported backend
metal_code = crosstl.translate('shader.cgl', backend='metal', save_shader='shader.metal')
hlsl_code = crosstl.translate('shader.cgl', backend='directx', save_shader='shader.hlsl')
glsl_code = crosstl.translate('shader.cgl', backend='opengl', save_shader='shader.glsl')

Reverse Translation - Import Existing Code

# Import existing shaders into CrossGL
conversions = [
    ('existing_shader.hlsl', 'unified.cgl'),     # DirectX to CrossGL
    ('gpu_kernel.cu', 'unified.cgl'),            # CUDA to CrossGL
    ('graphics.metal', 'unified.cgl'),           # Metal to CrossGL
]

for source, target in conversions:
    unified_code = crosstl.translate(source, backend='cgl', save_shader=target)
    print(f"Unified {source} into CrossGL: {target}")

Deploying to All Backends

import crosstl
from pathlib import Path

program = 'universal_shader.cgl'

deployment_targets = {
    'metal': '.metal',
    'directx': '.hlsl',
    'opengl': '.glsl',
    'vulkan': '.spvasm',
    'rust': '.rs',
    'mojo': '.mojo',
    'cuda': '.cu',
    'hip': '.hip',
    'slang': '.slang',
}

stem = Path(program).stem
for backend, extension in deployment_targets.items():
    output_file = f'deployments/{stem}_{backend}{extension}'
    try:
        crosstl.translate(program, backend=backend, save_shader=output_file)
        print(f"{backend}: {output_file}")
    except Exception as e:
        print(f"{backend}: {str(e)}")

For comprehensive language documentation, visit our Language Reference.

Contributing

CrossGL is a community-driven project. Find out more in our Contributing Guide.

Community

License

CrossTL is open-source and licensed under the Apache License 2.0.


The CrossGL Team

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

crosstl-2.0.0.tar.gz (1.2 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

crosstl-2.0.0-py3-none-any.whl (1.2 MB view details)

Uploaded Python 3

File details

Details for the file crosstl-2.0.0.tar.gz.

File metadata

  • Download URL: crosstl-2.0.0.tar.gz
  • Upload date:
  • Size: 1.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for crosstl-2.0.0.tar.gz
Algorithm Hash digest
SHA256 d43103d598facd200714a77c1f81818afc0e8cc9a2a723e43f57b3cd1928e7cd
MD5 c5eda7329506d7dc76d4da3f1a67a6e2
BLAKE2b-256 31365c00d52ef9fed136f7d703cb9aa48b6eae13143ee2b331acf4cf72348491

See more details on using hashes here.

File details

Details for the file crosstl-2.0.0-py3-none-any.whl.

File metadata

  • Download URL: crosstl-2.0.0-py3-none-any.whl
  • Upload date:
  • Size: 1.2 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for crosstl-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f933680d3e7101ca9ab0977279dd5c95c7ca910a232d075db4668cbbfc486d8a
MD5 53037485658f3ca0fcbb9a44a0ff255d
BLAKE2b-256 669f290904f74d32c00e3b39476b1f9ac114eeb141cb1413f4d290d37c667b63

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page